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" }); // 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"); map = new mapboxgl.Map({ container: mapElement, center: { lat: lat, lng: lng, }, style: 'mapbox://styles/mapbox/streets-v12', // style URL zoom: zoom, }); map.on("load", () => { map.addSource('tegola', { 'type': 'vector', 'tiles': [ `https://${tegola}/maps/nidus/{z}/{x}/{y}?organization_id=${organization_id}` ] }); map.addInteraction('nidus-mouseenter-interaction', { type: 'mouseenter', target: { layerId: 'nidus' }, handler: () => { map.getCanvas().style.cursor = 'pointer'; } }); map.addInteraction('nidus-mouseleave-interaction', { type: 'mouseleave', target: { layerId: 'nidus' }, handler: () => { map.getCanvas().style.cursor = ''; } }); 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' } }); 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' } }); 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' } }); 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 } })); } }); 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 } })); } }); 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 = `