atriusmaps-node-sdk
Version:
This project provides an API to Atrius Personal Wayfinder maps within a Node environment. See the README.md for more information
301 lines (297 loc) • 12.4 kB
JavaScript
'use strict';
var bearing = require('@turf/bearing');
var helpers = require('@turf/helpers');
var R = require('ramda');
var bounds = require('../../../src/utils/bounds.js');
var distance = require('../../../src/utils/distance.js');
var geodesy = require('../../../src/utils/geodesy.js');
var segmentBadges = require('./segmentBadges.js');
var segmentCategories = require('./segmentCategories.js');
const calculateSegmentGeneralDirection = ({ waypoints }, T) => {
const [firstWaypoint] = waypoints;
const lastWaypoint = waypoints[waypoints.length - 1];
const from = helpers.point([firstWaypoint.position.lng, firstWaypoint.position.lat]);
const to = helpers.point([lastWaypoint.position.lng, lastWaypoint.position.lat]);
const bearingDegrees = bearing.bearing(from, to);
return geodesy.bearingToDirection(bearingDegrees, T);
};
function getSteps(segments, startName = "", destinationName = "", floorIdToNameMap, T, queueTypes = {}, requiresAccessibility, securityPois = [], currentUnits) {
return segments.map((segment, index) => {
const securityWaitTimes = findPropWaypoints("securityWaitTimes")(segment.waypoints);
const eta = securityWaitTimes && !securityWaitTimes.isTemporarilyClosed ? securityWaitTimes.queueTime : Math.round(calculateSegmentEta(segment.waypoints));
const distance$1 = Math.round(calculateSegmentDistance(segment.waypoints));
const currentUnitsDistance = currentUnits === "yards" ? distance.metersToYards(distance$1) : distance$1;
const icon = getIcon(segment.segmentCategory);
const animationAnchor = getAnimationAnchor(segment.segmentCategory, segment.waypoints);
const direction = calculateSegmentGeneralDirection(segment, T);
const primaryText = getPrimaryText(
segments,
index,
startName,
destinationName,
floorIdToNameMap,
T,
securityPois,
queueTypes
);
const secondaryText = getSecondaryText(segment, eta, T, false);
const secondaryTextUI = getSecondaryText(segment, eta, T, true, direction, currentUnits, currentUnitsDistance);
const bounds$1 = bounds.findBoundsOfWaypoints(segment.waypoints);
const isAccessible = checkIfAccessible(segment);
const step = {
primaryText,
secondaryText,
secondaryTextUI,
icon,
animationAnchor,
eta,
distance: distance$1,
bounds: bounds$1,
isAccessible,
securityWaitTimes
};
if (segment.poiId) {
step.poiId = segment.poiId;
}
return step;
});
}
function calculateSegmentEta(waypoints) {
if (waypoints.length === 1) {
return waypoints[0].eta;
}
return waypoints.map(R.prop("eta")).slice(1).reduce((segmentEta, eta) => segmentEta + eta, 0);
}
function calculateSegmentDistance(waypoints) {
if (waypoints.length === 1) {
return waypoints[0].distance;
}
return waypoints.map((waypoint) => waypoint.distance).slice(1).reduce((segmentDistance, distance) => segmentDistance + distance, 0);
}
function getIcon(segmentCategory) {
switch (segmentCategory) {
case segmentCategories.START:
return segmentBadges.START;
case segmentCategories.WALKING_TO_END:
return segmentBadges.END;
case segmentCategories.ELEVATOR:
return segmentBadges.ELEVATOR;
case segmentCategories.ELEVATOR_UP:
return segmentBadges.ELEVATOR_UP;
case segmentCategories.ELEVATOR_DOWN:
return segmentBadges.ELEVATOR_DOWN;
case segmentCategories.STAIRS:
return segmentBadges.STAIRS;
case segmentCategories.STAIRS_UP:
return segmentBadges.STAIRS_UP;
case segmentCategories.STAIRS_DOWN:
return segmentBadges.STAIRS_DOWN;
case segmentCategories.ACCESSIBLE_STAIRS:
return segmentBadges.ACCESSIBLE_STAIRS;
case segmentCategories.ACCESSIBLE_STAIRS_UP:
return segmentBadges.ACCESSIBLE_STAIRS_UP;
case segmentCategories.ACCESSIBLE_STAIRS_DOWN:
return segmentBadges.ACCESSIBLE_STAIRS_DOWN;
case segmentCategories.ESCALATOR:
return segmentBadges.ESCALATOR;
case segmentCategories.ESCALATOR_UP:
return segmentBadges.ESCALATOR_UP;
case segmentCategories.ESCALATOR_DOWN:
return segmentBadges.ESCALATOR_DOWN;
case segmentCategories.WALKING_TO_PORTAL:
case segmentCategories.WALK:
case segmentCategories.WALK_DOWN:
case segmentCategories.WALK_UP:
return segmentBadges.WALK;
case segmentCategories.TRAIN:
return segmentBadges.TRAIN;
case segmentCategories.TRAIN_UP:
return segmentBadges.TRAIN_UP;
case segmentCategories.TRAIN_DOWN:
return segmentBadges.TRAIN_DOWN;
case segmentCategories.BUS:
return segmentBadges.BUS;
case segmentCategories.BUS_UP:
return segmentBadges.BUS_UP;
case segmentCategories.BUS_DOWN:
return segmentBadges.BUS_DOWN;
case segmentCategories.SECURITY_CHECKPOINT:
return segmentBadges.SECURITY_CHECKPOINT;
case segmentCategories.RAMP:
return segmentBadges.RAMP;
case segmentCategories.RAMP_UP:
return segmentBadges.RAMP_UP;
case segmentCategories.RAMP_DOWN:
return segmentBadges.RAMP_DOWN;
default:
return segmentBadges.WALK;
}
}
function getAnimationAnchor(segmentCategory, waypoints) {
let index;
switch (segmentCategory) {
case segmentCategories.START:
index = 0;
break;
case segmentCategories.WALKING_TO_END:
index = waypoints.length - 1;
break;
case segmentCategories.WALKING_TO_PORTAL:
index = Math.min(waypoints.length - 1, Math.ceil(waypoints.length / 2));
break;
default:
index = waypoints.length - 1;
}
return waypoints[index].position;
}
function getPrimaryText(segments, index, startName, destinationName, floorIdToNameMap, T, securityPois, queueTypes) {
const segment = segments[index];
switch (segment.segmentCategory) {
case segmentCategories.START:
return getPositionName(segment.waypoints[0].position, startName);
case segmentCategories.WALKING_TO_END:
return getPositionName(segment.waypoints[segment.waypoints.length - 1].position, destinationName);
case segmentCategories.WALKING_TO_SECURITY_CHECKPOINT: {
const lookAheadWaypoints = segments[index + 1].waypoints;
const laneName = getSecurityLaneName(queueTypes, lookAheadWaypoints);
const securityLane = laneName ? T(`wayfinder:Security Lane`, { laneName }) : null;
const poiTitle = getClosestSecurityPoiTitle(lookAheadWaypoints, securityPois);
const displayName = securityLane || poiTitle;
return displayName || T(`wayfinder:${segment.type}`);
}
case segmentCategories.WALKING_TO_PORTAL:
return T(`wayfinder:${segments[index + 1].type}`);
case segmentCategories.SECURITY_CHECKPOINT: {
const laneName = getSecurityLaneName(queueTypes, segment.waypoints);
const securityLane = laneName ? T(`wayfinder:Security Lane`, { laneName }) : null;
const poiTitle = getClosestSecurityPoiTitle(segment.waypoints, securityPois);
const displayName = securityLane || poiTitle;
return displayName || getLevelName(segment, floorIdToNameMap);
}
case segmentCategories.ELEVATOR:
case segmentCategories.ELEVATOR_DOWN:
case segmentCategories.ELEVATOR_UP:
case segmentCategories.ESCALATOR:
case segmentCategories.ESCALATOR_DOWN:
case segmentCategories.ESCALATOR_UP:
case segmentCategories.STAIRS:
case segmentCategories.STAIRS_DOWN:
case segmentCategories.STAIRS_UP:
case segmentCategories.ACCESSIBLE_STAIRS:
case segmentCategories.ACCESSIBLE_STAIRS_DOWN:
case segmentCategories.ACCESSIBLE_STAIRS_UP:
return getLevelName(segment, floorIdToNameMap);
default:
return T(`wayfinder:${segment.type}`);
}
}
function getPositionName(position, poiName) {
if (position.name) {
return position.name;
}
return poiName;
}
function getLevelName(segment, floorIdToNameMap) {
return floorIdToNameMap[R.last(segment.waypoints).position.floorId];
}
function getSecondaryText(segment, minutes, T, ui, direction, currentUnits, currentUnitsDistance) {
const zeroOrOtherKeys = translateZeroOrOther(minutes, T);
const travelVerb = "Proceed";
switch (segment.segmentCategory) {
case segmentCategories.START:
return T("wayfinder:Begin route at");
case segmentCategories.ELEVATOR:
return T("wayfinder:Take elevator to");
case segmentCategories.ELEVATOR_UP:
return T("wayfinder:Take elevator up to");
case segmentCategories.ELEVATOR_DOWN:
return T("wayfinder:Take elevator down to");
case segmentCategories.STAIRS:
return T("wayfinder:Take stairs to");
case segmentCategories.STAIRS_UP:
return T("wayfinder:Take stairs up to");
case segmentCategories.STAIRS_DOWN:
return T("wayfinder:Take stairs down to");
case segmentCategories.ACCESSIBLE_STAIRS:
return T("wayfinder:Take accessible stairs to");
case segmentCategories.ACCESSIBLE_STAIRS_UP:
return T("wayfinder:Take accessible stairs up to");
case segmentCategories.ACCESSIBLE_STAIRS_DOWN:
return T("wayfinder:Take accessible stairs down to");
case segmentCategories.ESCALATOR:
return T("wayfinder:Take escalator to");
case segmentCategories.ESCALATOR_UP:
return T("wayfinder:Take escalator up to");
case segmentCategories.ESCALATOR_DOWN:
return T("wayfinder:Take escalator down to");
case segmentCategories.WALK:
case segmentCategories.WALKING_TO_SECURITY_CHECKPOINT:
case segmentCategories.WALKING_TO_PORTAL:
case segmentCategories.WALKING_TO_END: {
if (!ui) {
return zeroOrOtherKeys(`${travelVerb} <1 minute to`, `${travelVerb} xx minute to`);
} else {
return createSecondaryTextWithHeading(currentUnitsDistance, currentUnits, direction, T);
}
}
case segmentCategories.WALK_DOWN:
return zeroOrOtherKeys(`${travelVerb} <1 minute down to`, `${travelVerb} xx minute down to`);
case segmentCategories.WALK_UP:
return zeroOrOtherKeys(`${travelVerb} <1 minute up to`, `${travelVerb} xx minute up to`);
case segmentCategories.TRAIN:
return zeroOrOtherKeys("Take train <1 minute", "Take train xx minute");
case segmentCategories.BUS:
return zeroOrOtherKeys("Take bus <1 minute", "Take bus xx minute");
case segmentCategories.SECURITY_CHECKPOINT: {
return T("wayfinder:Through");
}
case segmentCategories.RAMP:
return T("wayfinder:Take ramp to");
case segmentCategories.RAMP_UP:
return T("wayfinder:Take ramp up to");
case segmentCategories.RAMP_DOWN:
return T("wayfinder:Take ramp down to");
default:
return "";
}
}
const createSecondaryTextWithHeading = (currentUnitsDistance, currentUnits, direction, T) => {
return currentUnits === "meters" ? T(`wayfinder:Proceed __direction__ __count__ meter to`, {
direction,
count: currentUnitsDistance
}) : T(`wayfinder:Proceed __direction__ __count__ yard to`, {
direction,
count: currentUnitsDistance
});
};
const translateZeroOrOther = (count, T) => (zeroKey, otherKey) => count === 0 ? T("wayfinder:" + zeroKey) : T("wayfinder:" + otherKey, { count });
const checkIfAccessible = R.compose(R.not, R.includes(R.__, ["escalator", "stairs"]), R.toLower, R.prop("type"));
const getSecurityLaneName = (queueTypes, waypoints) => {
const lane = findPropWaypoints("securityLane")(waypoints);
if (!lane) {
return;
}
const types = R.prop(lane.type, queueTypes);
const laneType = R.find(R.propEq(lane.id, "id"), types);
return R.prop("displayText", laneType);
};
const findPropWaypoints = (propName) => R.compose(R.prop(propName), R.find(R.prop(propName)), R.drop(1));
const getClosestSecurityPoiTitle = (waypoints, securityPois) => {
if (!waypoints || waypoints.length === 0) {
return null;
}
const checkpointWaypoint = waypoints[0];
const { lat, lng, floorId } = checkpointWaypoint.position;
const sameFloorCandidates = securityPois.filter((poi) => poi.position && poi.position.floorId === floorId);
let closest = null;
let minDistance = Infinity;
sameFloorCandidates.forEach((poi) => {
const d = geodesy.distance(lat, lng, poi.position.latitude, poi.position.longitude);
if (d < minDistance) {
minDistance = d;
closest = poi;
}
});
return closest ? closest.name : null;
};
module.exports = getSteps;