I have a list of points describing the boundaries of Spain. I want to be able to tell whether a pair of lat,lon is within these boundaries. I have tried the following:
import shapefile
import matplotlib.pyplot as plt
from shapely.geometry import MultiPoint, Point, Polygon
from shapely.geometry.polygon import Polygon
sf = shapefile.Reader(r"\ESP_adm0.shp")
shapes = sf.shapes()
lat = []; lon = []
for i in range(len(shapes[0].points)):
lon.append(shapes[0].points[i][0]);lat.append(shapes[0].points[i][1])
I know I am retrieving the points, because I'm able to plot and get the desired results:
plt.plot(lon,lat,'.', ms=0.1)
(plot in the link below)
plot result
I do the following to get the poitns into a polygon:
coords = list(zip(lat,lon))
spain_pol = Polygon(coords)
And then I use the contains function, always getting false.
spain_pol.contains(Point(0,42))
spain_pol.contains(Point(42,0))
These both return false. In fact I haven't been able to get a single point I've tried to return a True.
I have tried all sorts of things, and I think I must be missing something fundamental. Perhaps the facts Spain has islands and there's more than one polygon is the problem? I'm lost. Any help welcome.
Just in case someone else has the same issue. The following code worked perfectly:
import shapefile
import fiona
from shapely.geometry import MultiPoint, Point, Polygon,shape
from shapely.geometry.polygon import Polygon
multipol = fiona.open(r"C:\Users\Jordi\Downloads\ESP_adm_shp\ESP_adm0.shp")
multi = next(iter(multipol))
point = Point(0,42)
point.within(shape(multi['geometry']))
This returns a very welcome "True" :)
Related
I'm trying to calculate the size of a polygon of geographic coordinates using shapely, which seems to require a transformation into a suitable projection to yield a results in square meter. I found a couple of examples online, but I couldn't get it working for my example polygon.
I therefore tried to use the same example polygons that came with the code snippets I found, and I noticed that it works for some whole not for others. To reproduce the results, here's the minimal example code:
import json
import pyproj
from shapely.ops import transform
from shapely.geometry import Polygon, mapping
from functools import partial
coords1 = [(-97.59238135821987, 43.47456565304017),
(-97.59244690469288, 43.47962399877412),
(-97.59191951546768, 43.47962728271748),
(-97.59185396090983, 43.47456565304017),
(-97.59238135821987, 43.47456565304017)]
coords1 = reversed(coords1) # Not sure if important, but https://geojsonlint.com says it's wrong handedness
# Doesn't seem to affect the error message though
coords2 = [(13.65374516425911, 52.38533382814119),
(13.65239769133293, 52.38675829106993),
(13.64970274383571, 52.38675829106993),
(13.64835527090953, 52.38533382814119),
(13.64970274383571, 52.38390931824483),
(13.65239769133293, 52.38390931824483),
(13.65374516425911, 52.38533382814119)]
coords = coords1 # DOES NOT WORK
#coords = coords2 # WORKS
polygon = Polygon(coords)
# Print GeoJON to check on https://geojsonlint.com
print(json.dumps(mapping(polygon)))
projection = partial(pyproj.transform,
pyproj.Proj('epsg:4326'),
pyproj.Proj('esri:54009'))
transform(projection, polygon)
Both coords1 and coords2 are just copied from code snippets that supposedly work. However, only coords2 works for me. I've used https://geojsonlint.com to see if there's a difference between the two polygons, and it seems that the handedness/orientation of the polygon is not valid GeoJSON. I don't know if shapely even cares, but reversing the order -- and https://geojsonlint.com says it's valid GeoJSON then, and it shows the polygon on the map -- does not change the error.
So, it works with coords2, but when I use coords1 I get the following error:
~/env/anaconda3/envs/py36/lib/python3.6/site-packages/shapely/geometry/base.py in _repr_svg_(self)
398 if xmin == xmax and ymin == ymax:
399 # This is a point; buffer using an arbitrary size
--> 400 xmin, ymin, xmax, ymax = self.buffer(1).bounds
401 else:
402 # Expand bounds by a fraction of the data ranges
ValueError: not enough values to unpack (expected 4, got 0)
I assume there's something different about coords1 (and the example polygon from my own data) that causes the problem, but I cannot tell what could be different compared to coords2.
In short, what's the difference between coords1 and coords2, with one working and the other not?
UPDATE: I got it working by adding always_xy=True to the definition of the projections. Together with the newer syntax provided by shapely, avoiding partial, the working snippet looks like this:
project = pyproj.Transformer.from_proj(
pyproj.Proj('epsg:4326'), # source coordinate system
pyproj.Proj('epsg:3857'),
always_xy=True
) # destination coordinate system
transform(project.transform, polygon)
To be honest, even after reading the docs, I don't really know what always_xy is doing. Hence I don't want to provide is an answer.
i think you did good, only that the reversed does not create new dataset.
try to use this function to create reversed order list:
def rev_slice(mylist):
'''
return a revered list
mylist: is a list
'''
a = mylist[::-1]
return a
execute the function like so:
coords = rev_slice(coords1)
I'm having a lot of trouble with getting shapely points to actually snap on linestrings. This is really important to be able to construct a network graph from geospatial data for example. I would like to avoid using shapely.ops.snap as according to the docs this seems only to snap to line vertices. Instead I would like to use the linear referencing technique proposed in this question. For a simple point-in-linestring situation (see TEST2 below) this seems to work fine, and the within assertion evaluates to true. However, I can't seem to get it to work for a more complex linestring, even though matplotlib clearly shows the point to lie somewhere along the linestring.
from shapely.wkt import loads
from shapely.geometry import LineString, Point
import matplotlib.pyplot as plt
import geopandas
class Test:
def __init__(self, point, line):
self.point = point
self.line = line
def snap(self, point, line):
snapped_point = line.interpolate(line.project(point))
return snapped_point
def test(self):
snapped_point = self.snap(self.point, self.line)
plt.close()
fig = plt.figure()
ax = plt.gca()
geopandas.GeoDataFrame(geometry=[snapped_point]).plot(ax=ax)
geopandas.GeoDataFrame(geometry=[ls]).plot(ax=ax)
return snapped_point.within(self.line)
def show(self):
plt.show()
### TEST 1
point = loads('POINT (141508.3132070995 445104.7256057188)')
ls = loads('LINESTRING (141408.7699052179 445535.6376462562, 141420.9609999987 445491.5920000025, 141444.1930000001 445394.7640000023, 141465.3480000009 445299.004, 141486.3999999985 445202.0350000015, 141508.3209999985 445104.6910000004, 141529.2399999978 445006.3560000034, 141550.8399999999 444909.6550000002, 141572.6389999985 444811.9950000045, 141594.0459999999 444713.4210000001, 141614.9339999997 444616.2859999968)')
test1 = Test(point, ls)
print(test1.test())
test1.show()
### TEST 2
point = Point((0.5, 0.46))
ls = LineString([(0,0), (1,1), (2,2)])
test2 = Test(point, ls)
print(test2.test())
test2.show()
Why does the second linestring check evaluate to False? Shouldn't the interpolate and project methods be geometry independent?
I am hoping to create a region on a map and be able to automatically determine if points (coordinates) are inside that region. I'm using a geojson file of the entire US and coordinates for New York City for this example.
Geojson: https://github.com/johan/world.geo.json
I have read the shapely documentation and just can't figure out why my results are returning False. Any help would be much appreciated.
import json
from shapely.geometry import shape, GeometryCollection, Point
with open('USA.geo.json', 'r') as f:
js = json.load(f)
point = Point(40.712776, -74.005974)
for feature in js['features']:
polygon = shape(feature['geometry'])
if polygon.contains(point):
print ('Found containing polygon:', feature)
I'm hoping to print the contained coordinates, but nothing is printed.
You need to swap the values of the Point() around:
point = Point(-74.005974, 40.712776)
The dataset you're using has the longitude first and the latitude second in their coordinates.
How would one extract superpixels, retrieve edges then simplify those?
This is what I've gotten so far:
import skimage
import numpy as np
from rasterio import features
from shapely.geometry import Polygon, MultiPolygon
image = skimage.util.img_as_float(skimage.io.imread("image.jpg"))
labels = skimage.segmentation.slic(image, n_segments = 200, sigma = 5)
boundaries = skimage.segmentation.find_boundaries(labels).astype(np.uint8)
#this is where it goes wrong as rasterio creates shapes with distinct edges
shapes = rasterio.features.shapes(boundaries)
polygons = MultiPolygon([Polygon(sh[0]["coordinates"][0]) for sh in shapes])
out = polygons.simplify(0.05)
The problem here is that the simplification works on a per polygon basis and therefore its output isn't a tight mesh.
I'm looking to achieve something similar to this, so obtaining the edges and being able to simplify.
I get this error while using cascaded_union (I have also tried unary_union which produces the same error):
ValueError: No Shapely geometry can be created from null value
I have validated that my polygons are valid. Initially polyB isn't valid, but it is converted to a valid polygon using buffer(0).
Any idea on what I am doing wrong? Here's my code:
from shapely.geometry import Polygon
from shapely.ops import cascaded_union
def combineBorders(a, b):
polyA = Polygon(a)
polyB = Polygon(b)
pols = [polyA, polyB]
for p in pols:
if p.is_valid == False:
p = p.buffer(0)
print(p.is_valid)
True
True
newShape = cascaded_union(pols) # THIS IS WHERE THE ERROR KEEPS SHOWING UP
return newShape
Here is a link to the values for polyA, polyB and pols (after they are confirmed to be valid). I have the following versions installed on my Ubuntu 14.04 server:
python-shapely 1.3.0
libgeos 3.4.2
python 2.7
The problem in the question is that the buffered polygon was not put back in the list pols, so the invalid geometry was passed to cascaded_union
You could make this much simpler and versatile with the following, which can take any number of polygon geometries (not just two).
def combineBorders(*geoms):
return cascaded_union([
geom if geom.is_valid else geom.buffer(0) for geom in geoms
])
polyC = combineBorders(polyA, polyB)
Found out the problem. Not sure why this matters (I've seen examples showing it both ways), but it works after putting the polygons directly into cascaded_union like so: newShape = cascaded_union([polyA, polyB]). Here is the fully revised code that works:
from shapely.geometry import Polygon
from shapely.ops import cascaded_union
def combineBorders(a, b):
polyA = Polygon(a)
polyB = Polygon(b)
polyBufA = polyA.buffer(0)
polyBufB = polyB.buffer(0)
newShape = cascaded_union([polyBufA, polyBufB])
return newShape
This also works with unary_union