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