mirror of
https://github.com/noncomputable/AgentMaps.git
synced 2026-01-18 16:27:05 +00:00
525 lines
22 KiB
HTML
525 lines
22 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>JSDoc: Source: agents.js</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">Source: agents.js</h1>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<section>
|
|
<article>
|
|
<pre class="prettyprint source linenums"><code>let centroid = require('@turf/centroid').default,
|
|
buffer = require('@turf/buffer').default,
|
|
booleanPointInPolygon = require('@turf/boolean-point-in-polygon').default,
|
|
along = require('@turf/along').default,
|
|
nearestPointOnLine = require('@turf/nearest-point-on-line').default,
|
|
lineSlice = require('@turf/line-slice').default,
|
|
Agentmap = require('./agentmap').Agentmap,
|
|
encodeLatLng = require('./routing').encodeLatLng;
|
|
|
|
/* Here we define agentify, the agent base class, and all other functions and definitions they rely on. */
|
|
|
|
/**
|
|
* User-defined callback that gives a feature with appropriate geometry and properties to represent an agent.
|
|
*
|
|
* @callback agentFeatureMaker
|
|
* @param {number} i - A number used to determine the agent's coordinates and other properties.
|
|
* @returns {?Point} - Either a GeoJSON Point feature with properties and coordinates for agent i, including
|
|
* a "place" property that will define the agent's initial agent.place; or null, which will cause agentify
|
|
* to immediately stop its work & terminate.
|
|
*/
|
|
|
|
/**
|
|
* A standard {@link agentFeatureMaker} callback, which sets an agent's location as the center of a unit on the map.
|
|
*
|
|
* @memberof Agentmap
|
|
* @type {agentFeatureMaker}
|
|
*/
|
|
function seqUnitAgentMaker(i){
|
|
if (i > this.units.getLayers().length - 1) {
|
|
return null;
|
|
}
|
|
|
|
let unit = this.units.getLayers()[i],
|
|
unit_id = this.units.getLayerId(unit),
|
|
center_point = centroid(unit.feature);
|
|
center_point.properties.place = {"unit": unit_id},
|
|
center_point.properties.layer_options = {radius: .5, color: "red", fillColor: "red"};
|
|
|
|
return center_point;
|
|
}
|
|
|
|
/**
|
|
* Generate some number of agents and place them on the map.
|
|
*
|
|
* @param {number} count - The desired number of agents.
|
|
* @param {agentFeatureMaker} agentFeatureMaker - A callback that determines an agent i's feature properties and geometry (always a Point).
|
|
*/
|
|
function agentify(count, agentFeatureMaker) {
|
|
let agentmap = this;
|
|
|
|
if (!(this.agents instanceof L.LayerGroup)) {
|
|
this.agents = L.layerGroup().addTo(this.map);
|
|
}
|
|
|
|
let agents_existing = agentmap.agents.getLayers().length;
|
|
for (let i = agents_existing; i < agents_existing + count; i++) {
|
|
//Callback function aren't automatically bound to the agentmap.
|
|
let boundFeatureMaker = agentFeatureMaker.bind(agentmap),
|
|
feature = boundFeatureMaker(i);
|
|
if (feature === null) {
|
|
return;
|
|
}
|
|
|
|
let coordinates = L.A.reversedCoordinates(feature.geometry.coordinates),
|
|
place = feature.properties.place,
|
|
layer_options = feature.properties.layer_options;
|
|
|
|
//Make sure the agent feature is valid and has everything we need.
|
|
if (!L.A.isPointCoordinates(coordinates)) {
|
|
throw new Error("Invalid feature returned from agentFeatureMaker: geometry.coordinates must be a 2-element array of numbers.");
|
|
}
|
|
else if (typeof(place.unit) !== "number" &&
|
|
typeof(place.street) !== "number") {
|
|
throw new Error("Invalid feature returned from agentFeatureMaker: properties.place must be a {unit: unit_id} or {street: street_id} with an existing layer's ID.");
|
|
}
|
|
|
|
new_agent = agent(coordinates, layer_options, agentmap);
|
|
new_agent.place = place;
|
|
this.agents.addLayer(new_agent);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The main class representing individual agents, using Leaflet class system.
|
|
* @private
|
|
*
|
|
* @class Agent
|
|
*/
|
|
let Agent = L.Layer.extend({});
|
|
|
|
/**
|
|
* Constructor for the Agent class, using Leaflet class system.
|
|
*
|
|
* @name Agent
|
|
* @constructor
|
|
* @param {LatLng} lat_lng - A pair of coordinates to place the agent at.
|
|
* @param {Object} options - An array of options for the agent, namely its layer.
|
|
* @param {Agentmap} agentmap - The agentmap instance in which the agent exists.
|
|
* @property {number} feature.AgentMap_id - The agent's instance id, so it can be accessed from inside the Leaflet layer. To avoid putting the actual instance inside the feature object.
|
|
* @property {Agentmap} agentmap - The agentmap instance in which the agent exists.
|
|
* @property {Place} place - A place object containing the id of the place (unit, street, etc.) where the agent is currently at.
|
|
* @property {Object} travel_state - Properties detailing information about the agent's trip that change sometimes, but needs to be accessed by future updates.
|
|
* @property {boolean} travel_state.traveling - Whether the agent is currently on a trip.
|
|
* @property {?Point} travel_state.current_point - The point where the agent is currently located.
|
|
* @property {?Point} travel_state.goal_point - The point where the agent is traveling to.
|
|
* @property {?number} travel_state.lat_dir - The latitudinal direction. -1 if traveling to lower latitude (down), 1 if traveling to higher latitude (up).
|
|
* @property {?number} travel_state.lng_dir - The longitudinal direction. -1 if traveling to lesser longitude (left), 1 if traveling to greater longitude (right).
|
|
* @property {?number} travel_state.slope - The slope of the line segment formed by the two points between which the agent is traveling at this time during its trip.
|
|
* @property {Array} travel_state.path - A sequence of LatLngs; the agent will move from one to the next, popping each one off after it arrives until the end of the street; or, until the travel_state is changed/reset.
|
|
* @property {?function} update_func - Function to be called on each update.
|
|
*/
|
|
Agent.initialize = function(lat_lng, options, agentmap) {
|
|
this.agentmap = agentmap,
|
|
this.place = null,
|
|
this.travel_state = {
|
|
traveling: false,
|
|
current_point: null,
|
|
goal_point: null,
|
|
lat_dir: null,
|
|
lng_dir: null,
|
|
slope: null,
|
|
path: [],
|
|
};
|
|
this.update_func = function() {};
|
|
|
|
L.CircleMarker.prototype.initialize.call(this, lat_lng, options);
|
|
}
|
|
|
|
/**
|
|
* Stop the agent from traveling, reset all the properties of its travel state.
|
|
* @private
|
|
*/
|
|
Agent.resetTravelState = function() {
|
|
for (let key in this.travel_state) {
|
|
this.travel_state[key] =
|
|
key === "traveling" ? false :
|
|
key === "path" ? [] :
|
|
null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Start a trip along the path specified in the agent's travel_state.
|
|
* @private
|
|
*/
|
|
Agent.startTrip = function() {
|
|
if (this.travel_state.path.length > 0) {
|
|
this.travelTo(this.travel_state.path[0]);
|
|
}
|
|
else {
|
|
throw new Error("The travel state's path is empty! There's no path to take a trip along!");
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set the agent to travel to some point on the map.
|
|
* @private
|
|
*
|
|
* @param {LatLng} goal_point - The point to which the agent should travel.
|
|
*/
|
|
Agent.travelTo = function(goal_point) {
|
|
let state = this.travel_state;
|
|
state.traveling = true,
|
|
state.current_point = this.getLatLng(),
|
|
state.goal_point = L.latLng(goal_point),
|
|
|
|
//Negating so that neg result corresponds to the goal being rightward/above, pos result to it being leftward/below.
|
|
state.lat_dir = Math.sign(- (state.current_point.lat - state.goal_point.lat)),
|
|
state.lng_dir = Math.sign(- (state.current_point.lng - state.goal_point.lng)),
|
|
|
|
state.slope = Math.abs(((state.current_point.lat - state.goal_point.lat) / (state.current_point.lng - state.goal_point.lng)));
|
|
};
|
|
|
|
/**
|
|
* Given the agent's currently scheduled trips (its path), get the place from which a new trip should start (namely, the end of the current path).
|
|
* That is: If there's already a path in queue, start the new path from the end of the existing one.
|
|
* @private
|
|
*
|
|
* @returns {Place} - The place where a new trip should start.
|
|
*/
|
|
Agent.newTripStartPlace = function() {
|
|
if (this.travel_state.path.length === 0) {
|
|
start_place = this.place;
|
|
}
|
|
else {
|
|
start_place = this.travel_state.path[this.travel_state.path.length - 1].new_place;
|
|
}
|
|
|
|
return start_place;
|
|
}
|
|
|
|
/**
|
|
* Schedule the agent to travel to a point within the unit he is in.
|
|
* @private
|
|
*
|
|
* @param {LatLng} goal_lat_lng - LatLng coordinate object for a point in the same unit the agent is in.
|
|
*/
|
|
Agent.setTravelInUnit = function(goal_lat_lng, goal_place) {
|
|
let goal_point = L.A.pointToCoordinateArray(goal_lat_lng),
|
|
//Buffering so that points on the perimeter, like the door, are captured. Might be more
|
|
//efficient to generate the door so that it's slightly inside the area.
|
|
goal_polygon = buffer(this.agentmap.units.getLayer(goal_place.unit).toGeoJSON(), .001);
|
|
|
|
if (booleanPointInPolygon(goal_point, goal_polygon)) {
|
|
goal_lat_lng.new_place = goal_place;
|
|
this.travel_state.path.push(goal_lat_lng);
|
|
}
|
|
else {
|
|
throw new Error("The goal_lat_lng is not inside of the polygon of the goal_place!");
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Schedule the agent to travel directly from any point (e.g. of a street or unit) to a point (e.g. of another street or unit).
|
|
*
|
|
* @param {LatLng} goal_lat_lng - The point within the place to which the agent is to travel.
|
|
* @param {Place} goal_place - The place to which the agent will travel. Must be of form {"unit": unit_id} or {"street": street_id}.
|
|
* @param {Boolean} replace_trip - Whether to empty the currently scheduled path and replace it with this new trip; false by default (the new trip is
|
|
* simply appended to the current scheduled path).
|
|
*/
|
|
Agent.setTravelToPlace = function(goal_lat_lng, goal_place, replace_trip = false) {
|
|
let goal_layer = this.agentmap.units.getLayer(goal_place.unit) || this.agentmap.streets.getLayer(goal_place.street);
|
|
|
|
if (goal_layer) {
|
|
let goal_coords = L.A.pointToCoordinateArray(goal_lat_lng);
|
|
|
|
//Buffering so that points on the perimeter, like the door, are captured. Might be more
|
|
//efficient to generate the door so that it's slightly inside the area.
|
|
let goal_polygon = buffer(goal_layer.toGeoJSON(), .001);
|
|
|
|
if (booleanPointInPolygon(goal_coords, goal_polygon)) {
|
|
if (replace_trip === true) {
|
|
this.travel_state.path.length = 0;
|
|
}
|
|
|
|
let start_place = this.newTripStartPlace();
|
|
|
|
if (start_place.unit === goal_place.unit) {
|
|
this.setTravelInUnit(goal_lat_lng, goal_place);
|
|
return;
|
|
}
|
|
//Move to the street if it's starting at a unit and its goal is elsewhere.
|
|
else if (typeof(start_place.unit) === "number") {
|
|
let start_unit_door = this.agentmap.getUnitDoor(start_place.unit);
|
|
start_unit_door.new_place = start_place;
|
|
this.travel_state.path.push(start_unit_door);
|
|
|
|
let start_unit_street_id = this.agentmap.units.getLayer(start_place.unit).street_id,
|
|
start_unit_street_point = this.agentmap.getStreetNearDoor(start_place.unit);
|
|
start_unit_street_point.new_place = { street: start_unit_street_id };
|
|
this.travel_state.path.push(start_unit_street_point);
|
|
}
|
|
|
|
if (typeof(goal_place.unit) === "number") {
|
|
let goal_street_point = this.agentmap.getStreetNearDoor(goal_place.unit),
|
|
goal_street_point_place = { street: this.agentmap.units.getLayer(goal_place.unit).street_id };
|
|
|
|
//Move to the point on the street closest to the goal unit...
|
|
this.setTravelAlongStreet(goal_street_point, goal_street_point_place);
|
|
|
|
//Move from that point into the unit.
|
|
let goal_door = this.agentmap.getUnitDoor(goal_place.unit);
|
|
goal_door.new_place = goal_place;
|
|
this.travel_state.path.push(goal_door)
|
|
this.setTravelInUnit(goal_lat_lng, goal_place);
|
|
}
|
|
else if (typeof(goal_place.street) === "number") {
|
|
this.setTravelAlongStreet(goal_lat_lng, goal_place);
|
|
}
|
|
}
|
|
else {
|
|
throw new Error("The goal_lat_lng is not inside of the polygon of the goal_place!");
|
|
}
|
|
}
|
|
else {
|
|
throw new Error("No place exists matching the specified goal_place!");
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Schedule the agent to travel to a point along the streets, via streets.
|
|
* @private
|
|
*
|
|
* @param {LatLng} goal_lat_lng - The coordinates of a point on a street to which the agent should travel.
|
|
* @param {Place} goal_place - The place to which the agent will travel. Must be of form {"street": street_id}.
|
|
*/
|
|
Agent.setTravelAlongStreet = function(goal_lat_lng, goal_place) {
|
|
let goal_coords,
|
|
goal_street_id,
|
|
goal_street_point,
|
|
goal_street_feature,
|
|
start_place = this.newTripStartPlace(),
|
|
start_street_id,
|
|
start_street_point,
|
|
start_street_feature;
|
|
|
|
if (typeof(start_place.street) === "number" && typeof(goal_place.street) === "number") {
|
|
start_street_id = start_place.street,
|
|
start_street_point = this.travel_state.path.length !== 0 ?
|
|
this.travel_state.path[this.travel_state.path.length - 1] :
|
|
this.getLatLng();
|
|
start_street_point.new_place = {street: start_street_id};
|
|
|
|
goal_street_id = goal_place.street,
|
|
goal_street_feature = this.agentmap.streets.getLayer(goal_street_id).feature,
|
|
goal_coords = L.A.pointToCoordinateArray(goal_lat_lng),
|
|
goal_street_point = L.latLng(nearestPointOnLine(goal_street_feature, goal_coords).geometry.coordinates.reverse());
|
|
goal_street_point.new_place = goal_place;
|
|
}
|
|
else {
|
|
throw new Error("Both the start and end places must be streets!");
|
|
}
|
|
|
|
if (start_street_id === goal_street_id) {
|
|
this.setTravelOnSameStreet(start_street_point, goal_street_point, goal_street_feature, goal_street_id);
|
|
}
|
|
//If the start and end points are on different streets, move from the start to its nearest intersection, then from there
|
|
//to the intersection nearest to the end, and finally to the end.
|
|
else {
|
|
let start_nearest_intersection = this.agentmap.getNearestIntersection(start_street_point, start_place),
|
|
goal_nearest_intersection = this.agentmap.getNearestIntersection(goal_street_point, goal_place);
|
|
|
|
start_street_feature = this.agentmap.streets.getLayer(start_street_id).feature;
|
|
|
|
this.setTravelOnStreetNetwork(start_street_point, goal_street_point, start_nearest_intersection, goal_nearest_intersection);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Schedule the agent to travel between two points on the same street.
|
|
* @private
|
|
*
|
|
* @param start_lat_lng {LatLng} - The coordinates of the point on the street from which the agent will be traveling.
|
|
* @param goal_lat_lng {LatLng} - The coordinates of the point on the street to which the agent should travel.
|
|
* @param street_feature {Feature} - A GeoJSON object representing an OpenStreetMap street.
|
|
* @param street_id {number} - The ID of the street in the streets layerGroup.
|
|
*/
|
|
Agent.setTravelOnSameStreet = function(start_lat_lng, goal_lat_lng, street_feature, street_id) {
|
|
//lineSlice, regardless of the specified starting point, will give a segment with the same coordinate order
|
|
//as the original lineString array. So, if the goal point comes earlier in the array (e.g. it's on the far left),
|
|
//it'll end up being the first point in the path, instead of the last, and the agent will move to it directly,
|
|
//ignoring the street, and then travel along the street from the goal point to its original point (backwards).
|
|
//To fix this, I'm reversing the order of the coordinates in the segment if the last point in the line is closer
|
|
//to the agent's starting point than the first point on the line (implying it's a situation of the kind described above).
|
|
|
|
let start_coords = L.A.pointToCoordinateArray(start_lat_lng),
|
|
goal_coords = L.A.pointToCoordinateArray(goal_lat_lng),
|
|
street_path_unordered = L.A.reversedCoordinates(lineSlice(start_coords, goal_coords, street_feature).geometry.coordinates);
|
|
let start_to_path_beginning = start_lat_lng.distanceTo(L.latLng(street_path_unordered[0])),
|
|
start_to_path_end = start_lat_lng.distanceTo(L.latLng(street_path_unordered[street_path_unordered.length - 1]));
|
|
let street_path = start_to_path_beginning < start_to_path_end ? street_path_unordered : street_path_unordered.reverse();
|
|
let street_path_lat_lngs = street_path.map(coords => {
|
|
let lat_lng = L.latLng(coords);
|
|
lat_lng.new_place = { street: street_id };
|
|
|
|
return lat_lng;
|
|
});
|
|
this.travel_state.path.push(...street_path_lat_lngs);
|
|
}
|
|
|
|
/**
|
|
* Schedule the agent up to travel between two points on a street network.
|
|
* @private
|
|
*
|
|
* @param start_lat_lng {LatLng} - The coordinates of the point on the street from which the agent will be traveling.
|
|
* @param goal_lat_lng {LatLng} - The coordinates of the point on the street to which the agent should travel.
|
|
* @param start_int_lat_lng {LatLng} - The coordinates of the nearest intersection on the same street at the start_lat_lng.
|
|
* @param goal_int_lat_lng {LatLng} - The coordinates of the nearest intersection on the same street as the goal_lat_lng.
|
|
*/
|
|
Agent.setTravelOnStreetNetwork = function(start_lat_lng, goal_lat_lng, start_int_lat_lng, goal_int_lat_lng) {
|
|
let path = this.agentmap.getPath(start_int_lat_lng, goal_int_lat_lng, start_lat_lng, goal_lat_lng, true);
|
|
|
|
for (let i = 0; i <= path.length - 2; i++) {
|
|
let current_street_id = path[i].new_place.street,
|
|
current_street_feature = this.agentmap.streets.getLayer(current_street_id).feature;
|
|
|
|
this.setTravelOnSameStreet(path[i], path[i + 1], current_street_feature, current_street_id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Continue to move the agent directly from one point to another, without regard for streets,
|
|
* according to the time that has passed since the last movement. Also simulate intermediary movements
|
|
* during the interval between the current call and the last call to moveDirectly, by splitting that interval
|
|
* up with some precision (agentmap.settings.movement_precision) into some number of parts (steps_inbetween)
|
|
* and moving slightly for each of them, for more precise collision detection than just doing it after each
|
|
* call to moveDirectly from requestAnimationFrame (max, 60 times per second) would allow. Limiting movements to
|
|
* each requestAnimationFrame call was causing each agent to skip too far ahead at each call, causing moveDirectly
|
|
* to not be able to catch when the agent is within 1 meter of the goal_point... splitting the interval since the last
|
|
* call up and making intermediary calls fixes that.
|
|
* @private
|
|
*
|
|
* @param {number} rAF_time - The time when the browser's most recent animation frame was released.
|
|
*/
|
|
Agent.moveDirectly = function(animation_interval, intermediary_interval, steps_inbetween) {
|
|
let state = this.travel_state;
|
|
|
|
//Fraction of the number of ticks since the last call to move the agent forward by.
|
|
//Only magnitudes smaller than hundredths will be added to the lat/lng at a time, so that it doesn't leap ahead too far;
|
|
//as the tick_interval is usually < 1, and the magnitude will be the leap_fraction multiplied by the tick_interval.
|
|
const leap_fraction = .0001;
|
|
|
|
let move = (function(tick_interval) {
|
|
if (state.goal_point.distanceTo(state.current_point) < 1) {
|
|
if (typeof(state.path[0].new_place) === "object") {
|
|
this.place = state.path[0].new_place;
|
|
}
|
|
|
|
state.path.shift();
|
|
|
|
if (state.path.length === 0) {
|
|
this.resetTravelState();
|
|
return;
|
|
}
|
|
else {
|
|
this.travelTo(state.path[0]);
|
|
}
|
|
}
|
|
|
|
let lat_change = state.lat_dir * state.slope * (leap_fraction * tick_interval),
|
|
lng_change = state.lng_dir * (leap_fraction * tick_interval),
|
|
new_lat_lng = L.latLng([state.current_point.lat + lat_change, state.current_point.lng + lng_change]);
|
|
this.setLatLng(new_lat_lng);
|
|
state.current_point = new_lat_lng;
|
|
}).bind(this);
|
|
|
|
//Intermediary movements.
|
|
for (let i = 0; i < steps_inbetween; ++i) {
|
|
move(intermediary_interval);
|
|
|
|
if (state.traveling === false) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Latest requested movement.
|
|
if (state.traveling === true) {
|
|
latest_interval = animation_interval - (this.agentmap.settings.movement_precision * steps_inbetween);
|
|
move(latest_interval);
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Make the agent proceed with whatever it's doing and update its properties before the browser draws the next frame.
|
|
* @private
|
|
*
|
|
* @param {number} rAF_time - The time when the browser's most recent animation frame was released.
|
|
*/
|
|
Agent.update = function(animation_interval, intermediary_interval, steps_inbetween) {
|
|
this.update_func();
|
|
|
|
if (this.travel_state.traveling) {
|
|
this.moveDirectly(animation_interval, intermediary_interval, steps_inbetween);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns an agent object.
|
|
*
|
|
* @memberof Agentmap
|
|
*/
|
|
function agent(feature, options, agentmap) {
|
|
return new L.A.Agent(feature, options, agentmap);
|
|
}
|
|
|
|
Agentmap.prototype.agentify = agentify,
|
|
Agentmap.prototype.seqUnitAgentMaker = seqUnitAgentMaker;
|
|
|
|
exports.Agent = L.CircleMarker.extend(Agent),
|
|
exports.agent = agent;
|
|
</code></pre>
|
|
</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>Global</h3><ul><li><a href="global.html#agentify">agentify</a></li><li><a href="global.html#agentmap">agentmap</a></li><li><a href="global.html#buildingify">buildingify</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#getPath">getPath</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#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#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 Wed Aug 01 2018 23:53:46 GMT-0400 (Eastern Daylight Time)
|
|
</footer>
|
|
|
|
<script> prettyPrint(); </script>
|
|
<script src="scripts/linenumber.js"> </script>
|
|
</body>
|
|
</html>
|