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