I have a dataset of circular polygons that correspond to tree crowns.
Many polygons overlap with each other, and some are even completely covered by larger polygons (or larger polygons covered by many small polygons). I would like to clip polygons based on attribute value (tree height), where the maximum height polygons clip the polygons with lower height values.
Image below describes the situation, where 1 is the lowest tree height and 3 is the tallest:
I attempted using this workflow in QGIS (https://gis.stackexchange.com/questions/427555/cut-polygons-with-each-other-based-on-attribute-value), but it takes very long and was unusable for larger datasets.
I would prefer to use Python, but if you can accomplish with any other programming language I would accept. Thanks in advance!
Test dataset located at:
https://github.com/arojas314/data-sharing/blob/main/niwo010_treepolys.zip
I attempted but only got as far as splitting the polygons with the boundaries (lines) of each polygon, creating smaller polygons where they over lap:
import shapely
import geopandas as gpd
# 1. convert polys to lines
tree_lines = tree_polys_valid.boundary
# 2. Split polygons by lines
merged_lines = shapely.ops.linemerge(tree_lines.values)
border_lines = shapely.ops.unary_union(merged_lines)
decomposition = shapely.ops.polygonize(border_lines)
# 3. Convert into GeoSeries
poly_series = gpd.GeoSeries(list(decomposition))
Related
I want to replicate the effects shown in this Link of iteratively generating polygons with an increasing number of points from an svg path.
I currently managed to convert the svg path to a polygon, using svgpath2mpl's parse_path and to_polygons functions, then converting them to a Shapely polygons. However with this approach it generates a polygon containing all the points on the path. Is there a way to sample the polygon/path to get different accuracy of polygons? This can be done before or after the conversion to polygon.
Here is what I currently have:
from svgpath2mpl import parse_path
mpl_path = parse_path('M12.302 798.100 c0.300 ...')
coordinates = mpl_path.to_polygons()
from shapely.geometry import Polygon
poly = Polygon(coordinates[0])
I also generated matplotlibs plots before and after converting using shapely and they give identical results, while this is the original path itself. The plot for the svg path was generated using this online tool.
The function below should probably work:
def sample_polygon(polygon, n_points):
'''
Generates a new shapely Polygon with a specific number of points sampled
from the original input.
Parameters
----------
polygon : shapely.geometry.Polygon
The input polygon from which the points will be sampled
n_points : integer
The number of points to sample
Returns
-------
sampled_poly : shapely.geometry.Polygon
Newly-generated polygon. It will have as many points as the `n_points`
variable demands.
'''
# Sampling the coordinates
sample_coords = [polygon
.exterior
.interpolate(i/n_points,
normalized=True) for i in range(n_points)]
# Generating a new polygon
sampled_poly = shapely.geometry.Polygon(sample_coords)
return sampled_poly
I have an irregular grid of triangles in one polygon shapefile. These cells are themed to show only triangles above my threshold level for 'interest'. Adjacent triangles, that are visible, are considered real. Spatially isolated triangles need to be removed as they could be spurious.
I can filter using definition query to remove the triangles below threshold but I cannot figure out how to remove the isolate triangles.
I'm aware that I probably need to use polygon neighbors
screenshot from Arcgis
please send help!
I was facing similar kind of issue,so I did workaround and set appropriate threshold
from shapely.geometry import Polygon
coords1 = [(54.950899, 60.169158), (54.953492, 60.169158), (54.950958, 60.169990)]
poly1 = Polygon(coords1)
coords2 = [(24.950899, 60.169158), (24.953492, 60.169158), (24.950958, 60.169990)]
poly2 = Polygon(coords2)
poly1.distance(poly2)
# 29.997407
poly1.distance(poly1)
#0.0
You can set the threshold value to identify spatially isolated triangles
P.S. This workaround worked for me. This is solution is for your reference. Here Random polygons are taken.
Reference:
https://automating-gis-processes.github.io/site/index.html
I would use the Near tool using the same features as the Input Features and Near Features. After if runs, check the attribute table for the new field NEAR_DIST, storing distances to the nearest features.
All records with a NEAR_DIST = 0 touch a polygon. Where the NEAR_DIST > 0 will be the spatially isolated polygons that you are after.
I have a large list of polygons (>10^6) most of which are non-intersecting but some of these polygons are hole of another polygon (~10^3 cases). Here a image to explain the problem, the smaller polygon is a hole in the larger polygon but both are independent polygon in the list of polygons.
Now I would like to efficiently determine which polygons are holes and substract the holes i.e. subtract the smaller polygons which lie completely inside in another polygon and return a list of "cleaned" polygons. A pair of hole and parent polygon should be transformed like this (so basically hole subtracted from the parent):
There are plenty of similar questions on Stackoverflow and gis.stackexchange.com but I haven't found one that actually solves this problems. Here are some related questions:
1. https://gis.stackexchange.com/questions/5405/using-shapely-translating-between-polygons-and-multipolygons
2. https://gis.stackexchange.com/questions/319546/converting-list-of-polygons-to-multipolygon-using-shapely
Here is a sample code.
from shapely.geometry import Point
from shapely.geometry import MultiPolygon
from shapely.ops import unary_union
import numpy as np
#Generate a list of polygons, where some are holes in others;
def generateRandomPolygons(polygonCount = 100, areaDimension = 1000, holeProbability = 0.5):
pl = []
radiusLarge = 2 #In the real dataset the size of polygons can vary
radiusSmall = 1 #Size of holes can also vary
for i in range(polygonCount):
x, y = np.random.randint(0,areaDimension,(2))
rn1 = np.random.random(1)
pl.append(Point(x, y).buffer(radiusLarge))
if rn1 < holeProbability: #With a holeProbability add a hole in the large polygon that was just added to the list
pl.append(Point(x, y).buffer(radiusSmall))
return pl
polygons = generateRandomPolygons()
print(len(pl))
Output looks like this:
Now how can I create a new list of polygons with the holes removed. Shapely provides functions to subtract one polygon from another (difference) but is there a similar function for lists of polygons (maybe something like unary_union but where overlaps are removed)? Alternative how to efficiently determine which are holes and then subtract them from the larger polygons?
Your problem is you don't know which ones are "holes", right? To "efficiently determine which polygons are holes", you can use an rtree to speed up the intersection check:
from rtree.index import Index
# create an rtree for efficient spatial queries
rtree = Index((i, p.bounds, None) for i, p in enumerate(polygons))
donuts = []
for i, this_poly in enumerate(polygons):
# loop over indices of approximately intersecting polygons
for j in rtree.intersection(this_poly.bounds):
# ignore the intersection of this polygon with itself
if i == j:
continue
other_poly = polygons[j]
# ensure the polygon fully contains our match
if this_poly.contains(other_poly):
donut = this_poly.difference(other_poly)
donuts.append(donut)
break # quit searching
print(len(donuts))
TL;DR:
Exact question would be how to generate semi-equal polygons of desired size e.g around 100x100m 1000x1000m, 5000x5000m grid that will cover Earth ?**
Background story:
I'm building a python based microservice that for given LAT,LON (WGS84) will return a json with some data, eg. matched country/city/or selected polygon grid.
Part with country/city/clutter works fine so far as I'm using shapefile and R-tree for quick check if Point is within area.
I'm struggling with the following case: imagine I have high numbers of GPS based samples with some data that I would like to e.g average over some geo-bins (grid).
I'm trying to divide Earth in semi-rectangular areas (for Merkator projection) that I could later on use with "contains" or "within" functions.
Currently it is done by SQL query and "GROUP BY" using SIN/COS and rounding
Samples
into BINS
(LINKS TO PICTURES)
Since with shapefiles and upcoming data from the requests I'm working with WGS84 my idea was to jump into merkator (or webmerkator) generate geopandas Polygons and use to_crs function to jump back to WGS84.
world = world[(world.name != "Antarctica") & (world.name != "Fr. S. Antarctic Lands")]
world = world.to_crs({'init': 'epsg:3857'})
plotworld = world.plot( figsize=(20,10))
plotworld.set_title("Merkator")
# Keep map proportionate
plotworld.axis('equal')
#Draw saple polygon rectangle (in merkator)
x_point_list = [0.5*1e7,0.75*1e7,0.75*1e7,0.5*1e7]
y_point_list = [-0*1e7,0*1e7,0.25*1e7,0.25*1e7]
polygon_geom = Polygon(zip(x_point_list, y_point_list))
crs = {'init': 'epsg:3857'}
polygon = gp.GeoDataFrame(index=[0], crs=crs, geometry=[polygon_geom])
polygon.plot(ax=plotworld,color='red')
#transform to WGS84
world = world.to_crs({'init': 'epsg:4326'})
polygon = polygon.to_crs({'init': 'epsg:4326'})
plotworld2= world.plot( figsize=(20,10))
polygon.plot(ax=plotworld2,color='red')
My question is: how to generate semi-equal polygons of desired size e.g around 100x100mx 1000x1000m, 5000x5000m grid that will cover Earth?
I've gone through a number of geopandas/shapely sites, that shows some tutorial about some shapes, bins however no One mention how to draw/generate bins with desired size.
I truly understand that dimension of the polygons will vary a bit, but it does not hurt me that much.
Any help appreciated!
I am using the Delaunay triangulation on a set of points, trying to isolate clusters of points in a regular pattern.
My first experience with using the qhull.Delaunay object so bear with me...
from scipy.spatial import Delaunay
tri = Delaunay(array)
Currently looks like:
and I've found I can print (tri.simplices) to get the list. I want to isolate only those that are in the obvious clusters, which I imagine could be done by removing those with line length or volume over a certain threshold, but I'm unsure how to manipulate the result to do this?
Found the answer - posting in case it is useful for others.
The Delaunay output gives you the list of the coordinates for each point, and a nested list of which three points form each triangle.
To access their area, first you convert this into a list of Shapely polygons, then your polygons are your oyster.
from shapely.geometry.polygon import Polygon
coord_groups = [tri.points[x] for x in tri.simplices]
polygons = [Polygon(x) for x in coord_groups]
#area of the first polygon
polygons[0].area