@panoramax/web-viewer
Version:
Panoramax web viewer for geolocated pictures
146 lines (129 loc) • 5.12 kB
JavaScript
import Basic from "./Basic";
import Map from "../ui/Map";
import { getUserLayerId } from "../../utils/map";
import { NavigationControl } from "!maplibre-gl"; // DO NOT REMOVE THE "!": bundled builds breaks otherwise !!!
import "./CoverageMap.css";
import { default as InitParameters, alterMapState } from "../../utils/InitParameters";
/**
* Coverage Map is a basic map showing Panoramax coverage.
*
* Make sure to set width/height through CSS for proper display.
* @class Panoramax.components.core.CoverageMap
* @element pnx-coverage-map
* @extends Panoramax.components.core.Basic
* @fires Panoramax.components.core.Basic#select
* @fires Panoramax.components.core.Basic#ready
* @fires Panoramax.components.core.Basic#broken
* @property {Panoramax.components.ui.Loader} loader The loader screen
* @property {Panoramax.utils.API} api The API manager
* @property {Panoramax.components.ui.Map} map The MapLibre GL map itself
* @example
* ```html
* <pnx-coverage-map
* endpoint="https://panoramax.openstreetmap.fr/"
* map-options='{"bounds": [[-73.9876, 40.7661], [-73.9397, 40.8002]]}'
* style="width: 300px; height: 250px"
* />
* ```
*/
export default class CoverageMap extends Basic {
/**
* Component properties.
* @memberof Panoramax.components.core.CoverageMap#
* @type {Object}
* @mixes Panoramax.components.core.Basic#properties
* @property {string} endpoint URL to API to use (must be a [STAC API](https://github.com/radiantearth/stac-api-spec/blob/main/overview.md))
* @property {string} [picture] The picture ID to display
* @property {string} [sequence] The sequence ID of the picture displayed
* @property {object} [fetch-options] Set custom options for fetch calls made against API ([same syntax as fetch options parameter](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters))
* @property {string[]} [users=[geovisio]] List of users IDs to use for map display (defaults to general map, identified as "geovisio")
* @property {string|object} [map-style] The map's MapLibre style. This can be an a JSON object conforming to the schema described in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/), or a URL string pointing to one. Defaults to OSM vector tiles.
* @property {string} [lang] To override language used for labels. Defaults to using user's preferred languages.
* @property {object} [map-options] [Any map option available in Map class](#Panoramax.components.ui.Map).<br />Example: `map-options='{"bounds": [[-73.9876, 40.7661], [-73.9397, 40.8002]]}'`
*/
static properties = {
"map-options": {converter: Basic.GetJSONConverter()},
...Basic.properties
};
constructor() {
super();
this._mapContainer = document.createElement("div");
this.onceAPIReady().then(() => {
this.loader.setAttribute("value", 30);
this._initParams = new InitParameters(InitParameters.GetComponentProperties(CoverageMap, this));
this._initMap();
});
}
/** @private */
disconnectedCallback() {
super.disconnectedCallback();
this.map?.destroy();
}
getClassName() {
return "CoverageMap";
}
onceReady() {
if(this.map && this.map.waitForEnoughMapLoaded) {
return this.map.waitForEnoughMapLoaded();
}
else {
return new Promise(resolve => setTimeout(resolve, 100)).then(this.onceReady.bind(this));
}
}
/** @private */
render() {
return [this.loader, this._mapContainer];
}
getSubComponentsNames() {
const scn = super.getSubComponentsNames();
scn.push("map");
return scn;
}
/**
* Creates map object
* @private
*/
_initMap() {
// Override to avoid display of pictures symbols
class MyMap extends Map {
_getLayerStyleProperties(layer) {
if(layer === "pictures_symbols") {
return { layout: { visibility: "none" } };
}
else {
return super._getLayerStyleProperties(layer);
}
}
}
this.map = new MyMap(this, this._mapContainer, Object.assign({}, this._initParams.getMapInit(), { hash: true }));
this.map.addControl(new NavigationControl({ showCompass: false }));
this.loader.setAttribute("value", 70);
this.addEventListener("select", this._onSelect.bind(this));
this.map.on("picture-click", e => this.select(e.seqId, e.picId));
this.map.on("sequence-click", e => this.select(e.seqId));
this.map.waitForEnoughMapLoaded().then(async () => {
await alterMapState(this.map, this._initParams.getMapPostInit());
this.map.reloadLayersStyles();
this.loader.dismiss();
});
}
/**
* Select event handler
* @private
* @param {object} e Event details
*/
_onSelect(e) {
// Move thumbnail to match selected element
if(e.detail.picId || e.detail.seqId) {
const layer = e.detail.picId ? "pictures" : "sequences";
const features = this.map.queryRenderedFeatures({
layers: [...this.map._userLayers].map(l => getUserLayerId(l, layer)),
filter: ["==", ["get", "id"], e.detail.picId || e.detail.seqId]
});
if(features.length >= 0 && features[0] != null) {
this.map._attachPreviewToPictures({ features }, layer);
}
}
}
}
customElements.define("pnx-coverage-map", CoverageMap);