var map = null; // A map that can be used to locate a single point by setting its location explicitly // or by allowing the user to move a marker. class MapAggregate extends HTMLElement { constructor() { super(); // Create a shadow DOM this.attachShadow({ mode: "open" }); this._markers = []; // Initial render this.render(); } // 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(); } } // Lifecycle: watch these attributes for changes static get observedAttributes() { return [ "api-key", "latitude", "longitude", "organization-id", "tegola", "zoom", ]; } // Lifecycle: respond to attribute changes attributeChangedCallback(name, oldValue, newValue) { // Only handle if map exists and values actually changed if (!this._map || oldValue === newValue) return; if (name === "api-key") { this._apiKey = newValue; } if (name === "latitude" || name === "longitude") { if (this.hasAttribute("latitude") && this.hasAttribute("longitude")) { const lat = Number(this.getAttribute("latitude")); const lng = Number(this.getAttribute("longitude")); this._map.setCenter([lat, lng]); } } if (name === "organization-id") { this._organizationID = newValue; } if (name === "tegola") { this._tegola = newValue; } if (name === "zoom") { this._map.setZoom(Number(newValue)); } } _initializeMap() { const apiKey = this.getAttribute("api-key"); const lat = Number(this.getAttribute("latitude") || 36.2); const lng = Number(this.getAttribute("longitude") || -119.2); const organization_id = Number(this.getAttribute("organization-id") || 0); const tegola = this.getAttribute("tegola"); const zoom = Number(this.getAttribute("zoom") || 15); mapboxgl.accessToken = apiKey; const mapElement = this.shadowRoot.querySelector("#map"); this._map = new mapboxgl.Map({ container: mapElement, center: { lat: lat, lng: lng, }, style: "mapbox://styles/mapbox/streets-v12", // style URL zoom: zoom, }); this._map.on("load", () => { this._map.addSource("tegola", { type: "vector", tiles: [ `${tegola}maps/nidus/{z}/{x}/{y}?organization_id=${organization_id}`, ], }); this._map.addInteraction("nidus-mouseenter-interaction", { type: "mouseenter", target: { layerId: "nidus" }, handler: () => { map.getCanvas().style.cursor = "pointer"; }, }); this._map.addInteraction("nidus-mouseleave-interaction", { type: "mouseleave", target: { layerId: "nidus" }, handler: () => { map.getCanvas().style.cursor = ""; }, }); this._map.addLayer({ id: "mosquito_source", type: "fill", filter: [ "==", ["zoom"], ["+", 2, ["to-number", ["get", "resolution"]]], ], source: "tegola", "source-layer": "mosquito_source", paint: { "fill-opacity": 0.4, "fill-color": "#dc3545", }, }); this._map.addLayer({ id: "service_request", type: "fill", filter: [ "==", ["zoom"], ["+", 2, ["to-number", ["get", "resolution"]]], ], source: "tegola", "source-layer": "service_request", paint: { "fill-opacity": 0.4, "fill-color": "#ffc107", }, }); this._map.addLayer({ id: "trap", type: "fill", filter: [ "==", ["zoom"], ["+", 2, ["to-number", ["get", "resolution"]]], ], source: "tegola", "source-layer": "trap", paint: { "fill-opacity": 0.4, "fill-color": "#0dcaf0", }, }); this._map.addInteraction("tegola-click-mosquito-source", { type: "click", target: { layerId: "mosquito_source" }, handler: (e) => { const coordinates = e.feature.geometry.coordinates.slice(); const properties = e.feature.properties; this.dispatchEvent( new CustomEvent("cell-click", { bubbles: true, composed: true, // Allows event to cross shadow DOM boundary detail: { cell: properties.cell, }, }), ); }, }); this._map.addInteraction("tegola-click-service-request", { type: "click", target: { layerId: "service_request" }, handler: (e) => { const coordinates = e.feature.geometry.coordinates.slice(); const properties = e.feature.properties; this.dispatchEvent( new CustomEvent("cell-click", { bubbles: true, composed: true, // Allows event to cross shadow DOM boundary detail: { cell: properties.cell, }, }), ); }, }); this._map.addInteraction("tegola-click-trap", { type: "click", target: { layerId: "trap" }, handler: (e) => { const coordinates = e.feature.geometry.coordinates.slice(); const properties = e.feature.properties; this.dispatchEvent( new CustomEvent("cell-click", { bubbles: true, composed: true, // Allows event to cross shadow DOM boundary detail: { cell: properties.cell, }, }), ); }, }); }); } // Initial render of component render() { this.shadowRoot.innerHTML = `