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

194 lines (165 loc) 7.9 kB
'use strict'; var R = require('ramda'); var bounds = require('../../../src/utils/bounds.js'); var configUtils = require('../../../src/utils/configUtils.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 fetchURL = async (token, url) => { return fetch(url) }; const createFetchJson = (token) => url => fetchURL(token, url).then(response => response.json()); const createFetchText = (token) => url => fetchURL(token, url).then(response => response.text()); const baseContentUrl = (contentStage) => `https://api.content.locuslabs.com/${contentStage}`; const remapContentUrl = (url, category, contentStage, accountId, venueId, key) => { if (key === 'theme' || key === 'style') { return `${baseContentUrl(contentStage)}/${category}/${key}/${venueId}/${accountId}/${key}.json` } return url.replace(/https:\/\/content.locuslabs.com/gi, baseContentUrl(contentStage)) }; const remapEachURLForContentStage = (category, files, contentStage, accountId, venueId) => R__namespace.mapObjIndexed((file, key) => remapContentUrl(file, category, contentStage, accountId, venueId, key), files); const getContentStage = (vconfig) => { const stage = vconfig.deepLinkProps ? vconfig.deepLinkProps.contentStage : null; return stage === 'alpha' || stage === 'beta' || stage === 'prod' ? stage : null }; const getVenueDataFromUrls = async (vconfig, fetchJson, languagesToTry) => { const stages = { alpha: 'alpha-a.locuslabs.com', beta: 'beta-a.locuslabs.com', gamma: 'gamma-a.locuslabs.com', prod: 'a.locuslabs.com' }; const { assetStage, accountId, formatVersion } = vconfig; let { venueId } = vconfig; const stageUrl = stages[assetStage] || stages.prod; const accountUrl = `https://${stageUrl}/accounts/${accountId}`; const assetFormat = formatVersion || 'v5'; // Load the v5.json resource const venueList = (vconfig.dataFetch && configUtils.global[vconfig.dataFetch] && configUtils.global[vconfig.dataFetch].getFiles) ? await configUtils.global[vconfig.dataFetch].getFiles(vconfig) : await fetchJson(`${accountUrl}/${assetFormat}.json`); // Reassign venue id to venueId+langauge preference if that venue in that language is avaialbe to us in the v5 json if (languagesToTry.length > 0) { const convertedLangsToTry = languagesToTry.map(lang => lang.slice(0, 2)); // Browser langs can look like this [fr', 'fr-FR', 'ar', 'en-US', 'en'] so we need to process them so they match our lang codes which are just 2 digits for (let i = 0; i < convertedLangsToTry.length; i += 1) { if (venueList[`${venueId}${convertedLangsToTry[i]}`]) { venueId = `${venueId}${convertedLangsToTry[i]}`; vconfig.venueId = `${venueId}`; break } } } if (!venueList[venueId]) throw Error(`Attempt to access venue ${venueId} which is not within venue list: ${Object.keys(venueList)}`) const files = venueList[venueId].files; // mapping of asset "types" (spritesheet, style, badges, etc) to their URL const fetchedData = (vconfig.dataFetch && configUtils.global[vconfig.dataFetch] && configUtils.global[vconfig.dataFetch].getVenueData) ? await configUtils.global[vconfig.dataFetch].getVenueData(vconfig) : await fetchJson(files.venueData); const venueData = fetchedData[venueId]; // For tilemaps, we need to embellish the venue data with structure if (venueData.tileServerAuthInfo) embellishTilemapVenueData(venueData); venueData.venueList = venueList; const contentStage = getContentStage(vconfig); venueData.files = contentStage // if a contentStage is defined, remap content URLs (see docs/Content-And-Asset-Loading/contentStaging.md) ? remapEachURLForContentStage(venueData.category, files, contentStage, accountId, venueId) : files; return venueData }; const buildStructures = (venueData) => { const { structureOrder, structures } = venueData; return structureOrder.map(structureId => { const structure = structures[structureId]; // Was going to use this for selecting floors - but it seems floor bounds are incorrect? (was testing LAX/lax-msc-2 for example) Object.values(structure.levels).forEach(level => (level.bounds = bounds.findBoundsOfCoordinates(level.boundsPolygon))); const bounds$1 = bounds.findBoundsOfCoordinates(structure.boundsPolygon); return { ...structure, bounds: bounds$1 } }) }; function embellishTilemapVenueData (venueData) { const additionalFields = { defaultOrdinal: 0, // this and below override the venue data defaultStructureId: 'singleBuilding', formatVersion: 'v5', // Note: this structures object does not represent the "buildingsAndLevels" as // managed within this app. This is a mock structure for the purposes of rendering // a tiled map. It is always a single building, single floor. If user switches // buildings or floors, it unmounts this map and remounts a new map with that building/floor structures: { singleBuilding: { name: 'singleBuilding', boundsPolygon: [], defaultLevelId: 'singleLevel', id: 'singleBuilding', levels: { singleLevel: { boundsPolygon: [], clfloor: 0, details: '', id: 'singleLevel', name: 'singleLevel', ordinal: 0 } } } }, structureOrder: [ 'singleBuilding' ] }; for (const key in additionalFields) venueData[key] = additionalFields[key]; } const between = (val, lim1, lim2) => val > lim1 ? val <= lim2 : val => lim2; // exchanges the first and second items in an array, preserving the rest of the array untouched // i.e. [1,2,3,4,5] => [2,1,3,4,5] const swapFirstTwoItems = ([c1, c2, ...c3]) => [c2, c1, ...c3]; /** * Since coordinates are often expressed as [lat, lng] but required by GeoJSON to be [lng, lat], * this function will take a list of coordinate pairs and ensure they are in the required format * by comparing to the venue bounds. * * @param {array.array.float} coords list of coordinate pairs, such as [[36.3,-115.85], [36.5, -115.34]] * @param {object} venueBounds contains props {ne, sw} which each are objects containing {lat,lng} properties * @returns {array.array.float} with [lng,lat,...] for each item */ const normalizeCoords = (coords, venueBounds) => { if (!coords || !Array.isArray(coords) || coords.length < 1) return coords if (between(coords[0][0], venueBounds.ne.lng, venueBounds.sw.lng)) // looks like a lat - so already in proper order return coords // wrong order, so swtich em return coords.map(swapFirstTwoItems) }; // If config.useDynamicUrlParams is true, allow changing even security-related parameters in // the URL - else, only allow the changing of "end-user-changable" parameters, such as vid // export function updateConfigWithUrlParams (config) { // const dlp = config.deepLinkProps // if (dlp) { // const venueId = dlp.vid ? dlp.vid : config.venueId // const assetStage = config.useDynamicUrlParams && dlp.stage ? dlp.stage : config.assetStage // const accountId = assetStage === 'alpha' ? 'A1VPTJKREFJWX5' : config.accountId // return { ...config, venueId, assetStage, accountId } // } // return config // } exports.buildStructures = buildStructures; exports.createFetchJson = createFetchJson; exports.createFetchText = createFetchText; exports.getVenueDataFromUrls = getVenueDataFromUrls; exports.normalizeCoords = normalizeCoords;