diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 75094a8..d4eaa31 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,13 +14,15 @@ "leaflet": "^1.9.4", "leaflet-easybutton": "^2.4.0", "leaflet-iconmaterial": "^1.1.0", + "leaflet.markercluster": "^1.5.3", + "papaparse": "^5.4.1", "pinia": "^2.1.6", "swagger-ui": "^5.10.5", "vee-validate": "^4.13.2", "vite-plugin-vuetify": "^1.0.2", "vue": "^3.3.4", "vue-router": "^4.2.4", - "vuetify": "^3.3.21" + "vuetify": "^3.7.4" }, "devDependencies": { "@mdi/font": "^7.3.67", @@ -3832,6 +3834,15 @@ "integrity": "sha512-VnP6uAyc6GLxLKQSpycIzXbJd2kJWfAOz9WWbJD3AsXnG8AEJ2AoUva8GZPjgtkr9eNpaifdyGpVHr5hYMNAQA==", "license": "MIT" }, + "node_modules/leaflet.markercluster": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.5.3.tgz", + "integrity": "sha512-vPTw/Bndq7eQHjLBVlWpnGeLa3t+3zGiuM7fJwCkiMFq+nmRuG3RI3f7f4N4TDX7T4NpbAXpR2+NTRSEGfCSeA==", + "license": "MIT", + "peerDependencies": { + "leaflet": "^1.3.1" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4414,6 +4425,12 @@ "node": ">=6" } }, + "node_modules/papaparse": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", + "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==", + "license": "MIT" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6300,9 +6317,10 @@ } }, "node_modules/vuetify": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.4.9.tgz", - "integrity": "sha512-pgBPdbgrHHHZWRybWevzRFezMax6CP2MccTivjOZSOF0XsnzoNOJGGpkTgIfBrk4UCp9jKx6JOJIztGtx/IcSw==", + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.6.tgz", + "integrity": "sha512-lol0Va5HtMIqZfjccSD5DLv5v31R/asJXzc6s7ULy51PHr1DjXxWylZejhq0kVpMGW64MiV1FmA/p8eYQfOWfQ==", + "license": "MIT", "engines": { "node": "^12.20 || >=14.13" }, @@ -6312,10 +6330,9 @@ }, "peerDependencies": { "typescript": ">=4.7", - "vite-plugin-vuetify": ">=1.0.0-alpha.12", + "vite-plugin-vuetify": ">=1.0.0", "vue": "^3.3.0", - "vue-i18n": "^9.0.0", - "webpack-plugin-vuetify": ">=2.0.0-alpha.11" + "webpack-plugin-vuetify": ">=2.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -6324,9 +6341,6 @@ "vite-plugin-vuetify": { "optional": true }, - "vue-i18n": { - "optional": true - }, "webpack-plugin-vuetify": { "optional": true } diff --git a/frontend/package.json b/frontend/package.json index 7d97fec..7193692 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,6 +20,7 @@ "leaflet": "^1.9.4", "leaflet-easybutton": "^2.4.0", "leaflet-iconmaterial": "^1.1.0", + "leaflet.markercluster": "^1.5.3", "pinia": "^2.1.6", "swagger-ui": "^5.10.5", "vee-validate": "^4.13.2", diff --git a/frontend/src/stores/map.js b/frontend/src/stores/map.js index 9844959..5ae57d9 100644 --- a/frontend/src/stores/map.js +++ b/frontend/src/stores/map.js @@ -3,6 +3,7 @@ import { ref } from 'vue' import { ENDPOINTS } from '@/constants' import L from 'leaflet' import 'leaflet-iconmaterial/dist/leaflet.icon-material' +import 'leaflet.markercluster'; export const useMapStore = defineStore('map', () => { const leaflet = ref(null) @@ -10,6 +11,34 @@ export const useMapStore = defineStore('map', () => { const modelFeatures = ref({}) const perceptualModelsGeojson = ref([]) const mapLoaded = ref(false) + const markerClusterGroup = L.markerClusterGroup({ + iconCreateFunction: (cluster) => { + const childCount = cluster.getChildCount(); + + let color = 'blue'; + if (childCount > 10) { + color = 'red'; + } + + return L.divIcon({ + html: ` +
+ ${childCount} +
`, + className: 'custom-cluster-icon', + iconSize: [40, 40] + }); + }, + }); function onEachFeature(feature, layer) { let content = `

Perceptual model of ${feature.properties.location.long_name}

` @@ -117,7 +146,8 @@ export const useMapStore = defineStore('map', () => { }, pointToLayer: pointToLayer }) - layerGroup.value.addLayer(modelFeatures.value) + markerClusterGroup.addLayer(modelFeatures.value); // Add features to the cluster group + layerGroup.value.addLayer(markerClusterGroup); } function filterFeatures(filterFunction) { @@ -135,7 +165,8 @@ export const useMapStore = defineStore('map', () => { pointToLayer: pointToLayer }) // add filtered features - layerGroup.value.addLayer(modelFeatures.value) + markerClusterGroup.addLayer(modelFeatures.value); + layerGroup.value.addLayer(markerClusterGroup); } function resetFilter() { @@ -145,7 +176,8 @@ export const useMapStore = defineStore('map', () => { onEachFeature(feature, layer) } }) - layerGroup.value.addLayer(modelFeatures.value) + markerClusterGroup.addLayer(modelFeatures.value); + layerGroup.value.addLayer(markerClusterGroup); } return {