Generate random points on osmnx graph - graph

I have created a road network graph using osmnx library. now I want to generate some random points on the network but I don't have any idea how to do it. need some help :(
here is my code:
import geopandas as gpd
import osmnx as ox
top= gpd.GeoDataFrame(columns = ['name', 'geometry'], crs = 4326, geometry = 'geometry')
top.at[0, 'geometry'] = Point(100.40823730180041,14.207021554191956)
top.at[0, 'name'] = 'tl'
top.at[1, 'geometry'] = Point(100.74774714891429, 14.196946042603166)
top.at[1, 'name'] = 'tr'
bottom= gpd.GeoDataFrame(columns = ['name', 'geometry'], crs = 4326, geometry = 'geometry')
bottom.at[0, 'geometry'] = Point(100.38860578002853,13.612931284522707)
bottom.at[0, 'name'] = 'bl'
bottom.at[1, 'geometry'] = Point(100.7131032869639, 13.581503263247015)
bottom.at[1, 'name'] = 'br'
combined = top.append(bottom)
convex = combined.unary_union.convex_hull
graph_extent = convex.buffer(0.02)
graph = ox.graph_from_polygon(graph_extent, network_type = "drive")
Following are the steps of what I did:
I created two geodataframes top and bottom top define the extent of my road network
Then I combined them and used ox.graph_from_polygon to create a road network.
My road network looks something like this
roadNetwork
Now I want to generate some random points that should be on the links/edges of the network created.

The sample_points function does exactly that. See the OSMnx usage examples and documentation for usage: https://osmnx.readthedocs.io/en/stable/osmnx.html#osmnx.utils_geo.sample_points

Related

Add missing points in a 2D line represented by points in Julia

I have some 2D data in Julia representing a circular feature with some "holes" in it. I like to interpolate these holes by finding points fitting on a curve representing the shape of the feature.
Using Julia's PlotlyJS library I was able to find a nice spline interpolation curve for the points visually. However, I am not able to "access" that interpolation to calculate the actual points required. Any (alternative?) idea on how to get that?
Here is a working example in Julia:
using PlotlyJS
someData = [
3.47336 -0.471233;
3.53109 0.335963;
3.46748 1.10433;
3.13369 1.87227;
2.33268 2.51022;
1.21804 3.07551;
0.211065 3.3075;
-0.768256 3.18599;
-1.72856 2.87655;
-2.55477 2.58726;
-3.28657 1.99779;
-3.63637 1.31502;
-3.56652 -0.462201;
-2.96175 -0.956073;
-2.0519 -0.870708;
-1.07193 -0.837913;
-0.156219 -0.972855;
0.594719 -1.4576;
1.27607 -1.9387;
2.08427 -2.17288;
3.47336 -0.471233
]
plot([
scatter(
x=someData[:,1], y=someData[:,2], type="scatter", mode="markers", name="Some data",
marker=attr(size=8, color="orange", opacity=1)
),
scatter(
x=someData[:,1], y=someData[:,2], type="scatter", mode="lines", name="Plotly spline",
line=attr(color="green", width=2, shape="spline")
),
], Layout(scene=attr(aspectmode="data"), showlegend=true)
)
This was answered on the Julia Discourse here:
https://discourse.julialang.org/t/add-missing-points-in-a-2d-line/94211/3
Copy-pasting the solution (which isn't mine!):
using Dierckx, Plots
t = 1:size(data,1)
spl = ParametricSpline(t, data', bc="extrapolate", s=0.0)
tfine= range(1, size(data,1), 200)
Pfine = evaluate(spl, tfine)
t0, t1 = 12.5, 20.6
P0, P1 = evaluate.((spl,), [t0, t1])
plot(eachrow(Pfine)..., c=:blues)
scatter!(eachcol(data)...,legend=false )
scatter!([P0[1]], [P0[2]], ms=5, mc=:red)
scatter!([P1[1]], [P1[2]], ms=5, mc=:red)

How to calculate lat and lon of a rectangle from a center point

I would like to draw a rectangle based on a center point lat and lon assuming a given length and width, let's say 4.5m and 1.5m, respectively. I guess, we need the bearing too. I've made a simulation by drawing a rectangle on Google Earth, getting the positions and putting them on my code. However, I need something automatic. My question is how can I link the Cartesian coordinates to those four points (rectangle) in meters.
Here is my code:
import geopandas as gpd
from shapely.geometry import Polygon
lat_point_list = [41.404928, 41.404936, 41.404951, 41.404943]
lon_point_list = [2.177339, 2.177331, 2.177353, 2.177365]
polygon_geom = Polygon(zip(lon_point_list, lat_point_list))
import folium
m = folium.Map([41.4049364, 2.1773560], zoom_start=20)
folium.GeoJson(polygon_geom).add_to(m)
folium.LatLngPopup().add_to(m)
m
I would like this:
Update:
I know this is basic trigonometry. If I split the rectsngle into triangles, we can find the different points. I know it is basic for simple exercises, however, I don't know of it changes when using Cartesian coordinates. Then, my goal is to get the points A, B, C and D, knowing the center of the rectangle in latitude and longitude, length and width.
Get the rectangular (NE, SW) bounds of your point and use that as bounds to folium.Rectangle.
Example, using your data. 4.5m and 1.5m are a bit small to see the rectangle:
import geopy
import geopy.distance
import math
import folium
def get_rectangle_bounds(coordinates, width, length):
start = geopy.Point(coordinates)
hypotenuse = math.hypot(width/1000, length/1000)
# Edit used wrong formula to convert radians to degrees, use math builtin function
northeast_angle = 0 - math.degrees(math.atan(width/length))
southwest_angle = 180 - math.degrees(math.atan(width/length))
d = geopy.distance.distance(kilometers=hypotenuse/2)
northeast = d.destination(point=start, bearing=northeast_angle)
southwest = d.destination(point=start, bearing=southwest_angle)
bounds = []
for point in [northeast, southwest]:
coords = (point.latitude, point.longitude)
bounds.append(coords)
return bounds
# To get a rotated rectangle at a bearing, you need to get the points of the the recatangle at that bearing
def get_rotated_points(coordinates, bearing, width, length):
start = geopy.Point(coordinates)
width = width/1000
length = length/1000
rectlength = geopy.distance.distance(kilometers=length)
rectwidth = geopy.distance.distance(kilometers=width)
halfwidth = geopy.distance.distance(kilometers=width/2)
halflength = geopy.distance.distance(kilometers=length/2)
pointAB = halflength.destination(point=start, bearing=bearing)
pointA = halfwidth.destination(point=pointAB, bearing=0-bearing)
pointB = rectwidth.destination(point=pointA, bearing=180-bearing)
pointC = rectlength.destination(point=pointB, bearing=bearing-180)
pointD = rectwidth.destination(point=pointC, bearing=0-bearing)
points = []
for point in [pointA, pointB, pointC, pointD]:
coords = (point.latitude, point.longitude)
points.append(coords)
return points
start_coords = [41.4049364, 2.1773560]
length = 4.50 #in meters
width = 1.50
bearing = 45 #degrees
m = folium.Map(start_coords, zoom_start=20)
bounds = get_rectangle_bounds(tuple(start_coords),width, length )
points = get_rotated_points(tuple(start_coords), bearing, width, length)
folium.Rectangle(bounds=bounds,
fill=True,
color='orange',
tooltip='this is Rectangle'
).add_to(m)
# To draw a rotated rectangle, use folium.Polygon
folium.Polygon(points).add_to(m)

VTK Plane through 3D points

I have a set of 3D points and need to fit the best fitting plane which I am doing with the following code (found on stackoverflow):
points = np.reshape(points, (np.shape(points)[0], -1))
assert points.shape[0] <= points.shape[1], "There are only {} points in {} dimensions.".format(points.shape[1], points.shape[0])
ctr = points.mean(axis=1)
x = points - ctr[:, np.newaxis]
M = np.dot(x, x.T)
return ctr, svd(M)[0][:,-1] # return point and normal vector
Afterwards I want to display the plane in VTK. The problem is I have to scale the plane, but when I do so the plane is translated as well. How can I prevent that from happening ?
def create_vtk_plane_actor(point, normal_vector):
print("\n Display plane with point: %s and vector: %s" % (point, normal_vector))
plane_source = vtk.vtkPlaneSource()
plane_source.SetOrigin(point[0], point[1], point[2])
plane_source.SetNormal(normal_vector[0], normal_vector[1], normal_vector[2])
plane_source.Update()
transform = vtk.vtkTransform()
transform.Scale(1.5, 1.5, 1.0)
transform_filter = vtk.vtkTransformFilter()
transform_filter.SetInputConnection(plane_source.GetOutputPort())
transform_filter.SetTransform(transform)
actor = vtk.vtkActor()
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(transform_filter.GetOutputPort())
The scale is not applied on some coordinates local to your plan but on those you set. So indeed, the center will move. If you want to let it, you need to set a Translate on your transform.
Fitting planes is a builtin feature in vtkplotter, scaling is done with mesh.scale():
from vtkplotter import *
from vtkplotter import datadir
from vtkplotter.pyplot import histogram
plt = Plotter()
apple = load(datadir+"apple.ply").subdivide().addGaussNoise(1)
plt += apple.alpha(0.1)
variances = []
for i, p in enumerate(apple.points()):
pts = apple.closestPoint(p, N=12) # find the N closest points to p
plane = fitPlane(pts) # find the fitting plane and scale
variances.append(plane.variance)
if i % 400: continue
print(i, plane.variance)
plt += plane.scale(2)
plt += Points(pts)
plt += Arrow(plane.center, plane.center+plane.normal/10)
plt += histogram(variances).scale(6).pos(1.2,.2,-1)
plt.show()

Best way to count downstream with edge data

I have a NetworkX problem. I create a digraph with a pandas DataFrame and there is data that I set along the edge. I now need to count the # of unique sources for nodes descendants and access the edge attribute.
This is my code and it works for one node but I need to pass a lot of nodes to this and get unique counts.
graph = nx.from_pandas_edgelist(df, source="source", target="target",
edge_attr=["domain", "category"], create_using=nx.DiGraph)
downstream_nodes = list(nx.descendants(graph, node))
downstream_nodes.append(node)
subgraph = graph.subgraph(downstream_nodes).copy()
domain_sources = {}
for s, t, v in subgraph.edges(data=True):
if v["domain"] in domain_sources:
domain_sources[v["domain"]].append(s)
else:
domain_sources[v["domain"]] = [s]
down_count = {}
for k, v in domain_sources.items():
down_count[k] = len(list(set(v)))
It works but, again, for one node the time is not a big deal but I'm feeding this routine at least 40 to 50 nodes. Is this the best way? Is there something else I can do that can group by an edge attribute and uniquely count the nodes?
Two possible enhancements:
Remove copy from line creating the sub graph. You are not changing anything and the copy is redundant.
Create a defaultdict with keys of set. Read more here.
from collections import defaultdict
import networkx as nx
# missing part of df creation
graph = nx.from_pandas_edgelist(df, source="source", target="target",
edge_attr=["domain", "category"], create_using=nx.DiGraph)
downstream_nodes = list(nx.descendants(graph, node))
downstream_nodes.append(node)
subgraph = graph.subgraph(downstream_nodes)
domain_sources = defaultdict(set)
for s, t, v in subgraph.edges(data=True):
domain_sources[v["domain"]].add(s)
down_count = {}
for k, v in domain_sources.items():
down_count[k] = len(set(v))

Control edge with neato layout with processmapR

I'm using the bupaR process mining suite and processmapR to plot my log as a process map but when I try to set a custom position (which force the graph to use a neato layout) the edge become almost staight and the edge value hard to read:
Default graph with no custom position:
With custom position:
I tried to use
positions <- data.frame(act = c("node1","node2","node 3","node 4","node 5","Start", "End"),
y = c(5,4,3,2,1,6,0),
x = c(1,2,3,4,5,0,6),
stringsAsFactors = F)
graph = process_map(log, fixed_node_pos = positions, render = F)
map = add_global_graph_attrs(graph,
attr = "splines",
value = "true",
attr_type = "graph")
render_graph(map)
But I could not find any attribute to change the way edge are displayed, like adding more curve to them
How can I fix this problem ?
Thanks
Try the following:
map = add_global_graph_attrs(graph,
attr = "splines",
value = "curved",
attr_type = "graph")

Resources