Note: Most of these still aren't started, because the baseline simulation in the
-relevant area isn't working. Unknown traffic signal timing, bad guesses at the
-amount of off-street parking, lanes tagged incorrectly in OpenStreetMap, and
-simulation bugs cause unrealistic gridlock. It's hard to evaluate a change
-without a realistic baseline.
In April 2020, Seattle Department of Transportation started rolling out
-Stay Healthy Streets,
-restricting roads to through-traffic to give people walking and biking more
-space for social distancing.
-Seattle Neighborhood Greenways
-soon proposed extending this to a
-130-mile network.
-
Selecting the streets requires some planning:
-
-
These streets were selected to amplify outdoor exercise opportunities for
-areas with limited open space options, low car ownership and routes connecting
-people to essential services and food take out. We also ensured street
-closures did not impact newly opened food pick up loading zones, parking
-around hospitals for service for health care professionals, and bus routes.
-
-
I've spent the last two years building A/B Street,
-software to explore the impacts of changes like this on different modes of
-transportation. So, let's try implementing part of the proposed network and see
-what happens!
Let's start with one part of the proposal, closing Lake Washington Blvd to cars
-through the Arboretum. There's already a multi-use trail alongside this stretch,
-but its width makes it difficult to maintain 6 feet from people. There are some
-parking lots that become inaccessible with this proposal, but they're currently
-closed anyway.
Let's get started! If you want to follow along,
-install A/B Street, open sandbox mode, and switch the map
-to Lake Washington corridor. Zoom in on the southern tip of the Arboretum and
-hop into edit mode. We can see Lake Washington Blvd just has one travel lane in
-each direction here. Click each lane, convert it to a bike lane, and repeat
-north until Foster Island Road.
-
When we leave edit mode, the traffic simulation resets to midnight. Nothing
-really interesting happens until 5 or 6am, so we'll speed up time. Watching the
-section of road we edited, we'll only see pedestrians and bikes use this stretch
-of road. If we want, we can click an individual person and follow along their
-journey.
-
-
Something's weird though. There's lots of traffic cutting northbound through the
-neighborhood, along 29th, Ward, and 28th. We can open up the throughput layer to
-find which roads have the most traffic. More usefully, we can select "compare
-before edits" to see what roads are getting more or less traffic because of the
-road we modified. As expected, there's much less traffic along Lake Wash Blvd,
-but it's also clear that lots of cars are now cutting through 26th Ave E.
Let's say you want to nudge traffic to use 23rd Ave, the nearest north/south
-arterial, instead. (A/B Street is an unopinionated tool; if you have a different
-goal in mind, try using it for that instead.) In this simulation, drivers pick
-the fastest route, so we could try lowering speed limits or make some of the
-residential streets connecting to Madison one-way, discouraging through-traffic.
-In reality, the speed limit changes could be implemented through
-traffic calming
-or cheap, temporary alternatives.
I'm working to model "local access only" roads in A/B Street, and I'll describe
-how to measure the impact on travel times. Stay tuned to see more of the
-proposed network
-simulated, and get in touch if you'd like to help out!
In March 2020, the West Seattle bridge was closed due to cracks forming. As of
-May, COVID-19's impact on commuting means the area still hasn't seen how the
-area will handle losing the main route to the rest of Seattle. A local group,
-HPAC, published a list of
-requests to SDOT
-to prepare the area for these changes.
-
This page will try to explore some of the problems and solutions from HPAC's
-document using A/B Street, a traffic simulator designed
-to explore the impacts of changes like this on different modes of
-transportation.
HPAC has been asking for a protected left-turn stage at this intersection. I'm
-unfamiliar with this intersection and currently unable to scout in-person, so
-I'm blindly guessing the traffic signal currently has just two stages:
-
-
From watching the traffic, it seems like the east/west direction is busier, with
-lots of eastbound traffic headed towards WA-509. Holden St has no turn lanes, so
-a protected left turn stage makes sense. Let's make the change and see what
-happens:
-
-
Unfortuately, we can't evaluate the change yet, because the simulation gets
-stuck with unrealistic traffic jams in other parts of the map. This is mostly
-due to data quality issues in OpenStreetMap and incorrectly guessed traffic
-signal timings. These problems can be fixed with the help of somebody familiar
-with the area.
The 9th item from HPAC's list asks for measuring the amount of east-west traffic
-to figure out what streets people are using as arterials. That's an easy
-analysis, using the throughput layer.
-
-
By 6am, the busiest streets include Admiral Way, S Charlestown, SW Genesee, SW
-Alaska, SW Holden, and SW Roxbury St. Again, it's necessary to first fix data
-quality problems and run a full day before doing more analysis.
-
Once the simulation is running smoothly, A/B Street can be used to make changes
--- like lowering speed limits, adding a protected left turn stage, or converting
-part of the road into a bus lane -- and evaluate the effects on individual trips
-and aggregate groups.
Community proposals now includes a "repair the bridge" option, which should
-restore things to how they were before March 2020. This is useful as a baseline,
-to explore what traffic patterns were like before the closure.
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.
This
-Python example
-has everything you need to get started.
-
See
-all example code
--- there are different experiments in Go and Python that automate running a
-simulation, measuring some metric, and making a change to improve the metric.
The headless 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.
-
When you start the headless server, it always loads the montlake map with
-the weekday scenario. The only way you can change this is by calling
-/sim/load. For example:
You can also pass flags like --infinite_parking to the server to control
-SimOptions.
-These settings will apply for the entire lifetime of the server; you can't
-change them later.
Under construction: 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.
-
-
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:
-
-
/sim
-
-
GET /sim/reset: 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 /sim/load. If you made live map edits using
-things like /traffic-signals/set, they'll be reset to the edits from
-/sim/load.
-
POST /sim/load: Switch the scenario being simulated, and also optionally
-sets the map edits.
-
GET /sim/get-time: Returns the current simulation time.
-
GET /sim/goto-time?t=06:30:00: Simulate until 6:30 AM. If the time you
-specify is before the current time, you have to call /sim/reset first.
-
POST /sim/new-person: The POST body must be an
-ExternalPerson
-in JSON format.
-
-
-
/traffic-signals
-
-
GET /traffic-signals/get?id=42: Returns the traffic signal of
-intersection #42 in JSON.
-
POST /traffic-signals/set: The POST body must be a
-ControlTrafficSignal
-in JSON format.
-
GET /traffic-signals/get-delays?id=42&t1=03:00:00&t2=03:30:00: Returns
-the delay experienced by every agent passing through intersection #42 from
-3am to 3:30, grouped by direction of travel.
-
GET /traffic-signals/get-cumulative-thruput?id=42: Returns the number of
-agents passing through intersection #42 since midnight, grouped by direction
-of travel.
-
GET /traffic-signals/get-all-current-state: Returns the current state of
-all traffic signals, including the stage timing, waiting, and accepted
-agents.
-
-
-
/data
-
-
GET /data/get-finished-trips: 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 "Walk" or
-"Drive". If the trip was cancelled for any reason, duration will be null.
-
GET /data/get-agent-positions: Returns a JSON list of all active agents.
-Vehicle type (or pedestrian), person ID, and position is included.
-
GET /data/get-road-thruput: Returns a JSON list of (road, agent type,
-hour since midnight, throughput for that one hour period).
-
GET /data/get-blocked-by-graph: Returns a mapping from agent IDs to how
-long they've been waiting and why they're blocked.
-
GET /data/trip-time-lower-bound?id=123: 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).
-
GET /data/all-trip-time-lower-bounds: The faster equivalent of calling
-/data/trip-time-lower-bound for every trip in the simulation.
-
-
-
/map
-
-
GET /map/get-edits: Returns the current map edits in JSON. You can save
-this to a file in data/player/edits/city_name/map_name/ and later use it
-in-game normally. You can also later run the headless server with
---edits=name_of_edits.
-
GET /map/get-edit-road-command?id=123: Returns an object that can be
-modified and then added to map edits.
-
GET /map/get-intersection-geometry?id=123: 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.
-
GET /map/get-all-geometry: Returns a huge GeoJSON object with one
-feature per road and intersection in the map. The coordinate space is WGS84.
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 buildings, and each object there has a field parking. You coud set
-this object to { "Private": [100, false] } 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:
-
cargo run --bin json_to_binary_map -- --input=montlake.json out=data/system/seattle/maps/montlake_modified.bin`
-
A/B Street includes lots of large binary files to represent converted maps,
-scenarios, and prebaked simulation results. The files are too large to store in
-git, but the files are still logically tied to a version of the code, since the
-format sometimes changes. Additionally, all of the files are too large to
-include in the .zip release that most people use, but it should still be
-possible for players to download the optional content. Also, there are different
-versions of the game floating around, on native and web, that have to be
-associated with the proper version of these files.
-
It's all slightly confusing, so this page describes how it all works.
If you peek into the data/ directory, it's mainly split into 3 subdirectories.
-system/ is used when running the game and is the subject of this page.
-input/ is used to store input and intermediate files for importing maps, and
-only developers running the importer should care about it. player/ contains
-local settings, map edits, and other data created in-game.
-
data/MANIFEST.json is a listing of all files in data/system/, along with
-their size and md5sum. Different tools compare this manifest to the local
-filesystem to figure out what to do.
-
There are also some other scripts and files in data/, but they should probably
-be moved.
data/system/ and data/input/ are stored in Amazon S3, at
-http://abstreet.s3-website.us-east-2.amazonaws.com. This S3 bucket is organized
-into versions: dev, 0.2.17, 0.2.18, etc. dev represents the latest
-version of all data files. The numbered versions correspond to
-releases and only contain
-data/system/, not data/input/. Depending how large these directories grow
-over time, I'll commit to keeping around at least 3 of the previous numbered
-versions, but I might delete older ones after that.
-
In lieu of a proper document for the release process, the commands used to make
-a versioned copy of the data are something like:
For people building the game from source, the process to keep data
-files fresh is to cargo run --bin updater. This tool calculates md5sums of all
-local files, then compares it with the checked-in data/MANIFEST.json. Any
-difference results in a local file being deleted or a new file from S3 being
-downloaded. By editing data/player/data.json manually or using the UI in the
-game (found by loading a map, then choosing to download more maps), somebody can
-opt into downloading "extra/optional" cities.
When the weekly .zip binary release for Mac, Linux, and Windows is produced, the
-game crate is built with --features release_s3. When the downloader UI is
-opened in-game, this causes downloads to occur from a versioned S3 directory,
-like 0.2.17, depending on the version string compiled into the game at that
-time. So somebody can run off the weekly release, opt into more cities, and get
-the correct version of the files, even if the format has changed in /dev/
-since then.
The strategy for managing files gets more interested when the game is compiled
-to WebAssembly, since browsers can't read from the local filesystem.
-game/src/load.rs contains some crazy tricks to instead make asynchronous HTTP
-requests through the browser. When using game/run_web.sh, the files are served
-through a local HTTP server and symlinked to the local copy of data/system/.
-
Not all files are loaded through HTTP; some are actually statically compiled
-into the .wasm file itself! abstutil/src/io_web.rs does this magic using the
-include_dir crate. Only a few critical large files, needed at startup, are
-included. There's an IO layer for listing and reading files that, on web, merges
-results from the bundled-in files and the remote files that're declared to exist
-in the bundled-in copy of data/MANIFEST.json.
Everything's the same, except building with --features wasm_s3 causes the game
-to make HTTP requests to the S3 bucket, instead of localhost. The web version
-always pins to /dev, never a release version of the data, since the web client
-is always updated along with the data, for now.
On Linux, sudo apt-get install xorg-dev libxcb-shape0-dev libxcb-xfixes0-dev
-or the equivalent for your distro
-
-
One-time setup:
-
-
-
Download the repository:
-git clone https://github.com/dabreegster/abstreet.git
-
-
-
Grab the minimal amount of data to get started: cargo run --bin updater
-
-
-
Run the game: RUST_BACKTRACE=1 cargo run --bin game --release. On Windows,
-set environment variables like this:
-set RUST_BACKTRACE=1 && cargo run --bin game --release
Compile faster by just doing cargo run. The executable will have debug stack
-traces and run more slowly. You can do cargo run --release to build in
-optimized release mode; compilation will be slower, but the executable much
-faster.
-
Some in-game features are turned off by default or don't have a normal menu to
-access them. The list:
-
-
To toggle developer mode: press Control+S in game, or
-cargo run -- --dev
-
To warp to an object by numeric ID: press Control+j
-
To enter debug mode with all sorts of goodies: press Control+D
-
-
-
You can start the game in different modes using flags:
-
-
cargo run --bin game -- --dev data/system/seattle/maps/downtown.bin starts
-on a particular map
-
cargo run --bin game -- data/system/seattle/scenarios/downtown/weekday.bin
-starts with a scenario (which is tied to a certain map)
-
cargo run --bin game -- --challenge=trafficsig/tut2 starts on a particular
-challenge. See the list of aliases by passing in a bad value here.
-
cargo run --bin game -- data/player/saves/montlake/no_edits_unnamed/00h00m20.3s.bin
-restores an exact simulation state. Savestates are found in debug mode
-(Control+D) -- they're probably confusing for the normal player
-experience, so they're hidden for now.
-
cargo run --bin game -- --tutorial=12 starts somewhere in the tutorial
-
Adding --edits='name of edits' starts with edits applied to the map.
As data formats change over time, things in the data/ directory not under
-version control will get out of date. At any time, you can run
-cargo run --bin updater from the main repository directory to update only the
-files that have changed.
-
You can also opt into downloading updates for more cities by editing
-data/player/data.json. In the main UI, there's a button to download more
-cities that will help you manage this config file.
libgdal-dev: See https://gdal.org if your OS package manager doesn't have
-this. If you keep hitting linking errors, then just remove
---features scenarios from import.sh. You won't be able to build the
-Seattle scenarios.
-
Standard Unix utilities: curl, unzip, gunzip
-
-
The first stage of the importer, --raw, will download input files from OSM,
-King County GIS, and so on. If the mirrors are slow or the files vanish, you
-could fill out data/config and use the updater described above to grab the
-latest input.
-
Building contraction hierarchies for pathfinding occurs in the --map stage. It
-can take a few minutes for larger maps. To view occasional progress updates, you
-can run the importer with
All code is automatically formatted using
-https://github.com/rust-lang/rustfmt; please run cargo +nightly fmt before
-sending a PR. (You have to install the nightly toolchain just for fmt)
-
cargo fmt can't yet organize imports, but we follow a convention to minimize
-conflict with what some IDEs do. Follow existing code to group imports: std,
-external crates, other crates in the project, the current crate, then finally
-any module declarations.
The error handling is unfortunately inconsistent. The goal is to gracefully
-degrade instead of crashing the game. If a crash does happen, make sure the logs
-will have enough context to reproduce and debug. For example, giving up when
-some geometry problem happens isn't ideal, but at least make sure to print the
-road / agent IDs or whatever will help find the problem. It's fine to crash
-during map importing, since the player won't deal with this, and loudly stopping
-problems is useful. It's also fine to crash when initially constructing all of
-the renderable map objects, because this crash will consistently happen at
-startup-time and be noticed by somebody developing before a player gets to it.
-
Since almost none of the code ever needs to distinguish error cases, use
-anyhow. Most of the errors generated within
-A/B Street are just strings anyway; the bail! macro is a convenient way to
-return them.
Prefer using info!, warn!, error!, etc from the log crate rather than
-println.
-
Adjust the log level without recompiling via the RUST_LOG env variable.
-
RUST_LOG=debug cargo run --bin game
-
-
This can be done on a per lib basis:
-
RUST_LOG=my_lib=debug cargo run --bin game
-
-
Or a module-by-module basis:
-
RUST_LOG=my_lib::module=debug cargo run --bin game
-
-
You can mix and match:
-
# error logging by default, except the foo:bar module at debug level
-# and the entire baz crate at info level
-RUST_LOG=error,foo::bar=debug,baz=info cargo run --bin game
-
-
For some special cases, you might want to use regex matching by specifying a
-pattern with the "/":
-
# only log once every 10k
-RUST_LOG="fast_paths=debug/contracted node [0-9]+0000 " mike import_la
-
For https://github.com/dabreegster/abstreet/issues/326, I'm starting to figure
-out how to import hundreds of maps into A/B Street. There are many issues with
-scaling up the number of supported maps. This document just focuses on
-importing.
https://download.bbbike.org/ conveniently has 200 OSM extracts for major
-cities world-wide. The data/bbike.sh script downloads these. Then
-data/mass_import.sh attempts to import them into A/B Street.
-
The bbike extracts, however, cover huge areas surrounding major cities.
-Importing such large areas is slow, and the result is too large to work well in
-A/B Street or the OSM viewer. Ideally, we want just the area concentrated around
-the "core" of each city.
-
https://github.com/dabreegster/abstreet/blob/master/convert_osm/src/bin/extract_cities.rs
-transforms a huge .osm file into smaller pieces, each focusing on one city core.
-This tool looks for administrative boundary relations tagged as cities, produces
-a clipping polygon covering the city, and uses osmconvert to produce a smaller
-.osm file. The tool has two strategies for generating clipping polygons. One
-is to locate the admin_centre or label node for the region, then generate a
-circle of fixed radius around that point. Usually this node is located in the
-city core, so it works reasonably, except for "narrow" cities along a coast. The
-other strategy glues together the relation's multipolygon boundary, then
-simplifies the shape (usually with thousands of points) using a convex hull.
-This strategy tends to produce results that're too large, because city limits
-are often really huge.
Outside the US, administrative boundaries don't always have a "city" defined.
-In Tokyo in particular, this name isn't used. I'm not sure which boundary
-level to use yet.
The resulting maps are all "flattened" in A/B Street's list, so you can't see
-any hierarchy of areas. Two cities with the same name from different areas
-will arbitrarily collide.
As you've probably noticed, there aren't many. Lots of the interesting behavior
-in A/B Street - UI interactions, details of the simulation, map importing --
-would take lots of infrastructure to specify a setup and expected outcomes. If
-you have ideas for new tests, contributions always welcome! In the meantime, one
-useful test covers how
-OSM tags translate into individual lanes.
Downloading fresh OSM data or modifying any part of the map importing pipeline
-could easily break things. Expressing invariants about the map output is hard,
-because importing is far from perfect, and OSM data is often quite buggy. So the
-approach to preventing regressions here is to look for visual changes to the
-final rendered map.
-
-
When a new map is opted into this type of test, somebody manually squints
-carefully at it and sanity checks that it works to some degree.
-
They use the screen capture tool in debug mode to tile the map into 1920x960
-chunks and screengrab everything.
-
Later, somebody regenerates the map with some possible changes.
-
They grab screenshots again, then use compare_screenshots.sh to quickly
-look at the visual diff. Changes to intersection geometry, number of lanes,
-rendering, etc are all easy to spot.
-
If this manual inspection of the diff is good, they commit the new
-screenshots as the new goldenfiles.
One part runs the full importer against really simple .osm files. To iterate
-rapidly on interpreting turn restrictions, it produces goldenfiles describing
-all turns in the tiny map.
-
The "smoke-test" section simulates one hour on all maps, flushing out bugs with
-bus spawning, agents hitting odd parts of the map, etc
-
The "check proposals" section makes sure the edits shipped with the game still
-load properly.
Once upon a time, I made a little test harness that would run the simulation
-headlessly (without graphics), set up certain situations forcing a car to park
-in a certain spot, and asserted that different sim/src/events.rs were produced
-in the right order. The map_editor tool was used to manually draw really
-simple maps for these situations. I deleted everything, because the effort to
-specify the input and expected output were too tedious to maintain, and this
-never really helped catch bugs. There was a way to label roads and buildings in
-the synthetic maps, so the test code could assert person 2 made it to the
-"house" building, but even with all of this, it was pretty hard.
-
This approach is maybe worth reviving, though.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/elasticlunr.min.js b/elasticlunr.min.js
deleted file mode 100644
index 94b20dd2ef..0000000000
--- a/elasticlunr.min.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * elasticlunr - http://weixsong.github.io
- * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.5
- *
- * Copyright (C) 2017 Oliver Nightingale
- * Copyright (C) 2017 Wei Song
- * MIT Licensed
- * @license
- */
-!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o]+>|\t|)+|(?:\n)))/gm,r={case_insensitive:"cI",lexemes:"l",contains:"c",keywords:"k",subLanguage:"sL",className:"cN",begin:"b",beginKeywords:"bK",end:"e",endsWithParent:"eW",illegal:"i",excludeBegin:"eB",excludeEnd:"eE",returnBegin:"rB",returnEnd:"rE",relevance:"r",variants:"v",IDENT_RE:"IR",UNDERSCORE_IDENT_RE:"UIR",NUMBER_RE:"NR",C_NUMBER_RE:"CNR",BINARY_NUMBER_RE:"BNR",RE_STARTERS_RE:"RSR",BACKSLASH_ESCAPE:"BE",APOS_STRING_MODE:"ASM",QUOTE_STRING_MODE:"QSM",PHRASAL_WORDS_MODE:"PWM",C_LINE_COMMENT_MODE:"CLCM",C_BLOCK_COMMENT_MODE:"CBCM",HASH_COMMENT_MODE:"HCM",NUMBER_MODE:"NM",C_NUMBER_MODE:"CNM",BINARY_NUMBER_MODE:"BNM",CSS_NUMBER_MODE:"CSSNM",REGEXP_MODE:"RM",TITLE_MODE:"TM",UNDERSCORE_TITLE_MODE:"UTM",COMMENT:"C",beginRe:"bR",endRe:"eR",illegalRe:"iR",lexemesRe:"lR",terminators:"t",terminator_end:"tE"},b="",h={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};function _(e){return e.replace(/&/g,"&").replace(//g,">")}function E(e){return e.nodeName.toLowerCase()}function v(e,n){var t=e&&e.exec(n);return t&&0===t.index}function l(e){return n.test(e)}function g(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function R(e){var a=[];return function e(n,t){for(var r=n.firstChild;r;r=r.nextSibling)3===r.nodeType?t+=r.nodeValue.length:1===r.nodeType&&(a.push({event:"start",offset:t,node:r}),t=e(r,t),E(r).match(/br|hr|img|input/)||a.push({event:"stop",offset:t,node:r}));return t}(e,0),a}function i(e){if(r&&!e.langApiRestored){for(var n in e.langApiRestored=!0,r)e[n]&&(e[r[n]]=e[n]);(e.c||[]).concat(e.v||[]).forEach(i)}}function m(o){function s(e){return e&&e.source||e}function c(e,n){return new RegExp(s(e),"m"+(o.cI?"i":"")+(n?"g":""))}!function n(t,e){if(!t.compiled){if(t.compiled=!0,t.k=t.k||t.bK,t.k){function r(t,e){o.cI&&(e=e.toLowerCase()),e.split(" ").forEach(function(e){var n=e.split("|");a[n[0]]=[t,n[1]?Number(n[1]):1]})}var a={};"string"==typeof t.k?r("keyword",t.k):u(t.k).forEach(function(e){r(e,t.k[e])}),t.k=a}t.lR=c(t.l||/\w+/,!0),e&&(t.bK&&(t.b="\\b("+t.bK.split(" ").join("|")+")\\b"),t.b||(t.b=/\B|\b/),t.bR=c(t.b),t.endSameAsBegin&&(t.e=t.b),t.e||t.eW||(t.e=/\B|\b/),t.e&&(t.eR=c(t.e)),t.tE=s(t.e)||"",t.eW&&e.tE&&(t.tE+=(t.e?"|":"")+e.tE)),t.i&&(t.iR=c(t.i)),null==t.r&&(t.r=1),t.c||(t.c=[]),t.c=Array.prototype.concat.apply([],t.c.map(function(e){return function(n){return n.v&&!n.cached_variants&&(n.cached_variants=n.v.map(function(e){return g(n,{v:null},e)})),n.cached_variants||n.eW&&[g(n)]||[n]}("self"===e?t:e)})),t.c.forEach(function(e){n(e,t)}),t.starts&&n(t.starts,e);var i=t.c.map(function(e){return e.bK?"\\.?(?:"+e.b+")\\.?":e.b}).concat([t.tE,t.i]).map(s).filter(Boolean);t.t=i.length?c(function(e,n){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i')+n+(t?"":b):n}function o(){E+=null!=l.sL?function(){var e="string"==typeof l.sL;if(e&&!N[l.sL])return _(g);var n=e?C(l.sL,g,!0,f[l.sL]):O(g,l.sL.length?l.sL:void 0);return 0")+'"');return g+=n,n.length||1}var s=B(e);if(!s)throw new Error('Unknown language: "'+e+'"');m(s);var a,l=t||s,f={},E="";for(a=l;a!==s;a=a.parent)a.cN&&(E=c(a.cN,"",!0)+E);var g="",R=0;try{for(var d,p,M=0;l.t.lastIndex=M,d=l.t.exec(n);)p=r(n.substring(M,d.index),d[0]),M=d.index+p;for(r(n.substr(M)),a=l;a.parent;a=a.parent)a.cN&&(E+=b);return{r:R,value:E,language:e,top:l}}catch(e){if(e.message&&-1!==e.message.indexOf("Illegal"))return{r:0,value:_(n)};throw e}}function O(t,e){e=e||h.languages||u(N);var r={r:0,value:_(t)},a=r;return e.filter(B).filter(M).forEach(function(e){var n=C(e,t,!1);n.language=e,n.r>a.r&&(a=n),n.r>r.r&&(a=r,r=n)}),a.language&&(r.second_best=a),r}function d(e){return h.tabReplace||h.useBR?e.replace(t,function(e,n){return h.useBR&&"\n"===e?" ":h.tabReplace?n.replace(/\t/g,h.tabReplace):""}):e}function o(e){var n,t,r,a,i,o=function(e){var n,t,r,a,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",t=s.exec(i))return B(t[1])?t[1]:"no-highlight";for(n=0,r=(i=i.split(/\s+/)).length;n/g,"\n"):n=e,i=n.textContent,r=o?C(o,i,!0):O(i),(t=R(n)).length&&((a=document.createElementNS("http://www.w3.org/1999/xhtml","div")).innerHTML=r.value,r.value=function(e,n,t){var r=0,a="",i=[];function o(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){a+=""+E(e)+">"}function s(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var l=o();if(a+=_(t.substring(r,l[0].offset)),r=l[0].offset,l===e){for(i.reverse().forEach(u);s(l.splice(0,1)[0]),(l=o())===e&&l.length&&l[0].offset===r;);i.reverse().forEach(c)}else"start"===l[0].event?i.push(l[0].node):i.pop(),s(l.splice(0,1)[0])}return a+_(t.substr(r))}(t,R(a),i)),r.value=d(r.value),e.innerHTML=r.value,e.className=function(e,n,t){var r=n?c[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}(e.className,o,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function p(){if(!p.called){p.called=!0;var e=document.querySelectorAll("pre code");f.forEach.call(e,o)}}function B(e){return e=(e||"").toLowerCase(),N[e]||N[c[e]]}function M(e){var n=B(e);return n&&!n.disableAutodetect}return a.highlight=C,a.highlightAuto=O,a.fixMarkup=d,a.highlightBlock=o,a.configure=function(e){h=g(h,e)},a.initHighlighting=p,a.initHighlightingOnLoad=function(){addEventListener("DOMContentLoaded",p,!1),addEventListener("load",p,!1)},a.registerLanguage=function(n,e){var t=N[n]=e(a);i(t),t.aliases&&t.aliases.forEach(function(e){c[e]=n})},a.listLanguages=function(){return u(N)},a.getLanguage=B,a.autoDetection=M,a.inherit=g,a.IR=a.IDENT_RE="[a-zA-Z]\\w*",a.UIR=a.UNDERSCORE_IDENT_RE="[a-zA-Z_]\\w*",a.NR=a.NUMBER_RE="\\b\\d+(\\.\\d+)?",a.CNR=a.C_NUMBER_RE="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",a.BNR=a.BINARY_NUMBER_RE="\\b(0b[01]+)",a.RSR=a.RE_STARTERS_RE="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",a.BE=a.BACKSLASH_ESCAPE={b:"\\\\[\\s\\S]",r:0},a.ASM=a.APOS_STRING_MODE={cN:"string",b:"'",e:"'",i:"\\n",c:[a.BE]},a.QSM=a.QUOTE_STRING_MODE={cN:"string",b:'"',e:'"',i:"\\n",c:[a.BE]},a.PWM=a.PHRASAL_WORDS_MODE={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},a.C=a.COMMENT=function(e,n,t){var r=a.inherit({cN:"comment",b:e,e:n,c:[]},t||{});return r.c.push(a.PWM),r.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),r},a.CLCM=a.C_LINE_COMMENT_MODE=a.C("//","$"),a.CBCM=a.C_BLOCK_COMMENT_MODE=a.C("/\\*","\\*/"),a.HCM=a.HASH_COMMENT_MODE=a.C("#","$"),a.NM=a.NUMBER_MODE={cN:"number",b:a.NR,r:0},a.CNM=a.C_NUMBER_MODE={cN:"number",b:a.CNR,r:0},a.BNM=a.BINARY_NUMBER_MODE={cN:"number",b:a.BNR,r:0},a.CSSNM=a.CSS_NUMBER_MODE={cN:"number",b:a.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},a.RM=a.REGEXP_MODE={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[a.BE,{b:/\[/,e:/\]/,r:0,c:[a.BE]}]},a.TM=a.TITLE_MODE={cN:"title",b:a.IR,r:0},a.UTM=a.UNDERSCORE_TITLE_MODE={cN:"title",b:a.UIR,r:0},a.METHOD_GUARD={b:"\\.\\s*"+a.UIR,r:0},a});hljs.registerLanguage("properties",function(r){var t="[ \\t\\f]*",e="("+t+"[:=]"+t+"|[ \\t\\f]+)",s="([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",n="([^\\\\:= \\t\\f\\n]|\\\\.)+",a={e:e,r:0,starts:{cN:"string",e:/$/,r:0,c:[{b:"\\\\\\n"}]}};return{cI:!0,i:/\S/,c:[r.C("^\\s*[!#]","$"),{b:s+e,rB:!0,c:[{cN:"attr",b:s,endsParent:!0,r:0}],starts:a},{b:n+e,rB:!0,r:0,c:[{cN:"meta",b:n,endsParent:!0,r:0}],starts:a},{cN:"attr",r:0,b:n+t+"$"}]}});hljs.registerLanguage("json",function(e){var i={literal:"true false null"},n=[e.QSM,e.CNM],r={e:",",eW:!0,eE:!0,c:n,k:i},t={b:"{",e:"}",c:[{cN:"attr",b:/"/,e:/"/,c:[e.BE],i:"\\n"},e.inherit(r,{b:/:/})],i:"\\S"},c={b:"\\[",e:"\\]",c:[e.inherit(r)],i:"\\S"};return n.splice(n.length,0,t,c),{c:n,k:i,i:"\\S"}});hljs.registerLanguage("javascript",function(e){var r="[A-Za-z$_][0-9A-Za-z$_]*",t={keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await static import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},a={cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},s={cN:"subst",b:"\\$\\{",e:"\\}",k:t,c:[]},c={b:"html`",e:"",starts:{e:"`",rE:!1,c:[e.BE,s],sL:"xml"}},n={b:"css`",e:"",starts:{e:"`",rE:!1,c:[e.BE,s],sL:"css"}},o={cN:"string",b:"`",e:"`",c:[e.BE,s]};s.c=[e.ASM,e.QSM,c,n,o,a,e.RM];var i=s.c.concat([e.CBCM,e.CLCM]);return{aliases:["js","jsx"],k:t,c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,c,n,o,e.CLCM,e.CBCM,a,{b:/[{,]\s*/,r:0,c:[{b:r+"\\s*:",rB:!0,r:0,c:[{cN:"attr",b:r,r:0}]}]},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{cN:"function",b:"(\\(.*?\\)|"+r+")\\s*=>",rB:!0,e:"\\s*=>",c:[{cN:"params",v:[{b:r},{b:/\(\s*\)/},{b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,c:i}]}]},{cN:"",b:/\s/,e:/\s*/,skip:!0},{b:/,e:/(\/[A-Za-z0-9\\._:-]+|[A-Za-z0-9\\._:-]+\/)>/,sL:"xml",c:[{b:/<[A-Za-z0-9\\._:-]+\s*\/>/,skip:!0},{b:/<[A-Za-z0-9\\._:-]+/,e:/(\/[A-Za-z0-9\\._:-]+|[A-Za-z0-9\\._:-]+\/)>/,skip:!0,c:[{b:/<[A-Za-z0-9\\._:-]+\s*\/>/,skip:!0},"self"]}]}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:r}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:i}],i:/\[|%/},{b:/\$[(.]/},e.METHOD_GUARD,{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor get set",e:/\{/,eE:!0}],i:/#(?!!)/}});hljs.registerLanguage("xml",function(s){var e={eW:!0,i:/,r:0,c:[{cN:"attr",b:"[A-Za-z0-9\\._:-]+",r:0},{b:/=\s*/,r:0,c:[{cN:"string",endsParent:!0,v:[{b:/"/,e:/"/},{b:/'/,e:/'/},{b:/[^\s"'=<>`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf"],cI:!0,c:[{cN:"meta",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},s.C("\x3c!--","--\x3e",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"meta",b:/<\?xml/,e:/\?>/,r:10},{b:/<\?(php)?/,e:/\?>/,sL:"php",c:[{b:"/\\*",e:"\\*/",skip:!0},{b:'b"',e:'"',skip:!0},{b:"b'",e:"'",skip:!0},s.inherit(s.ASM,{i:null,cN:null,c:null,skip:!0}),s.inherit(s.QSM,{i:null,cN:null,c:null,skip:!0})]},{cN:"tag",b:"",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"
-
-
-
-
-
-
-
-
-
-
-
-
-
Off-street: most buildings have at least a few parking spots in a driveway
-or carport
-
-
Currently experimenting in the downtown map: set the number of available
-spots based on number of cars seeded at midnight
-
-
-
Parking lots: the number of spots is inferred
-
-
-
Restrictions
-
-
All spots are public except for the few spots associated with each building
-
No time restrictions or modeling of payment
-
-
-
How cars park
-
-
Drivers won't look for parking until they first reach their destination
-building. Then they'll drive to the nearest open parking spot (magically
-knowing what spots are open, even if they're a few blocks away). If somebody
-else has taken the spot when they arrive, they'll try again.
-
Once a driver finds an open spot, they'll take 10-15 seconds to park. They
-block the road behind them in the meantime. There are no conflicts between
-pedestrians and cars when using a driveway. Cars won't make left turns into
-or out of driveways.
-
-
-
Some parking along the boundary of the map is "blackholed", meaning it's
-impossible to actually reach it. Nobody will use these spots.
Multi-use trails like the Burke Gilman and separated cycle-tracks like the
-one along Broadway are currently missing
-
Cyclists won't use an empty parking lane
-
On roads without a bike lane, cyclists currently won't stick to the
-rightmost lane
-
No over-taking yet, so cars can get stuck behind a bike even if there's a
-passing lane
-
-
-
Elevation change isn't factored into route choice or speed yet; pretend
-everybody has an e-bike
-
Beginning or ending a cycling trip takes 30-45 seconds. Locking up at bike
-racks with limited capacity isn't modeled; in practice, it's always easy in
-Seattle to find a place to lock up.
Conflicting movements are coarse: a second vehicle won't start a conflicting
-turn, even if the first vehicle is physically out of the way but still
-partially in the intersection
-
Most of the time, vehicles won't "block the box" -- if there's no room in the
-target lane, a vehicle won't start turning and risk getting stuck in the
-intersection