Giter Club home page Giter Club logo

turfpy's Introduction

Turfpy

Build Status PyPI PyPI - Python Version PyPI - Status PyPI - License Total alerts code quality: python Downloads Conda (channel only) Conda Downloads Join the Gitter Chat Documentation Status Code style: black commits since GitHub contributors

Demo:

Binder

A Python library for performing geospatial data analysis which reimplements turf.js.

Installation

You can install the Turfpy from PyPI:

pip install turfpy

If you prefer to use conda:

conda install -c conda-forge turfpy

Test Suite

You can run the test suite locally:

pip install -r dev_requirements.txt
pytest -s -v --cov=turfpy tests

Features

It supports below features:

Documentation

Documentation can be found at: docs

turfpy's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

turfpy's Issues

precision not honored

When comparing the transform_translate function with the transformTranslate function in the JS turf library, the coordinates precision is reduce to 6 values after the decimal in the python library.

Is there a way to get to the precision that I've started with (7).

transform_translate({"coordinates": [5.1036861, 52.0850478], "type": "Point"}, 5.9323754494890855, 63.45775765114114, 'm')

I would expect this to have at least 7, but the response is:

{'coordinates': [5.103764, 52.085072], 'type': 'Point'}

Please help if there's any idea. I tried changing the units to 'cm', but I don't think this was a valid option.

Add support for turf.sector

I'm currently using shapely + pyproj to draw circular sectors (viewcones for geolocated images), which takes an awful long time since I have to reproject every vertex when building each of my 4k polygons. Are you planning to include this method in turfpy?

image

Match documentation to implementation. If distance is expection (long, latt) specify in the doc string as such.

As others have pointed out measurements.py::distance says in the doc string to pass two "tuples of (latitude, longitude)" when in all reality the actual code flips this order thus resulting inaccurate measurements. Without knowing what else the code does I would advocate for at least changing the documentation to reflect that in reality you must pass two "tuples of (longitude, latitude)"

Proposed change:

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 (longitude, latitude) in decimal degrees.
    :param point2: second point; tuple of (longitude, latitude) 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])

turfpy.misc.line_intersect is not working correctly

Hi Omkar,

Currently the documentation for this method reads "Takes any LineString or Polygon GeoJSON and returns the intersecting point(s)." however, I tried to intersect a Linestring Feature with various Polygons and it only returns an intersection point max when in turf.js for some of these polygons there are as many as 14. Some of them don't even intersect, but if you display them there are clearly min 2 points in which the features should intersect.

Thanks in advance and keep up the great work!

The Return of "does not work on derived classes?"

Hello,

I have the same problem as #94 with other function calls: An Edge is a subclass of a Feature and point_to_line_distance fails to recognize an edge as a Feature...

I think that there is a more general issue with type/subtype checking.

Also, the documentation does not always reflect the code: For exemple, distance is documented as distance(Feature, Feature). But distance(Point, Point) also works and is not documented.

I have the feeling that more generally, type should be enforced at function parameter level: point_to_line_distance(Feature, Feature) is too loose. The coder must check that the Feature geometry has the appropriate type. point_to_line_distance(Point, Line) is more precise and type checking is done by Python at runtime. There is no need to add more checks inside the code of the function.

BTW, point_to_line_distance should return the closest point somehow as well. May be a function nearest_point_on_line(Point, Line) returns the nearest point, and its distance to the line can be determined with distance.

Here is a piece of code that exhibit the issue:

`
from geojson import Point, LineString, Feature
from turfpy.measurement import point_to_line_distance

class Edge(Feature):
def init(self, node: str, start: Feature, end: Feature):
Feature.init(self, geometry=LineString((start["geometry"]["coordinates"], end["geometry"]["coordinates"])))
self.name = node

p1 = Point((25.25458, 51.623879))
f1 = Feature(geometry=p1)
p2 = Point((25.254626, 51.624053))
f2 = Feature(geometry=p2)

e1 = Edge("e1", start=f1, end=f2)

p0 = Point((25.0, 51.0))
f0 = Feature(geometry=p0)

fline = Feature(geometry=LineString((f1["geometry"]["coordinates"], f2["geometry"]["coordinates"])))
print(point_to_line_distance(f0, fline))
#71.59329853730718

fedge = Edge(node="e1", start=f1, end=f2)
print(isinstance(fedge, Feature))
#True
print(point_to_line_distance(f0, fedge))
Traceback (most recent call last):
File "/Users/pierre/Developer/Internet/js/gip/emitpy/tests/ft3.py", line 28, in
print(point_to_line_distance(f0, fedge))
File "/usr/local/miniconda3/lib/python3.9/site-packages/turfpy/measurement.py", line 1004, in point_to_line_distance
feature_of(line, "LineString", "line")
File "/usr/local/miniconda3/lib/python3.9/site-packages/turfpy/helper.py", line 120, in feature_of
raise Exception(
Exception: Invalid input to line, Feature with geometry required
`

P.

incorrect measurement due to swap of lat/long

Hi, thanks for all the work, I've been using this for a while, and recently I discover an issue about the points measurement

in the measure function, the comments shows that points should be defined as (lat, long)
but in the calculation below, it is expecting (long, lat). which leads distance calculation error.
this is an easy fix, but I am not sure, if this also leads to calculation error in point to line, point to polygon etc.

dlat = radians((coordinates2[1] - coordinates1[1]))
dlon = radians((coordinates2[0] - coordinates1[0]))

"
: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])"

line_segment() doesn't return all segments of MultiLineString

I just discovered weird behavior of line_segment() when used with MultiLineString:

from turfpy.misc import line_segment
from json import dumps

mls = {
  "type": "Feature",
    "properties": {},
    "geometry": {
        "type": "MultiLineString",
        "coordinates": [
            [
                [51,47],
                [51,43]
            ],
            [
                [45,47],
                [45,43]
            ]
        ]
    }
}

print(dumps(line_segment(mls), indent=4))

result contains only first segment:

{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "geometry": {
                "type": "LineString",
                "coordinates": [
                    [51,43],
                    [51,47]
                ]
            },
            "properties": {},
            "bbox": [51,43,51,47],
            "id": 0
        }
    ]
}

If I add return True on line misc.py:157 to let iterate over all features (instead of stopping after first feature), line_segment() returns correct result...

Is it bug or feature?

`turfpy.measurement.points_within_polygon` and `turfpy.measurement.boolean_point_in_polygon` are very slow

Hey there,

I noticed that points_within_polygon (and the foreach-point-callback boolean_point_in_polygon) was fairly slow compared to the Shapely package's shapely.geometry.Polygon.contains method.

As a test, I used a featureCollection called points consisting of roughly 5,000 points (in the Columbus, Ohio metro area fyi) and a geojson.Polygon called polygon (compatible with turfpy methods).

Method 1: turfpy.measurement.points_within_polygon

Calling turfpy.measurement.points_within_polygon(points,polygon) takes roughly 26 seconds.

Method 2: shapely.geometry.Polygon.contains

I wrote a method that:

  1. instantiates a shapely.geometry.Polygon called polygon from a geojson.Polygon and also creates a Python list of points of type shapely.geometry.Point,
  2. overwrites the list of points to only contain those where polygon.contains(point) is True,
  3. converts the list of points back to a geojson.featureCollection, which it returns.

This takes roughly 5 seconds.

Conclusion

The two methods return the same number of points; furthermore, I plotted the points on a map in my browser (using Folium), and neither one is doing anything wrong.

Even with all the object creation (literally copying the list of points, taking up additional memory, too), Method 2 (Shapely) took <1/4 the time.

So - in case anyone intends to use this method, perhaps it can be reworked. For now, I'll post my "faster" method here.

from geojson import Polygon, Point, Feature, FeatureCollection
import shapely.geometry
import shapely

def create_point(lon,lat):
	return Feature(geometry=Point((lon,lat)))

def point_to_shapely(point):
	lon,lat = get_lon_lat(point)
	return shapely.geometry.Point(lon,lat)

def get_list_of_shapely_points(featureCollection):
	return [point_to_shapely(point) for point in featureCollection['features']]

def polygon_to_shapely(polygon):
	return shapely.geometry.Polygon(polygon['coordinates'][0])

def shapely_points_to_featureCollection(points):
	geojson_pts = [create_point(point.x,point.y) for point in points]
	return FeatureCollection(geojson_pts)

def get_contained_points_shapelystyle(points,polygon):
	shply_polygon = polygon_to_shapely(polygon)
	shapely_points = [shply_point for shply_point in get_list_of_shapely_points(points) if shply_polygon.contains(shply_point)]
	return shapely_points_to_featureCollection(shapely_points)

Implement Buffer

Hi,
Thanks for porting this lib from JS to Python.
Do you have any vision on when the buffer transformation will be implemented ?

turfpy.transformation union function precision

There is any way to incresase the precision of the union function?
I am working on an university project and I need more decimal places for the output coordinetes of union function.
Thank you.

Trouble with transformation circle

I tried creating a geofence using Circle transformation by giving radius. The coordinates of the result polygon is incorrect verified by visualizing the polygon, it had an ellipsoid shape.

Also the distance returned from distance function between 2 coordinates is off by 10 miles.

Any help on these would be appreciated!

Indicate area return unit

I see that measurements.length says something about the return value (default km), but measurements.area doesn't. This an be easily fixed, but it would be perhaps more helpful to allow for different langth and area units. There are some packages for handling units in a way that they can be used in calculations, but these are likely not enough standardized...

TurfPy Concave throws ZeroDivisionError: float division by zero for concave hull

import json
from turfpy.transformation import concave
from geojson import FeatureCollection, Feature, Point
import sys
import pandas as pd

def get_points_csv(file_path):
	df = pd.read_csv(file_path)
	long_coords, lat_coords = df['Longitude'].tolist() , df['Latitude'].tolist()
	points = []

	for i in range(0, len(long_coords)):

		points.append((long_coords[i],lat_coords[i]))
	return points

points = get_points_csv(sys.argv[1])

fc = []
for p in points:
	fc.append(Feature(geometry=Point(p)))
	# print(p)

ch = concave(FeatureCollection(fc), alpha=100)
print(json.dumps(ch, indent=2, sort_keys=True))
Traceback (most recent call last):
  File "C:\Users\Khaalidi\Desktop\area_turfpy.py", line 26, in <module>
    ch = concave(FeatureCollection(fc), alpha=100)
  File "C:\Users\Khaalidi\AppData\Local\Programs\Python\Python39\lib\site-packages\turfpy\transformation.py", line 418, in concave
    concave_hull, edges = _alpha_shape(points, alpha)
  File "C:\Users\Khaalidi\AppData\Local\Programs\Python\Python39\lib\site-packages\turfpy\transformation.py", line 340, in _alpha_shape
    circum_r = a * b * c / (4.0 * area)
ZeroDivisionError: float division by zero

The coordinates csv : coord.csv
The features Collection is: geo.txt

Intersect() does not always preserve order of coordinates

Taking the example from https://turfpy.readthedocs.io/en/latest/turfpy.transformation.html#turfpy.transformation.intersect it appears that intersect([a, a]) sometimes changes the coordinates order in the output which makes comparison pretty challenging.

from turfpy.transformation import intersect
from geojson import Feature

f = Feature(geometry={"coordinates": [
[[-122.801742, 45.48565], [-122.801742, 45.60491],
[-122.584762, 45.60491], [-122.584762, 45.48565],
[-122.801742, 45.48565]]], "type": "Polygon"})

b = Feature(geometry={"coordinates": [
[[-122.520217, 45.535693], [-122.64038, 45.553967],
[-122.720031, 45.526554], [-122.669906, 45.507309],
[-122.723464, 45.446643], [-122.532577, 45.408574],
[-122.487258, 45.477466], [-122.520217, 45.535693]
]], "type": "Polygon"})

inter = intersect([b, b])

print(inter == b)
print(inter)
print(b)

Output:

False

{"geometry": {"coordinates": [[[-122.64038, 45.553967], [-122.520217, 45.535693],
[-122.487258, 45.477466], [-122.532577, 45.408574], [-122.723464, 45.446643],
[-122.669906, 45.507309], [-122.720031, 45.526554], [-122.64038, 45.553967]]],
"type": "Polygon"}, "properties": {}, "type": "Feature"}

{"geometry": {"coordinates": [[[-122.520217, 45.535693], [-122.64038, 45.553967],
[-122.720031, 45.526554], [-122.669906, 45.507309], [-122.723464, 45.446643],
[-122.532577, 45.408574], [-122.487258, 45.477466], [-122.520217, 45.535693]]],
"type": "Polygon"}, "properties": {}, "type": "Feature"}

does not work on derived classes?

Hello,
I'm no python specialist, just a regular user.
I derived a class from Feature but then, turfpy operations on those derived classes no longer work...

from geojson import Point, Feature
from turfpy.measurement import distance

class Vertex(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)
print("df: ",df)  # df:  0.019606799666682842

print("v1 is feature?", isinstance(v1, Feature))  # true

dv = distance(v1, v2)
print("dv: ", dv)
# Error:
# Traceback (most recent call last):
#   File "ft.py", line 19, in <module>
#     dv = distance(v1, v2)
#   File "/usr/local/miniconda3/lib/python3.9/site-packages/turfpy/measurement.py", line 112, in distance
#     coordinates1 = get_coord(point1)
#   File "/usr/local/miniconda3/lib/python3.9/site-packages/turfpy/helper.py", line 80, in get_coord
#     raise Exception("coord must be GeoJSON Point or an Array of numbers")
# Exception: coord must be GeoJSON Point or an Array of numbers

I'd rather derive my Vertex class from Feature rather than "compose" by adding a feature attribute to my Vertex class.
Thanks in advance for your help.
P.

Trouble with boolean_point_in_polygon() on irregular polygons.

I have a program that uses scipy spatial SpericalVoronoi to generate irregular non-overlapping polygons covering the surface of a sphere and I'm trying to use turfy to rasterize the polygons with boolean_point_in_polygon(). Some example outputs are:

https://imgur.com/a/nIQTSNF
https://imgur.com/a/8DpitYe

The top image is a method of rasterizing that calculates the distance from a raster square to every edge of every polygon (an edge in this case refers to a single great circle segment between vertices) by projecting the point onto the line segment, finds the edge the raster point is closest to, and determines which side of this edge its on using the sign of the cross track distance (using a custom implementation, not the turpy implementation), and therefore which polygon it belongs to. This has some issues, as can be seen with the inlcusions of the wrong coloured polygon. This occurs when two edges are equally close to a raster point, i.e. the vertex shared by two edges is the closest point, and the wrong edge is selected, resulting in an incorrect rasterization.

I'm trying to get around this by using turfpy to rasterize the polygons, the results of which are shown in the lower image. The trouble is that the geojson polygon needs the polygon vertices to be supplied in clockwise order. While I can create a directed edge for each polygon, I'm having trouble detecting whether a given directed edge is defined clockwise or not, if it isn't I can just reverse the list order. I've tried using Python implementations of two methods described here without much luck: https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order/1180256#1180256

Signed Area

area = 0
for i in range(len(verts)):
    v1 = verts[i]
    v2 = verts[(i+1)%len(verts)]
    area += (v2[0] - v1[0]) * (v2[1] + v1[1])
if area < 0: verts = verts[::-1]
self.poly = Polygon([verts])

Convex Hull Point

minLat = 1e18
A = None
for v in verts:
    if v[1] < minLat:
        minLat = v[1]
        A = v
    elif v[1] == minLat and v[0] > A[0]:
        A = v
Aindex = indexOf(verts, A)
A = np.array(A)
B = np.array(verts[(Aindex-1)%len(verts)])
C = np.array(verts[(Aindex+1)%len(verts)])
AB = B - A
AC = C - A
cross = np.cross(AB, AC)
if cross < 0: verts = verts[::-1]
self.poly = Polygon([verts])

If anybody knows what is going wrong, or what algorithm turfpy uses for boolean_point_in_polygon so I can write an implementation that is direction agnostic, that would be super helpful.

Create Demo Notebook

Create a demo Jupyter notebook to show functionality and visualizations of the turfpy.

nearest_point always returns first feature in collection?

Hello,

Could you please explain this behavior?
To me, it seems that nearest_point always returns the first feature in the supplied collection, not the closest one.

P.

`
from geojson import Point, Feature, FeatureCollection
from turfpy.measurement import distance, nearest_point

p0 = Point((25.0, 51.0))

p1 = Point((25.2, 51.2))
f1 = Feature(geometry=p1)
p2 = Point((25.4, 51.6))
f2 = Feature(geometry=p2)

print("to p1", distance(p0, p1))
#to p1 26.260267785645777

print("to p2", distance(p0, p2))
#to p2 72.28066331033915

fc1 = FeatureCollection(features=[f1, f2])
print("to p1", nearest_point(p0, fc1))
#to p1 {"geometry": {"coordinates": [25.2, 51.2], "type": "Point"}, "properties": {"distanceToPoint": 26.260267785645777, "featureIndex": 0}, "type": "Feature"}

fc2 = FeatureCollection(features=[f2, f1])
print("to p2", nearest_point(p0, fc2))
#to p2 {"geometry": {"coordinates": [25.4, 51.6], "type": "Point"}, "properties": {"distanceToPoint": 72.28066331033915, "featureIndex": 0}, "type": "Feature"}
`

Generalize feature collection param in nearest_point?

It would be much more convenient to allow arbitrary feature collections in turfpy.measurements.nearest_point(pt, fc). Right now this has to be a list of points, which the user will likely often have to create from an existing feature collection. Such a change might result in a more general API than the one originally offered by turf.js. Which needs to be explained, etc.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.