How to create polygons from enclosed polylines - python

I have a set of polylines that intersect and form enclosed areas. Is there any good way in Python of getting the coordinates for each point of the lines that forms a given polygon or to somehow convert enclosed areas into polygons?

You can do it with "shapely" I think it's a good library to work with polygons and lines.
from shapely.geometry import Polygon
x = Polygon ( [[0,2], [1,1], [2,3]])
from shapely.geometry import Polygon
x = Polygon ( [[0,2], [1,1], [2,3]]) #1st polyline
y = Polygon ( [[0,1], [1,4], [2,1]]) #1st polyline
intersec = x.intersection(y)
boundaryCoords = list (intersec.boundary.coords) #as list of tuple
boundaryCoords = np.asarray(intersec.boundary.coords) #as numpy array
print(boundaryCoords)

Related

How do apply K-means clustering if you have a polygon set of latitude and longitude

I got some geometry coordinates.
E.g. POLYGON ((15927.81230000034 36864.30379999988, 15926.792399999686 36861.118400000036, 15923.173100000247 36862.27639999986, 15924.19299999997 36865.4617999997, 15927.81230000034 36864.30379999988))
E.g. I went and converted each pair into the latitude longitude version
POLYGON = [(1.1603180714482149, 103.9129638389025), (1.160308848641466, 103.912935217908), (1.1602761166689228, 103.91294562159307), (1.1602853394755797, 103.91297424258724), (1.1603180714482149, 103.9129638389025)]
Normally for k means clustering
From what I understand is that 1 polygon set represents 1 building. So how do i convert 1 set of polygon which has a few pair of lat & lon into 1 single lat lon to represent the building?
If you're using shapely then you can convert the polygon into the corresponding X matrix like this:
import numpy as np
from shapely.geometry import Polygon
polygon = Polygon([
(1.1603180714482149, 103.9129638389025),
(1.160308848641466, 103.912935217908),
(1.1602761166689228, 103.91294562159307),
(1.1602853394755797, 103.91297424258724),
(1.1603180714482149, 103.9129638389025)
])
X = np.vstack(polygon.exterior.xy).T
print(X)
Result:
[[ 1.16031807 103.91296384]
[ 1.16030885 103.91293522]
[ 1.16027612 103.91294562]
[ 1.16028534 103.91297424]
[ 1.16031807 103.91296384]]
Which is the right format for sklearn's KMeans.

splitting circle into two using LineString Python Shapely

I have created a circle using Shapely and would like to split it into two using LineString.
I created the circle as follows
from functools import partial
import pyproj
from shapely import geometry
from shapely.geometry import Point, Polygon, shape, MultiPoint, LineString, mapping
from shapely.ops import transform, split
radius = 92600
lon = 54.08
lat = 17.05
local_azimuthal_projection = "+proj=aeqd +R=6371000 +units=m +lat_0={} +lon_0={}".format(
lat, lon
)
wgs84_to_aeqd = partial(
pyproj.transform,
pyproj.Proj("+proj=longlat +datum=WGS84 +no_defs"),
pyproj.Proj(local_azimuthal_projection),
)
aeqd_to_wgs84 = partial(
pyproj.transform,
pyproj.Proj(local_azimuthal_projection),
pyproj.Proj("+proj=longlat +datum=WGS84 +no_defs"),
)
center = Point(float(lon), float(lat))
point_transformed = transform(wgs84_to_aeqd, center)
buffer = point_transformed.buffer(radius)
# Get the polygon with lat lon coordinates
circle_poly = transform(aeqd_to_wgs84, buffer)
For the line Splitter I have the following:
splitter = LingString([Point(54.79,16.90), Point(53.56,16.65)])
Now I want to see the two split shapes so I used split function.
result = split(circle_poly, splitter)
However, this only results the same circle and not two shapes.
At the end I would like to use one section of the split to form another shape.
To split a circle or a polygon, you can use spatial difference operation with another polygon. Shapely does not allow the use of line to do so.
"Shapely can not represent the difference between an object and a lower dimensional object (such as the difference between a polygon and a line or point) as a single object."
See document:
In your case, you can build two polygons that have the line as the common edges.
Make sure that the 2 polygons together are big enough to cover the whole polygon you are splitting. Then you use that polygons to do the job.
If you want crude results from the difference operation,
you can turn the line into a slim polygon by buffer operation, and use it.
For the second approach, here is the relevant code:
the_line = LineString([(54.95105, 17.048144), (53.40473921, 17.577181)])
buff_line = the_line.buffer(0.000001) #is polygon
# the `difference` operation between 2 polygons
multi_pgon = circle_poly.difference(buff_line)
The result is a multi-polygon geometry object.

Convert Column to Polygon in Python to perform Point in Polygon

I have written Code to establish Point in Polygon in Python, the program uses a shapefile that I read in as the Polygons.
I now have a dataframe I read in with a column containing the Polygon e.g [[28.050815,-26.242253],[28.050085,-26.25938],[28.011934,-26.25888],[28.020216,-26.230127],[28.049828,-26.230704],[28.050815,-26.242253]].
I want to transform this column into a polygon in order to perform Point in Polygon, but all the examples use geometry = [Point(xy) for xy in zip(dataPoints['Long'], dataPoints['Lat'])] but mine is already zip?
How would I go about achieving this?
Thanks
taking your example above you could do the following:
list_coords = [[28.050815,-26.242253],[28.050085,-26.25938],[28.011934,-26.25888],[28.020216,-26.230127],[28.049828,-26.230704],[28.050815,-26.242253]]
from shapely.geometry import Point, Polygon
# Create a list of point objects using list comprehension
point_list = [Point(x,y) for [x,y] in list_coords]
# Create a polygon object from the list of Point objects
polygon_feature = Polygon([[poly.x, poly.y] for poly in point_list])
And if you would like to apply it to a dataframe you could do the following:
import pandas as pd
import geopandas as gpd
df = pd.DataFrame({'coords': [list_coords]})
def get_polygon(list_coords):
point_list = [Point(x,y) for [x,y] in list_coords]
polygon_feature = Polygon([[poly.x, poly.y] for poly in point_list])
return polygon_feature
df['geom'] = df['coords'].apply(get_polygon)
However, there might be geopandas built-in functions in order to avoid "reinventing the wheel", so let's see if anyone else has a suggestion :)

Python: how to check if shape areas contain points?

I have a shapefile of Singapore that I visualize in this way:
import shapefile
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
### Plot shapefile
sf = shapefile.Reader("myShape.shp")
recs = sf.records()
shapes = sf.shapes()
Nshp = len(shapes)
cns = []
for nshp in xrange(Nshp):
cns.append(recs[nshp][1])
cns = array(cns)
cm = get_cmap('Dark2')
cccol = cm(1.*arange(Nshp)/Nshp)
# -- plot --
fig,ax = plt.subplots(figsize=(20,10))
for nshp in xrange(Nshp):
ptchs = []
pts = array(shapes[nshp].points)
prt = shapes[nshp].parts
par = list(prt) + [pts.shape[0]]
for pij in xrange(len(prt)):
ptchs.append(Polygon(pts[par[pij]:par[pij+1]]))
ax.add_collection(PatchCollection(ptchs,facecolor=cccol[nshp,:],edgecolor='k', linewidths=.1))
ax.set_xlim(103.6,104.1)
ax.set_ylim(1.15,1.48)
Now I have a list of points with coordinates in a dataframe and I want to check in which region of the shapefile the points are.
mypoints
lon lat
0 103.619740 1.280485
1 103.622632 1.268944
2 103.622632 1.274714
3 103.622632 1.277600
4 103.622632 1.280485
In particular from the shapefile I can extract information of the specific area such as the name. In fact, in recs I have this information. For instance:
recs[0]:
[42,
1,
'STRAITS VIEW',
'SVSZ01',
'Y',
'STRAITS VIEW',
'SV',
'CENTRAL REGION',
'CR',
'21451799CA1AB6EF',
datetime.date(2016, 5, 11),
30832.9017,
28194.0843,
5277.76082729,
1127297.23737]
In this case STRAITS VIEW is the name of the area. At the the end I would like to have a dataframe like:
mypoints
lon lat name
0 103.619740 1.280485 STRAITS VIEW
1 103.622632 1.268944 STRAITS VIEW
2 103.622632 1.274714 DOWNTOWN
3 103.622632 1.277600 BEDOK
4 103.622632 1.280485 CHANGI
To check if a coordinate lies inside some Polygon you could use the Intersects method from GDAL. This method compares two OGRGeometry objects and return a boolean value indicating whether they intercept or not.
With this you can iterate over your points, and for each one test if it Intersects() with all your Polygon areas. It should look something like:
for point_geom in your_points:
#iterate over your area polygons and check
for area_geom in your_areas:
if area_geom.Intersects(point_geom):
#then you have a match, save or do as you want
Note: In case your points are not an OGRGeometry (your Polygons most probably are as you are reading them from a Shapefile), you can create such Geometry as suggested here:
from osgeo import ogr
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(1198054.34, 648493.09)
and in the case of Polygons:
ring = ogr.Geometry(ogr.wkbLinearRing)
ring.AddPoint(1179091.1646903288, 712782.8838459781)
ring.AddPoint(1161053.0218226474, 667456.2684348812)
#etc...add all your points, in a loop would be better
# Create polygon
poly = ogr.Geometry(ogr.wkbPolygon)
poly.AddGeometry(ring)

Find indices of raster cells that intersect with a polygon

I want to get a list of indices (row,col) for all raster cells that fall within or are intersected by a polygon feature. Looking for a solution in python, ideally with gdal/ogr modules.
Other posts have suggested rasterizing the polygon, but I would rather have direct access to the cell indices if possible.
Since you don't provide a working example, it's bit unclear what your starting point is. I made a dataset with 1 polygon, if you have a dataset with multiple but only want to target a specific polygon you can add SQLStatement or where to the gdal.Rasterize call.
Sample polygon
geojson = """{"type":"FeatureCollection",
"name":"test",
"crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}},
"features":[
{"type":"Feature","properties":{},"geometry":{"type":"MultiPolygon","coordinates":[[[[-110.254,44.915],[-114.176,37.644],[-105.729,36.41],[-105.05,43.318],[-110.254,44.915]]]]}}
]}"""
Rasterizing
Rasterizing can be done with gdal.Rasterize. You need to specify the properties of the target grid. If there is no predefined grid these could be extracted from the polygon itself
ds = gdal.Rasterize('/vsimem/tmpfile', geojson, xRes=1, yRes=-1, allTouched=True,
outputBounds=[-120, 30, -100, 50], burnValues=1,
outputType=gdal.GDT_Byte)
mask = ds.ReadAsArray()
ds = None
gdal.Unlink('/vsimem/tmpfile')
Converting to indices
Retrieving the indices from the rasterized polygon can be done with Numpy:
y_ind, x_ind = np.where(mask==1)
Clearly Rutger's solution above is the way to go with this, however I will leave my solution up. I developed a script that accomplished what I needed with the following:
Get the bounding box for each vector feature I want to check
Use the bounding box to limit the computational window (determine what portion of the raster could potentially have intersections)
Iterate over the cells within this part of the raster and construct a polygon geometry for each cell
Use ogr.Geometry.Intersects() to check if the cell intersects with the polygon feature
Note that I have only defined the methods, but I think implementation should be pretty clear -- just call match_cells with the appropriate arguments (ogr.Geometry object and geotransform matrix). Code below:
from osgeo import ogr
# Convert projected coordinates to raster cell indices
def parse_coords(x,y,gt):
row,col = None,None
if x:
col = int((x - gt[0]) // gt[1])
# If only x coordinate is provided, return column index
if not y:
return col
if y:
row = int((y - gt[3]) // gt[5])
# If only x coordinate is provided, return column index
if not x:
return row
return (row,col)
# Construct polygon geometry from raster cell
def build_cell((row,col),gt):
xres,yres = gt[1],gt[5]
x_0,y_0 = gt[0],gt[3]
top = (yres*row) + y_0
bottom = (yres*(row+1)) + y_0
right = (xres*col) + x_0
left = (xres*(col+1)) + x_0
# Create ring topology
ring = ogr.Geometry(ogr.wkbLinearRing)
ring.AddPoint(left,bottom)
ring.AddPoint(right,bottom)
ring.AddPoint(right,top)
ring.AddPoint(left,top)
ring.AddPoint(left,bottom)
# Create polygon
box = ogr.Geometry(ogr.wkbPolygon)
box.AddGeometry(ring)
return box
# Iterate over feature geometries & check for intersection
def match_cells(inputGeometry,gt):
matched_cells = []
for f,feature in enumerate(inputGeometry):
geom = feature.GetGeometryRef()
bbox = geom.GetEnvelope()
xmin,xmax = [parse_coords(x,None,gt) for x in bbox[:2]]
ymin,ymax = [parse_coords(None,y,gt) for y in bbox[2:]]
for cell_row in range(ymax,ymin+1):
for cell_col in range(xmin,xmax+1):
cell_box = build_cell((cell_row,cell_col),gt)
if cell_box.Intersects(geom):
matched_cells += [[(cell_row,cell_col)]]
return matched_cells
if you want to do this manually you'll need to test each cell for:
Square v Polygon intersection and
Square v Line intersection.
If you treat each square as a 2d point this becomes easier - it's now a Point v Polygon problem. Check in Game Dev forums for collision algorithms.
Good luck!

Categories

Resources