From 9e96b45e58fe1dee8317ec1d0071d14aee32861b Mon Sep 17 00:00:00 2001 From: Luke Stanley Date: Tue, 13 Aug 2024 11:21:49 +0100 Subject: [PATCH] Map country areas and makes map points optional if country is used --- .../blocks/map-block/map-block.component.ts | 90 ++++++++++++++++--- 1 file changed, 80 insertions(+), 10 deletions(-) diff --git a/src/app/blocks/map-block/map-block.component.ts b/src/app/blocks/map-block/map-block.component.ts index 466065f81..4de309ede 100644 --- a/src/app/blocks/map-block/map-block.component.ts +++ b/src/app/blocks/map-block/map-block.component.ts @@ -1,7 +1,8 @@ import {Component} from '@angular/core'; import {BaseBlockComponent} from '../base-block/base-block.component'; -import {icon, latLng, marker, tileLayer} from 'leaflet'; +import {icon, latLng, marker, tileLayer, divIcon, geoJSON} from 'leaflet'; import {get, isArray, set} from 'lodash-es'; +import * as DOMPurify from 'dompurify'; @Component({ selector: 'app-map-block', @@ -28,23 +29,92 @@ contributors, CC-BY-SA markerClusterData = []; markerClusterOptions = {}; + // Country GeoJSON provider base URL + countryProviderBaseURL = 'https://raw.githubusercontent.com/AshKyd/geojson-regions/main/public/countries/50m/'; + countryExtension = '.geojson'; + + // Country area polygon styling + countryStyle = { + color: 'gray', + weight: 2, + opacity: 0.3, + fillColor: 'gray', + fillOpacity: 0.5 + }; + + // In-memory cache for country GeoJSON data + countryCache: { [key: string]: any } = {}; + onConfigUpdate(config: any) { this.height = get(config, 'height', 500); set(this.options, 'zoom', get(config, 'zoom', 8)); const latln = get(config, 'latlng', [51.505, -0.09]); set(this.options, 'center', latLng(latln[0], latln[1])); + + // Allow setting custom country provider base URL + this.countryProviderBaseURL = get(config, 'countryProviderBaseURL', this.countryProviderBaseURL); + // Allow setting custom country extension + this.countryExtension = get(config, 'countryExtension', this.countryExtension); + + // Update country style from config + this.countryStyle = { + ...this.countryStyle, + ...get(config, 'countryStyle', {}) + }; + } + + private fetchGeoJsonData(countryCode: string): Promise { + if (this.countryCache[countryCode]) { + return Promise.resolve(this.countryCache[countryCode]); + } else { + const geoJsonFilename = `${countryCode}${this.countryExtension}`; + return fetch(`${this.countryProviderBaseURL}${geoJsonFilename}`) + .then(response => response.json()) + .then(geojsonData => { + this.countryCache[countryCode] = geojsonData; // Cache the data + return geojsonData; + }); + } } onData(data: any, firstChange: boolean) { if (isArray(data)) { - this.layers = data.map(({ lat, long, label }) => marker(latLng(lat, long), { - icon: icon({ - iconSize: [ 25, 41 ], - iconAnchor: [ 13, 41 ], - iconUrl: '/assets/marker-icon.png', - shadowUrl: '/assets/marker-shadow.png' - }) - }).bindPopup(label)); + this.layers = []; + + data.forEach(({ lat, long, label, customPin, country }) => { + let markerIcon; + if (customPin) { + const sanitizedHtml = DOMPurify.sanitize(customPin); + markerIcon = divIcon({ + html: `
${sanitizedHtml}
`, + iconSize: [25, 41], + className: 'custom-marker' // avoid the ugly default marker class + }); + } else { + markerIcon = icon({ + iconSize: [25, 41], + iconAnchor: [13, 41], + iconUrl: '/assets/marker-icon.png', + shadowUrl: '/assets/marker-shadow.png' + }); + } + + if (lat && long) { + this.layers.push( + marker(latLng(lat, long), { icon: markerIcon }).bindPopup(label) + ); + } + + if (country) { + const countryCode = country.toUpperCase(); + this.fetchGeoJsonData(countryCode).then(geojsonData => { + const geojsonLayer = geoJSON(geojsonData, { + style: this.countryStyle, + }); + this.layers.push(geojsonLayer); + }); + } + }); } } -} +} \ No newline at end of file