2020-08-09 17:51:13 +03:00
<!DOCTYPE HTML>
< html lang = "en" class = "sidebar-visible no-js light" >
< head >
<!-- Book generated using mdBook -->
< meta charset = "UTF-8" >
< title > Map model - A/B Street< / title >
< meta content = "text/html; charset=utf-8" http-equiv = "Content-Type" >
< meta name = "description" content = "" >
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
< meta name = "theme-color" content = "#ffffff" / >
< link rel = "shortcut icon" href = "../favicon.png" >
< link rel = "stylesheet" href = "../css/variables.css" >
< link rel = "stylesheet" href = "../css/general.css" >
< link rel = "stylesheet" href = "../css/chrome.css" >
< link rel = "stylesheet" href = "../css/print.css" media = "print" >
<!-- Fonts -->
< link rel = "stylesheet" href = "../FontAwesome/css/font-awesome.css" >
< link href = "https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel = "stylesheet" type = "text/css" >
< link href = "https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel = "stylesheet" type = "text/css" >
<!-- Highlight.js Stylesheets -->
< link rel = "stylesheet" href = "../highlight.css" >
< link rel = "stylesheet" href = "../tomorrow-night.css" >
< link rel = "stylesheet" href = "../ayu-highlight.css" >
<!-- Custom theme stylesheets -->
< / head >
< body >
<!-- Provide site root to javascript -->
< script type = "text/javascript" >
var path_to_root = "../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
< / script >
<!-- Work around some values being stored in localStorage wrapped in quotes -->
< script type = "text/javascript" >
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') & & theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') & & sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
< / script >
<!-- Set the theme before any content is loaded, prevents flash -->
< script type = "text/javascript" >
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
< / script >
<!-- Hide / unhide sidebar before it is displayed -->
< script type = "text/javascript" >
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
< / script >
< nav id = "sidebar" class = "sidebar" aria-label = "Table of contents" >
< div class = "sidebar-scrollbox" >
2020-11-09 22:42:58 +03:00
< ol class = "chapter" > < li class = "chapter-item expanded " > < a href = "../index.html" > < strong aria-hidden = "true" > 1.< / strong > Overview< / a > < / li > < li class = "chapter-item expanded " > < a href = "../howto/index.html" > < strong aria-hidden = "true" > 2.< / strong > Instructions< / a > < / li > < li > < ol class = "section" > < li class = "chapter-item expanded " > < a href = "../howto/map_parking.html" > < strong aria-hidden = "true" > 2.1.< / strong > How to map on-street parking< / a > < / li > < li class = "chapter-item expanded " > < a href = "../howto/new_city.html" > < strong aria-hidden = "true" > 2.2.< / strong > Importing a new city< / a > < / li > < / ol > < / li > < li class = "chapter-item expanded " > < a href = "../how_it_works.html" > < strong aria-hidden = "true" > 3.< / strong > How it works< / a > < / li > < li class = "chapter-item expanded " > < a href = "../case_studies/index.html" > < strong aria-hidden = "true" > 4.< / strong > Case studies< / a > < / li > < li > < ol class = "section" > < li class = "chapter-item expanded " > < a href = "../case_studies/lake_wash.html" > < strong aria-hidden = "true" > 4.1.< / strong > Lake Washington Blvd Stay Healthy Street< / a > < / li > < li class = "chapter-item expanded " > < a href = "../case_studies/west_seattle.html" > < strong aria-hidden = "true" > 4.2.< / strong > West Seattle mitigations< / a > < / li > < li class = "spacer" > < / li > < / ol > < / li > < li class = "chapter-item expanded " > < a href = "../dev/index.html" > < strong aria-hidden = "true" > 5.< / strong > Developer guide< / a > < / li > < li > < ol class = "section" > < li class = "chapter-item expanded " > < a href = "../dev/misc_tricks.html" > < strong aria-hidden = "true" > 5.1.< / strong > Misc developer tricks< / a > < / li > < li class = "chapter-item expanded " > < a href = "../dev/api.html" > < strong aria-hidden = "true" > 5.2.< / strong > API< / a > < / li > < li class = "chapter-item expanded " > < a href = "../dev/testing.html" > < strong aria-hidden = "true" > 5.3.< / strong > Testing< / a > < / li > < li class = "chapter-item expanded " > < a href = "../dev/mass_import.html" > < strong aria-hidden = "true" > 5.4.< / strong > Importing many maps< / a > < / li > < li class = "chapter-item expanded " > < a href = "../dev/data.html" > < strong aria-hidden = "true" > 5.5.< / strong > Data organization< / a > < / li > < / ol > < / li > < li class = "chapter-item expanded " > < a href = "../map/index.html" class = "active" > < strong aria-hidden = "true" > 6.< / strong > Map model< / a > < / li > < li > < ol class = "section" > < li class = "chapter-item expanded " > < a href = "../map/details.html" > < strong aria-hidden = "true" > 6.1.< / strong > Details< / a > < / li > < li class = "chapter-item expanded " > < a href = "../map/importing/index.html" > < strong aria-hidden = "true" > 6.2.< / strong > Importing< / a > < / li > < li > < ol class = "section" > < li class = "chapter-item expanded " > < a href = "../map/importing/convert_osm.html" > < strong aria-hidden = "true" > 6.2.1.< / strong > convert_osm< / a > < / li > < li class = "chapter-item expanded " > < a href = "../map/importing/geometry.html" > < strong aria-hidden = "true" > 6.2.2.< / strong > Road/intersection geometry< / a > < / li > < li class = "chapter-item expanded " > < a href = "../map/importing/rest.html" > < strong aria-hidden = "true" > 6.2.3.< / strong > The rest< / a > < / li > < li class = "chapter-item expanded " > < a href = "../map/importing/misc.html" > < strong aria-hidden = "true" > 6.2.4.< / strong > Misc< / a > < / li > < / ol > < / li > < li class = "chapter-item expanded " > < a href = "../map/edits.html" > < strong aria-hidden = "true" > 6.3.< / strong > Live edits< / a > < / li > < li class = "chapter-item expanded " > < a href = "../map/platform.html" > < strong aria-hidden = "true" > 6.4.< / strong > Exporting< / a > < / li > < / ol > < / li > < li class = "chapter-item expanded " > < a href = "../trafficsim/index.html" > < strong aria-hidden = "true" > 7.< / strong > Traffic simulation< / a > < / li > < li > < ol class = "section" > < li class = "chapter-item expanded " > < a href = "../trafficsim/discrete_event.html" > < strong aria-hidden = "true" > 7.1.< / strong > Discrete event simulation< / a > < / li > < li class = "chapter-item expanded " > < a href = "../trafficsim/travel_demand.html" > < strong aria-hidden = "true" > 7.2.< / strong > Travel demand< / a > < / li > < li class = "chapter-item expanded " > < a href = "../trafficsim/gridlock.html" > < strong aria-hidden = "true" > 7.3.< / strong > Gridlock< / a > < / li > < li class = "chapter-item expanded " > < a href = "../trafficsim/trips.html" > < strong aria-hidden = "true" > 7.4.< / strong > Multi-modal trips< / a > < / li > < li class = "chapter-item expanded " > < a href = "../trafficsim/live_edits.html" > <
2020-08-09 17:51:13 +03:00
< / div >
< div id = "sidebar-resize-handle" class = "sidebar-resize-handle" > < / div >
< / nav >
< div id = "page-wrapper" class = "page-wrapper" >
< div class = "page" >
< div id = "menu-bar-hover-placeholder" > < / div >
< div id = "menu-bar" class = "menu-bar sticky bordered" >
< div class = "left-buttons" >
< button id = "sidebar-toggle" class = "icon-button" type = "button" title = "Toggle Table of Contents" aria-label = "Toggle Table of Contents" aria-controls = "sidebar" >
< i class = "fa fa-bars" > < / i >
< / button >
< button id = "theme-toggle" class = "icon-button" type = "button" title = "Change theme" aria-label = "Change theme" aria-haspopup = "true" aria-expanded = "false" aria-controls = "theme-list" >
< i class = "fa fa-paint-brush" > < / i >
< / button >
< ul id = "theme-list" class = "theme-popup" aria-label = "Themes" role = "menu" >
< li role = "none" > < button role = "menuitem" class = "theme" id = "light" > Light (default)< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "rust" > Rust< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "coal" > Coal< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "navy" > Navy< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "ayu" > Ayu< / button > < / li >
< / ul >
< button id = "search-toggle" class = "icon-button" type = "button" title = "Search. (Shortkey: s)" aria-label = "Toggle Searchbar" aria-expanded = "false" aria-keyshortcuts = "S" aria-controls = "searchbar" >
< i class = "fa fa-search" > < / i >
< / button >
< / div >
< h1 class = "menu-title" > A/B Street< / h1 >
< div class = "right-buttons" >
< a href = "../print.html" title = "Print this book" aria-label = "Print this book" >
< i id = "print-button" class = "fa fa-print" > < / i >
< / a >
< / div >
< / div >
< div id = "search-wrapper" class = "hidden" >
< form id = "searchbar-outer" class = "searchbar-outer" >
< input type = "search" name = "search" id = "searchbar" name = "searchbar" placeholder = "Search this book ..." aria-controls = "searchresults-outer" aria-describedby = "searchresults-header" >
< / form >
< div id = "searchresults-outer" class = "searchresults-outer hidden" >
< div id = "searchresults-header" class = "searchresults-header" > < / div >
< ul id = "searchresults" >
< / ul >
< / div >
< / div >
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
< script type = "text/javascript" >
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
< / script >
< div id = "content" class = "content" >
< main >
< h1 > < a class = "header" href = "#map-model" id = "map-model" > Map model< / a > < / h1 >
2020-10-12 04:38:32 +03:00
< p > A/B Street transforms OpenStreetMap (OSM) data into a detailed geometric and
semantic representation of the world for traffic simulation. This chapter
describes that map model, with the hopes that it'll be useful for purposes
beyond this project.< / p >
< h2 > < a class = "header" href = "#overview" id = "overview" > Overview< / a > < / h2 >
< p > A < code > Map< / code > covers everything inside some hand-drawn boundary, usually scoped to a
city or a few of a city's districts. Unlike OSM, it doesn't cover the entire
world; it only has areas specifically extracted for some purpose.< / p >
< p > A map consists of many objects. Mainly, there are roads, broken down into
individual lanes, and intersections. A road is a single segment connecting
exactly two intersections (as opposed to OSM, where a single " way" may span many
intersections). Lanes within a road have a specific type, which dictates their
direction of travel (or lack of travel, like on-street parking) and uses.
Sidewalks are represented as bidirectional lanes. Roads connect at
intersections, which contain an explicit set of turns, each linking a source
lane to a destination lane.< / p >
< p > Maps also contain parking lots and buildings, which connect to the nearest
driveable lane and a sidewalk. Maps have water and park areas, only used for
drawing. They also represent public transit stops and routes.< / p >
< h2 > < a class = "header" href = "#how-is-a-map-used" id = "how-is-a-map-used" > How is a map used?< / a > < / h2 >
< p > Unlike some GIS systems, maps don't use any kind of database -- they're just a
file, anywhere from 1 to ~500MB (depending on the size of their boundary). Once
loaded into memory, different objects from the map can be accessed directly,
along with a large API to perform various queries.< / p >
< p > Most of the map's API is read-only; once built, a map doesn't change until
user-created edits are applied.< / p >
< p > The pipeline to import a map from OSM data (and also optional supplementary,
city-specific data) is complex and may take a few minutes to run, but it happens
once offline. Applications using maps just read the final file.< / p >
< h2 > < a class = "header" href = "#features" id = "features" > Features< / a > < / h2 >
< p > Why use A/B Street's map model instead of processing OSM directly?< / p >
< p > TODO: Order these better. For each one, show before/after pictures< / p >
< h3 > < a class = "header" href = "#area-clipping" id = "area-clipping" > Area clipping< / a > < / h3 >
< p > Bodies of water, forests, parks, and other areas are represented in OSM as
relations, requiring the user to stitch together multiple polylines in undefined
orders and handle inner holes. A/B Street maps handle all of that, and also clip
the area's polygon to the boundary of the entire map -- including coastlines.< / p >
< h3 > < a class = "header" href = "#road-and-intersection-geometry" id = "road-and-intersection-geometry" > Road and intersection geometry< / a > < / h3 >
< p > OSM represents roads as a polyline of the physical center of the road. A/B
Street infers the number and type of lanes from OSM metadata, then creates
individual lanes of appropriate width, each with a center-line and polygon for
geometry. At intersections, the roads and lanes are " trimmed back" to avoid
overlapping, and the " common area" becomes the intersection's polygon. This
heuristic process is reasonably robust to complex shapes, with special treatment
of highway on/off-ramps, although it does still have some bugs.< / p >
< h3 > < a class = "header" href = "#turns" id = "turns" > Turns< / a > < / h3 >
< p > At each intersection, A/B Street infers all legal movements between vehicle
lanes and sidewalks. This process makes use of OSM metadata about turn lanes,
inferring reasonable defaults for multi-lane roads. OSM turn restriction
relations, which may span a sequence of several roads to describe U-turns around
complex intersections, are also used.< / p >
< h3 > < a class = "header" href = "#parking-lots" id = "parking-lots" > Parking lots< / a > < / h3 >
< p > OSM models parking lots as areas along with the driveable aisles. Usually the
capacity of a lot isn't tagged. A/B Street automatically fills paring lots with
individual stalls along the aisles, estimating the capacity just from this
geometry.< / p >
< h3 > < a class = "header" href = "#stop-signs" id = "stop-signs" > Stop signs< / a > < / h3 >
< p > At unsignalized intersections, A/B Street infers which roads have to stop, and
which have right-of-way.< / p >
< h3 > < a class = "header" href = "#traffic-signals" id = "traffic-signals" > Traffic signals< / a > < / h3 >
< p > OSM has no way to describe how traffic signals are configured. A/B Street models
fixed-timer signals, automatically inferring the number of phases, their
duration, and the movements that are prioritized and permitted during each
phase.< / p >
< h3 > < a class = "header" href = "#pathfinding" id = "pathfinding" > Pathfinding< / a > < / h3 >
< p > A/B Street can determine routes along lanes and turns for vehicles and
pedestrians. These routes obey OSM's turn restriction relations that span
multiple road segments. They also avoid roads that're tagged as not allowing
through-traffic, depending on the route's origin and destination and vehicle
type. The pathfinding optionally makes use of contraction hierarchies to greatly
speed up query performance, at the cost of a slower offline importing process.< / p >
< h3 > < a class = "header" href = "#bridge-z-ordering" id = "bridge-z-ordering" > Bridge z-ordering< / a > < / h3 >
< p > OSM tags bridges and tunnels, but the roads that happen to pass underneath
bridges aren't mapped. A/B Street detects these and represents the z-order for
drawing.< / p >
< h3 > < a class = "header" href = "#buildings" id = "buildings" > Buildings< / a > < / h3 >
< p > Similar to areas, A/B Street consolidates the geometry of OSM buildings, which
may be split into multiple polygons. Each building is also associated with the
nearest driveable lane and sidewalk, and metadata is used to infer a land-use
(like residential and commercial) and commercial amenities available.< / p >
< h3 > < a class = "header" href = "#experimental-public-transit" id = "experimental-public-transit" > Experimental: public transit< / a > < / h3 >
< p > A/B Street uses bus stops and route relations from OSM to build a model of
public transit routes. OSM makes few guarantees about how the specifics of the
route are specified, but A/B Street produces specific paths, handling clipping
to the map boundary.< / p >
< p > ... All of this isn't the case yet, but it's a WIP!< / p >
< h3 > < a class = "header" href = "#experimental-separated-cyclepaths-tramways-and-walking-paths" id = "experimental-separated-cyclepaths-tramways-and-walking-paths" > Experimental: separated cyclepaths, tramways, and walking paths< / a > < / h3 >
< p > Some cyclepaths, tram lines, and footpaths in OSM are tagged as separate ways,
with no association to a " main" road. Sometimes this is true -- they're
independent trails that only occasionally cross roads. But often they run
alongside a road. A/B Street attempts to detect these and " snap" them to the
main road as extra lanes.< / p >
< p > ... But this doesn't work yet at all.< / p >
2020-08-09 17:51:13 +03:00
< / main >
< nav class = "nav-wrapper" aria-label = "Page navigation" >
<!-- Mobile navigation buttons -->
2020-11-09 22:42:58 +03:00
< a rel = "prev" href = "../dev/data.html" class = "mobile-nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
2020-08-09 17:51:13 +03:00
< i class = "fa fa-angle-left" > < / i >
< / a >
2020-10-12 04:38:32 +03:00
< a rel = "next" href = "../map/details.html" class = "mobile-nav-chapters next" title = "Next chapter" aria-label = "Next chapter" aria-keyshortcuts = "Right" >
2020-08-09 17:51:13 +03:00
< i class = "fa fa-angle-right" > < / i >
< / a >
< div style = "clear: both" > < / div >
< / nav >
< / div >
< / div >
< nav class = "nav-wide-wrapper" aria-label = "Page navigation" >
2020-11-09 22:42:58 +03:00
< a rel = "prev" href = "../dev/data.html" class = "nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
2020-08-09 17:51:13 +03:00
< i class = "fa fa-angle-left" > < / i >
< / a >
2020-10-12 04:38:32 +03:00
< a rel = "next" href = "../map/details.html" class = "nav-chapters next" title = "Next chapter" aria-label = "Next chapter" aria-keyshortcuts = "Right" >
2020-08-09 17:51:13 +03:00
< i class = "fa fa-angle-right" > < / i >
< / a >
< / nav >
< / div >
< script type = "text/javascript" >
window.playpen_copyable = true;
< / script >
< script src = "../elasticlunr.min.js" type = "text/javascript" charset = "utf-8" > < / script >
< script src = "../mark.min.js" type = "text/javascript" charset = "utf-8" > < / script >
< script src = "../searcher.js" type = "text/javascript" charset = "utf-8" > < / script >
< script src = "../clipboard.min.js" type = "text/javascript" charset = "utf-8" > < / script >
< script src = "../highlight.js" type = "text/javascript" charset = "utf-8" > < / script >
< script src = "../book.js" type = "text/javascript" charset = "utf-8" > < / script >
<!-- Custom JS scripts -->
< / body >
< / html >