I have LineString and Point, and I want to calculate distance between them in meters:
line = LINESTRING (7.301606 52.5602366, 7.300065099999999 52.5587741)
point = POINT (8.02 52.303333)
I use: line.distance(point). I got a distance, but I don't know how to convert to meters or km. I am using geopandas, and I read that unit is CRS. Any help will be appreciated.
as per comments you need to specify the CRS I am using geopandas, and I read that unit is CRS. I assume by this statement you mean WSG84 CRS, which is EPSG:4326
given limitation of geopandas to have only one geometry column per GeoDataFrame have put LINESTRING and POINT into separate data frames
project from WSG84 onto UTM CRS https://en.wikipedia.org/wiki/Universal_Transverse_Mercator_coordinate_system. This then means co-ordinates are expressed in meters. This will not work if LINESTRING and POINT are in different UTM zones
then it's exceptionally simple to use https://geopandas.readthedocs.io/en/latest/docs/reference/api/geopandas.GeoSeries.distance.html
import shapely.wkt
import geopandas as gpd
gdfl = gpd.GeoDataFrame(geometry=[shapely.wkt.loads("LINESTRING (7.301606 52.5602366, 7.300065099999999 52.5587741)")], crs="EPSG:4326")
gdfp = gpd.GeoDataFrame(geometry=[shapely.wkt.loads("POINT (8.02 52.303333)")], crs="EPSG:4326")
utm = gdfl.estimate_utm_crs()
gdfl.to_crs(utm).distance(gdfp.to_crs(utm))
output
56592.878178
Related
I have the following Polygon geometry. When I calculate the centroid, I get an invalid / inaccurate POINT. I am using geopandas centroid to calculate the point.
https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoSeries.centroid.html
import shapely
from shapely import wkt
from shapely.geometry import box
import geopandas as gpd
g = "POLYGON ((-96.8115234375 32.87109375, -96.8115234375 -96.767578125, 32.8271484375 -96.767578125, 32.8271484375 32.87109375, -96.8115234375 32.87109375))"
print(wkt.loads(g).centroid)
POINT (-31.9921875 -31.9482421875)
How do I calculate the centroid POINT lat, long coordinates of the box? The shape of the Polygon is rectangle.
Your code is fine, your polygon is not and does not represents a rectangle area in Dallas, TX. Let's split it into vertices to make it apparent:
g = "POLYGON ((-96.8115234375 32.87109375,
-96.8115234375 -96.767578125,
32.8271484375 -96.767578125,
32.8271484375 32.87109375,
-96.8115234375 32.87109375))"
The first point is good and is indeed in Dallas. The second point has wrong latitude, -96.7... (it is south of South pole :) - however Shapely does not care and accepts it). Third point has wrong both latitude and longitude, etc. Apparently, latitudes and longitudes got mixed up here.
I have two data frames. One has polygons of buildings (around 70K) and the other has points that may or not be inside the polygons (around 100K). I need to identify if a point is inside a polygon or not.
When I plot both dataframes (example below), the plot shows that some points are inside the polygons and other are not. However, when I use .within(), the outcome says none of the points are inside polygons.
I recreated the example creating one polygon and one point "by hand" rather than importing the data and in this case .within() does recognize that the point is in the polygon. Therefore, I assume I'm making a mistake but I don't know where.
Example: (I'll just post the part that corresponds to one point and one polygon for simplicity. In this case, each data frame contains either a single point or a single polygon)
1) Using the imported data. The data frame dmR has the points and the data frame dmf has the polygon
import pandas as pd
import geopandas as gpd
import numpy as np
import matplotlib.pyplot as plt
from shapely import wkt
from shapely.geometry import Point, Polygon
plt.style.use("seaborn")
# I'm skipping the data manipulation stage and
# going to the point where the data are used.
print(dmR)
geometry
35 POINT (-95.75207 29.76047)
print(dmf)
geometry
41964 POLYGON ((-95.75233 29.76061, -95.75194 29.760...
# Plot
fig, ax = plt.subplots(figsize=(5,5))
minx, miny, maxx, maxy = ([-95.7525, 29.7603, -95.7515, 29.761])
ax.set_xlim(minx, maxx)
ax.set_ylim(miny, maxy)
dmR.plot(ax=ax, c='Red')
dmf.plot(ax=ax, alpha=0.5)
plt.savefig('imported_data.png')
The outcome
shows that the point is inside the polygon. However,
print(dmR.within(dmf))
35 False
41964 False
dtype: bool
2) If I try to recreate this by hand, it would be as follows (there may be a better way to do this but I couldn't figure it out):
# Get the vertices of the polygon to create it by hand
poly1 = dmf['geometry']
g = [i for i in poly1]
x,y = g[0].exterior.coords.xy
x,y
(array('d', [-95.752332508564, -95.75193554162979, -95.75193151831627, -95.75232848525047, -95.752332508564]),
array('d', [29.760606530637265, 29.760607694859385, 29.76044470363038, 29.76044237518235, 29.760606530637265]))
# Create the polygon by hand using the corresponding vertices
coords = [(-95.752332508564, 29.760606530637265),
(-95.75193554162979, 29.760607694859385),
(-95.75193151831627, 29.7604447036303),
(-95.75232848525047, 29.76044237518235),
(-95.752332508564, 29.760606530637265)]
poly = Polygon(coords)
# Create point by hand (just copy the point from 1) above
p1 = Point(-95.75207, 29.76047)
# Create the GeoPandas data frames from the point and polygon
ex = gpd.GeoDataFrame()
ex['geometry']=[poly]
ex = ex.set_geometry('geometry')
ex_p = gpd.GeoDataFrame()
ex_p['geometry'] = [p1]
ex_p = ex_p.set_geometry('geometry')
# Plot and print
fig, ax = plt.subplots(figsize=(5,5))
ax.set_xlim(minx, maxx)
ax.set_ylim(miny, maxy)
ex_p.plot(ax=ax, c='Red')
ex.plot(ax = ax, alpha=0.5)
plt.savefig('by_hand.png')
In this case, the outcome also shows the point in the polygon. However,
ex_p.within(ex)
0 True
dtype: bool
which recognize that the point is in the polygon. All suggestions on what to do are appreciated! Thanks.
I don't know if this is the most efficient way to do it but I was able to do what I needed within Python and using Geopandas.
Instead of using point.within(polygon) approach, I did a spatial join (geopandas.sjoin(df_1, df_2, how = 'inner', op = 'contains')) This results in a new data frame that contains the points that are within polygons and excludes the ones that are not. More information on how to do this can be found here.
I assume something is fishy about your coordinate reference system (crs). I cannot tell about dmr as it is not provided but ex_p is a naive geometry as you generated it from points without specifying the crs. You can check the crs using:
dmr.crs
Let's assume it's in 4326, then it will return:
<Geographic 2D CRS: EPSG:4326>
Name: WGS 84
Axis Info [ellipsoidal]:
- Lat[north]: Geodetic latitude (degree)
- Lon[east]: Geodetic longitude (degree)
Area of Use:
- name: World
- bounds: (-180.0, -90.0, 180.0, 90.0)
Datum: World Geodetic System 1984
- Ellipsoid: WGS 84
- Prime Meridian: Greenwich
In this case you would need to set a CRS for ex_p first using:
ex_p = ex_p.set_crs(epsg=4326)
If you want to inherit the crs of dmr dynamically you can also use:
ex_p = ex_p.set_crs(dmr.crs)
After you set a crs, you can re-project from one crs to another using:
ex_p = ex_p.to_crs(epsg=3395)
More on that topic:
https://geopandas.org/projections.html
I'm using pandas, and a dataset I obtained has a location column in a WKT format. For example:
hospital.get_value(1,'WKT')
POLYGON ((-58.4932 -34.5810,-58.4925 -34.5815,-58.4924 -34.5817))
There's a lot more points and with bigger precision in this example, but I shortened it for illustrative purposes. Also, I don't know whether it is a WKT or just a string. How do I obtain the center of this polygon so I can use it as a coordinate? Thanks in advance.
You almost have WKT, except that a polygons' linear ring needs to be closed.
Shapely has a .centroid property to get the center point:
from shapely import wkt
g = wkt.loads(
'POLYGON ((-58.4932 -34.5810,-58.4925 -34.5815,-58.4924 -34.5817,-58.4932 -34.5810))')
print(g.centroid) # POINT (-58.49270000000001 -34.5814)
print(g.centroid.coords[0]) # (-58.492700000000006, -34.581399999999995)
as i know, shapely use only cartesian coordinate system. I have two point on the earth with lat and lon coordinates. I need create buffer with 1km radius around this two points and find polygon, where this buffers intersect.
But construstion
buffer = Point(54.4353,65.87343).buffer(0.001) create simple circle, but in projection on the Earth it becomes ellipse, but i need two real circle with 1 km radius.
I think, i need convert my buffers into new projection and then intersect it, but dont now how correct do it.
You need to do what you say. For that, you will need to use a library that handles projections (pyproj is the choice here). There is a similar question in Geodesic buffering in python
import pyproj
from shapely.geometry import MultiPolygon, Polygon, Point
from shapely.ops import transform as sh_transform
from functools import partial
wgs84_globe = pyproj.Proj(proj='latlong', ellps='WGS84')
def point_buff_on_globe(lat, lon, radius):
#First, you build the Azimuthal Equidistant Projection centered in the
# point given by WGS84 lat, lon coordinates
aeqd = pyproj.Proj(proj='aeqd', ellps='WGS84', datum='WGS84',
lat_0=lat, lon_0=lon)
#You then transform the coordinates of that point in that projection
project_coords = pyproj.transform(wgs84_globe, aeqd, lon, lat)
# Build a shapely point with that coordinates and buffer it in the aeqd projection
aeqd_buffer = Point(project_coords).buffer(radius)
# Transform back to WGS84 each coordinate of the aeqd buffer.
# Notice the clever use of sh_transform with partial functor, this is
# something that I learned here in SO. A plain iteration in the coordinates
# will do the job too.
projected_pol = sh_transform(partial(pyproj.transform, aeqd, wgs84_globe),
aeqd_buffer)
return projected_pol
The function point_buff_on_globe will give you a polygon in lat lon that is the result of buffering the given point in the Azimuthal Equidistant Projection centered in that point (the best you can do with your requirements. Two observations:
I don't remember the units of the radius argument. I think is in meters, so if you need a 10 km buffer, you will need to pass it 10e3. But please, check it!
Beware of using this with radius to wide or points that are to far away from each other. Projections work well when the points are near to the point you are centering the projection.
I calculated linestring from google maps directions api.
I converted the linestring to GEOSGeometry object. I need to another region which covers all the points at a distance of 'd' from the linestring object.
The distance is in m, km.
GEOS API provides GEOSGeometry.buffer(width, quadsegs=8) to do so which works well in 2-D projection.
But how to do so for spherical model ? Is it related to SRID.
from django.contrib.gis.geos import LineString
from django.contrib.gis.geos import GEOSGeometry
directions = maps_client.directions(source, destination)
overview_polyline = decode_polyline(directions[0]['overview_polyline'])
linestring_obj = LineString(overview_polyline)
# FOR 2-D projection
bounding_box = linestring_obj.buffer(width=100)
# For spherical model
# ???
For geographical distance in meters to make sense, you will always have to go through a projected coordinate system, so I would suggest that you transform your data to a projected coordinate system, create the buffer and project it back. For instance:
# Specify the original srid of your data
orig_srid = 4326
# Create the linestring with the correct srid
linestring_obj = LineString(overview_polyline, srid=orig_srid)
# Transform (project) the linestring into a projected coorinate system
linestring_obj.transform(3857)
# Compute bbox in in that system
bounding_box = linestring_obj.buffer(width=100)
# Transform bounding box into the original coorinate system of your data
bounding_box.transform(orig_srid)