AgentMaps/devdocs/index.html
noncomputable d55b2a4d0e Updates
2018-08-31 18:14:55 -04:00

247 lines
23 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Home</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Home</h1>
<h3> </h3>
<section>
<article><h1>AgentMaps Documentation</h1><p>The sidebar of this page contains a list of classes and functions used by Agentmaps.
Click them to see their methods and properties, their purposes, and their types.
If you are looking at the <a href="https://noncomputable.github.io/AgentMaps/docs">normal docs</a>,
you will only see the items necessary for using AgentMaps. However, if you are looking at
the <a href="https://noncomputable.github.io/AgentMaps/devdocs/">developer docs</a>, you will see
everything, including functions used only in the internal implementation of AgentMaps.</p>
<p>Here I'll explain some features of AgentMaps that the auto-generated docs probably aren't sufficiently helpful for.</p>
<h4>Table of Contents</h4><p><a href="#prerequisites">Prerequisites</a></p>
<p><a href="#basic-structure">Basic Structure of Agentmaps</a></p>
<p><a href="#generating-buildings">Generating Buildings</a></p>
<p><a href="#navigating-streets">Navigating Streets</a></p>
<p><a href="#navigating-within-units">Navigating Within Units</a></p>
<p><a href="#feature-styling">Feature Styling</a></p>
<p><a href="#neighbors">Neighbors</a></p>
<p><a href="#intersections">Intersections</a></p>
<p><a href="#agentfeaturemakers">AgentFeatureMakers</a></p>
<p><a href="#moving-to-places">Moving To Places</a></p>
<p><a href="#controllers">Controllers</a></p>
<p><a href="#animation-speed">Animation Speed</a></p>
<h2>Prerequisites</h2><p>You can find a bundle for AgentMaps here: <a href="https://unpkg.com/agentmaps@2.0.0/site/dist/agentmaps.js">https://unpkg.com/agentmaps@2.0.0/site/dist/agentmaps.js</a>.</p>
<p>Making simulations with AgentMaps will be a lot easier for you if you can:</p>
<ul>
<li>Program with Javascript</li>
<li>Use the <a href="https://leafletjs.com/">Leaflet</a> mapping library</li>
</ul>
<p>Leaflet doesn't come bundled with AgentMaps, so you'll have to either include it in your web page with its own \&lt;script> tag or
install it with <a href="https://www.npmjs.com/package/leaflet">npm</a> and bundle it yourself.</p>
<p>It might also help to be familiar with <a href="http://turfjs.org/">turf.js</a>, a library that contains lots of tools that make geospatial work (like intersection detection and line slicing) quick and easy.</p>
<p>AgentMaps expects geographic data in the form of <a href="http://geojson.org/">GeoJSON</a>, a format for representing geospatial information,
so it would be useful to take a look at that.</p>
<p>How do you get the GeoJSON data of some neighborhood you're interested in? I use <a href="https://www.openstreetmap.org/">OpenStreetMap</a> (OSM),
a free, collaborative map of the world! You can get an OSM file by using the &quot;export&quot; tool on the OSM website;
you can also use it to get the coordinates of the two points bounding your neighborhood. Then, using <a href="https://tyrasd.github.io/osmtogeojson/">OSMToGeoJSON</a>,
you can plug in your OSM file and get the JSON in return.</p>
<p>All of the above is pretty important to be able to contribute to AgentMaps or understand its internal implementation as well.</p>
<h2><a name="basic-structure"></a>Basic Structure</h2><p>AgentMaps functions as an extension of <a href="https://github.com/Leaflet/Leaflet">Leaflet</a>.
Since everything in Leaflet is in the namespace <code>L</code> (e.g. <code>L.Map</code>, <code>L.latLng</code>), everything in AgentMaps is in its own namespace <code>A</code> inside of <code>L</code>, <code>L.A</code> (e.g. <code>L.A.Agentmap</code>, <code>L.A.agent</code>).</p>
<p>The main object of AgentMaps is an Agentmap (<a href="./Agentmap.html">L.A.Agentmap</a>).
Since AgentMaps is built on top of Leaflet, the Agentmap constructor requires a Leaflet map as an argument.
All the classes in AgentMaps have corresponding, lowercase factory functions that return new instances of them provided their
constructor's arguments. For example, <code>L.A.agentmap(map)</code> is equivalent to <code>new L.A.Agentmap(map)</code>.</p>
<p>An Agentmap stores its units, streets, and agents as Leaflet <a href="https://leafletjs.com/reference-1.3.4.html#featuregroup">FeatureGroups</a> (<code>Agentmap.units</code>, <code>Agentmap.streets</code>, and <code>Agentmap.agents</code>).
These FeatureGroups can be looped through like any other Leaflet FeatureGroup (using the <a href="https://leafletjs.com/reference-1.3.4.html#featuregroup-eachlayer">FeatureGroup.eachLayer()</a> method).</p>
<h2><a name="generating-buildings"></a>Generating Buildings</h2><p>To setup an Agentmap and build its streets and units, you need to provide some information about the neighborhood of interest:</p>
<ul>
<li><a href="http://geojson.org/">GeoJSON</a> data representing its streets</li>
<li>The coordinates of the top left and bottom right corners of a rectangle containing the neighborhood, LatLng order</li>
</ul>
<p>You can get this information with both the OpenStreetMap <a href="http://openstreetmap.org">web interface</a> and its <a href="http://overpass-api.de">Overpass API</a>. For converting between formats, you can use <a href="https://tyrasd.github.io/osmtogeojson/">OSMToGeoJSON</a>.</p>
<p>The <a href="./Agentmap.html#buildingify">Agentmap.buildingify</a> method does this work. If the GeoJSON data for the neighborhood is
stored in a variable <code>my_data</code> and the coordinates of the top left and bottom right corners of the bounding rectangle are <code>[43.3071, -88.0158]</code> and <code>[43.2884, -87.9759]</code> respectively, the corresponding call to <code>Agentmap.buildingify</code> would look something like:</p>
<pre class="prettyprint source lang-javascript"><code>agentmap.buildingify(my_data, [[43.3071, -88.0158], [43.2884, -87.9759]]);</code></pre><p><code>Agentmap.buildingify</code> accepts more arguments specifying the dimension and appearance of the units and streets it will build. For more on that, see the section on <a href="#feature-styling">Feature Styling</a>.</p>
<p><em>Note</em>: <code>Agentmap.buildingify</code> does a lot of work checking for and removing overlapping units, and so the bigger your neighborhood, the noticeably longer it will take. </p>
<h2><a name="navigating-streets"></a>Navigating Streets</h2><p>Given a neighborhood's streets in GeoJSON, AgentMaps extracts a street network and converts it to a <a href="https://en.wikipedia.org/wiki/Graph_(discrete_mathematics">graph</a> with the help of the <a href="https://github.com/anvaka/ngraph.graph">ngraph.graph</a> library. Then, it uses <a href="https://github.com/anvaka/ngraph.path">ngraph.path</a> to find an (approximately) shortest path. The graph itself is made out of the start point, end point, and intersections of each street.</p>
<p>The graph is stored in the <code>Agentmap.streets.graph</code> property. It is a symmetric graph; for each edge between two points, an inversely directed edge between them also exists. That is, by default, there are no one-way streets. However, if you'd like to remove some of the directed edges of certain streets from the graph (i.e. for making one-way streets), a very accessible guide to manipulating the graphs is available in the ngraph.graph <a href="https://github.com/anvaka/ngraph.graph/blob/master/README.md">README</a>.</p>
<h2><a name="navigating-within-units"></a>Navigating Within Units</h2><p>Every Agentmap has an <a href="./Agentmap.html#.getUnitPoint">Agentmap.getUnitPoint</a> method which makes it easy to specify a position inside of a unit, relative to one of its corners, and get back the global coordinates of that spot. </p>
<p>Given a unit ID, an x value between 0 and 1, and a y value between 0 and 1, <code>Agentmap.getUnitPoint</code> will get a position down the width and into the depth of a unit according to the supplied x and y values, and return the global coordinates of the position it lands on.
More specifically, starting from the front corner of the unit that comes first along its street, getUnitPoint will effectively return a <a href="./Global.html#LatLng">LatLng</a> representing the position x <em> 100 percent along its width and y </em> 100 percent into its depth.</p>
<h2><a name="feature-styling"></a>Feature Styling</h2><p>Every feature that AgentMaps places on the map is an instance of a Leaflet layer. Streets are L.Polylines, units are L.Polygons, and agents are L.CircleMarkers.</p>
<p>The methods for creating agents (<a href="./Agentmap.html#agentify">agentify</a>), units (<a href="./Agentmap.html#buildingify">buildingify</a>), and streets (buildingify) provide options parameters to which you can pass a Leaflet options object
specifying the style you want (colors, outlines, transparency, radius, etc.).
See the <a href="https://leafletjs.com/reference-1.3.2.html">Leaflet docs</a> for each of the aforementioned classes to learn about all the possible options.</p>
<p>An options object may look something like this:</p>
<pre class="prettyprint source lang-javascript"><code>let options = {
radius: .5,
color: &quot;pink&quot;,
weight: 3,
opacity: .5
};</code></pre><p>Buildingify's unit_options parameter is different from the other options parameters: you can provide extra AgentMaps-only options to specify the length, depth, front-buffer (how far the front of a unit is from its street), and side-buffer (how far a unit is from adjacent units on the same street) of the units in meters.</p>
<p>You can modify an individual street, unit, or agent's (Leaflet) style after it's already on the map by calling its <a href="https://leafletjs.com/reference-1.3.4.html#path-setstyle">setStyle</a> method and passing it an options object.</p>
<h2><a name="neighbors"></a>Neighbors</h2><p>Every unit has a neighbors property, <code>unit.neighbors</code>: a three element array of layer IDs representing the previous unit, the next unit, and the
unit directly across the street respectively.</p>
<h2><a name="intersections"></a>Intersections</h2><p>Every street has an intersections property (street.intersections): an object mapping the ID of another street the given street has intersections with to an array of the specific intersections. Each individual intersection itself is a 2-element array whose first element is the coordinates of the intersection,
and whose second element is an object mapping the ID of each street to the index of the intersection point in its coordinate array.</p>
<p>Here's an example of a street.intersections object:</p>
<pre class="prettyprint source lang-javascript"><code>street.intersections = {
&quot;68&quot;: [
[
{
&quot;lat&quot;: 40.64315,
&quot;lng&quot;: -73.522418
},
{
&quot;57&quot;: 36,
&quot;68&quot;: 48
}
],
[
{
&quot;lat&quot;: 40.64355,
&quot;lng&quot;: -73.523129
},
{
&quot;57&quot;: 32,
&quot;62&quot;: 9
}
]
],
&quot;61&quot;: [
{
&quot;lat&quot;: 40.646255,
&quot;lng&quot;: -73.524835
},
{
&quot;57&quot;: 23,
&quot;61&quot;: 0
}
]
};</code></pre><h2><a name="agentfeaturemakers"></a>AgentFeatureMakers</h2><p>The <code>Agentmap.agentify</code> <a href="./Agentmap.html#agentify">method</a> creates and places agents on the map. Its first parameter is the number of agents to be created.
Its second parameter is a kind of function called an <a href="./global.html#agentFeatureMaker">AgentFeatureMaker</a> that specifies where the agents will be placed, what they look like, and what their properties are.</p>
<p>The AgentFeatureMaker you provide should behave as follows: given the leaflet ID of the agent, return a GeoJSON Point feature whose coordinates are where the agent should be placed,
whose <code>properties.place</code> property is a valid <a href="https://noncomputable.github.io/AgentMaps/docs/global.html#Place">Place</a> containing those coordinates,
and whose <code>properties.layer_options</code> property is an object containing options for the agent's CircleMarker
(like color, outline, radius, and all the other options listed <a href="https://leafletjs.com/reference-1.3.2.html#circlemarker-option">here</a>).
Any other properties defined in the <code>properties</code> property (like, say, <code>feature.properties.phone_number</code>) will be transferred to a new Agent instance. </p>
<p>For example, the AgentFeatureMaker in an epidemic simulation may look something like this:</p>
<pre class="prettyprint source lang-javascript"><code>function epidemicAgentMaker = function(id) {
let feature = {
&quot;type&quot;: &quot;Feature&quot;,
&quot;properties&quot;: {
&quot;place&quot;: {
&quot;type&quot;: &quot;unit&quot;,
&quot;id&quot;: random_unit_id
},
&quot;layer_options&quot;: {
&quot;color&quot;: &quot;blue&quot;,
&quot;radius&quot;: .5
},
&quot;infected&quot;: Math.random() > .15 ? false : true,
&quot;ticks_until_recovery&quot;: Math.random() * 2000,
},
&quot;geometry&quot;: {
&quot;type&quot;: &quot;Point&quot;,
&quot;coordinates&quot;: center_coords
},
};
return feature;
}</code></pre><p>The corresponding call to <code>Agentmap.agentify</code> might look something like this:</p>
<pre class="prettyprint source lang-javascript"><code>agentmap.agentify(100, epidemicAgentMaker);</code></pre><h2><a name="moving-to-places"></a>Moving To Places</h2><p>The agents' <a href="./Agent.html#.scheduleTrip">Agent.scheduleTrip</a> method makes scheduling trips between any places on the map very convenient.
<code>Agent.scheduleTrip</code> works by keeping track of the kind of place the agent is at and is going to at any given time. The <a href="./global.html#Place">place</a>
can be either a unit, a street,
or &quot;unanchored&quot;, meaning anywhere on the map with no relation to whatever features (streets or units) may or may not be there. </p>
<p>Depending on where an agent is, and where it intends to travel to, the agent will travel in different ways.
If it's leaving from or going to an unanchored place, it will ignore the roads and travel directly.
If it's moving between streets or units, it will by default move along the roads and in and out through the front (&quot;doors&quot;) of the units.</p>
<p>To schedule an agent to move somewhere, all you need to do is give <code>Agent.scheduleTrip</code> two arguments: the coordinates of where you want the agent to go and a <a href="./global.html#Place">Place</a> object describing what's there.
Optionally you can provide three more arguments: </p>
<ul>
<li>A custom speed greater than or equal to .1 (1 by default)</li>
<li>A true/false value specifying whether the agent should ignore the roads and move directly to its goal (false by default, and redundant if the agent is moving from or going to an unanchored place) </li>
<li>A true/false value specifying whether the agent should give up on its current trip, emptying its schedule (false by default).</li>
</ul>
<p>Beyond just scheduling an agent to move somewhere, for information about actually <em>making</em> it move, see the section on <a href="#controllers">controllers</a>.</p>
<p><em>Note</em>: Over long distances, as agent movements aren't precise enough for multi-hundred mile paths to slope properly, the agent's path may be very roundabout.</p>
<h2><a name="controllers"></a>Controllers</h2><p>What actually happens on the Agentmap and to each Agent is determined by the controller functions you define. On each tick of the simulation, the Agentmap calls its own <code>Agentmap.controller</code> and then each existing Agent's <code>Agent.controller</code>, all of which are by default empty.</p>
<p>Whatever trip an Agent has scheduled (with <code>Agent.scheduleTrip</code>), it will only actually move when its <code>Agent.moveIt</code> method is called (usually by its controller function).
You can place the call to <code>Agent.moveIt</code> anywhere within the controller function depending on what (if anything) you want to have happen before or after the agent moves.</p>
<p>Since on each tick, an Agent will move according to the speed specified by the next point in its scheduled path, you may have an Agent move a large distance per tick, and only be able to access its position before and after you make the movement (by calling Agent.moveIt) within the controller function. If you would like more precision, at the cost of some performance, you can define an <code>Agent.fine_controller</code> function, which is called before and after each individual step an Agent makes (approximately half a meter).</p>
<p>To specify what happens during your simulation, you should define controllers where you need to.</p>
<p>If you want some event to happen that doesn't differ for different agents, I suggest specifying it in an <code>Agentmap.controller</code>.</p>
<p>If you want some event to vary and occur at different times for different agents, I suggest generating varying <code>Agent.controller</code>s in a loop like this:</p>
<pre class="prettyprint source lang-javascript"><code>agentmap.agents.eachLayer(function(agent) {
agent.controller = function() {
//custom operations that vary based on the given agent's properties
}
});</code></pre><p>* I didn't follow this rule of thumb in the Basic Walkthrough to spice things up.</p>
<p>You can start, pause, and resume an AgentMaps simulation using the <a href="./Agentmap.html#.run">Agentmap.run</a> and <a href="./Agentmap.html#.pause">Agentmap.pause</a> methods. When <code>Agentmap.run</code> is called,
the Agentmap and Agents will run their controller functions, the Agentmap will increment its tick clock (<code>Agentmap.state.ticks</code>),
and a new animation frame will be requested to do the same thing over again.</p>
<p>After <code>Agentmap.pause()</code> is called, the tick will not be incremented, the request for the next animation frame will be cancelled, and the controller functions will stop being called. Calling <code>Agentmap.run()</code> after pausing will set things back in motion.</p>
<p><a href="./Agentmap.html/clear">Agentmap.clear</a> will reset the Agentmap's state (including the tick counter) and remove all the AgentMaps layers from the map.</p>
<h2><a name="animation-speed"></a>Animation Speed</h2><p>You can pause or resume an Agent's trip with its <a href="./Agent.html#.pauseTrip">Agent.pauseTrip</a> and <a href="./Agent.html#.resumeTrip">Agent.resumeTrip</a> methods. You can also alter the speeds an Agent is scheduled to travel using several methods: <a href="./Agent.html#.increaseSpeed">Agent.setSpeed</a>, <a href="./Agent.html#.multiplySpeed">Agent.multiplySpeed</a>, and <a href="./Agent.html#.increaseSpeed">Agent.increaseSpeed</a>. But that's not the kind of speed this section is about.</p>
<p>Time in an Agentmap is measured by ticks (recorded in <code>Agentmap.state.ticks</code>). A tick can be interpreted differently based on what you have Agents do on each tick: it can be a second, a minute, an hour, or something less standard. But how long does it take for a tick to elapse in real life; that is, how long will your computer take to complete the operations that should happen during a tick?</p>
<p>Typically, it's a few miliseconds. But the more Agents you have and the more complex instructions you give them, the longer it'll take, and the slower your simulation will run. The biggest drain on speed is animation: drawing and redrawing tens or hundreds of Agents everytime they take a tiny step takes a lot of resources and a (relatively) long time.</p>
<p>To help deal with this, an Agentmap's constructor, along with a Leaflet map, accepts an &quot;animation_interval&quot; argument, a nonnegative integer. An Agent will only be animated every <code>Agentmap.animation_interval</code> steps, where a step is typically less than a meter. </p>
<p>By default, it is 1, meaning it will be redrawn after every step. The higher the value, the choppier the animation will look, but the faster it should proceed. </p>
<p>Zero is a special value: if <code>Agentmap.animation_interval</code> is 0, then the animation will stop completely while the simulation continues under-the-hood.</p>
<p>You can also change the <code>animation_interval</code> after creating the Agentmap with the <a href="./Agentmap.html#setAnimationInterval">Agentmap.setAnimationInterval</a> method.</p></article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Agent.html">Agent</a></li><li><a href="Agentmap.html">Agentmap</a></li></ul><h3>Tutorials</h3><ul><li><a href="tutorial-quickstart.html">quickstart</a></li></ul><h3>Global</h3><ul><li><a href="global.html#addStreetLayerIntersections">addStreetLayerIntersections</a></li><li><a href="global.html#agent">agent</a></li><li><a href="global.html#agentmap">agentmap</a></li><li><a href="global.html#checkEndExcess">checkEndExcess</a></li><li><a href="global.html#checkStartExcess">checkStartExcess</a></li><li><a href="global.html#decodeCoordString">decodeCoordString</a></li><li><a href="global.html#encodeLatLng">encodeLatLng</a></li><li><a href="global.html#generateUnitFeatures">generateUnitFeatures</a></li><li><a href="global.html#getIntersections">getIntersections</a></li><li><a href="global.html#getPathFinder">getPathFinder</a></li><li><a href="global.html#getStreetFeatures">getStreetFeatures</a></li><li><a href="global.html#getUnitAnchors">getUnitAnchors</a></li><li><a href="global.html#getUnitFeatures">getUnitFeatures</a></li><li><a href="global.html#getUnitNeighborLayerIDs">getUnitNeighborLayerIDs</a></li><li><a href="global.html#isPointCoordinates">isPointCoordinates</a></li><li><a href="global.html#noOverlaps">noOverlaps</a></li><li><a href="global.html#pointToCoordinateArray">pointToCoordinateArray</a></li><li><a href="global.html#reversedCoordinates">reversedCoordinates</a></li><li><a href="global.html#setupStreetFeatures">setupStreetFeatures</a></li><li><a href="global.html#setupUnitFeatures">setupUnitFeatures</a></li><li><a href="global.html#streetsToGraph">streetsToGraph</a></li><li><a href="global.html#unitsOutOfStreets">unitsOutOfStreets</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Fri Aug 31 2018 18:14:49 GMT-0400 (Eastern Daylight Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>