Convert SVG path to polygon with specific number of points python - python

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

Related

Overlapping LineString and Polygon returning false in Shapely

I am using shapely to find if a LineString and Polygon overlap.
I have defined the polygon based on the map area that I need to find if points are overlapping in:
polygon_map = Polygon([(-126.03561599522513,60.08405276856493),(-65.91842849522513,60.08405276856493),(-65.91842849522513,76.84958092750016),(-126.03561599522513,76.84958092750016),(-126.03561599522513,60.08405276856493)])
For the LineString, I have a long list of coordinates in two columns, x and y. I have taken the maximum and minimum coordinates of x and y to generate a LineString.
line = LineString([(32.823101,87.988993),(-153.01468,30.001368)])
When I plot these on a map, they overlap (as expected)
m = folium.Map([61.08405, -66.918], zoom_start=3, tiles='cartodbpositron')
folium.GeoJson(line).add_to(m)
folium.GeoJson(polygon_map).add_to(m)
folium.LatLngPopup().add_to(m)
m
[Image of map created showing intersecting polygon and linestring]
However, when I do:
line.overlaps(polygon_map)
It returns false, and I can't work out why.
I have simplified the LineString to only include the minimum and maximum coordinates as I have hundreds of coordinates in my original dataframe and I'm worried it will take too long to loop through each set of coordinates. I haven't used Shapely before so I'm not sure if this is why it isn't working.
This is all down to geographic projections. As pure cartesian geometry without accounting for curvature of the earth they do not overlap. (See below image). shapely has no knowledge of geographic projections, it is pure cartesian geometry. As cartesian geometric objects this polygon and LineString do not overlap.
Only after setting the CRS does folium show these geometries overlapping.
import geopandas as gpd
from shapely.geometry import Polygon, LineString
polygon_map = Polygon([(-126.03561599522513,60.08405276856493),(-65.91842849522513,60.08405276856493),(-65.91842849522513,76.84958092750016),(-126.03561599522513,76.84958092750016),(-126.03561599522513,60.08405276856493)]) # fmt: skip
line = LineString([(32.823101, 87.988993), (-153.01468, 30.001368)])
gdf = gpd.GeoDataFrame(geometry=[polygon_map, line])
gdf.explore(height=300, width=300)
# gdf.set_crs("epsg:4386").explore(height=300, width=300)

Clip lower value polygons with larger value polygons in shapefile

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))

Determining what points and polygons are in a grid square

I am using python and geojson to do this, I want to specify a point and that point will be the center of a square, assuming the square is 1 mile by one mile I want to list all the points and polys found in the square, including polys bigger than the square.
I have multiple geojson files so will need to do the check a few times which is fine. I have been playing with the code below which checks to see if the cell center is near the centre of the square but will have issues for oddly shaped polygons. I really want to know all items / features that are found in the square.
import json
from shapely.geometry import shape, Point
from shapely.geometry import asShape, mapping
point = Point(14.9783266342289, 16.87265432621112)
max_distance_from_center = 1
with open('cells.geojson') as f:
js = json.load(f)
for feature in js['features']:
polygon = asShape(feature['geometry'])
distance = point.distance(polygon.centroid)
# print(f'{distance} - {polygon.centroid}')
if distance < max_distance_from_center:
print (f'Found cells containing polygon:{feature}')
For source data I was using a exported map from https://azgaar.github.io/Fantasy-Map-Generator/ the grid should be 10 miles by 10 miles. Suggestions on how to do this?
Update:
Here is a poorly drawn diagram. Within the grid square I want to identify all markers and polygons that fall within the bounds of the square even if they go out side of it. I want to have a list of all features that have some presence in the grid square. I highlighted the areas in yellow.
Poorly draw image
I looked at intersects and it may do it. Will try tonight.
you can try this:
First, create grid.
from shapely.geometry import Point
from matplotlib.pyplot as plt
point = Point(0, -10)
square = point.buffer(0.5).envelope
fig, ax = plt.subplots(figsize=(5,5))
gpd.GeoSeries(square).plot(ax=ax)
gpd.GeoSeries(point).plot(ax=ax, color = "black",markersize=30)
plt.grid()
plt.show()
and then,
import geopandas as gpd
# get geodataframe from geojson file
geo_df = gpd.GeoDataFrame.from_file('cells.geojson')
geo_df['grid_yn'] = geo_df['geometry'].apply(lambda x : x.intersects(square))

Create polygon grid that covers Earth

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!

How to select from the output of scipy Delaunay triangulation only simplices under certain volume (or under total line length)?

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

Categories