How to Get distance time and class through OpenstreetmapX? - julia

I've been trying to query some edge information from an exported network from Openstreet map via this package OpenstreetmapX.jl . However, it appears that some functionality are not directly available. For instance:
Suppose you have a set of links in the form of Vector{Tuple{Int64, Int64}} and you wish to identify their distance, travel time, and class type. I know for the distance you can call something like the following command:
Links = [(301972446, 60649731)
(60649731, 301972446)
(2505989518, 2489651510)
(2489651510, 60649731)
(60649731, 301972503)
(267224410, 2895172669)
(2895172669, 267224410)
(582794232, 582794289)]
# for example to know the distance of the first edge in Link set
distance(map.nodes[links[1][1]],map.nodes[links[1][2]] )
But this is not efficient. Is there anything that directly return the distance, time, and class of each edges? Like:
distance( (582794232, 582794289))
distance( links[1])
time(links[1])
class(links[1])

You will need to access the MapData struct (type ?MapData for details).
Edges
Suppose you have a map:
using OpenStreetMapX
m = get_map_data(joinpath(dirname(pathof(OpenStreetMapX)),"..","test/data/reno_east3.osm");use_cache=false);
All edges are just a Vector
julia> links = m.e[1:2] # sample 2 edges on map
2-element Vector{Tuple{Int64, Int64}}:
(2441017888, 2975020216)
(139988738, 385046328)
Edge classes
Edge classes are also a vector so for the above example you could do m.class[1:2]. Or you could search the id on the edge list:
m.class[findfirst(==((140044645, 140044632)), m.e)]
This yields class number - for translation to class name use OpenStreetMapX.ROAD_CLASSES
Edge lenghts
The distances are available in w matrix, so you can do:
julia> m.w[m.v[140044645], m.v[140044632]]
41.239883456012905
You can also recalculate it using distance function:
julia> distance(m.nodes, [(140044645, 140044632)])
1-element Vector{Float64}:
40.99997118352562
You might notice that this value is slightly lower as the distance function just calculates a stright line distance. However the get_map_data function when parsing the map, by default compresses irrelevant edges (the parameter only_intersections=true), and the distances in w are calculated before compression. In other words w includes curvature of the road and distance is just a straight line.
Travel times
You can get the travel times for edges at once:
julia> OpenStreetMapX.network_travel_times(m)
4220-element Vector{Float64}:
1.5857447369115734
2.3649894625077863
...
Routing
Finally, you can use A-star algorithm to find a distance between any two nodes:
julia> shortest_route(m, 140044645, 140044632)
([140044645, 140044632], 41.239883456012905, 1.4846358044164647)
The returned tuple contains:
the shortest route (nodes)
the distance in meters
the driving time in seconds

Related

two dimensional identifier that is circular?

i need an identifier value that is circular when lerping to the next one. Example:
0,1,2,3
When lerping form any id to the next one in the row, we always get a value between both ids. Except for 3: when lerping to the next id (back to zero) it will be lerped through all ids.
So what i am searching for is something two dimesional for example:
(0,0), (0,1), (1,1), (1,0)
When learping from (1,0) to (0,0) it will lerp fine.
How do I warp this in a function or loop to convert for examle 0,1,2,3,4 to two-dimensional ids?
So you are looking for a function that takes a number k from 0 to N-1 and returns a point on a circle? That's what the trigonometric functions sine and cosine do by definition.
You would transform the number k into the point (x, y) where:
x = cos(k*360°/N)
y = sin(k*360°/N)
(Note that most software libraries take the input in radians and not degrees, if that's your case you would replace 360 degrees with 2*pi, or use a built-in "toRadians" function)

Most efficient strategy for maximum-distance search of all-pairs shortest paths with graph-tool?

I am working with large weighted undirected graphs and need to calculate a form of betweenness and closeness up to a certain maximum distance.
What is the most efficient way to go about maximum-distance search of all-pairs shortest paths with graph-tool?
graph-tool provides betweenness and closeness methods, but these will search the entire (potentially large) graph, whereas I only need to search up to a maximum distance threshold from each vertex. (I also need to compute a custom centrality measure so need the raw shortest path information).
My current solution involves the following:
# make a tuple of the vertices - this will be used for iterating 'i'
verts = tuple(sorted([i for i in graph.vertices()]))
# create a deque version of the same vertices - this will be used for iterating 'j'
verts_shrinking = deque(verts)
# iterate the 'i' verts
for i_v in verts:
# remove the corresponding vert from the j vertices set
# this is to avoid duplicating the same search from j to i
verts_shrinking.popleft()
# search from i to all verticess in the 'j' vertices set
distance_map, pred_map = shortest_distance(graph, source=i_v, target=verts_shrinking,
weights=weight, max_dist=max_distance, pred_map=True)
# iterate all vertices in the j-vertices set
for j_v in verts_shrinking:
# get the vertex and edge lists for the shortest path from 'i' to 'j'
# by using the predecessor map from the above shortest_distance() step.
vert_list, edge_list = shortest_path(graph, source=i_v, target=j_v,
weights=weight, pred_map=pred_map)
# use the vert and edge lists to run my calculations...
Note that the shortest_distance() method will search all-pairs shortest-paths if no source vertex is provided, however, in this case the maximum distance parameter is of no effect.
The code seems to be running fairly slowly, so I am wondering if there are other strategies using graph-tool that might be more efficient? For example, is there a way to share predecessor maps between subsequent method calls to reduce the amount of computation needed?

How to find whole distance between two points in a curved line in R?

I have a similar line graph plotted using R plot function (plot(df))
I want to get distance of the whole line between two points in the graph (e.g., between x(1) and x(3)). How can I do this?
If your function is defined over a fine grid of points, you can compute the length of the line segment between each pair of points and add them. Pythagoras is your friend here:
To the extent that the points are not close enough together that the function is essentially linear between the points, it will tend to (generally only slightly) underestimate the arc length.
Note that if your x-values are stored in increasing order, these ẟx and ẟy values can be obtained directly by differencing (in R that's diff)
If you have a functional form for y as a function of x you can apply the integral for the arc length -- i.e. integrate
∫ √[1+(dy/dx)²] dx
between a and b. This is essentially just the calculation in 1 taken to the limit.
If both x and y are parametric functions of another variable (t, say) you can simplify the parametric form of the above integral (if we don't forget the Jacobian) to integrating
∫ √[(dx/dt)²+(dy/dt)²] dt
between a and b
(Note the direct parallel to 1.)
if you don't have a convenient-to-integrate functional form in 2. or 3. you can use numerical quadrature; this can be quite efficient (which can be handy when the derivative function is expensive to evaluate).

How to find the two opposite normals or two segments?

I have a two segments AB and CD (in red). These two segments are facing each others. They are not completely parallel but will never be perpendicular to each others either.
From that, I need to find the two normals of these segments (in blue) that oppose each others (i.e. the two normals are outside ABCD). I know how to calculate the normals of the segments but obviously each segment has two normals, and I cannot figure out how to programmatically select the ones I need. Any suggestion?
Calculate the vector v between the midpoints of the two segments, pointing from AB to CD. Now the projection of the desired normal to AB onto v must be negative and the projection of the desired normal to CD onto v must be positive. So just calculate the normals, check against v, and negate the normals if needed to make them satisfy the condition.
Here it is in Python:
# use complex numbers to define minimal 2d vector datatype
def vec2d(x,y): return complex(x,y)
def rot90(v): return 1j * v
def inner_prod(u, v): return (u * v.conjugate()).real
def outward_normals(a, b, c, d):
n1 = rot90(b - a)
n2 = rot90(d - c)
mid = (c + d - a - b) / 2
if inner_prod(n1, mid) > 0:
n1 = -n1
if inner_prod(n2, mid) < 0:
n2 = -n2
return n1, n2
Note that I assume the endpoints define lines meeting the conditions in the problem. Nor do I check for the edge case when the lines have the same midpoint; the notion of "outside" doesn't apply in that case.
I think there are two cases to consider:
Case 1: Intersection between lines occurs outside the endpoints of either segment.
In this case the midpoint method suggested by #Michael J. Barber will work for sure. So form a vector between the midpoints of the segments, compute the dot product of your normal vectors with this midpoint vector and check the sign.
If you're computing the normal for lineA, the dot product of the normal with the vector midB -> midA should be +ve.
Case 2: Intersection between lines occurs inside the endpoints of one segment.
In this case form a vector between either one of the endpoints of the segment that does not enclose the intersection point and the intersection point itself.
The dot product of the normal for the segment that does enclose the intersection point and this new vector should be +ve.
You can find the outward normal for the other segment by requiring that the dot product between the two normals is -ve (which would only be ambiguous in the case of perpendicular segments).
I've assumed that the segments are not co-linear or actually intersecting.
Hope this helps.
You can reduce the four combinations for the signs as follows:
Calculate the dot product of the normals, a negative sign indicates that both show outside or inside.
As I suppose that your normals have unit lenght, you can detect parallelity if the dot product has magnitude one. A positive value indicates that both show in the same direction, a negative value says that both show in different directions.
It the normals are not parallel: parametrize lines as x(t) = x0 + t * n for a normal n and calculate the t for which both intersect. A negative t will indicate that both show outside. It is enough if you do this for one of the normals, as you reduced your combinations from 4 to 2 in step 1.
If both normals are parralel: Calculate the time t for which the normals hit the midpoint between of your segments. As in 2. is enough if you do this for one of the normals, as you reduced your combinations from 4 to 2 in step 1.

General formula to calculate Polyhedron volume

Given a list of vertices (v), and a list of edges connecting the vertices (e), and a list of surfaces that connect the edges (s), how to calculate the volume of the Polyhedron?
Take the polygons and break them into triangles.
Consider the tetrahedron formed by each triangle and an arbitrary point (the origin).
Sum the signed volumes of these tetrahedra.
Notes:
This will only work if you can keep a consistent CW or CCW order to the triangles as viewed from the outside.
The signed volume of the tetrahedron is equal to 1/6 the determinant of the following matrix:
[ x1 x2 x3 x4 ]
[ y1 y2 y3 y4 ]
[ z1 z2 z3 z4 ]
[ 1 1 1 1 ]
where the columns are the homogeneous coordinates of the verticies (x,y,z,1).
It works even if the shape does not enclose the origin by subracting off that volume as well as adding it in, but that depends on having a consistent ordering.
If you can't preserve the order you can still find some way to break it into tetrahedrons and sum 1/6 absolute value of the determinant of each one.
Edit:
I'd like to add that for triangle mesh where one vertex (say V4) of the tetrahedron is (0,0,0) the determinante of the 4x4 matrix can be simplified to the upper left 3x3 (expansion along the 0,0,0,1 column) and that can be simplified to Vol = V1xV2.V3 where "x" is cross product and "." is dot product. So compute that expression for every triangle, sum those volumes and divide by 6.
Similarly with a polygon where we can split it into triangles and sum the areas,
you could split a polyhedron into pyramids and sum their volumes. But I'm not sure how hard is to implement an algorithm for that.
(I believe there is a mathematical way/formula, like using vectors and matrices.
I suggest to post your question also on http://mathoverflow.net)
I have done this before, but the surface mesh I used always had triangular facets. If your mesh has non triangular facets, you can easily break them up into triangular facets first. Then I fed it to TetGen to obtain a tetrahedralization of the interior. Finally, I added up all the volumes of the tetrahedra. TetGen is reasonably easy to use, and is the only library other than CGAL I know of that can handle complicated meshes. CGAL is pretty easy to use if you don't mind installing a gigantic library and use templates like crazy.
First, break every face into triangles by drawing in new edges.
Now look at one triangle, and suppose it's on the "upper" surface (some of these details will turn out to be unimportant later). Look at the volume below the triangle, down to some horizontal plane below the polyhedron. If {h1, h2, h3} are the heights of the three points, and A is the area of the base, then the volume of the solid will be A(h1+h2+h3)/3. Now we have to add up the volumes of these solids for the upper faces, and subtract them for the lower faces to get the volume of the polyhedron.
Play with the algebra and you'll see that the height of the polyhedron above the horizontal plane doesn't matter. The plane can be above the polyhedron, or pass through it, and the result will still be correct.
So what we need is (1) a way to calculate the area of the base, and (2) a way to tell an "upper" face from a "lower" one. The first is easy if you have the Cartesian coordinates of the points, the second is easy if the points are ordered, and you can combine them and kill two birds with one stone. Suppose for each face you have a list of its corners, in counter-clockwise order. Then the projection of those points on the x-y plane will be counterclockwise for an upper face and clockwise for a lower one. If you use this method to calculate the area of the base, it will come up positive for an upper face and negative for a lower one, so you can add them all together and have the answer.
So how do you get the ordered lists of corners? Start with one triangle, pick an ordering, and for each edge the neighbor that shares that edge should list those two points in the opposite order. Move from neighbor to neighbor until you have a list for every triangle. If the volume of the polyhedron comes up negative, just multiply by -1 (it means you chose the wrong ordering for that first triangle, and the polyhedron was inside-out).
EDIT:
I forgot the best part! If you check the algebra for adding up these volumes, you'll see that a lot of terms cancel out, especially when combining triangles back into the original faces. I haven't worked this out in detail, but it looks as if the final result could be a surprisingly simple function.
Here's a potential implementation for that in Python.
Can anyone please check if it's correct?
I believe that I am missing permutations of the points because my second test (cube) gives 0.666 and not 1. Ideas anyone?
Cheers
EL
class Simplex(object):
'''
Simplex
'''
def __init__(self,coordinates):
'''
Constructor
'''
if not len(coordinates) == 4:
raise RuntimeError('You must provide only 4 coordinates!')
self.coordinates = coordinates
def volume(self):
'''
volume: Return volume of simplex. Formula from http://de.wikipedia.org/wiki/Tetraeder
'''
import numpy
vA = numpy.array(self.coordinates[1]) - numpy.array(self.coordinates[0])
vB = numpy.array(self.coordinates[2]) - numpy.array(self.coordinates[0])
vC = numpy.array(self.coordinates[3]) - numpy.array(self.coordinates[0])
return numpy.abs(numpy.dot(numpy.cross(vA,vB),vC)) / 6.0
class Polyeder(object):
def __init__(self,coordinates):
'''
Constructor
'''
if len(coordinates) < 4:
raise RuntimeError('You must provide at least 4 coordinates!')
self.coordinates = coordinates
def volume(self):
pivotCoordinate = self.coordinates[0]
volumeSum = 0
for i in xrange(1,len(self.coordinates)-3):
newCoordinates = [pivotCoordinate]
for j in xrange(i,i+3):
newCoordinates.append(self.coordinates[j])
simplex = Simplex(newCoordinates)
volumeSum += simplex.volume()
return volumeSum
coords = []
coords.append([0,0,0])
coords.append([1,0,0])
coords.append([0,1,0])
coords.append([0,0,1])
s = Simplex(coords)
print s.volume()
coords.append([0,1,1])
coords.append([1,0,1])
coords.append([1,1,0])
coords.append([1,1,1])
p = Polyeder(coords)
print p.volume()

Resources