@panoramax/web-viewer
Version:
Panoramax web viewer for geolocated pictures
138 lines (122 loc) • 4.32 kB
JavaScript
import { PanoramaxPresetsURL } from "./services";
// Iconify aliases for whole collections
const ICON_COLLECTIONS_SUB = {
"fas": "fa6-solid"
};
// Iconify aliases for specific icons
const ICON_SUBS = {
"fa6-solid:directions": "fa6-solid:diamond-turn-right"
};
/**
* Presets Manager handle retrieval of presets against dedicated endpoint.
* It allows an easily search between all presets, and handles translations.
*
* @class Panoramax.utils.PresetsManager
* @typicalname presetsManager
* @param {string} [lang] The user prefered language. Defaults to web browser preferences.
* @param {boolean} [skipLoad=false] Set to true to disable automatic load of API data
*/
export default class PresetManager {
constructor(lang = null, skipLoad = false) {
this._ready = false;
this._presets = null;
this._translations = {};
if(!skipLoad) { this._load(lang); }
}
/**
* Downloads various JSON file against presets API.
* @private
*/
async _load(lang) {
lang = lang || window.navigator.languages[0]?.substring(0, 2);
try {
const [translationsENRes, translationsLangRes, presetsRes] = await Promise.all([
fetch(`${PanoramaxPresetsURL()}/translations/en.min.json`),
lang ? fetch(`${PanoramaxPresetsURL()}/translations/${lang}.min.json`) : Promise.resolve({ok: true}),
fetch(`${PanoramaxPresetsURL()}/presets.min.json`),
]);
if (
!translationsENRes || !translationsLangRes || !presetsRes
|| !translationsENRes.ok || !translationsLangRes.ok || !presetsRes.ok
) {
this._ready = -1;
throw new Error("Presets service is not available");
}
this._presets = await presetsRes.json();
this._translations.en = (await translationsENRes.json())?.en?.presets;
if(lang) {
this._translations[lang] = (await translationsLangRes.json())?.[lang]?.presets;
}
// Post-process presets
Object.entries(this._presets).forEach(([pid,p]) => {
// Add labels
if(this._translations[lang]?.presets?.[pid]?.name) {
p.name = this._translations[lang].presets[pid].name;
}
else if(this._translations.en?.presets?.[pid]?.name) {
p.name = this._translations.en.presets[pid].name;
}
// Create iconify-compatible icon
if(p.icon) {
const iconColl = p.icon.split("-")[0];
const iconCollSub = ICON_COLLECTIONS_SUB[iconColl];
p.iconify = iconCollSub ? p.icon.replace(iconColl+"-", iconCollSub+":") : p.icon.split("-")[0]+":"+p.icon.split("-").slice(1).join("-");
if(ICON_SUBS[p.iconify]) { p.iconify = ICON_SUBS[p.iconify]; }
}
});
this._ready = true;
} catch (error) {
console.error("Presets service throws an error:", error);
this._ready = -1;
}
}
/**
* Resolves when the all necessary data was downloaded.
* @memberOf Panoramax.utils.PresetsManager#
* @returns {Promise}
*/
async onceReady() {
if(!this._ready) {
await new Promise(resolve => setTimeout(resolve, 250));
return this.onceReady();
}
else if(this._ready === -1) { return Promise.reject(); }
else { return Promise.resolve(); }
}
/**
* Find the best matching preset for a given STAC feature.
* @memberOf Panoramax.utils.PresetsManager#
* @returns {Promise}
* @fulfil {object|null} The preset JSON that best matches, or null if no matches
*/
async getPreset(f) {
return this.onceReady().then(() => {
const candidatePresets = Object.values(this._presets).filter(p => {
if(Object.keys(p.tags).length === 0) { return false; }
// Check if every tag on preset exists in feature
for(let pk in p.tags) {
const pv = p.tags[pk];
if(!f.semantics.find(s => s.key === pk && (pv === "*" || pv === s.value))) {
return false;
}
}
return true;
});
// Find best matching preset
candidatePresets.sort((a,b) => {
const nbTagsA = Object.keys(a.tags).length;
const nbTagsB = Object.keys(b.tags).length;
if(nbTagsA > nbTagsB) { return -1; }
else if(nbTagsA === nbTagsB) {
const nbTagsStarA = Object.values(a.tags).filter(v => v === "*").length;
const nbTagsStarB = Object.values(b.tags).filter(v => v === "*").length;
if(nbTagsStarA < nbTagsStarB) { return -1; }
else if(nbTagsStarA > nbTagsStarB) { return 1; }
else { return 0; }
}
else { return 1; }
});
return candidatePresets.shift();
});
}
}