abstreet/dev/api.html

366 lines
25 KiB
HTML
Raw Normal View History

<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>API - 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">
<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/new_city.html"><strong aria-hidden="true">2.1.</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" class="active"><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"><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"><strong aria-hidden="true">7.5.</strong> Live edits</a></li><li class="chapter-item expanded "><a href="../trafficsim/parking.html"><strong aria-hidde
</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="#api" id="api">API</a></h1>
<p>Suppose you're tired of manually fiddling with traffic signals, and you want to
use machine learning to do it. You can run A/B Street without graphics and
automatically control it through an API.</p>
<h2><a class="header" href="#examples" id="examples">Examples</a></h2>
<p>This
<a href="https://github.com/dabreegster/abstreet/blob/master/headless/examples/python_client.py">Python example</a>
has everything you need to get started.</p>
<p>See
<a href="https://github.com/dabreegster/abstreet/tree/master/headless/examples">all example code</a>
-- there are different experiments in Go and Python that automate running a
simulation, measuring some metric, and making a change to improve the metric.</p>
<h2><a class="header" href="#control-flow" id="control-flow">Control flow</a></h2>
<p>The <code>headless</code> API server that you run contains a single map and simulation at a
time. Even though you can theoretically have multiple clients make requests to
it simultaneously, the server will only execute one at a time. If you're trying
to do something other than use one script to make API calls in sequence, please
get in touch, so we can figure out something better suited to your use case.</p>
<p>When you start the <code>headless</code> server, it always loads the <code>montlake</code> map with
the <code>weekday</code> scenario. The only way you can change this is by calling
<code>/sim/load</code>. For example:</p>
<pre><code>curl http://localhost:1234/sim/load -d '{ &quot;scenario&quot;: &quot;data/system/seattle/scenarios/downtown/monday.bin&quot;, &quot;modifiers&quot;: [], &quot;edits&quot;: null }' -X POST`
</code></pre>
<p>You can also pass flags like <code>--infinite_parking</code> to the server to control
<a href="https://dabreegster.github.io/abstreet/rustdoc/sim/struct.SimOptions.html">SimOptions</a>.
These settings will apply for the entire lifetime of the server; you can't
change them later.</p>
<h2><a class="header" href="#api-details" id="api-details">API details</a></h2>
<blockquote>
<p><strong>Under construction</strong>: The API will keep changing. There are no backwards
compatibility guarantees yet. Please make sure I know about your project, so I
don't break your client code.</p>
</blockquote>
<p>For now, the API is JSON over HTTP. The exact format is unspecified, error codes
are missing, etc. A summary of the commands available so far:</p>
<ul>
<li><strong>/sim</strong>
<ul>
<li><strong>GET /sim/reset</strong>: Reset all temporary map edits and the simulation state.
The trips that will run don't change; they're determined by the scenario
specified by the last call to <code>/sim/load</code>. If you made live map edits using
things like <code>/traffic-signals/set</code>, they'll be reset to the <code>edits</code> from
<code>/sim/load</code>.</li>
<li><strong>POST /sim/load</strong>: Switch the scenario being simulated, and also optionally
sets the map edits.</li>
<li><strong>GET /sim/get-time</strong>: Returns the current simulation time.</li>
<li><strong>GET /sim/goto-time?t=06:30:00</strong>: Simulate until 6:30 AM. If the time you
specify is before the current time, you have to call <strong>/sim/reset</strong> first.</li>
<li><strong>POST /sim/new-person</strong>: The POST body must be an
<a href="https://dabreegster.github.io/abstreet/rustdoc/sim/struct.ExternalPerson.html">ExternalPerson</a>
in JSON format.</li>
</ul>
</li>
<li><strong>/traffic-signals</strong>
<ul>
<li><strong>GET /traffic-signals/get?id=42</strong>: Returns the traffic signal of
intersection #42 in JSON.</li>
<li><strong>POST /traffic-signals/set</strong>: The POST body must be a
<a href="https://dabreegster.github.io/abstreet/rustdoc/map_model/struct.ControlTrafficSignal.html">ControlTrafficSignal</a>
in JSON format.</li>
<li><strong>GET /traffic-signals/get-delays?id=42&amp;t1=03:00:00&amp;t2=03:30:00</strong>: Returns
the delay experienced by every agent passing through intersection #42 from
3am to 3:30, grouped by direction of travel.</li>
<li><strong>GET /traffic-signals/get-cumulative-thruput?id=42</strong>: Returns the number of
agents passing through intersection #42 since midnight, grouped by direction
of travel.</li>
<li><strong>GET /traffic-signals/get-all-current-state</strong>: Returns the current state of
all traffic signals, including the stage timing, waiting, and accepted
agents.</li>
</ul>
</li>
<li><strong>/data</strong>
<ul>
<li><strong>GET /data/get-finished-trips</strong>: Returns a JSON list of all finished trips.
Each tuple is (time the trip finished in seconds after midnight, trip ID,
mode, duration of trip in seconds). The mode is a string like &quot;Walk&quot; or
&quot;Drive&quot;. If the trip was cancelled for any reason, duration will be null.</li>
<li><strong>GET /data/get-agent-positions</strong>: Returns a JSON list of all active agents.
Vehicle type (or pedestrian), person ID, and position is included.</li>
<li><strong>GET /data/get-road-thruput</strong>: Returns a JSON list of (road, agent type,
hour since midnight, throughput for that one hour period).</li>
<li><strong>GET /data/get-blocked-by-graph</strong>: Returns a mapping from agent IDs to how
long they've been waiting and why they're blocked.</li>
<li><strong>GET /data/trip-time-lower-bound?id=123</strong>: Returns a reasonable lower bound
for the total duration of trip 123, in seconds. The time is calculated
assuming no delay at intersections, travelling full speed along every road,
and using the primary mode for the entire trip (so just driving).</li>
</ul>
</li>
<li><strong>/map</strong>
<ul>
<li><strong>GET /map/get-edits</strong>: Returns the current map edits in JSON. You can save
this to a file in <code>data/player/edits/city_name/map_name/</code> and later use it
in-game normally. You can also later run the <code>headless</code> server with
<code>--edits=name_of_edits</code>.</li>
<li><strong>GET /map/get-edit-road-command?id=123</strong>: Returns an object that can be
modified and then added to map edits.</li>
<li><strong>GET /map/get-intersection-geometry?id=123</strong>: Returns a GeoJSON object with
one feature for the intersection and a feature for all connecting roads. The
polygon coordinates are measured in meters, with the origin centered at the
intersection's center.</li>
<li><strong>GET /map/get-all-geometry</strong>: Returns a huge GeoJSON object with one
feature per road and intersection in the map. The coordinate space is WGS84.</li>
</ul>
</li>
</ul>
<h2><a class="header" href="#working-with-the-map-model" id="working-with-the-map-model">Working with the map model</a></h2>
<p>If you need to deeply inspect the map, you can dump it to JSON:</p>
<pre><code>cargo run --bin dump_map data/system/seattle/maps/montlake.bin &gt; montlake.json
</code></pre>
<p>See some example code that
<a href="https://github.com/dabreegster/abstreet/blob/master/headless/examples/generate_traffic.py">reads this JSON and finds buildings</a>.</p>
<p>You could also edit the map JSON, convert it back to binary, and use it in the
simulation. This isn't recommended generally, but one possible use case could be
tuning the amount of offstreet parking per building. The map JSON has a list
called <code>buildings</code>, and each object there has a field <code>parking</code>. You coud set
this object to <code>{ &quot;Private&quot;: [100, false] }</code> to indicate 100 parking spots, for
a building not explicitly designated in OpenStreetMap as a garage. After editing
the JSON, you have to convert it back to the binary format:</p>
<pre><code>cargo run --bin json_to_binary_map -- --input=montlake.json out=data/system/seattle/maps/montlake_modified.bin`
</code></pre>
<p>... Except this tool doesn't seem to work yet!</p>
<p>The format of the map isn't well-documented yet. See the
<a href="https://dabreegster.github.io/abstreet/rustdoc/map_model/index.html">generated API docs</a>
and <a href="../map/index.html">the map model docs</a> in the meantime.</p>
<h2><a class="header" href="#working-with-individual-trips" id="working-with-individual-trips">Working with individual trips</a></h2>
<p>You can use the <strong>/sim/new-person</strong> API in the middle of a simulation, if
needed. If possible, it's simpler to create a Scenario as input.</p>
<h2><a class="header" href="#working-with-scenarios" id="working-with-scenarios">Working with Scenarios</a></h2>
<p>You can
<a href="../trafficsim/travel_demand.html#custom-import">import trips from your own data</a>.</p>
<p>You can also generate different variations of one of the
<a href="../trafficsim/travel_demand.html#proletariat-robot">demand models</a> by specifying
an RNG seed:</p>
<pre><code>cargo run --bin random_scenario -- --rng=123 --map=data/system/seattle/maps/montlake.bin --scenario_name=home_to_work
</code></pre>
<p>You can also dump Scenarios (the file that defines all of the people and trips)
to JSON:</p>
<pre><code>cargo run --bin dump_scenario data/system/seattle/scenarios/montlake/weekday.bin &gt; montlake_weekday.json
</code></pre>
<p>You can modify the JSON, then put the file back in the appropriate directory and
use it in-game:</p>
<pre><code>cargo run --bin game data/system/seattle/scenarios/montlake/modified_scenario.json
</code></pre>
<p>The Scenario format is also undocumented, but see the
<a href="https://dabreegster.github.io/abstreet/rustdoc/sim/struct.Scenario.html">generated API docs</a>
anyway.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../dev/misc_tricks.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="../dev/testing.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<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">
<a rel="prev" href="../dev/misc_tricks.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="../dev/testing.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<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>