def line_offset(geojson: Feature, distance: float, unit: str = "km") -> Feature: """ Takes a linestring or multilinestring and returns a line at offset by the specified distance. :param geojson: input GeoJSON :param distance: distance to offset the line (can be of negative value) :param unit: Units in which distance to be calculated, values can be 'deg', 'rad', 'mi', 'km', default is 'km' :return: Line feature offset from the input line Example: >>> from geojson import MultiLineString, Feature >>> from turfpy.transformation import line_offset >>> ls = Feature(geometry=MultiLineString([ ... [(3.75, 9.25), (-130.95, 1.52)], ... [(23.15, -34.25), (-1.35, -4.65), (3.45, 77.95)] ... ])) >>> line_offset(ls, 2, unit='mi') """ if not geojson: raise Exception("geojson is required") if not distance: raise Exception("distance is required") type = get_type(geojson) properties = geojson.get("properties", {}) if type == "LineString": return line_offset_feature(geojson, distance, unit) elif type == "MultiLineString": coords = [] def callback_flatten_each(feature, feature_index, multi_feature_index): nonlocal coords coords.append( line_offset_feature(feature, distance, unit).geometry.coordinates) return True flatten_each(geojson, callback_flatten_each) return Feature(geometry=MultiLineString(coords), properties=properties)
def line_slice( start_pt: Point, stop_pt: Point, line: LineString, ) -> LineString: """ Takes a LineString, a start Point, and a stop Point and returns a subsection of the line in-between those points. The start & stop points don't need to fall exactly on the line. This can be useful for extracting only the part of a route between waypoints. :param start_pt: starting point :param stop_pt: stopping point :param line: line to slice :return: sliced line as LineString Feature """ if not line or get_type(line) != "LineString": raise Exception("line must be a LineString") coords = get_coords(line) start_vertex = nearest_point_on_line(line, start_pt) stop_vertex = nearest_point_on_line(line, stop_pt) if start_vertex["properties"]["index"] <= stop_vertex["properties"][ "index"]: ends = [start_vertex, stop_vertex] else: ends = [stop_vertex, start_vertex] clip_coords = [get_coord(ends[0])] clip_coords.extend(coords[ends[0]["properties"]["index"] + 1:ends[1]["properties"]["index"] + 1]) clip_coords.append(get_coord(ends[1])) return Feature(geometry=LineString(clip_coords), properties=line["properties"].copy())
def scale(feature, factor, origin): is_point = get_type(feature) == "Point" origin = define_origin(feature, origin) if factor == 1 or is_point: return feature def _callback_coord_each( coord, coord_index, feature_index, multi_feature_index, geometry_index ): nonlocal factor, origin original_distance = rhumb_distance(GeoPoint(origin), GeoPoint(coord)) bearing = rhumb_bearing(GeoPoint(origin), GeoPoint(coord)) new_distance = original_distance * factor new_coord = get_coord(rhumb_destination(GeoPoint(origin), new_distance, bearing)) coord[0] = new_coord[0] coord[1] = new_coord[1] if len(coord) == 3: coord[2] = coord[2] * factor coord_each(feature, _callback_coord_each) return feature
def polygon_tangents(point, polygon): """ Finds the tangents of a (Multi)Polygon from a Point. :param point: Point or Point Feature. :param polygon: (Multi)Polygon or (Multi)Polygon Feature. :return: FeatureCollection of two tangent Point Feature. Example: >>> from turfpy.measurement import polygon_tangents >>> from geojson import Polygon, Point, Feature >>> point = Feature(geometry=Point((61, 5))) >>> polygon = Feature(geometry=Polygon([(11, 0), (22, 4), (31, 0), (31, 11), ... (21, 15), (11, 11), (11, 0)])) >>> polygon_tangents(point, polygon) """ point_coords = get_coords(point) poly_coords = get_coords(polygon) enext = 0 bbox_points = bbox(polygon) nearest_pt_index = 0 nearest = None if (bbox_points[0] < point_coords[0] < bbox_points[2] and bbox_points[1] < point_coords[1] < bbox_points[3]): nearest = nearest_point(point, explode(polygon)) nearest_pt_index = nearest.properties.featureIndex geo_type = get_type(polygon) if geo_type == "Polygon": rtan = poly_coords[0][nearest_pt_index] ltan = poly_coords[0][0] if nearest: if nearest["geometry"]["coordinates"][1] < point_coords[1]: ltan = poly_coords[0][nearest_pt_index] eprev = _is_left( poly_coords[0][0], poly_coords[0][len(poly_coords[0]) - 1], point_coords, ) out = process_polygon(poly_coords[0], point_coords, eprev, enext, rtan, ltan) rtan = out[0] ltan = out[1] elif geo_type == "MultiPolygon": closest_feature = 0 closest_vertex = 0 vertices_counted = 0 for i in range(0, len(poly_coords[0])): closest_feature = i vertice_found = False for i2 in range(0, len(poly_coords[0][i])): closest_vertex = i2 if vertices_counted == nearest_pt_index: vertice_found = True break vertices_counted += 1 if vertice_found: break rtan = poly_coords[0][closest_feature][closest_vertex] ltan = poly_coords[0][closest_feature][closest_vertex] eprev = _is_left( poly_coords[0][0][0], poly_coords[0][0][len(poly_coords[0][0]) - 1], point_coords, ) for ring in poly_coords: out = process_polygon(ring[0], point_coords, eprev, enext, rtan, ltan) rtan = out[0] ltan = out[1] return FeatureCollection( [Feature(geometry=Point(rtan)), Feature(geometry=Point(ltan))])