def rhumb_bearing(start, end, final=False): """ Takes two points and finds the bearing angle between them along a Rhumb line, i.e. the angle measured in degrees start the north line (0 degrees). :param start: Start Point or Point Feature. :param end: End Point or Point Feature. :param final: Calculates the final bearing if true :return: bearing from north in decimal degrees, between -180 and 180 degrees (positive clockwise) Example: >>> from turfpy.measurement import rhumb_bearing >>> from geojson import Feature, Point >>> start = Feature(geometry=Point((-75.343, 39.984))) >>> end = Feature(geometry=Point((-75.534, 39.123))) >>> rhumb_bearing(start, end, True) """ if final: bear_360 = calculate_rhumb_bearing(get_coord(end), get_coord(start)) else: bear_360 = calculate_rhumb_bearing(get_coord(start), get_coord(end)) if bear_360 > 180: bear_180 = -1 * (360 - bear_360) else: bear_180 = bear_360 return bear_180
def distance(point1: Feature, point2: Feature, units: str = "km"): """ Calculates distance between two Points. A point is containing latitude and logitude in decimal degrees and ``unit`` is optional. It calculates distance in units such as kilometers, meters, miles, feet and inches. :param point1: first point; tuple of (latitude, longitude) in decimal degrees. :param point2: second point; tuple of (latitude, longitude) in decimal degrees. :param units: A string containing unit, E.g. kilometers = 'km', miles = 'mi', meters = 'm', feet = 'ft', inches = 'in'. :return: The distance between the two points in the requested unit, as a float. Example: >>> from turfpy import measurement >>> from geojson import Point, Feature >>> start = Feature(geometry=Point((-75.343, 39.984))) >>> end = Feature(geometry=Point((-75.534, 39.123))) >>> measurement.distance(start,end) """ coordinates1 = get_coord(point1) coordinates2 = get_coord(point2) dlat = radians((coordinates2[1] - coordinates1[1])) dlon = radians((coordinates2[0] - coordinates1[0])) lat1 = radians(coordinates1[1]) lat2 = radians(coordinates2[1]) a = pow(sin(dlat / 2), 2) + pow(sin(dlon / 2), 2) * cos(lat1) * cos(lat2) b = 2 * atan2(sqrt(a), sqrt(1 - a)) return radians_to_length(b, units)
def rhumb_distance(start, to, units="km"): """ Calculates the distance along a rhumb line between two points in degrees, radians, miles, or kilometers. :param start: Start Point or Point Feature from which distance to be calculated. :param to: End Point or Point Feature upto which distance to be calculated. :param units: Units in which distance to be calculated, values can be 'deg', 'rad', 'mi', 'km' :return: Distance calculated from provided start to end Point. Example: >>> from turfpy.measurement import rhumb_distance >>> from geojson import Point, Feature >>> start = Feature(geometry=Point((-75.343, 39.984))) >>> end = Feature(geometry=Point((-75.534, 39.123))) >>> rhumb_distance(start, end,'mi') """ origin = get_coord(start) dest = get_coord(to) if dest[0] - origin[0] > 180: temp = -360 elif origin[0] - dest[0] > 180: temp = 360 else: temp = 0 dest[0] += temp distance_in_meters = _calculate_rhumb_distance(origin, dest) ru_distance = convert_length(distance_in_meters, "m", units) return ru_distance
def callback_flatten_each(feature, feature_index, multi_feature_index): nonlocal length nonlocal closest_pt coords = get_coords(feature) for i, coord in enumerate(coords[:-1]): # start start = Feature(geometry=Point(coord)) start.properties = {"dist": dist(point, start, options)} # stop stop = Feature(geometry=Point(coords[i + 1])) stop.properties = {"dist": dist(point, stop, options)} # section length section_length = dist(start, stop, options) # perpendicular height_distance = max(start.properties["dist"], stop.properties["dist"]) direction = bearing(start, stop) perpendicular_pt1 = destination(Feature(geometry=point), height_distance, direction + 90, options) perpendicular_pt2 = destination(Feature(geometry=point), height_distance, direction - 90, options) intersect = line_intersect( Feature(geometry=LineString([ get_coord(perpendicular_pt1), get_coord(perpendicular_pt2) ])), Feature(geometry=LineString( [get_coord(start), get_coord(stop)])), ) intersect_pt = None if len(intersect["features"]) > 0: intersect_pt = intersect["features"][0] intersect_pt.properties["dist"] = dist(point, intersect_pt, options) intersect_pt.properties["location"] = length + dist( start, intersect_pt, options) if start.properties["dist"] < closest_pt.properties["dist"]: closest_pt = start closest_pt.properties["index"] = i closest_pt.properties["location"] = length if stop.properties["dist"] < closest_pt.properties["dist"]: closest_pt = stop closest_pt.properties["index"] = i + 1 closest_pt.properties["location"] = length + section_length if (intersect_pt and intersect_pt.properties["dist"] < closest_pt.properties["dist"]): closest_pt = intersect_pt closest_pt.properties["index"] = i # update length length += section_length # process all Features return True
def test_get_coord(): """Test get_coord function.""" point = Point((73, 19)) feature = Feature(geometry=point) flist = [73, 19] c1 = get_coord(point) c2 = get_coord(feature) c3 = get_coord(flist) assert c1 == [73, 19] assert c1 == c2 == c3 # Test for derived class from Feature class Vertex(Feature): """Derived from Feature""" def __init__(self, node: str, point: Point): Feature.__init__(self, geometry=point) self.nodeid = node p1 = Point((25.25458, 51.623879)) f1 = Feature(geometry=p1) v1 = Vertex("v1", point=p1) p2 = Point((25.254626, 51.624053)) f2 = Feature(geometry=p2) v2 = Vertex("v2", point=p2) df = distance(f1, f2) dv = distance(v1, v2) assert df == dv
def define_origin(geojson, origin): if not origin: origin = "centroid" if isinstance(origin, list): return get_coord(origin) bb = bbox(geojson) west = bb[0] south = bb[1] east = bb[2] north = bb[3] if (origin == "sw" or origin == "southwest" or origin == "westsouth" or origin == "bottomleft"): return [west, south] elif (origin == "se" or origin == "southeast" or origin == "eastsouth" or origin == "bottomright"): return [east, south] elif (origin == "nw" or origin == "northwest" or origin == "westnorth" or origin == "topleft"): return [west, north] elif (origin == "ne" or origin == "northeast" or origin == "eastnorth" or origin == "topright"): return [east, north] elif origin == "center": return center(geojson)["geometry"]["coordinates"] elif origin is None or origin == "centroid": return centroid(geojson)["geometry"]["coordinates"] else: raise Exception("invalid origin")
def rhumb_destination(origin, distance, bearing, options) -> Feature: """ Returns the destination Point having travelled the given distance along a Rhumb line from the origin Point with the (varant) given bearing. :param origin: Starting Point :param distance: Distance from the starting point :param bearing: Varant bearing angle ranging from -180 to 180 degrees from north :param options: A dict of two values 'units' for the units of distance provided and 'properties' that are to be passed to the Destination Feature Point Example :- {'units':'mi', 'properties': {"marker-color": "F00"}} :return: Destination Feature Point Example: >>> from turfpy.measurement import rhumb_destination >>> from geojson import Point, Feature >>> start = Feature(geometry=Point((-75.343, 39.984)), properties={"marker-color": "F00"}) >>> distance = 50 >>> bearing = 90 >>> rhumb_destination(start, distance, bearing, {'units':'mi', 'properties': {"marker-color": "F00"}}) """ was_negative_distance = distance < 0 distance_in_meters = convert_length(abs(distance), options.get("units", ""), "m") if was_negative_distance: distance_in_meters = -1 * (abs(distance_in_meters)) coords = get_coord(origin) destination_point = _calculate_rhumb_destination(coords, distance_in_meters, bearing) return Feature( geometry=Point(destination_point), properties=options.get("properties", ""), )
def _callback_coord_each( coord, coord_index, feature_index, multi_feature_index, geometry_index ): nonlocal pivot, angle initial_angle = rhumb_bearing(GeoPoint(pivot), GeoPoint(coord)) final_angle = initial_angle + angle distance = rhumb_distance(GeoPoint(pivot), GeoPoint(coord)) new_coords = get_coord(rhumb_destination(GeoPoint(pivot), distance, final_angle)) coord[0] = new_coords[0] coord[1] = new_coords[1]
def _callback_coord_each(coord, coord_index, feature_index, multi_feature_index, geometry_index): nonlocal distance, direction, units, z_translation new_coords = get_coord( rhumb_destination(GeoPoint(coord), distance, direction, {"units": units})) coord[0] = new_coords[0] coord[1] = new_coords[1] if z_translation and len(coord) == 3: coord[2] += z_translation
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
def boolean_point_in_polygon(point, polygon, ignore_boundary=False): """ Takes a Point or a Point Feature and Polygon or Polygon Feature as input and returns True if Point is in given Feature. :param point: Point or Point Feature. :param polygon: Polygon or Polygon Feature. :param ignore_boundary: [Optional] default value is False, specify whether to exclude boundary of the given polygon or not. :return: True if the given Point is in Polygons else False Example: >>> from turfpy.measurement import boolean_point_in_polygon >>> from geojson import Point, MultiPolygon, Feature >>> point = Feature(geometry=Point((-77, 44))) >>> polygon = Feature(geometry=MultiPolygon([([(-81, 41), (-81, 47), (-72, 47), (-72, 41), (-81, 41)],), >>> ([(3.78, 9.28), (-130.91, 1.52), (35.12, 72.234), (3.78, 9.28)],)])) >>> boolean_point_in_polygon(point, polygon) """ if not point: raise Exception("point is required") if not polygon: raise Exception("polygon is required") pt = get_coord(point) geom = get_geom(polygon) geo_type = geom["type"] bbox = polygon.get("bbox", None) polys = geom["coordinates"] if bbox and not in_bbox(pt, bbox): return False if geo_type == "Polygon": polys = [polys] inside_poly = False for i in range(0, len(polys)): if in_ring(pt, polys[i][0], ignore_boundary): in_hole = False k = 1 while k < len(polys[i]) and not in_hole: if in_ring(pt, polys[i][k], not ignore_boundary): in_hole = True k += 1 if not in_hole: inside_poly = True return inside_poly
def destination(origin: Union[Feature, Point], distance, bearing, options: dict = {}) -> Feature: """ Takes a Point and calculates the location of a destination point given a distance in degrees, radians, miles, or kilometers and bearing in degrees. :param origin: Start point. :param distance: distance upto which the destination is from origin. :param bearing: Direction in which is the destination is from origin. :param options: Option like units of distance and properties to be passed to destination point feature, value for units are 'mi', 'km', 'deg' and 'rad'. :return: Feature: destination point in at the given distance and given direction. Example: >>> from turfpy.measurement import destination >>> from geojson import Point, Feature >>> origin = Feature(geometry=Point([-75.343, 39.984])) >>> distance = 50 >>> bearing = 90 >>> options = {'units': 'mi'} >>> destination(origin,distance,bearing,options) """ coordinates1 = get_coord(origin) longitude1 = radians(float(coordinates1[0])) latitude1 = radians(float(coordinates1[1])) bearingRad = radians(float(bearing)) if "units" in options: radian = length_to_radians(distance, options["units"]) else: radian = length_to_radians(distance) latitude2 = asin((sin(latitude1) * cos(radian)) + (cos(latitude1) * sin(radian) * cos(bearingRad))) longitude2 = longitude1 + atan2( sin(bearingRad) * sin(radian) * cos(latitude1), cos(radian) - sin(latitude1) * sin(latitude2), ) lng = degrees(longitude2) lat = degrees(latitude2) point = Point((lng, lat)) return Feature( geometry=point, properties=options["properties"] if "properties" in options else {}, )
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())