// A map that shows multiple single point locations. // Points have additional detail popups. // The background layer is proxied from Arcgis class MapProxiedArcgisTile extends HTMLElement { static observedAttributes = ["latitude", "longitude"]; constructor() { super(); // Create a shadow DOM this.attachShadow({ mode: "open" }); // Initial render this.render(); // Keep track of any 'on' calls to add to the map as soon as we create it. this._preOns = []; this._map = null; this._markers = []; } attributeChangedCallback(name, old_value, new_value) { //console.log("map-arcgis-tile: attribute changed", name, old_value, new_value); if ((name == "latitude" || name == "longitude") && this._map != null) { const latitude = parseFloat(this.getAttribute("latitude")); const longitude = parseFloat(this.getAttribute("longitude")); this._map.jumpTo({ center: [longitude, latitude], zoom: 19, }); } } // Lifecycle: when element is added to the DOM connectedCallback() { // Initialize the map when the element is added to the DOM setTimeout(() => this._initializeMap(), 0); } disconnectedCallback() { if (this._map) { this._map.remove(); } } _initializeMap() { const latitude = parseFloat(this.getAttribute("latitude")); const longitude = parseFloat(this.getAttribute("longitude")); const organization_id = Number(this.getAttribute("organization-id") || 0); const tegola = this.getAttribute("tegola"); const url_tiles = this.getAttribute("url-tiles"); const mapElement = this.shadowRoot.querySelector("#map"); this._map = new maplibregl.Map({ center: [longitude, latitude], container: mapElement, style: "https://tiles.stadiamaps.com/styles/osm_bright.json", zoom: 19, }); this._map.on("load", () => { if (organization_id != 0) { this._map.addSource("tegola", { type: "vector", tiles: [ `${tegola}maps/nidus/{z}/{x}/{y}?id=${organization_id}&organization_id=${organization_id}`, ], }); this._map.addLayer({ id: "service-area", source: "tegola", "source-layer": "service-area-bounds", type: "line", paint: { "line-color": "#f00", }, }); } this._map.addSource("flyover", { type: "raster", tiles: [url_tiles], }); this._map.addLayer({ id: "flyover-layer", source: "flyover", type: "raster", }); this.dispatchEvent(new CustomEvent("load"), { bubbles: true, composed: true, // Allows event to cross shadow DOM boundary detail: { map: this, }, }); this._map.on("click", (e) => { this.dispatchEvent( new CustomEvent("map-click", { bubbles: true, composed: true, detail: { lng: e.lngLat.lng, lat: e.lngLat.lat, map: this, point: e.point, }, }), ); }); }); for (const on of this._preOns) { this._map.on(...on); } } // Initial render of component render() { this.shadowRoot.innerHTML = `
`; } addLayer(a) { return this._map.addLayer(a); } addSource(a, b) { return this._map.addSource(a, b); } jumpTo(args) { return this._map.jumpTo(args); } on(...args) { if (this._map != null) { return this._map.on(...args); } else { this._preOns.push(args); } } once(a, b) { return this._map.once(a, b); } queryRenderedFeatures(a) { return this._map.queryRenderedFeatures(a); } FitBounds(bounds, options) { return this._map.fitBounds(bounds, options); } SetLayoutProperty(layout, property, value) { return this._map.setLayoutProperty(layout, property, value); } SetMarkers(markers) { console.log("Setting map markers", markers); this._markers.forEach((marker) => marker.remove()); this._markers = markers; for (let m of markers) { m.addTo(this._map); } } } customElements.define("map-proxied-arcgis-tile", MapProxiedArcgisTile);