Skip to content

Commit

Permalink
Cleanup, commenting, and updates to intersection clustering code and …
Browse files Browse the repository at this point in the history
…documentation.

The list of cluster IDs is no longer returned, since it can easily can be extracted from the intersection ID to cluster ID mapping dictionary.

Issue #31

Signed-off-by: Ted Steiner <tsteiner2@gmail.com>
  • Loading branch information
tedsteiner committed Jan 14, 2015
1 parent 371b68f commit fc0dc9d
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 28 deletions.
8 changes: 5 additions & 3 deletions docs/data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,18 @@ The only required input is the highway dictionary returned by "getOSMData()." A
dictionary of "Intersection" types is returned, intexed by the node ID of the
intersection.

In some cases, such as boulevards and other divided roads, OpenStreetMap represents a street as two one-way highways. This can result in multiple "intersections" detected per true intersection. If desired, these intersections can be "clustered," replacing these multiple intersection-lets with a single node. To do this, we must first cluster the highways, by gathering all highways with a common name (note that this is dependent on the quality of street name tags in your source data).
We then search for proximal instances of these highway sets crossing one another. Flag max_dist can be used to change the required proximity of the nodes to be considered an intersection (the default is 15 meters). Note that this proximity is the maximum distance the node can be from the centroid of all nodes in the intersection at the time the node is added.
In some cases, such as boulevards and other divided roads, OpenStreetMap represents a street as two one-way highways. This can result in multiple "intersections" detected per true intersection. If desired, these intersections can be "clustered," replacing these multiple intersection-lets with a single node. This gives a better estimate of the total number of highway intersections in a region.

To do this, we first "cluster" the highways, by gathering all highways with a common name (note that this is dependent on the quality of street name tags in your source data). We then search for proximal instances of these highway sets crossing one another. Flag max_dist can be used to change the required proximity of the nodes to be considered an intersection (the default is 15 meters). Note that this proximity is the maximum distance the node can be from the centroid of all nodes in the intersection at the time the node is added. If an intersection involves the same highways as an existing cluster during the search but is further away than max_dist, a new cluster will be formed, initialized at that point.

The code to accomplish this is as follows:

.. code-block:: python
highway_sets = findHighwaySets(highways)
intersection_mapping, intersection_nodes = findIntersectionClusters(nodes,intersections,highway_sets,max_dist=15)
intersection_mapping = findIntersectionClusters(nodes,intersections,highway_sets,max_dist=15)
replaceHighwayNodes!(highways,intersection_mapping)
cluster_node_ids = unique(collect(values(intersection_mapping)))
The optional flag "max_dist" is in the units of your "nodes" object.

Expand Down
44 changes: 20 additions & 24 deletions src/intersections.jl
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ end


### Cluster highway intersections into higher-level intersections ###
# Note that there may be multiple intersection clusters containing the same
# streets, due to curved streets. Parameter max_dist controls how far apart an
# intersection must be from an existing cluster to create a new cluster.
function findIntersectionClusters( nodes::Dict{Int,ENU},
intersections_in::Dict{Int,Intersection},
highway_clusters::Vector{HighwaySet};
Expand All @@ -95,7 +98,7 @@ function findIntersectionClusters( nodes::Dict{Int,ENU},
end

# Deep copy intersections dictionary and replace highways with highway
# clusters where available
# sets where available
intersections = deepcopy(intersections_in)
for (node,inter) in intersections
hwys = [inter.highways...]
Expand All @@ -119,23 +122,26 @@ function findIntersectionClusters( nodes::Dict{Int,ENU},
push!(hwy_counts[hwy_cnt], node)
end

cluster_mapping = Dict{Int,Int}()
clusters = Set{Int}[]
clusters_nodes = Set{Int}[]
clusters = Set{Int}[] # Array of sets of highway IDs in each cluster
clusters_nodes = Set{Int}[] # Array of sets of node IDs in each cluster
intersection_mapping = Dict{Int,Int}() # [intersection id => index in `clusters`]

for kk = 1:(length(hwy_counts)-1)
# Start with intersections with most highways, as they are the best
# "seeds" for new clusters because all intersection nodes added to the cluster
# must have their highways be a subset of the highways already in the cluster.
# Skip checking intersections with only 1 highway (road ends)
k = length(hwy_counts)+1-kk
# Skip intersections with only 1 highway (road ends)

for inter in hwy_counts[k]
found = false
for index = 1:length(clusters)
if issubset(intersections[inter].highways,clusters[index])
# Check distance to cluster centroid
c = centroid(nodes,[clusters_nodes[index]...])
dist = distance(c,nodes[inter])
if dist < max_dist
cluster_mapping[inter] = index
c_dist = distance(c,nodes[inter])
if c_dist < max_dist
intersection_mapping[inter] = index
clusters_nodes[index] = Set([clusters_nodes[index]...,inter])
found = true
break
Expand All @@ -145,36 +151,26 @@ function findIntersectionClusters( nodes::Dict{Int,ENU},
if !found
push!(clusters,intersections[inter].highways)
push!(clusters_nodes,Set(inter))
cluster_mapping[inter] = length(clusters)
intersection_mapping[inter] = length(clusters)
end
end
end

cluster_nodes = Int[]
cluster_map = Dict{Int,Int}()
# Create new node at centroid of each intersection cluster
cluster_map = Dict{Int,Int}() # [Intersection Node ID => Cluster Node ID]
for k = 1:length(clusters_nodes)
if length(clusters_nodes[k]) > 1
n = [clusters_nodes[k]...]
c = centroid(nodes,n)
push!(cluster_nodes,addNewNode(nodes,c))
cluster_node_id = addNewNode(nodes,c)

for j = 1:length(n)
cluster_map[n[j]] = cluster_nodes[end]
end

if false
println("#####")
nds = [clusters_nodes[k]...]
for kk = 1:length(nds)
println("node: $(nds[kk]), loc: $(nodes[nds[kk]]), dist: $(distance(nodes,cluster_nodes[end],nds[kk]))")
end
println("centroid: $c")
println("node ID: $(cluster_nodes[end])")
cluster_map[n[j]] = cluster_node_id
end
end
end

return cluster_map, cluster_nodes
return cluster_map
end


Expand Down
11 changes: 10 additions & 1 deletion test/intersections.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,20 @@ highway_sets = findHighwaySets(hwys)
@test length(highway_sets) == 4

# Cluster intersections
intersection_cluster_mapping, intersection_cluster_nodes = findIntersectionClusters(nodes,intersections,highway_sets,max_dist=15)
intersection_cluster_mapping = findIntersectionClusters(nodes,intersections,highway_sets,max_dist=15)
intersection_clusters = unique(collect(values(intersection_cluster_mapping)))
@test sort!(intersection_clusters) == [1,2,3,4,5]
@test length(intersection_clusters) == 5

# Replace Nodes in Highways
replaceHighwayNodes!(hwys,intersection_cluster_mapping)
intersections_clustered = findIntersections(hwys)
@test length(intersections_clustered) == 82

# Check how many semi-redundant intersections we were able to remove
removed = length(intersections) - length(intersections_clustered)
@test removed == 9

end # module TestIntersections


0 comments on commit fc0dc9d

Please sign in to comment.