diff --git a/docs/source/index.rst b/docs/source/index.rst index 9f04609..63b4e28 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -70,6 +70,8 @@ A Python library for performing geospatial data analysis which reimplements `tur Line Intersect Line Segment + Line Arc + Sector .. toctree:: :maxdepth: 1 diff --git a/docs/source/misc/line_arc.rst b/docs/source/misc/line_arc.rst new file mode 100644 index 0000000..851b508 --- /dev/null +++ b/docs/source/misc/line_arc.rst @@ -0,0 +1,46 @@ +Line Arc +================ +Creates a circular arc, of a circle of the given radius and center point, between bearing1 and bearing2; 0 bearing is North of center point, positive clockwise. + +Example +------- + +.. jupyter-execute:: + + from turfpy.misc import line_arc + from geojson import Feature, Point + + center = Feature(geometry=Point((-75, 40))) + radius = 5 + bearing1 = 25 + bearing2 = 47 + + line_arc(center=center, radius=radius, bearing1=bearing1, bearing2=bearing2) + + + +Interactive Example +------------------- + +.. jupyter-execute:: + + from ipyleaflet import Map, GeoJSON, LayersControl + from turfpy.misc import line_arc + from geojson import Feature, Point, LineString, FeatureCollection + + center = Feature(geometry=Point((-75, 40))) + radius = 5; + bearing1 = 25; + bearing2 = 47; + + m = Map(center=[40.011313056309056, -74.97720068362348], zoom=12) + + feature = line_arc(center=center, radius=radius, bearing1=bearing1, bearing2=bearing2) + + fc = FeatureCollection([feature, center]) + + layer = GeoJSON(name="Line_Arc", data=fc, style={'color':'red'}) + + m.add_layer(layer) + m + diff --git a/docs/source/misc/sector.rst b/docs/source/misc/sector.rst new file mode 100644 index 0000000..da5900f --- /dev/null +++ b/docs/source/misc/sector.rst @@ -0,0 +1,47 @@ +Sector +================ +Creates a circular sector of a circle of given radius and center Point, between (clockwise) bearing1 and bearing2; 0 bearing is North of center point, positive clockwise. + +Example +------- + +.. jupyter-execute:: + + from turfpy.misc import sector + from geojson import Feature, Point + + center = Feature(geometry=Point((-75, 40))) + radius = 5 + bearing1 = 25 + bearing2 = 45 + + feature = sector(center, radius, bearing1, bearing2, options={"properties":{"length":3}}) + + + +Interactive Example +------------------- + +.. jupyter-execute:: + + from ipyleaflet import Map, GeoJSON, LayersControl + from turfpy.misc import sector + from geojson import Feature, Point, LineString, FeatureCollection + + center = Feature(geometry=Point((-75, 40))) + radius = 5; + bearing1 = 25; + bearing2 = 45; + + m = Map(center=[40.011313056309056, -74.97720068362348], zoom=12) + + feature = sector(center, radius, bearing1, bearing2, options={"properties":{"length":3}}) + + + fc = FeatureCollection([feature, center]) + + layer = GeoJSON(name="Line_Arc", data=fc, style={'color':'red'}) + + m.add_layer(layer) + m + diff --git a/examples/Misc.ipynb b/examples/Misc.ipynb index 86315a2..6c0917c 100644 --- a/examples/Misc.ipynb +++ b/examples/Misc.ipynb @@ -75,6 +75,56 @@ "\n", "line_segment(poly)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Line Arc\n", + "Creates a circular arc, of a circle of the given radius and center point, between bearing1 and bearing2; 0 bearing is North of center point, positive clockwise." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from turfpy.misc import line_arc\n", + "from geojson import Feature, Point\n", + "\n", + "center = Feature(geometry=Point((-75, 40)))\n", + "radius = 5\n", + "bearing1 = 25\n", + "bearing2 = 47\n", + "\n", + "line_arc(center=center, radius=radius, bearing1=bearing1, bearing2=bearing2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Sector\n", + "Creates a circular sector of a circle of given radius and center Point, between (clockwise) bearing1 and bearing2; 0 bearing is North of center point, positive clockwise." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from turfpy.misc import sector\n", + "from geojson import Feature, Point\n", + "\n", + "center = Feature(geometry=Point((-75, 40)))\n", + "radius = 5\n", + "bearing1 = 25\n", + "bearing2 = 45\n", + "\n", + "feature = sector(center, radius, bearing1, bearing2, options={\"properties\":{\"length\":3}})" + ] } ], "metadata": { @@ -93,7 +143,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.9" } }, "nbformat": 4, diff --git a/misc.md b/misc.md index a756b71..8fa309e 100644 --- a/misc.md +++ b/misc.md @@ -61,3 +61,60 @@ poly = { line_segment(poly) ``` + + * Line Arc : Creates a circular arc, of a circle of the given radius and center point, + between bearing1 and bearing2; 0 bearing is + North of center point, positive clockwise. + +| Argument | Type | Description | +| ------- | ------ | ----------- | +| `center` | Feature | A `Point` object representing center point of circle | +| `radius` | float | An int representing radius of the circle | +| `bearing1` | float | Angle, in decimal degrees, of the first radius of the arc | +| `bearing2` | float | Angle, in decimal degrees, of the second radius of the arc | +| `options` | float | A dict representing additional properties,which can be `steps` which has default values as 64 and `units` which has default values of `km` | + +| Return | Type | Description | +| ------- | ------ | ----------- | +| `line string` | Feature | A Line String feature object. | + +```python +from turfpy.misc import line_arc +from geojson import Feature, Point + +center = Feature(geometry=Point((-75, 40))) +radius = 5 +bearing1 = 25 +bearing2 = 47 + +line_arc(center=center, radius=radius, bearing1=bearing1, bearing2=bearing2) +``` + + + * Line Arc : Creates a circular sector of a circle of given radius and center Point , + between (clockwise) bearing1 and bearing2; 0 + bearing is North of center point, positive clockwise. + +| Argument | Type | Description | +| ------- | ------ | ----------- | +| `center` | Feature | A `Point` object representing center point of circle | +| `radius` | float | An int representing radius of the circle | +| `bearing1` | float | Angle, in decimal degrees, of the first radius of the arc | +| `bearing2` | float | Angle, in decimal degrees, of the second radius of the arc | +| `options` | float | A dict representing additional properties, which can be `steps` which has default values as 64, `units` which has default values of `km`, and `properties` which will be added to resulting Feature as properties. | + +| Return | Type | Description | +| ------- | ------ | ----------- | +| `polygon` | Feature | A polygon feature object. | + +```python +from turfpy.misc import sector +from geojson import Feature, Point + +center = Feature(geometry=Point((-75, 40))) +radius = 5 +bearing1 = 25 +bearing2 = 45 + +feature = sector(center, radius, bearing1, bearing2, options={"properties":{"length":3}}) +``` \ No newline at end of file diff --git a/turfpy/__version__.py b/turfpy/__version__.py index 6e81f7d..29ccc53 100644 --- a/turfpy/__version__.py +++ b/turfpy/__version__.py @@ -1,3 +1,3 @@ """Project version information.""" -__version__ = "0.0.6" +__version__ = "0.0.7" diff --git a/turfpy/helper.py b/turfpy/helper.py index 73dfe28..bb82166 100644 --- a/turfpy/helper.py +++ b/turfpy/helper.py @@ -141,3 +141,10 @@ def radians_to_degrees(radians: float): """#TODO: Add description""" degrees = abs(radians) % (2 * math.pi) * (1 if radians >= 0 else -1) return degrees * 180 / math.pi + + +def convert_angle_to_360(alfa: float): + beta = alfa % 360 + if beta < 0: + beta += 360 + return beta diff --git a/turfpy/measurement.py b/turfpy/measurement.py index 5435359..90746d4 100644 --- a/turfpy/measurement.py +++ b/turfpy/measurement.py @@ -587,6 +587,7 @@ def _callback_feature_each(pt, feature_index): if float(distance_to_point) < min_dist: best_feature_index = feature_index min_dist = distance_to_point + return True feature_each(points, _callback_feature_each) diff --git a/turfpy/misc.py b/turfpy/misc.py index dc58116..2b1f69a 100644 --- a/turfpy/misc.py +++ b/turfpy/misc.py @@ -18,10 +18,10 @@ from shapely.geometry import mapping, shape import turfpy._compact as compat -from turfpy.helper import get_coord, get_coords, get_type +from turfpy.helper import convert_angle_to_360, get_coord, get_coords, get_type from turfpy.measurement import bearing, destination, distance -from turfpy.meta import flatten_each -from turfpy.transformation import intersect +from turfpy.meta import coord_each, flatten_each +from turfpy.transformation import circle, intersect def line_intersect( @@ -377,3 +377,155 @@ def line_slice( clip_coords.append(get_coord(ends[1])) return Feature(geometry=LineString(clip_coords), properties=line["properties"].copy()) + + +def line_arc( + center: Feature, radius: int, bearing1: int, bearing2: int, options: dict = {} +) -> Feature: + """ + Creates a circular arc, of a circle of the given radius and center point, + between bearing1 and bearing2; 0 bearing is + North of center point, positive clockwise. + + :param center: A `Point` object representing center point of circle. + :param radius: An int representing radius of the circle. + :param bearing1: Angle, in decimal degrees, of the first radius of the arc. + :param bearing2: Angle, in decimal degrees, of the second radius of the arc. + :param options: A dict representing additional properties,which can be `steps` + which has default values as 64 and `units` which has default values of `km` + :return: A Line String feature object. + + Example: + + + >>> from turfpy.misc import line_arc + >>> from geojson import Feature, Point + >>> center = Feature(geometry=Point((-75, 40))) + >>> radius = 5 + >>> bearing1 = 25 + >>> bearing2 = 47 + >>> feature = line_arc(center=center, radius=radius, + >>> bearing1=bearing1, bearing2=bearing2) + """ + if not options: + options = {} + steps = int(options["steps"]) if options.get("steps") else 64 + units = str(options.get("units")) if options.get("units") else "km" + + angle1 = convert_angle_to_360(bearing1) + angle2 = convert_angle_to_360(bearing2) + properties = {} + if center.get("type"): + if center.get("type") == "Feature": + properties = center.get("properties") + else: + raise Exception("Invalid Feature value for center parameter") + + if angle1 == angle2: + return Feature( + geometry=LineString( + circle(center, radius, steps, units)["geometry"]["coordinates"][0] + ), + properties=properties, + ) + + arc_start_degree = angle1 + arc_end_degree = angle2 if angle1 < angle2 else angle2 + 360 + + alfa = arc_start_degree + coordinates = [] + i = 0 + + while alfa < arc_end_degree: + coordinates.append( + destination(center, radius, alfa, {"steps": steps, "units": units})[ + "geometry" + ]["coordinates"] + ) + i += 1 + alfa = arc_start_degree + i * 360 / steps + + if alfa > arc_end_degree: + coordinates.append( + destination(center, radius, arc_end_degree, {"steps": steps, "units": units})[ + "geometry" + ]["coordinates"] + ) + + return Feature(geometry=LineString(coordinates, properties=properties)) + + +def sector( + center: Feature, radius: int, bearing1: int, bearing2: int, options: dict = {} +) -> Feature: + """ + Creates a circular sector of a circle of given radius and center Point , + between (clockwise) bearing1 and bearing2; 0 + bearing is North of center point, positive clockwise. + + :param center: A `Point` object representing center point of circle. + :param radius: An int representing radius of the circle. + :param bearing1: Angle, in decimal degrees, of the first radius of the arc. + :param bearing2: Angle, in decimal degrees, of the second radius of the arc. + :param options: A dict representing additional properties, which can be `steps` + which has default values as 64, `units` which has default values of `km`, + and `properties` which will be added to resulting Feature as properties. + :return: A polygon feature object. + + Example: + + + >>> from turfpy.misc import sector + >>> from geojson import Feature, Point + >>> center = Feature(geometry=Point((-75, 40))) + >>> radius = 5 + >>> bearing1 = 25 + >>> bearing2 = 45 + >>> sector(center, radius, bearing1, bearing2) + """ + if not options: + options = {} + steps = int(options["steps"]) if options.get("steps") else 64 + units = str(options.get("units")) if options.get("units") else "km" + + properties = options.get("properties") if options.get("properties") else {} + + if not center: + raise Exception("center if required") + + if center.get("type") != "Feature": + raise Exception("Invalid Feature value for center parameter") + + if not radius: + raise Exception("Radius is required") + + if not bearing1: + raise Exception("bearing1 is required") + + if not bearing2: + raise Exception("bearing2 is required") + + if convert_angle_to_360(bearing1) == convert_angle_to_360(bearing2): + return circle(center, radius, steps, units) + + coords = get_coords(center) + + arc = line_arc(center, radius, bearing1, bearing2, options) + + sliceCoords = [[coords]] + + def _callback_coord_each( + coord, + coord_index, + feature_index, + multi_feature_index, + geometry_index, + ): + nonlocal sliceCoords + sliceCoords[0].append(coord) + + coord_each(arc, _callback_coord_each) + + sliceCoords[0].append(coords) + + return Feature(geometry=Polygon(sliceCoords), properties=properties) diff --git a/turfpy/transformation.py b/turfpy/transformation.py index 5b70981..2381aab 100644 --- a/turfpy/transformation.py +++ b/turfpy/transformation.py @@ -337,7 +337,10 @@ def add_edge(edges, edge_points, coords, i, j): # Area of triangle by Heron's formula area = math.sqrt(s * (s - a) * (s - b) * (s - c)) - circum_r = a * b * c / (4.0 * area) + if area > 0: + circum_r = a * b * c / (4.0 * area) + else: + circum_r = 0 # Here's the radius filter. # print circum_r