omanges / turfpy Goto Github PK
View Code? Open in Web Editor NEWA Python library for performing geospatial data analysis which reimplements turf.js.
License: MIT License
A Python library for performing geospatial data analysis which reimplements turf.js.
License: MIT License
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.
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.
asking with sincere curiosity - I am sure there are benefits to that
Implement transformRotate, transformTranslate, transformScale functionality in transformation
https://turfjs.org/docs/#transformRotate
Implement tesselate functionality in transformation
Please add which components (East, West, ...) are returned in which order.
https://github.com/omanges/turfpy/blob/master/turfpy/transformation.py#L659
0 is the same as None in that expression.
Always test None in python with:
if direction is None:
raise Exception("direction is required")
Create a demo Jupyter notebook to show functionality and visualizations of the turfpy.
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...
i've got a list of lines, and i want to merge these lines into some polygons, can i use union to accomplish the purpose?
Hi,
Thanks for porting this lib from JS to Python.
Do you have any vision on when the buffer transformation will be implemented ?
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])
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!
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?
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"}
`
Add new functionality for randomPoint
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.
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])"
I'm missing another function: lineSlice(). Is it possible to add it?
Thank you!
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"}
So this widespread idiom would work:
import turfpy
print(turfpy.__version__)
I'm missing this function: lineIntersect(). Is it possible to add it?
Add new functionality for randomLineString
There seems to be some issue in rhumb_bearing.
Current code coverage is low and needs to be increased.
Add more tests.
Explore Documentation like - https://ipyleaflet.readthedocs.io/en/latest/
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.
Would seem to make a lot of sense especially together with its hypothesis-geojson plugin (although apparently no more maintained recently):
Implement the below functions:
link - https://turfjs.org/docs/
buffer
clone
concave -- DONE
convex -- DONE
difference --DONE
dissolve. --DONE
lineOffset
simplify
tesselate
transformRotate
transformTranslate
transformScale
union --DONE
voronoi
Implement lineOffset functionality in transformation
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.
Add new functionality for randomPolygon
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
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.
Add new functionality for randomPosition
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).
turfpy.measurement.points_within_polygon
Calling turfpy.measurement.points_within_polygon(points,polygon)
takes roughly 26 seconds.
shapely.geometry.Polygon.contains
I wrote a method that:
shapely.geometry.Polygon
called polygon
from a geojson.Polygon
and also creates a Python list
of points of type shapely.geometry.Point
,polygon.contains(point)
is True
,geojson.featureCollection
, which it returns.This takes roughly 5 seconds.
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)
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!
Implement Voronoi functionality in the transformation
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.