Intersection of points and polygons using geopandas - leaflet

I am using the Australian Bureau of Statistics data (ABS) to visualize meshblocks and boundaries of buffer data from a centre of location with certain radius.
I want to create the following plot:
intersection plot
So far, I am able to create a function which can give me a circle with certain radius depending on the initial coordinates (lon, lat) I have provided.
I have used the following function to plot the circle around the required location on the map:
import geopandas as gpd
import pandas as pd
import leaflet
from osgeo import gdal
import shapefile as shp
def func_radius_around_point(point_list,m_list,crs_from,crs_to):
points_df = pd.DataFrame(point_list).reset_index()
points_df = points_df.rename(columns={0:'geometry'})
points_gpd = gpd.GeoDataFrame(points_df,geometry='geometry',crs=crs_from)
for m in m_list:
buffered= points_gpd.to_crs(epsg=crs_to).buffer(m).to_crs(epsg=crs_from)
col_name = 'buffered_{m}'.format(m=m)
points_gpd[col_name] = buffered
return points_gpd
I have the data for mesh block and statistical area 1. All I want to create is the intersection of points and polygon. How do I create this intersection using geopandas?
With R, it seems so much easier using the simple features library. Is there a simple features package in geopandas that I have missed out on?

Related

Find points where a polygon and the road network intersect

I want to find the intersecting points between a polygon and the road network and add them to the network as nodes.
import osmnx as ox
import folium
from shapely.geometry import Polygon
G = ox.graph_from_place('Lebanon, NH, USA', network_type='drive')
m1 = ox.plot_graph_folium(G, edge_color = "red")
polygon = Polygon([(-72.2460023,43.6225941), (-72.2547570,43.6063131),(-72.2329560,43.6007194),(-72.2272912,43.6136463),(-72.2460023,43.6225941)])
folium.GeoJson(data=polygon).add_to(m1)
The second part of what I seek to do can be accomplished in a pretty straightforward fashion - has previously been answered on Stack Overflow here.
I'm presuming I would have to get both the polygon and the road network as linestrings (perhaps by rounding the points in the line string to a number of significant digits) to find the common latitude and longitude points.
Here is an image of the points (encircled in black) I'd like to add to the network, as a minimalistic example.
The folium map is reproducible from the code above.
An alternative method that could also be useful in my case, is if I am able to say that there are two unconnected road network segments (in orange) within the polygon (might be more "unconnected" segments in some other road networks).
Thank you for your help in advance!

OSMnx finding the shortest path of a directed graph using networkx

In OSMnx the streets are directed in order to preserve one-way directionality and therefore, when I try to find the shortest path using Networkx I get NetworkXNoPath: No path to (osmid). How do I fix this issue? I need to find the shortest path in a network with one-way streets.
See code below:
import osmnx as ox
import networkx as nx
# define place
centreLat=40.771687
centreLon=-73.957233
# download osm data
G= ox.graph_from_point((centreLat,centreLon), distance=1000, network_type='drive',simplify = True)
# plot the graph
fig,ax = ox.plot_graph(G)
# Get origin x and y coordinates
orig_xy = 40.7647425, -73.9683181
# Get target x and y coordinates
target_xy =40.7804348, -73.9498809
# Find the node in the graph that is closest to the origin point (here, we want to get the node id)
orig_node = ox.get_nearest_node(G, orig_xy, method='euclidean')
# Find the node in the graph that is closest to the target point (here, we want to get the node id)
target_node = ox.get_nearest_node(G, target_xy, method='euclidean')
# Get shortest path
route = nx.shortest_path(G, source=orig_node, target=target_node, weight='length')
# Plot the shortest path
fig, ax = ox.plot_graph_route(G, route, origin_point=orig_xy, destination_point=target_xy)
This issue was fixed by creating the strongly connected components of the directed graph in order to make sure that every vertex is accessible from every other vertex.
G=ox.geo_utils.get_largest_component(G,strongly=True)

How do I get the "boundary" of a Delaunay triangulation?

Visually, it is clear to me that given a Delaunay triangulation, there are some points which form its "boundary". This boundary is different from a convex hull, since it is not minimal in the number of points, and it is not necessarily convex.
What is it called? Is there a way to get it from a scipy Delaunay triangulation?
(Note: I am not looking for an algorithm on how to determine this boundary, but rather a pre-baked scipy function. I already have an idea for how I can get the boundary of a Delaunay triangulation, but prefer to not re-invent the wheel.)
This is actually quite simple, there is no need for convex/concave hulls.
Assuming you have triangulated a closed surface, the key observation is to note that an edge of the triangulation belongs to the boundary if it's not shared by two different triangles.
From the scipy.spatial.Delaunay documentation, If P is a set of points and you have computed T = Delaunay(P), then T.neighbors equals -1 in it's kth coordinate if the edge opposite to the kth vertex belongs to the boundary.
Example code:
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay
# Create triangulation of a rectangle
x = np.linspace(0,1,9)
y = np.linspace(0,1,9)
X,Y = np.meshgrid(x,y)
P = np.array([X.flatten(),Y.flatten()]).T
T = Delaunay(P)
# Find edges at the boundary
boundary = set()
for i in range(len(T.neighbors)):
for k in range(3):
if (T.neighbors[i][k] == -1):
nk1,nk2 = (k+1)%3, (k+2)%3
boundary.add(T.simplices[i][nk1])
boundary.add(T.simplices[i][nk2])
# Plot result
plt.triplot(P[:,0], P[:,1], T.simplices)
plt.plot(P[:,0], P[:,1], 'o')
plt.plot(P[list(boundary),0], P[list(boundary),1], 'or')
plt.show()
I'm pretty sure this is not recently present in scipy, it would make no sense to place it somewhere else than among the scipy.spatial.Delaunay object methods, and list of the methods is pretty short and doesn't contain anything that specific.
The method find_simplex might help you find the convex hull (assuming this means the smallest convex superset), but the engagement of the circumscribed circle (if I understood well the Delaunay triangulation definition) seemms like a completely new kind of obstacle.
Assuming that the simplex is a word for a "side" of this "triangle" , you would need to find union of all such simplexes to find the convex hull, so for the boundary you don't want all simplexex, only some of them, and here my imagination ends.

Extract constrained polygon using OSMnx

I'm playing around with OSMnx package in order to solve the following task:
- there is a point X on the map defined by latitude and longitude
- we need to detect a polygon that contains that point X and is constrained by the neighboring roads
- so basically the point X is inside the polygon and the neighboring roads will be borders of that polygon.
So far I've managed only to plot the visualization of the graph on the map and find the closest edge/node to point X.
In the attached image I've highlighted the area I want to extract in red.
As you are trying to find a polygon containing your point, you first need to generate polygons out of multilinestring geometry. As you did not provide your data I am downloading a sample from OSM using OSMnx.
import osmnx as ox
import geopandas as gpd
import shapely
point = (40.742623, -73.977857)
streets_graph = ox.graph_from_point(point, distance=500, network_type='drive')
streets_graph = ox.project_graph(streets_graph)
I have reprojected it as it is way more convenient than working with degrees, especially if you want to measure anything.
You then have to convert OSMnx graph to geopandas GeoDataFrame.
streets = ox.save_load.graph_to_gdfs(streets_graph, nodes=False, edges=True,
node_geometry=False, fill_edge_geometry=True)
To get some point I can work with, I'll just use the one in the centre of this geodataframe.
point = streets.unary_union.centroid
This is what it looks like.
Next you need to get polygons of your blocks defined by streets, using shapely.ops.polygonize as I suggested in the comment above and store them as GeoSeries.
polygons = shapely.ops.polygonize(streets.geometry)
polygons = gpd.GeoSeries(polygons)
The only thing you have to do next is to find which polygon contains your point.
target = polygons.loc[polygons.contains(point)]
Plotting it again:
ax = target.plot()
gpd.GeoSeries([point]).plot(ax=ax, color='r')
If you want to know which streets are forming the boundary of this polygon, just intersect it with the original network. I am filtering for MultiLineString to exclude streets which intersects the polygon only in one point.
target_streets = streets.loc[streets.intersection(target.iloc[0]).type == 'MultiLineString']
This is how the result of that looks like.
ax = target_streets2.plot()
gpd.GeoSeries([point]).plot(ax=ax, color='r')
Hope it helps.

matplotlib — are there actual PyPlot objects that interact with Basemap objects?

Looking at the Basemap docs, I can see how a Basemap object is instantiated and overlaid with content:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
map = Basemap(projection='ortho',lat_0=45,lon_0=-100,resolution='l')
map.drawcoastlines(linewidth=0.25)
...
The object is named map and various Basemap methods act on it. But I can't understand how, later in the same code, PyPlot (imported as plt) behaves. There seems to be no distinct PyPlot object, and the calls to the PyPlot module never mention map:
# make up some data on a regular lat/lon grid.
nlats = 73; nlons = 145; delta = 2.*np.pi/(nlons-1)
lats = (0.5*np.pi-delta*np.indices((nlats,nlons))[0,:,:])
lons = (delta*np.indices((nlats,nlons))[1,:,:])
wave = 0.75*(np.sin(2.*lats)**8*np.cos(4.*lons))
mean = 0.5*np.cos(2.*lats)*((np.sin(2.*lats))**2 + 2.)
# compute native map projection coordinates of lat/lon grid.
x, y = map(lons*180./np.pi, lats*180./np.pi)
# contour data over the map.
cs = map.contour(x,y,wave+mean,15,linewidths=1.5)
plt.title('contour lines over filled continent background')
plt.show()
How and where does PyPlot interact with the map object?
Also, in the third line of code from the bottom, why is variable cs being assigned, since it isn't actually used elsewhere in the code? I see no difference in the output with cs = removed.
pyplot is just an interface to various matplotlib modules. For example, plt.plot() finds the current Axes object (let's call it ax) and the uses the ax.plot() method to do the drawing.
In general, operating on the Axes, or in this case Basemap objects directly is the greatly preferred style.