Polygon intersection error in Shapely: "shapely.geos.TopologicalError: The operation 'GEOSIntersection_r' produced
Tag : python , By : ArdentRogue
Date : March 29 2020, 07:55 AM
I wish did fix the issue. I have been trying to debug this problem but unable to do so. I am trying to find the intersection of two Polygon objects. It works most of the time but for the following case, it raises the following exception: , You are getting this exception because p1 is not a valid polygon. >>> p1.is_valid
False
>>> p2.is_valid
True
>>> list(p1.exterior.coords)
[(35.004100000000001, -88.195499999999996), (34.991799999999998, -85.606800000000007), (32.840400000000002, -85.175600000000003), (32.259300000000003, -84.892700000000005), (32.153500000000001, -85.034199999999998), (31.794699999999999, -85.135800000000003), (31.52, -85.043800000000005), (31.3384, -85.083600000000004), (31.209299999999999, -85.106999999999999), (31.002300000000002, -84.994399999999999), (30.9953, -87.600899999999996), (30.942299999999999, -87.592600000000004), (30.853899999999999, -87.625600000000006), (30.674499999999998, -87.407200000000003), (30.4404, -87.368799999999993), (30.1463, -87.524000000000001), (30.154599999999999, -88.386399999999995), (31.893899999999999, -88.474299999999999), (34.893799999999999, -88.102099999999993), (34.947899999999997, -88.1721), (34.910699999999999, -88.146100000000004), (35.004100000000001, -88.195499999999996)]
>>> p1.exterior.type
'LinearRing'
>>> p1.exterior.is_valid
False
>>> l1 = LineString(p1.exterior.coords)
>>> l1.is_simple
False
>>> import cgpolyencode
>>> encoder = cgpolyencode.GPolyEncoder()
>>> encoder.encode((y, x) for x, y in p1.exterior.coords)
{'points': 'svstEzthyOzkAkrxNfecL_fsAznpBcgv@ftSjsZnaeA~yRzst@_~P~mb@vwFzeXfqCvlg@w~Tvj@ra|NfjI{r@ngPfmEf`b@_ti@bvl@_oFbmx@~h]{r@~lgDsurIjdPk|hQgugAaqIntLlgFoaDwfQvsH', 'numLevels': 18, 'zoomFactor': 2, 'levels': 'PPLMKMKGKPNIKLMNPLLKJP'}
|
Apply a pairwise shapely function on two numpy arrays of shapely objects
Tag : python , By : user186831
Date : March 29 2020, 07:55 AM
Hope that helps The following code shows how to apply a function on geometric objects contained in two arrays of different length. This approach avoids using loops. Pandas' apply and Numpy's .vectorize and broadcasting options are required. First consider doing some imports and the two following arrays: import numpy as np
import pandas as pd
from shapely.geometry import Polygon, Point
polygons = [[(1,1),(4,3),(4,1),(1,1)],[(2,4),(2,6),(4,6),(4,4),(2,4)],[(8,1),(5,1),(5,4),(8,1)]]
points = [(3,5),(7,3),(7,6),(3,2)]
geo_polygons = pd.DataFrame({'single_column':polygons}).single_column.apply(lambda x: Polygon(x)).values
geo_points = pd.DataFrame({'single_column':points}).single_column.apply(lambda x: Point(x[0], x[1])).values
# As you might noticed, the arrays have different length.
def contains(a_polygon, a_point):
return a_polygon.contains(a_point)
contains_vectorized = np.vectorize(contains)
contains_vectorized(geo_polygons, geo_points[:,np.newaxis])
array([[False, True, False],
[False, False, False],
[False, False, False],
[ True, False, False]], dtype=bool)
from descartes import PolygonPatch
import matplotlib.pyplot as plt
fig = plt.figure(1, figsize = [10,10], dpi = 300)
ax = fig.add_subplot(111)
offset_x = lambda xy: (xy[0] + 0.1, xy[1])
offset_y = lambda xy: (xy[0], xy[1] - 0.5)
for i,j in enumerate(geo_polygons):
ax.add_patch(PolygonPatch(j, alpha=0.5))
plt.annotate('polygon {}'.format(i + 1), xy= offset_y(tuple(j.centroid.coords[0])))
for i,j in enumerate(geo_points):
ax.add_patch(PolygonPatch(j.buffer(0.07),fc='orange',ec='black'))
plt.annotate('point {}'.format(i + 1), xy= offset_x(tuple(j.coords[0])))
ax.set_xlim(0, 9)
ax.set_ylim(0, 7)
ax.set_aspect(1)
plt.show()
|
Shapely Split LineStrings at Intersections with other LineStrings
Date : March 29 2020, 07:55 AM
should help you out Here is a more general way: calculating the distance along the line for all points (start and end point of the line + points where you want to split), sort by these points and then generate the line segments in the right order. Together in a funtion: def cut_line_at_points(line, points):
# First coords of line (start + end)
coords = [line.coords[0], line.coords[-1]]
# Add the coords from the points
coords += [list(p.coords)[0] for p in points]
# Calculate the distance along the line for each point
dists = [line.project(Point(p)) for p in coords]
# sort the coords based on the distances
# see http://stackoverflow.com/questions/6618515/sorting-list-based-on-values-from-another-list
coords = [p for (d, p) in sorted(zip(dists, coords))]
# generate the Lines
lines = [LineString([coords[i], coords[i+1]]) for i in range(len(coords)-1)]
return lines
In [13]: SplitSegments = cut_line_at_points(MyLine, IntPoints)
In [14]: gpd.GeoSeries(SplitSegments)
Out[14]:
0 LINESTRING (0 0, 2.75 0)
1 LINESTRING (2.75 0, 5.833333333333333 0.5)
2 LINESTRING (5.833333333333333 0.5, 10 3)
dtype: object
def cut_line_at_points(line, points):
# First coords of line
coords = list(line.coords)
# Keep list coords where to cut (cuts = 1)
cuts = [0] * len(coords)
cuts[0] = 1
cuts[-1] = 1
# Add the coords from the points
coords += [list(p.coords)[0] for p in points]
cuts += [1] * len(points)
# Calculate the distance along the line for each point
dists = [line.project(Point(p)) for p in coords]
# sort the coords/cuts based on the distances
# see http://stackoverflow.com/questions/6618515/sorting-list-based-on-values-from-another-list
coords = [p for (d, p) in sorted(zip(dists, coords))]
cuts = [p for (d, p) in sorted(zip(dists, cuts))]
# generate the Lines
#lines = [LineString([coords[i], coords[i+1]]) for i in range(len(coords)-1)]
lines = []
for i in range(len(coords)-1):
if cuts[i] == 1:
# find next element in cuts == 1 starting from index i + 1
j = cuts.index(1, i + 1)
lines.append(LineString(coords[i:j+1]))
return lines
In [3]: SplitSegments = cut_line_at_points(MyLine, IntPoints)
In [4]: gpd.GeoSeries(SplitSegments)
Out[4]:
0 LINESTRING (0 0, 2.75 0)
1 LINESTRING (2.75 0, 5 0, 5.833333333333333 0.5)
2 LINESTRING (5.833333333333333 0.5, 10 3)
dtype: object
|
Shapely: Polygon from String?
Date : March 29 2020, 07:55 AM
hope this fix your issue Shapely can directly parse this: import shapely.wkt
P = shapely.wkt.loads('POLYGON ((51.0 3.0, 51.3 3.61, 51.3 3.0, 51.0 3.0))')
print(P)
|
Sectors representing and intersections in shapely
Date : March 29 2020, 07:55 AM
Does that help You can create a sector as a shapely object with the following function: from shapely.geometry import Point, Polygon
import math
def sector(center, start_angle, end_angle, radius, steps=200):
def polar_point(origin_point, angle, distance):
return [origin_point.x + math.sin(math.radians(angle)) * distance, origin_point.y + math.cos(math.radians(angle)) * distance]
if start_angle > end_angle:
start_angle = start_angle - 360
else:
pass
step_angle_width = (end_angle-start_angle) / steps
sector_width = (end_angle-start_angle)
segment_vertices = []
segment_vertices.append(polar_point(center, 0,0))
segment_vertices.append(polar_point(center, start_angle,radius))
for z in range(1, steps):
segment_vertices.append((polar_point(center, start_angle + z * step_angle_width,radius)))
segment_vertices.append(polar_point(center, start_angle+sector_width,radius))
segment_vertices.append(polar_point(center, 0,0))
return Polygon(segment_vertices)
center = Point(0,0)
sect = sector(center, 10, 60, 20)
square = Polygon([(0,0), (0,10),(10,10), (10,0)])
intersection = sect.intersection(square)
calculated_area = intersection.area
|