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)
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 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
I have coordinates (lat and lng) in an excel document. I have a shapefile which contains all the different Canada provinces shapes. I would like to be able to generate a new field in excel in order to classify the different coordinates into the different Canada provinces. I tried the below code, but it is not working.
import fiona
import shapely.geometry
with fiona.open(r"D:\Users\Jonathan\Desktop\CRA-Project v2\Census Division\lcd_000b16a_e.shp") as fiona_collection:
shapefile_record = fiona_collection.next()
# Use Shapely to create the polygon
shape = shapely.geometry.asShape(shapefile_record['geometry'])
#print(shape)
point = shapely.geometry.Point(46.362914,-63.503809) # longitude, latitude
# Alternative: if point.within(shape)
if shape.contains(point):
print("Found shape for point.")
Update 1:
point = shapely.geometry.Point(46.362914,-63.503809)
Polygon:
Link
Update Solution:
I am updating this post because I found the solution and hopefully it can help someone!
If you are using shapefiles, there is a projection associated with
it (documentation)
Review the documentation and identify which
projection it is using. For example: In Canada it is Lambert
conformal conic.
Convert your lat/long to the projection equivalency
Loop through the shapefile to identify if the new lat/long
equivalent is within a polygon/multipolygon. You can do this with
python!
There are several issues with this. First, your shapefile (Multipolygon) seems to be in a different projection so in order to work in longitude/latitude coordinates, one needs to transform it. Moreover, even after the transformation, the x-coordinate is the longitude, thus the coordinates of the point you are testing against should be swapped. An example might look as shown below. The input projection is assumed to be PCS_Lambert_Conformal_Conic (EPSG:3347) - this is specified in the accompanying prj file.
from functools import partial
import sys
import fiona
from shapely.geometry import Point, Polygon, asShape
from shapely.ops import transform
from shapely.wkt import loads
import pyproj
project = partial(
pyproj.transform,
pyproj.Proj(init='epsg:3347'),
pyproj.Proj(init='epsg:4326'))
P = Point(-63.503809, 46.362914)
with fiona.open(sys.argv[1]) as F:
for idx,feature in enumerate(F):
G = transform(project, asShape(feature['geometry']))
if G.contains(P):
print(feature['properties'])
break
This produces:
OrderedDict([('CDUID', '1102'), ('CDNAME', 'Queens'), ('CDTYPE', 'CTY'), ('PRUID', '11'), ('PRNAME', 'Prince Edward Island / Île-du-Prince-Édouard')])
i.e., it finds the coordinates within the Prince Edward Island.
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've two linestrings Line1,Line2.
line1 = "LINESTRING(72.863221 18.782499,72.863736 18.770147,72.882275 18.756169,72.881417 18.750805,72.878842 18.736987,72.874379 18.709512,72.860989 18.679593,72.864422 18.653897)"
line2 = "LINESTRING(72.883133 18.780793,72.882103 18.760314,72.862534 18.716422,72.860474 18.683577)"
I'm trying to perform the following query of POSTGIS in shapely. As of now I haven't been able to find the alternative of ST_DWithin command.
road2 = "ST_GeographyFromText('SRID=4326;%s')"%line1
road4 = "ST_GeographyFromText('SRID=4326;%s')"%line2
cur.execute("SELECT ST_AsText(road1) from %s as road1,%s as road2
where ST_DWithin(road1,road2,500)"%(road2,road4))
res = cur.fetchall()
print res
Does anyone knows what is the alternative of ST_DWithin in shapely ?
As far as I know, shapely only supports operations in planar coordinates (no geography types). However, for LineStrings which are not too large so that the curvature of the globe can be neglected, one could partially "circumvent" this by:
working in some planar projection (for example directly in the lat/lon or lon/lat coordinates)
following the second note in the documentation of ST_DWithin and the first note in the documentation of ST_Expand, i.e.:
checking if the bounding box of the second LineString intersects with the expanded bounding box of the first LineString
if yes, checking if the minimum distance is indeed below the prescribed threshold
For example:
from shapely.wkt import dumps, loads
from shapely.geometry.geo import box
spec = [
"LINESTRING(72.863221 18.782499,72.863736 18.770147,72.882275 18.756169,72.881417 18.750805,72.878842 18.736987,72.874379 18.709512,72.860989 18.679593,72.864422 18.653897)",
"LINESTRING(72.883133 18.780793,72.882103 18.760314,72.862534 18.716422,72.860474 18.683577)"
]
lines = list(map(loads, spec))
eta = 0.005
b1 = box(*lines[0].bounds).buffer(eta)
b2 = box(*lines[1].bounds)
flag = b2.intersects(b1) and (lines[0].distance(lines[1]) < eta)
print(eta, flag)
Alternatively, if you would like to check if the entire second LineString is within prescribed threshold from the first LineString, you could also use the buffer method as:
lines[0].buffer(eta).contains(lines[1])
The threshold supplied here to the buffer method is expressed in the same coordinate system in which the LineStrings are defined. Within the lon/lat system, this would represent the "central angle" - the issue then consists in the fact that the great circle distance corresponding to a fixed eta not only depends on the particular values of latitude and longitude but also on the direction of the displacement. However, if the LineStrings are not too large and the required precision is not too high, it probably won't matter that much.