let centroid = require('@turf/centroid').default;
/**
* The main class representing individual iconagents, using Leaflet class system.
* @private
*
* @class IconAgent
*/
var IconAgent = {};
/**
* Constructor for the IconAgent class, using Leaflet class system.
*
* @name IconAgent
* @constructor
* @param {LatLng} lat_lng - A pair of coordinates to place the iconagent at.
* @param {Object} icon - A Leaflet Icon instance that will depict the IconAgent.
* @param {Agentmap} agentmap - The agentmap instance in which the iconagent exists.
* @property {Agentmap} agentmap - The agentmap instance in which the iconagent exists.
* @property {Place} place - A place object specifying where the iconagent is currently at.
* @property {number} [steps_made=0] - The number of steps the iconagent has moved since the beginning.
* @property {Object} this.trip - Properties detailing information about the iconagent's trip that change sometimes, but needs to be accessed by future updates.
* @property {boolean} this.trip.moving - Whether the iconagent currently moving.
* @property {boolean} this.trip.paused - Whether the iconagent should be allowed to move along its trip.
* @property {?Point} this.trip.current_point - The point where the iconagent is currently located.
* @property {?Point} this.trip.goal_point - The point where the iconagent is traveling to.
* @property {?number} this.trip.lat_dir - The latitudinal direction. -1 if traveling to lower latitude (down), 1 if traveling to higher latitude (up).
* @property {?number} this.trip.lng_dir - The longitudinal direction. -1 if traveling to lesser longitude (left), 1 if traveling to greater longitude (right).
* @property {?number} this.trip.speed - The speed that the iconagent should travel, in meters per tick.
* @property {?number} this.trip.angle - The angle between the current point and the goal.
* @property {?number} this.trip.slope - The slope of the line segment formed by the two points between which the iconagent is traveling at this time during its trip.
* @property {Array} this.trip.path - A sequence of LatLngs; the iconagent will move from one to the next, popping each one off after it arrives until the end of the street; or, until the trip is changed/reset.
* @property {?function} controller - User-defined function to be called on each update (each tick).
* @property {?function} fine_controller - User-defined function to be called before & after each movemnt (on each step an iconagent performs during a tick).
*/
IconAgent.initialize = function(lat_lng, icon, agentmap) {
this.agentmap = agentmap,
this.place = null,
this.steps_made = 0,
this.trip = {
paused: false,
moving: false,
current_point: null,
goal_point: null,
lat_dir: null,
lng_dir: null,
slope: null,
angle: null,
speed: null,
path: [],
},
this.controller = function() {},
this.fine_controller = function() {};
L.Marker.prototype.initialize.call(this, lat_lng, {"icon": icon});
}
//Copy over the agents' movement methods.
IconAgent.resetTrip = L.A.Agent.prototype.resetTrip,
IconAgent.startTrip = L.A.Agent.prototype.startTrip,
IconAgent.pauseTrip = L.A.Agent.prototype.pauseTrip,
IconAgent.resumeTrip = L.A.Agent.prototype.resumeTrip,
IconAgent.travelTo = L.A.Agent.prototype.travelTo,
IconAgent.newTripStartPlace = L.A.Agent.prototype.newTripStartPlace,
IconAgent.setTravelInUnit = L.A.Agent.prototype.setTravelInUnit,
IconAgent.setTravelToPlace = L.A.Agent.prototype.setTravelToPlace,
IconAgent.scheduleTrip = L.A.Agent.prototype.scheduleTrip,
IconAgent.setTravelAlongStreet = L.A.Agent.prototype.setTravelAlongStreet,
IconAgent.setTravelOnSameStreet = L.A.Agent.prototype.setTravelOnSameStreet,
IconAgent.setTravelOnStreetNetwork = L.A.Agent.prototype.setTravelOnStreetNetwork,
IconAgent.setSpeed = L.A.Agent.prototype.setSpeed,
IconAgent.multiplySpeed = L.A.Agent.prototype.multiplySpeed,
IconAgent.increaseSpeed = L.A.Agent.prototype.increaseSpeed,
IconAgent.checkSpeed = L.A.Agent.prototype.checkSpeed,
IconAgent.travel = L.A.Agent.prototype.travel,
IconAgent.step = L.A.Agent.prototype.step,
IconAgent.checkArrival = L.A.Agent.prototype.checkArrival,
IconAgent.moveIt = L.A.Agent.prototype.moveIt;
IconAgent = L.Marker.extend(IconAgent);
/**
* Returns a IconAgent object.
*
* @param {LatLng} lat_lng - A pair of coordinates to place the iconagent at.
* @param {Object} icon - A Leaflet Icon to depict the agent with.
* @param {Agentmap} agentmap - The agentmap instance in which the iconagent exists.
*/
function iconagent(lat_lng, icon, agentmap) {
return new IconAgent(lat_lng, icon, agentmap);
}
//Add IconAgent to the AgentMaps namespace.
L.A.IconAgent = IconAgent;
/**
* A user-defined callback function that returns a feature with appropriate geometry and properties to represent a iconagent.
*
* @callback iconAgentFeatureMaker
* @param {number} id - The agent's Leaflet layer ID.
* @returns {Point} - A GeoJSON Point object with geometry and other properties for the iconagent, including
* a "place" property that will set the iconagent's initial {@link Place}, and an "icon" property that determines the icon which will depict the iconagent.
* All other provided properties will be transferred to the IconAgent object once it is created.
* See {@link https://leafletjs.com/reference-1.3.2.html#marker} and {@link https://leafletjs.com/reference-1.3.2.html#marker}
* for all possible Icon and Marker options.
*
* @example
* let icon_A = L.icon({
* "iconUrl": "pic.jpg",
* "iconSize": [38, 95],
* "iconAnchor": [22, 94]
* }),
* point = {
* "type": "Feature",
* "properties": {
* "icon": icon_A,
* "place": {
* "type": "unit",
* "id": 89
* },
* age: 72,
* home_city: "LA"
* },
* "geometry" {
* "type": "Point",
* "coordinates": [
* 14.54589,
* 57.136239
* ]
* }
* }
*/
/**
* A standard {@link iconagentFeatureMaker}, which sets an iconagent's location to be the point near the center of the i�� unit of the map,
* its place property to be that unit's, and its layer_options to be red and of radius .5 meters.
* @memberof Agentmap
* @instance
* @type {iconagentFeatureMaker}
*/
function seqUnitIconAgentMaker(id){
let index = this.agents.count();
let icon_A = L.icon({
"iconUrl": "https://leafletjs.com/examples/custom-icons/leaf-orange.png",
"iconSize": [38, 95],
"iconAnchor": [22, 94]
});
if (index > this.units.getLayers().length - 1) {
throw new Error("seqUnitIconAgentMaker cannot accommodate more agents than there are units.");
}
let unit = this.units.getLayers()[index],
unit_id = this.units.getLayerId(unit),
center_point = centroid(unit.feature);
center_point.properties.place = {"type": "unit", "id": unit_id},
center_point.properties.center_point.properties.icon = icon_A;
return center_point;
}
/**
* Generate some number of IconAgents and place them on the map.
* @memberof Agentmap
* @instance
*
* @param {number} count - The desired number of iconagents.
* @param {iconagentFeatureMaker} iconagentFeatureMaker - A callback that determines an iconagent i's feature properties and geometry (always a Point).
*/
function IconAgentify(count, iconagentFeatureMaker) {
let agentmap = this;
if (!(this.agents instanceof L.LayerGroup)) {
this.agents = L.featureGroup().addTo(this.map);
}
let agents_existing = agentmap.agents.getLayers().length;
for (let i = agents_existing; i < agents_existing + count; i++) {
let new_iconagent = iconagent(null, null, agentmap);
//Callback function aren't automatically bound to the agentmap.
let boundFeatureMaker = iconagentFeatureMaker.bind(agentmap),
iconagent_feature = boundFeatureMaker(new_iconagent._leaflet_id);
let coordinates = L.A.reversedCoordinates(iconagent_feature.geometry.coordinates),
place = iconagent_feature.properties.place,
icon = iconagent_feature.properties.icon;
//Make sure the agent feature is valid and has everything we need.
if (!L.A.isPointCoordinates(coordinates)) {
throw new Error("Invalid feature returned from iconagentFeatureMaker: geometry.coordinates must be a 2-element array of numbers.");
}
else if (typeof(place.id) !== "number") {
throw new Error("Invalid feature returned from iconagentFeatureMaker: properties.place must be a {unit: unit_id} or {street: street_id} with an existing layer's ID.");
}
//markers dont have setStyle options like circleMarkers
new_iconagent.setLatLng(coordinates);
new_iconagent.setIcon(icon);
new_iconagent.place = place;
Object.assign(new_iconagent, iconagent_feature.properties);
this.agents.addLayer(new_iconagent);
}
}
exports.iconagent = iconagent,
exports.iconagentify = IconAgentify,
exports.seqUnitIconAgentMaker = seqUnitIconAgentMaker;