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

268 lines (233 loc) 9.95 kB
'use strict'; var R = require('ramda'); var geom = require('../../../src/utils/geom.js'); var segmentCategories = require('./segmentCategories.js'); var stepBuilder = require('./stepBuilder.js'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var R__namespace = /*#__PURE__*/_interopNamespaceDefault(R); const setSegmentCategory = (segments) => { // Set the category of each segment based on the type of the current segment or the type of the next segment in case we are walking // to a portal or to a security checkpoint segments.forEach((segment, index) => { if (index === 0) segment.segmentCategory = segmentCategories.START; else if (segment.waypoints[segment.waypoints.length - 1].isDestination) segment.segmentCategory = segmentCategories.WALKING_TO_END; else if (segment.type === 'Security Checkpoint') segment.segmentCategory = segmentCategories.SECURITY_CHECKPOINT; else if (segment.type === 'Bus') segment.segmentCategory = segmentCategories.BUS; else if (segment.type === 'Train') segment.segmentCategory = segmentCategories.TRAIN; else if (segment.type === 'Stairs') { if (segment.levelDifference > 0) segment.segmentCategory = segmentCategories.STAIRS_UP; else if (segment.levelDifference < 0) segment.segmentCategory = segmentCategories.STAIRS_DOWN; else segment.segmentCategory = segmentCategories.STAIRS; } else if (segment.type === 'Elevator') { if (segment.levelDifference > 0) segment.segmentCategory = segmentCategories.ELEVATOR_UP; else if (segment.levelDifference < 0) segment.segmentCategory = segmentCategories.ELEVATOR_DOWN; else segment.segmentCategory = segmentCategories.ELEVATOR; } else if (segment.type === 'Escalator') { if (segment.levelDifference > 0) segment.segmentCategory = segmentCategories.ESCALATOR_UP; else if (segment.levelDifference < 0) segment.segmentCategory = segmentCategories.ESCALATOR_DOWN; else segment.segmentCategory = segmentCategories.ESCALATOR; } else if (segment.type === 'Ramp') { if (segment.levelDifference > 0) segment.segmentCategory = segmentCategories.RAMP_UP; else if (segment.levelDifference < 0) segment.segmentCategory = segmentCategories.RAMP_DOWN; else segment.segmentCategory = segmentCategories.RAMP; } else if (segments[index + 1].type === 'Security Checkpoint') segment.segmentCategory = segmentCategories.WALKING_TO_SECURITY_CHECKPOINT; else if (segments[index + 1].type !== 'Walk') segment.segmentCategory = segmentCategories.WALKING_TO_PORTAL; }); }; const existsEnd = (segments, lastWaypoint) => { // if we have only one segment, add an extra segment with only the last point to simulate the end, // that way we can draw separate markers for the start and the end of the navigation if (segments.length === 1) { const segment = { segmentCategory: undefined, waypoints: [] }; segment.segmentCategory = segmentCategories.WALKING_TO_END; segment.type = 'Walk'; segment.waypoints = [lastWaypoint]; segments.push(segment); } }; // todo refactor to not affect step builder const joinSegments = (segments) => { segments.forEach((segment, index) => { if (index > 1) { const firstWaypointOfSegment = R__namespace.head(segment.waypoints); if (firstWaypointOfSegment.levelDifference === 0) { const lastWaypointOfPreviousSegment = R__namespace.last(segments[index - 1].waypoints); segment.waypoints = R__namespace.prepend(lastWaypointOfPreviousSegment, segment.waypoints); } } }); }; const calculateCurveLineCoordinates = segment => segment.waypoints.flatMap((waypoint, index) => { // todo index > 0 is hacky fix if (index > 0 && waypoint.curvedPathForward && waypoint.curvedPathForward.length > 0) { return waypoint.curvedPathForward .flatMap(p => geom.bezierCurveTo(p.start.lng, p.start.lat, p.in.lng, p.in.lat, p.out.lng, p.out.lat, p.end.lng, p.end.lat)) .map(el => [el.x, el.y]) } else { return [[waypoint.position.lng, waypoint.position.lat]] } }); const addCurveLineCoordinates = R__namespace.map( R__namespace.converge(R__namespace.assoc('coordinates'), [calculateCurveLineCoordinates, R__namespace.identity])); // todo refactor const createSegments = (waypoints) => { const segments = []; let segment = { segmentCategory: undefined, waypoints: [] }; let lastWaypoint = null; let segmentWaypoints = []; // Always add the first point to be the start of the route segment.waypoints = [waypoints[0]]; segment.type = waypoints[0].isPortal ? waypoints[0].portalType : 'Walk'; segments.push(segment); segment = { segmentCategory: undefined, waypoints: [] }; waypoints.forEach(waypoint => { segmentWaypoints.push(waypoint); if (!lastWaypoint) { // true on first waypoint only segment.type = waypoint.isPortal ? waypoint.portalType : 'Walk'; lastWaypoint = waypoint; } else { if ((lastWaypoint.isPortal === waypoint.isPortal) && (lastWaypoint.isSecurityCheckpoint === waypoint.isSecurityCheckpoint)) { // todo not sure if this can happen segment.levelDifference = waypoint.levelDifference; } else { segment.waypoints = segmentWaypoints; if (waypoint.isPortal || lastWaypoint.isPortal) { if (segmentWaypoints.length > 1) segmentWaypoints.pop(); // if the portal is not train or bus, we only want it to be one point segment if (waypoint.isPortal && (waypoint.portalType.toLowerCase() === 'train' || waypoint.portalType.toLowerCase() === 'bus')) segmentWaypoints = [segmentWaypoints[segmentWaypoints.length - 1], waypoint]; else segmentWaypoints = [waypoint]; } else segmentWaypoints = []; if (lastWaypoint.poiId) segment.poiId = lastWaypoint.poiId; segments.push(segment); segment = { segmentCategory: undefined, waypoints: [] }; segment.type = waypoint.isPortal ? waypoint.portalType : 'Walk'; segment.levelDifference = waypoint.levelDifference; } lastWaypoint = waypoint; } }); segment.waypoints = segmentWaypoints; if (segmentWaypoints.length === 0) segment.waypoints = [lastWaypoint]; segments.push(segment); setSegmentCategory(segments); existsEnd(segments, lastWaypoint); joinSegments(segments); return segments }; const getSegmentType = (segment) => { if (segment.type === 'Train') { return 'nav.train' } if (segment.type === 'Bus') { return 'nav.transit' } if (segment.type === 'Security Checkpoint') { return 'nav.secure' } return 'nav.primary' }; /** * * @typedef Badge * @property {string} canonicalName * @property {Coordinate} coordinates * * @typedef Segment * @property {string} levelId * @property {string} ordinalId * @property {string} segmentType * @property {Boolean} shouldDrawSegment - to not show edges like stairs or elevator * @property {Array.<Coordinate>} coordinates - list of coordinate pairs [lng, lat] * @property {Array<Badge>} badges * * @param {Waypoint} waypoints * @param {Endpoint} fromEndpoint * @param {Endpoint} toEndpoint * @param {Object.<string, string>} floorIdToNameMap - dictionary of floor id to floor name * @param T - i18n translations function * @param {QueueTypes} queueTypes * @param {boolean} requiresAccessibility * @return {{steps: Step[], segments: Segment[]}} */ const buildSegments = (waypoints, fromEndpoint, toEndpoint, floorIdToNameMap, T, queueTypes, requiresAccessibility, securityPois) => { let rawSegments = createSegments(waypoints); rawSegments = addCurveLineCoordinates(rawSegments); // add start location (kiosk) as first coordinate to link it with the navline if (fromEndpoint) rawSegments[0].coordinates.unshift([fromEndpoint.lng, fromEndpoint.lat]); if (toEndpoint) R__namespace.last(rawSegments).coordinates.push([toEndpoint.lng, toEndpoint.lat]); const segments = rawSegments.map((segment, index) => { const startWaypoint = R__namespace.last(segment.waypoints); const coordinates = segment.coordinates; const shouldDrawSegment = !(segment.levelDifference && segment.waypoints.every(R__namespace.prop('isPortal'))); const cookedSegment = { levelId: startWaypoint.position.structureId, ordinalId: `ordinal: ${startWaypoint.position.ordinal}`, coordinates, shouldDrawSegment }; const badges = []; if (segmentCategories.WALKING_TO_PORTAL === segment.segmentCategory) { const nextSegment = rawSegments[index + 1]; badges.push({ canonicalName: `wayfinding.${nextSegment.segmentCategory}`, coordinates: R__namespace.last(coordinates) }); } else if (segmentCategories.START !== segment.segmentCategory) { badges.push({ canonicalName: `wayfinding.${segment.segmentCategory}`, coordinates: R__namespace.last(coordinates) }); } cookedSegment.badges = badges; cookedSegment.segmentType = getSegmentType(segment); if (segment.poiId) cookedSegment.poiId = segment.poiId; return cookedSegment }); const steps = stepBuilder(rawSegments, R__namespace.prop('title', fromEndpoint), R__namespace.prop('title', toEndpoint), floorIdToNameMap, T, queueTypes, requiresAccessibility, securityPois); return { segments, steps } }; exports.buildSegments = buildSegments;