Welcome to OpenStreetMap.jl’s documentation!¶
OpenStreetMap.jl is a Julia package that provides basic functionality for parsing, viewing, and working with OpenStreetMap map data. The package is intended mainly for researchers who want to incorporate this rich, global data into their work, and has been designed with both speed and simplicity in mind, especially for those who might be new to Julia.
Contents¶
Overview¶
This package is provided to give researchers quick and convenient access to OpenStreetMap data in Julia. It provides means for extracting and classifying map data, basic route planning, and convenient data visualization.
I found comparable tools for Matlab to be painfully slow, and therefore decided to write a new set of functions from scratch in Julia. Julia provides an excellent platform for quickly and easily working with very large datasets. With the exception of the plotting tools, the functions in this Julia package run significantly faster than comparable tools available in Matlab.
Features¶
The following features are provided:
Parse an OpenStreetMap XML datafile (OSM files)
Crop maps to specified boundaries
Convert maps between LLA, ECEF, and ENU coordinates
Extract highways, buildings, and tagged features from OSM data
Filter data by various classes
- Ways suitable for driving, walking, or cycling
- Freeways, major city streets, residential streets, etc.
- Accomodations, shops, industry, etc.
Draw detailed maps using Julia’s Winston graphics package with a variety of options
Compute shortest or fastest driving, cycling, and walking routes using Julia’s Graphs package
Package Status¶
All the functionality that I personally need for my work is now implemented in this package. Therefore, future updates will depend on GitHub issues (bug reports or feature requests) created by users. Pull requests for additional functionality are very welcome, as well.
Data Types¶
This page gives an overview of the main data types used by OpenStreetMap.jl.
Map Data¶
These types pertain directly to map elements.
Highway¶
All roads and paths in OpenStreetMap are generically called “highways.” These types must include a list of nodes that comprises the path of the highway. All other fields are optional, and are empty strings when missing from the OSM database.
When a highway is labeled as “oneway,” the road or path is only legally traversable in the order in which the nodes are listed.
type Highway
class::String # Type of highway
lanes::Int # Number of lanes (1 if unspecified)
oneway::Bool # True if road is one-way
sidewalk::String # Sidewalk classifier, if available
cycleway::String # Cycleway classifier, if available
bicycle::String # Bicycle classifier, if available
name::String # Name, if available
nodes::Array{Int,1} # List of nodes
end
“Segments” represent a subset of a highway, and can be used for faster route planning. They begin and end at highway intersections (see below). Segments can be extracted from a list of roads and intersections using “extractSegments().”
type Segment
node0::Int # Source node ID
node1::Int # Target node ID
nodes::Array{Int,1} # List of nodes falling within node0 and node1
class::Int # Class of the segment
parent::Int # ID of parent highway
oneway::Bool # True if road is one-way
end
Feature¶
- “Features” are nodes tagged with additional data. OpenStreetMap.jl currently ignores some of these tags (e.g., crosswalks), but the following feature classes are currently extracted from OSM files:
- amentity
- shop
- building
- craft
- historic
- sport
- tourism
Many of these features also have a specified name and class detail (e.g., shop:restaurant). Nodes with no tags are never made into features.
type Feature
class::String # Shop, amenity, crossing, etc.
detail::String # Class qualifier
name::String # Name
end
Building¶
Buildings in OpenStreetMap may optionally have a name and class (though typically buildings are unlabeled). Like highways, they include a list of nodes.
type Building
class::String # Building type (usually "yes")
name::String # Building name (usually unavailable)
nodes::Array{Int,1} # List of nodes
end
Intersection¶
OpenStreetMap.jl includes an intersection detector. An intersection is a node which is included in at least two highways’ lists of nodes. The intersection object maintains a Set (no duplicates allowed) of highway ids that use that node.
type Intersection
highways::Set{Int} # Set of highway IDs
end
Region Boundaries¶
Region boundaries include the minimum and maximum latitude and longitude of a region. While the names of the types are clearly focused on the LLA coordinate system, Bounds can also be used with ENU coordinates. They will not work well with ECEF coordinates.
type Bounds
min_lat # Or min_north
max_lat # Or max_north
min_lon # Or min_east
max_lon # Or max_east
end
Point Types¶
These types give alternative representations for point locations in OpenStreetMap.jl.
Latitude-Longitude-Altitude (LLA) Coordinates¶
Used to store node data in OpenStreetMap XML files.
type LLA
lat
lon
alt
end
Because OpenStreetMap typically does not store altitude data, the following alias is available for convenience: LLA(lat, lon) = LLA(lat, lon, 0)
Earth-Centered-Earth-Fixed (ECEF) Coordinates¶
Global cartesian coordinate system rotating with the Earth.
type ECEF
x
y
z
end
East-North-Up (ENU) Coordinates¶
Local cartesian coordinate system, centered on a reference point.
type ENU
east
north
up
end
Additional Types¶
Transportation Network¶
The Network type is used to represent a street transportation network as a graph. This type nicely encapsulates the graph data from the user, simplifying the use of Graphs.jl for route planning. Most users will not need to interact with the internals of these objects.
type Network
g # Incidence graph of streets
v # Dictionary of vertices
v_inv # Inverted list of vertices
w # Edge weights
class # Edge classification
end
Plot Styles¶
The Style type is used to define custom plot elements. More information on its usage can be found on the Plots page.
type Style
color::Uint32 # Line color
width::Real # Line width
spec::String # Line type
end
style(x, y) = style(x, y, "-")
Reading OSM Data¶
OpenStreetMap data is available in a variety of formats. However, the easiest and most common to work with is the OSM XML format. OpenStreetMap.jl makes reading data from these files easy and straightforward:
- getOSMData(filename::String[, nodes=false, highways=false, buildings=false, features=false])¶
- Inputs:
- Required:
- filename [String]: Filename of OSM datafile.
- Optional:
- nodes [Bool]: true to read node data
- highways [Bool]: true to read highway data
- buildings [Bool]: true to read building data
- features [Bool]: true to read feature data
- Required:
- Outputs:
- nodes [false or Dict{Int,LLA}]: Dictionary of node locations
- highways [false or Dict{Int,Highway}]: Dictionary of highways
- buildings [false or Dict{Int,Building}]: Dictionary of buildings
- features [false or Dict{Int,Feature}]: Dictionary of features
These four outputs store all data from the file. highways, buildings, and features are dictionaries indexed by their OSM ID number, and contain an object of their respective type at each index. “Features” actually represent tags attached to specific nodes, so their ID numbers are the node numbers. The Highway and Building types both contain lists of nodes within them.
Example Usage:
nodes, hwys, builds, feats = getOSMData( MAP_FILENAME, nodes=true, highways=true, buildings=true, features=true)``
Usage Notes: Reading data is generally very fast unless your system runs out of memory. This is because LightXML.jl loads the entire xml file into memory as a tree rather than streaming it. A 150 MB OSM file seems to take up about 2-3 GB of RAM on my machine, so load large files with caution.
Extracting Intersections¶
A simple function is provided to find all highway ends and intersections:
- findIntersections(highways::Dict{Int, Highway})¶
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.
Working with Segments¶
Usually routing can be simplified to the problem of starting and ending at a specified intersection, rather than any node in a highway. In these cases, we can use “Segments” rather than “Highways” to greatly reduce the computation required to compute routes. Segments are subsets of highways that begin and end on nodes, keep track of their parent highway, and hold all intermediate nodes in storage (allowing them to be converted back to Highway types for plotting or cropping). The following functions extract Segments from Highways and convert Segments to Highways, respectively:
- segmentHighways(highways, intersections, classes, levels=Set(1:10...))¶
- highwaySegments(segments::Array{Segment, 1})¶
Note: By default, segmentHighways() converts only the first 10 levels into segments. If you wish to exclude certain road classes, you should do so here prior to routing. By default, OpenStreetMap.jl uses only 8 road classes, but only classes 1-6 represent roads used for typical routing (levels 7 and 8 are service and pedestrian roads, such as parking lot entrances and driveways). In the United States, roads with class 8 should not be used by cars.
Working with Data¶
This page gives details on the functions provided by OpenStreetMap.jl for working with OSM data.
Cropping Maps¶
OSM XML files do not provide sharp edges on boundaries. Also, it is often the case that one wants to focus on one subregion of a large OSM file. A cropping function is provided for these cases:
function cropMap!( nodes::Dict,
bounds::Bounds;
highways=nothing,
buildings=nothing,
features=nothing,
delete_nodes::Bool=true )
Classifying Map Elements¶
OpenStreetMap.jl can classify map elements according to the following schemes: * Roadways [8 levels] * Cycleways [4 levels] * Walkways [4 levels] * Building Types [5 levels] * Feature Types [7 levels]
Each of these schemes classifies map elements using their OSM tags according to multiple levels. The definitions of these levels is encoded in classes.jl.
The following functions take their respective map element lists as the single parameter and output a classification dictionary of type Dict{Int,Int}. The keys of the dictionary are the highway ID numbers, and the values provide the classification of that map element.
- roadways( highways )
- walkways( highways )
- cycleways( highways )
- classify( buildings )
- classify( features )
These classification dictionaries can be used for both route planning and map plotting.
Converting Map Coordinate Systems¶
OpenStreetMap.jl is capable of converting map data between LLA, ECEF, and ENU coordinates (see “Data Types”) for definitions of these standard coordinates. Because point location data is ONLY stored in the nodes dictionary (type Dict{Int,Point-Type}), only this object needs to be converted. Note that Bounds objects also need to be converted, although they don’t technically store map data. The following functions can be used to convert between coordinate systems:
- lla2ecef( nodes::Dict{Int,LLA} )
- ecef2lla( nodes::Dict{Int,ECEF} )
- ecef2enu( nodes::Dict{Int,ECEF}, reference::LLA )
- lla2enu( nodes::Dict{Int,LLA}, reference::LLA )
East-North-Up coordinates require an additional input parameter, reference, which gives the origin of the ENU coordinate system. LLA and ECEF coordinates both have their origins fixed at the center of the earth.
Coordinate System Selection¶
An effort has been made to allow users to work in the coordinate system of their choice. However, often times a specific coordinate system might not make sense for a given task, and thus functionality has not been implemented for it. Below are a few examples:
- Map cropping and plotting do not work in ECEF coordinates (these operations are fundamentally 2D operations, which is convenient only for LLA and ENU coordinates)
- Route planning does not work in LLA coordinates (spherical distances have note been implemented)
Route Planning¶
OpenStreetMap.jl provides a user-friendly interface to the Graphs.jl package for route planning on transportation networks. Either shortest or fastest routes may be computed using Dijkstra’s algorithm.
Transportation Network¶
In order to plot routes within the map, the streets must first be converted into a transportation network using createGraph():
- createGraph(nodes, highways, classes, levels)¶
Inputs:
- nodes [Dict{Int,ENU} or Dict{Int,ECEF}]: Dictionary of node locations
- highways [Dict{Int,Highway}]: Dictionary of highways
- classes [Dict{Int,Int}]: Dictionary of highway classifications
- levels [Set{Integer}]: Set of highway classification levels allowed for route planning
Output:
- Network type, containing all data necessary for route planning with Graphs.jl
A transportation network graph can alternatively be created using highway “segments” rather than highways. These segments begin and end at intersections, eliminating all intermediate nodes, and can greatly speed up route planning.
- createGraph(nodes, segments, intersections)
Inputs:
- nodes [Dict{Int,ENU} or Dict{Int,ECEF}]: Dictionary of node locations
- segments [Array{Segment,1}]: Array of segments
- intersections [Dict{Int,Intersection}]: Dictionary of intersections, indexed by node ID
Output:
- Network type, containing all data necessary for route planning with Graphs.jl
Shortest Routes¶
Compute the route with the shortest total distance between two nodes.
- shortestRoute(network, node0, node1)¶
Inputs:
- network [Network]: Transportation network
- node0 [Int]: ID of start node
- node1 [Int]: ID of finish node
Outputs:
- route_nodes [Array{Int,1}]: Ordered list of nodes along route
- distance [Float64]: Total route distance
Fastest Routes¶
Given estimated typical speeds for each road type, compute the route with the shortest total traversal time between two nodes.
- fastestRoute(network, node0, node1, class_speeds=SPEED_ROADS_URBAN)¶
Inputs:
- network [Network]: Transportation network
- node0 [Int]: ID of start node
- node1 [Int]: ID of finish node
- class_speeds [Dict{Int,Real}]: Traversal speed (km/hr) for each road class
Outputs:
- route_nodes [Array{Int,1}]: Ordered list of nodes along route
- route_time [Float64]: Estimated total route time
Note 1: A few built-in speed dictionaries are available in speeds.jl. Highway classifications are defined in classes.jl.
Note 2: Routing does not account for stoplights, traffic patterns, etc. fastestRoute merely weights each edge by both distance and typical speed.
Route Distance¶
It is often of use to compute the total route distance, which is not returned by fastestRoute(). An additional function is available for this purpose:
- distance(nodes, route)¶
Inputs:
- nodes [Dict{Int,ENU} or Dict{Int,ECEF}]: Dictionary of node locations
- route [Array{Int,1}]: Ordered list of nodes along route
Outputs:
- distance [Float64]: Total route distance
For added convenience, distance() is additionally overloaded for the following inputs, all of which return a Euclidean distance:
- distance(nodes::Dict{Int, ECEF}, node0::Int, node1::Int)
- distance(loc0::ECEF, loc1::ECEF)
- distance(nodes::Dict{Int, ENU}, node0::Int, node1::Int)
- distance(loc0::ENU, loc1::ENU)
- distance(x0, y0, z0, x1, y1, z1)
Edge Extraction¶
shortestRoute() and fastestRoute() both return a list of nodes, which comprises the route. routeEdges() can then convert this list of nodes into the list of edges, if desired:
- routeEdges(network::Network, route::Array{Int, 1})¶
The output is a list of edge indices with type Array{Int,1}.
Displaying Maps¶
OpenStreetMap.jl includes a single plotting function. This function has numerous options, allowing a great deal of flexibility when displaying maps:
function plotMap( nodes;
highways=nothing,
buildings=nothing,
features=nothing,
bounds=nothing,
intersections=nothing,
roadways=nothing,
cycleways=nothing,
walkways=nothing,
feature_classes=nothing,
building_classes=nothing,
route=nothing,
highway_style::Style = Style(0x007CFF,1.5,"-"),
building_style::Style = Style(0x000000,1,"-"),
feature_style = Style(0xCC0000,2.5,"."),
route_style = Style(0xFF0000, 3, "-"),
intersection_style::Style = Style(0x000000,3,"."),
width::Integer=500,
realtime::Bool=false )
The function, plotMap(), has a single required input: nodes. However, providing plotMap() with only the list of nodes will result in an empty plot. The user then has the choice between a variety of plotting options. It is important to note that this function is designed for convenience rather than speed. It is highly recommended that a Bounds object is input, as this is used to provided plot scaling.
The following subsecions step through some of the plotting options. Essentially, the user builds up a series of “layers” through providing multiple inputs.
Data Inputs¶
These parameters provided the actual data to be plotted.
- nodes [Dict{Int,LLA} or Dict{Int,ENU}]: List of all point locations
- features [Dict{Int,Feature}]: List of features to display
- buildings [Dict{Int,Building}]: List of buildings to display
- highways [Dict{Int,Highway}]: List of highways to display
- intersections [Dict{Int,Intersection}]: List of highway intersections
- route [Array{Int,1} or Array{Array{Int,1},1}]: List of nodes comprising a highway route OR a list of lists of routes (if multiple routes are to be displayed).
Data Classifiers¶
These parameters classify the map elements according to a layer specification. When these parameters are passed to plotMap(), only the classified map elements are plotted (all map elements not in these dictionaries are ignored).
- roadways: Dictionary of highway types suitable for driving
- cycleways: Dictionary of highway types suitable for cycling
- walkways: Dictionary of highway types suitable for walking
- building_classes: Dictionary of building classifications
- feature_classes: Dictionary of feature classifications
Note 1: These layers use their own Layer dictionaries, containing one Style type for each element classification level, to define plotting styles. Therefore, any additional style inputs related to these classifiers will be ignored without any explicit warnings to the user.
Note 2: Using multiple highway classifiers on one plot may cause them to overlap and occlude one another. The ordering, from bottom to top, is roadways, cycleways, walkways.
Plot Display Options¶
- bounds [Bounds]: X and Y axes limits of plot, also used to compute appropriate plot aspect ratio
- width [Int]: Width of the plot, in pixels
- realtime [Bool]: When true, elements are added to the map individually (this drastically slows down plotting, but is fun to watch)
Plot Customization¶
The following optional inputs allow the user to customize the map display.
- highway_style [Style or Dict{Int,Style}]: See note 3 below.
- building_style [Style]: See note 3 below.
- feature_style [Style or Dict{Int,Style}]: See note 3 below.
- route_style [Style or Array{Style,1}]: Use an array of Style types to plot multiple routes with different appearances.
- intersection_style [Style]
These inputs all take a Style type, which is constructed as follows:
style = OpenStreetMap.Style( color, width, spec )
For example:
highway_style = OpenStreetMap.Style( "b", 1.5, "-")
feature_style = OpenStreetMap.Style( 0xf57900, 2, ".")
Note 1: color must be a hex color code.
Note 2: spec is a line specification code used by Winston.jl. Common examples are the following:
- "-": Solid line
- ".": Filled, square points
- "o": Open, round points
Note 3: For highways, buildings, and features, if an additional classifier is input (e.g., roadways), the respective style input must be a dictionary of styles, with type Dict{Int,Style}, with a style given for each classification. This dictionary is called a “layer” in OpenStreetMap terminology, and defines how a specific map layer is displayed. The default layers are defined as constants in layers.jl.
Examples¶
Read data from an OSM XML file:
nodes, hwys, builds, feats = getOSMData( MAP_FILENAME, nodes=true, highways=true, buildings=true, features=true)
println("Number of nodes: $(length(nodes))")
println("Number of highways: $(length(hwys))")
println("Number of buildings: $(length(builds))")
println("Number of features: $(length(feats))")
Define map boundary and crop:
bounds = OpenStreetMap.Bounds(42.365,42.3675,-71.1,-71.094)
cropMap!(nodes, boston, highways=hwys, buildings=builds, features=feats, delete_nodes=false)
Find highway intersections:
inters = findIntersections(hwys)
println("Found $(length(inters)) intersections.")
Extract map components and classes:
roads = roadways( hwys )
peds = walkways( hwys )
cycles = cycleways( hwys )
bldg_classes = classify( builds )
feat_classes = classify( feats )
Convert map nodes to ENU coordinates:
reference = OpenStreetMap.centerBounds(boston)
nodesENU = lla2enu( nodes, reference )
boundsENU = lla2enu( bounds, reference )
Create transportation network:
network = createGraph( nodesENU, hwys, roads, Set(1:8...) )
println("Graph formed with $(Graphs.num_vertices(network.g)) vertices and $(Graphs.num_edges(network.g)) edges.")
Route planning:
loc_start = OpenStreetMap.ENU(-5000,5500,0)
loc_end = OpenStreetMap.ENU(5500,-4000,0)
node0 = nearestNode( nodesENU, loc_start, network.v_inv )
node1 = nearestNode( nodesENU, loc_end, network.v_inv )
shortest_route, shortest_distance = shortestRoute( network, node0, node1 )
fastest_route, fastest_time = fastestRoute( network, node0, node1 )
fastest_distance = distance( nodesENU, fastest_route )
println("Shortest route: $(shortest_distance) m (Nodes: $(length(shortest_route)))")
println("Fastest route: $(fastest_distance) m Time: $(fastest_time/60) min (Nodes: $(length(fastest_route)))")
Display shortest and fastest routes:
fignum_shortest = plotMap(nodesENU, highways=hwys, bounds=boundsENU, roadways=roads, route=shortest_route)
fignum_fastest = plotMap(nodesENU, highways=hwys, bounds=boundsENU, roadways=roads, route=fastest_route)
Display classified roadways, buildings, and features:
fignum = plotMap( nodesENU,
highways=hwys,
buildings=builds,
features=feats,
bounds=boundsENU,
width=1000,
feature_classes=feat_classes,
building_classes=bldg_classes,
roadways=roads)
Winston.savefig("osm_map.png")
Note: Winston currently distorts figures slightly when it saves them. Therefore, whenever equal axes scaling is required, export figures as EPS and rescale them as necessary.