Apply a column containing polygon to Polygon() shapely - python

I have a dataframe with a column called NewPolygon :
NewPolygon
[(1.23,10),(4.4, 10)...]
[(16.0,10),(8.1, 10)...]
[(2.2,10),(0, 10)...]
My code :
from shapely.geometry import Point
from shapely.geometry.polygon import Polygon
polygon = pd.read_csv(file_path)
point = Point(10, 1.1)
polygon = Polygon() ####How to apply the value from column `NewPolygon` here iteratively
print(polygon.contains(point))
How can I apply all values from NewPolygon to Polygon() iteratively?
Polygon() take a list of tuple as value such as Polygon([(1.23,10),(4.4, 10)...])

Simply, use, Series.transform:
df['NewPolygon'] = df['NewPolygon'].transform(Polygon)
To use methods on polygon objects stored inside NewPolygon column, Use:
df['NewPolygon'].apply(lambda p : p.contains(point))

Related

Vectorized creation of shapely Polygons from GeoPandas DataFrame

I have a GeoDataFrame with a point geometry.
From the point geometry, I want to define a square polygon geometry in a quite straightforward manner.
Given a point, the point should be the left bottom corner in a square with sides 250 units of length.
I.e, left bottom corner is the current point, right bottom corner is the current point + 250 on the x axis etc.
My naive way of doing this is the following:
Create the corners as new columns in the GeoDataFrame:
After that, I try to define a new columns as:
gdf['POLY'] = shapely.Geometry([gdf['BOTTOM_LEFT'], gdf['BOTTOM_RIGHT'], gdf['TOP_LEFT'], gdf['TOP_RIGHT']])
But this returns the following error message:
AttributeError: 'list' object has no attribute '__array_interface__'
Your implementation is close, but you can't call shapely.geometry.Polygon with an array of points - it can only be done one at a time. So the trick is to use df.apply to call Polygon on every row of the DataFrame:
gdf['geometry'] = gdf.apply(
lambda s: shapely.geometry.Polygon(
[s['BOTTOM_LEFT'], s['BOTTOM_RIGHT'], s['TOP_LEFT'], s['TOP_RIGHT']],
axis=1,
)
)
You could do that with your original point using translate:
gdf['geometry'] = gdf.apply(
lambda s: shapely.geometry.Polygon(
[
s['POINT'],
s['POINT'].translate(xoff=250),
s['POINT'].translate(yoff=250, xoff=250),
s['POINT'].translate(yoff=250),
],
axis=1,
)
)
Let's assume you have a GeoDataFrame with only single point. It is called gdf and it looks as follows:
X Y geometry
0 5 6 POINT (5.00000 6.00000)
You can access the x and y components of the point using the following lambda function:
#Access x and y components of point geometry
X = gdf.geometry.apply(lambda x: x.x)
Y = gdf.geometry.apply(lambda x: x.y)
Now you can create a square object using shapely.geometry.Polygon. You need to specify the four vertices of the square. You can do it using:
gdf_square = shapely.geometry.Polygon([[X[0], Y[0]],
[X[0]+250, Y[0]],
[X[0]+250, Y[0]+250],
[X[0], Y[0]+250]])
You can get a square polygon object as shown below:
Note that if you have many points in the GeoDataFrame, modify the last function such that it creates the square polygon for point in each row one by one.
In my case it was more than 5 times faster to build the triangles using list comprehension than using geopandas.apply :
polys = [Polygon(((x, y), (x, y+d), (x+d, y+d), (x+d, y))) for x in xs for y in ys]
gdf = gpd.GeoDataFrame(geometry=polys)

Convert a column of GeoJSON-like strings to geometry objects in GeoPandas

I have a column in a GeoPandas dataframe with strings like this one '{type=Point, coordinates=[37.55, 55.71]}' or this '{type=MultiPoint, coordinates=[[37.6, 55.4]]}'. It can be a polygon or any other geometry as well. Then there are a few points in the form of nested list. How can I transform it to the ordinary GeoPandas geometry objects?
Use shapely.geometry.shape to convert geojson strings to shapely geometry.
from shapely.geometry import shape
df['geometry'] = df.apply(lambda: row: shape(row['jsoncolumn']), axis=1)
I implemented it as follows. Thanks to #martinfleis
# Add necessary shapes and keys
coordinates = 'coordinates'
type = 'type'
Point = 'Point'
MultiPoint = 'MultiPoint'
Polygon = 'Polygon'
MultiPolygon = 'MultiPolygon'
center='center'
df['geometry'] = df.geoData.apply(lambda x: shape(eval(x.replace('=',':'))))
From this source : on github
I built the following function :
import geopandas as gpd
import geojson
import json
def geojsonification(x):
geom = x['geom']
if type(geom) == dict:
s = json.dumps(geom)
s2 = geojson.loads(s)
res = shape(s2)
return res
else:
return np.nan
Which you can use as this :
gdf.geometry = gdf.apply(geojsonification, axis=1)

geopandas point in polygon distance to nearest edge

So I have a geopandas dataframe of ~10,000 rows like this. Each point is within the polygon (I've made sure of it).
point name field_id geometry
POINT(-0.1618445 51.5103873) polygon1 1 POLYGON ((-0.1642799 51.5113756, -0.1639581 51.5089851, -0.1593661 51.5096729, -0.1606536 51.5115358, -0.1642799 51.5113756))
I want to add a new column called distance_to_nearest_edge. Which is the distance from the point to the nearest boundary of the polygon.
There is a shapely function that calculates what I want:
from shapely import wkt
poly = wkt.loads('POLYGON ((-0.1642799 51.5113756, -0.1639581 51.5089851, -0.1593661 51.5096729, -0.1606536 51.5115358, -0.1642799 51.5113756))')
pt = wkt.loads('POINT(-0.1618445 51.5103873)')
dist = poly.boundary.distance(pt)
---
dist = 0.0010736436340879488
But I'm struggling to apply this to 10k rows.
I've tried creating a function, but I keep getting errors ("'Polygon' object has no attribute 'encode'", 'occurred at index 0')
Eg:
def fxy(x, y):
poly = wkt.loads(x)
pt = wkt.loads(y)
return poly.exterior.distance(pt)
Appreciate any help!
I think your data has missing values.
you can try this:
df['distance'] = df.apply(lambda row : row['point'].distance(row['geometry'].boundary) if pd.notnull(row['point']) & pd.notnull(row['geometry']) else np.nan, axis=1)

How to create a geographical heatmap passing custom radii

I want to create a visualization on a map using folium. In the map I want to observe how many items are related to a particular geographical point building a heatmap. Below is the code I'm using.
import pandas as pd
import folium
from folium import plugins
data = [[41.895278,12.482222,2873494.0,20.243001,20414,7.104243],
[41.883850,12.333330,3916.0,0.835251,4,1.021450],
[41.854241,12.567000,22263.0,1.132390,35,1.572115],
[41.902147,12.590388,19505.0,0.839181,37,1.896950],
[41.994240,12.48520,16239.0,1.383981,25,1.539504]]
df = pd.DataFrame(columns=['latitude','longitude','population','radius','count','normalized'],data=data)
middle_lat = df['latitude'].median()
middle_lon = df['longitude'].median()
m = folium.Map(location=[middle_lat, middle_lon],tiles = "Stamen Terrain",zoom_start=11)
# convert to (n, 2) nd-array format for heatmap
points = df[['latitude', 'longitude', 'normalized']].dropna().values
# plot heatmap
plugins.HeatMap(points, radius=15).add_to(m)
m.save(outfile='map.html')
Here the result
In this map, each point has the same radius. Insted, I want to create a heatmap in which the points radius is proportional with the one of the city it belongs to. I already tried to pass the radii in a list, but it is not working, as well as passing the values with a for loop.
Any idea?
You need to add one point after another. So you can specify the radius for each point. Like this:
import random
import numpy
pointArrays = numpy.split(points, len(points))
radii = [5, 10, 15, 20, 25]
for point, radius in zip(pointArrays, radii):
plugins.HeatMap(point, radius=radius).add_to(m)
m.save(outfile='map.html')
Here you can see, each point has a different size.

Merging two GEOJSON polygons in Python

Is there a way to merge two overlapping GEOJSON polygons in python, returning a single merged GEOJSON object?
This is how I was able to do it using the packages/modules json, geojson, shapely, pyproj, and partial from functools:
import json
import geojson
from functools import partial
import pyproj
import shapely.geometry
import shapely.ops
# reading into two geojson objects, in a GCS (WGS84)
with open('file1.json') as geojson1:
poly1_geojson = json.load(geojson1)
with open('file2.json') as geojson2:
poly2_geojson = json.load(geojson2)
# pulling out the polygons
poly1 = shapely.geometry.asShape(poly1_geojson['features'][2]['geometry'])
poly2 = shapely.geometry.asShape(poly2_geojson['features'][2]['geometry'])
# checking to make sure they registered as polygons
print poly1.geom_type
print poly2.geom_type
# merging the polygons - they are feature collections, containing a point, a polyline, and a polygon - I extract the polygon
# for my purposes, they overlap, so merging produces a single polygon rather than a list of polygons
mergedPolygon = poly1.union(poly2)
# using geojson module to convert from WKT back into GeoJSON format
geojson_out = geojson.Feature(geometry=mergedPolygon, properties={})
# outputting the updated geojson file - for mapping/storage in its GCS format
with open('Merged_Polygon.json', 'w') as outfile:
json.dump(geojson_out.geometry, outfile, indent=3, encoding="utf-8")
outfile.close()
# reprojecting the merged polygon to determine the correct area
# it is a polygon covering much of the US, and dervied form USGS data, so using Albers Equal Area
project = partial(
pyproj.transform,
pyproj.Proj(init='epsg:4326'),
pyproj.Proj(init='epsg:5070'))
mergedPolygon_proj = shapely.ops.transform(project,mergedPolygon)
This example from here seems to be a lot more concise:
from shapely.geometry import Polygon
from shapely.ops import cascaded_union
polygon1 = Polygon([(0, 0), (5, 3), (5, 0)])
polygon2 = Polygon([(0, 0), (3, 10), (3, 0)])
polygons = [polygon1, polygon2]
u = cascaded_union(polygons)
The dissolve() function within GeoPandas makes this very simple. For example, I had a GeoDataFrame containing US counties (along with their polygons) and their corresponding Catholic diocese. I wanted to create polygons showing the outline of each diocese for each county. To do so, I used the following code:
diocese_boundaries = df_dioceses.dissolve(by = 'Diocese')
This returned a new DataFrame containing one row for each diocese. Each row had a new geometry column that contained the outline of each diocese.

Categories