UNPKG

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
'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;