const map = new maplibregl.Map({ style: 'https://tiles.openfreemap.org/styles/liberty', center: [12.55, 55.67], zoom: 11, container: 'map', }); const cluster_max_zoom = 14; const map_element = document.getElementById('map'); fetch('static/js/data.json') .then(response => response.json()) .then(data => { const markersData = data; var ul = document.getElementById('links'); // Convert your data to a GeoJSON format const geojson = { type: 'FeatureCollection', features: Object.values(markersData).map(marker => ({ type: 'Feature', geometry: { type: 'Point', coordinates: [marker.lng, marker.lat], // Ensure lng and lat match your data attributes }, properties: { image: marker.image, message: marker.message, } })), }; // Add the GeoJSON data as a source map.on('load', () => { map.addSource('markers', { type: 'geojson', data: geojson, cluster: true, clusterMaxZoom: cluster_max_zoom, // Max zoom to cluster points on clusterRadius: 50, // Radius of each cluster when clustering points }); // Layer for clusters map.addLayer({ id: 'clusters', type: 'circle', source: 'markers', filter: ['has', 'point_count'], paint: { 'circle-color': [ 'step', ['get', 'point_count'], '#51bbd6', // Color for circle based on count 10, '#f1f075', 30, '#f28cb1', 200, '#a275ef', ], 'circle-radius': [ 'step', ['get', 'point_count'], 20, /* Size */ 20, /* Count */ 25, /* Size */ 200, /* Count */ 30, /* Size */ ] } }); // Layer for cluster count map.addLayer({ id: 'cluster-count', type: 'symbol', source: 'markers', filter: ['has', 'point_count'], layout: { 'text-field': '{point_count_abbreviated}', 'text-font': ['Noto Sans Regular'], 'text-size': 12, } }); // Layer for individual unclustered points map.addLayer({ id: 'unclustered-point', type: 'circle', source: 'markers', filter: ['!', ['has', 'point_count']], paint: { 'circle-color': '#11b4da', 'circle-radius': 4, 'circle-stroke-width': 1, 'circle-stroke-color': '#fff' } }); // Create HTML markers for individual points Object.entries(markersData).forEach(([name, markerData]) => { // Create a custom DOM element for the marker const markerElement = document.createElement('div'); markerElement.className = 'marker'; const markerImgElement = document.createElement('img'); markerImgElement.classList.add('marker-img', 'marker-img-hoverable'); markerImgElement.src = `/static/thumbnail/${markerData.image}`; // Image path markerImgElement.alt = name; // Set alt text for accessibility markerImgElement.addEventListener("click", (ev) => { close_all_markers([ev.target]); open_marker(ev.target) }); markerElement.appendChild(markerImgElement); // Add marker to map const markerInstance = new maplibregl.Marker({ element: markerElement }) .setLngLat([markerData.lng, markerData.lat]) // Manage visibility of custom markers based on zoom level map.on('zoom', () => { const zoomLevel = map.getZoom(); if (zoomLevel > cluster_max_zoom) { markerInstance.addTo(map); // Show custom markers } else { markerInstance.remove(); // Hide custom markers } }); add_entry_to_list(name, markerInstance); }); // Cluster click events map.on('click', 'clusters', async (e) => { const features = map.queryRenderedFeatures(e.point, { layers: ['clusters'] }); const clusterId = features[0].properties.cluster_id; const zoom = await map.getSource('markers').getClusterExpansionZoom(clusterId); map.easeTo({ center: features[0].geometry.coordinates, zoom: zoom }); }); // Unclustered point click events map.on('click', 'unclustered-point', (e) => { const coordinates = e.features[0].geometry.coordinates.slice(); const message = e.features[0].properties.message; /*new maplibregl.Popup() .setLngLat(coordinates) .setHTML(message) .addTo(map);*/ }); // Change cursor style on mouse enter/leave map.on('mouseenter', 'clusters', () => { map.getCanvas().style.cursor = 'pointer'; }); map.on('mouseleave', 'clusters', () => { map.getCanvas().style.cursor = ''; }); // Close markers when zooming out map.on('wheel', 'clusters', () => { close_all_markers(); }); // Close all markers when clicking the map map.on('click', (e) => { console.log(e.originalEvent.target.classList) if (!e.originalEvent.target.classList.contains('marker-img')) { close_all_markers(); } }); }); }); /* Image resizer hack * The marker image elements are placed by map libre, modifying it's height will move it, * hence we add a style sheet with javascript to set max dimensions */ const image_size = 0.75; function set_image_size(){ marker_img_size_stylesheet.replaceSync( `.marker-img-open { max-height: ${map_element.offsetHeight * image_size}px; max-width: ${map_element.offsetWidth * image_size}px; }`) } const marker_img_size_stylesheet = new CSSStyleSheet() document.adoptedStyleSheets = [marker_img_size_stylesheet]; set_image_size() addEventListener("resize", (event) => { set_image_size(); })