diff --git a/frontend/public/images/voro.png b/frontend/public/images/voro.png new file mode 100644 index 0000000..3231ed7 Binary files /dev/null and b/frontend/public/images/voro.png differ diff --git a/frontend/public/voronoi_sevilla_geovoronoi.geojson b/frontend/public/voronoi_sevilla_geovoronoi.geojson new file mode 100644 index 0000000..e827652 --- /dev/null +++ b/frontend/public/voronoi_sevilla_geovoronoi.geojson @@ -0,0 +1,727 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "groupId": 3 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.975220574710803, + 37.358725234526325 + ], + [ + -5.956629501395188, + 37.34271453941203 + ], + [ + -5.90939488095618, + 37.339843612523126 + ], + [ + -5.927653856389463, + 37.35123031855536 + ], + [ + -5.9677328204994975, + 37.36945244319422 + ], + [ + -5.975220574710803, + 37.358725234526325 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 5 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.969424709766254, + 37.37478267183256 + ], + [ + -5.963298496465492, + 37.38284229005861 + ], + [ + -5.927653856389463, + 37.35123031855536 + ], + [ + -5.9677328204994975, + 37.36945244319422 + ], + [ + -5.969424709766254, + 37.37478267183256 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 16 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.9062693851557535, + 37.33903066950347 + ], + [ + -5.9402008847769965, + 37.39163172552388 + ], + [ + -5.939432400548112, + 37.41302637751862 + ], + [ + -5.85, + 37.42131204097132 + ], + [ + -5.85, + 37.30641403730114 + ], + [ + -5.9062693851557535, + 37.33903066950347 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 18 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.953484151391864, + 37.45 + ], + [ + -5.892547063477412, + 37.45 + ], + [ + -5.85, + 37.45 + ], + [ + -5.85, + 37.43338874148842 + ], + [ + -5.85, + 37.42131204097132 + ], + [ + -5.939432400548112, + 37.41302637751862 + ], + [ + -5.950157126973102, + 37.42313784097292 + ], + [ + -5.953484151391864, + 37.45 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 2 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -6.042673351034033, + 37.31235131332692 + ], + [ + -5.956629501395188, + 37.34271453941203 + ], + [ + -5.975220574710803, + 37.358725234526325 + ], + [ + -5.991667436508618, + 37.359093048817556 + ], + [ + -6.042673351034033, + 37.31235131332692 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 1 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -6.056335292289438, + 37.30350028295088 + ], + [ + -6.042673351034033, + 37.31235131332692 + ], + [ + -5.956629501395188, + 37.34271453941203 + ], + [ + -5.90939488095618, + 37.339843612523126 + ], + [ + -5.9062693851557535, + 37.33903066950347 + ], + [ + -5.85, + 37.30641403730114 + ], + [ + -5.85, + 37.3 + ], + [ + -6.061145744247712, + 37.3 + ], + [ + -6.056335292289438, + 37.30350028295088 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 6 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -6.006210657955016, + 37.39204750434759 + ], + [ + -5.999699040810209, + 37.38464226854157 + ], + [ + -6.056335292289438, + 37.30350028295088 + ], + [ + -6.061145744247712, + 37.3 + ], + [ + -6.1, + 37.3 + ], + [ + -6.1, + 37.39522072259649 + ], + [ + -6.006210657955016, + 37.39204750434759 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 17 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.996638048161243, + 37.42409467426387 + ], + [ + -6.011677116677642, + 37.45 + ], + [ + -5.953484151391864, + 37.45 + ], + [ + -5.950157126973102, + 37.42313784097292 + ], + [ + -5.965724477190091, + 37.41053204103079 + ], + [ + -5.996638048161243, + 37.42409467426387 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 13 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -6.1, + 37.416037697292964 + ], + [ + -6.1, + 37.45 + ], + [ + -6.057485769579995, + 37.45 + ], + [ + -6.011677116677642, + 37.45 + ], + [ + -5.996638048161243, + 37.42409467426387 + ], + [ + -5.9941297848131985, + 37.406630612305094 + ], + [ + -6.006210657955016, + 37.39204750434759 + ], + [ + -6.1, + 37.39522072259649 + ], + [ + -6.1, + 37.416037697292964 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 12 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.966630745640582, + 37.40714989057746 + ], + [ + -5.953473975522407, + 37.39083552675785 + ], + [ + -5.963175863543545, + 37.38403450765386 + ], + [ + -5.975285181668732, + 37.39565697942774 + ], + [ + -5.966630745640582, + 37.40714989057746 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 10 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.953473975522407, + 37.39083552675785 + ], + [ + -5.9402008847769965, + 37.39163172552388 + ], + [ + -5.9062693851557535, + 37.33903066950347 + ], + [ + -5.90939488095618, + 37.339843612523126 + ], + [ + -5.927653856389463, + 37.35123031855536 + ], + [ + -5.963298496465492, + 37.38284229005861 + ], + [ + -5.963175863543545, + 37.38403450765386 + ], + [ + -5.953473975522407, + 37.39083552675785 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 15 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.966630745640582, + 37.40714989057746 + ], + [ + -5.965724477190091, + 37.41053204103079 + ], + [ + -5.950157126973102, + 37.42313784097292 + ], + [ + -5.939432400548112, + 37.41302637751862 + ], + [ + -5.9402008847769965, + 37.39163172552388 + ], + [ + -5.953473975522407, + 37.39083552675785 + ], + [ + -5.966630745640582, + 37.40714989057746 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 14 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.966630745640582, + 37.40714989057746 + ], + [ + -5.965724477190091, + 37.41053204103079 + ], + [ + -5.996638048161243, + 37.42409467426387 + ], + [ + -5.9941297848131985, + 37.406630612305094 + ], + [ + -5.981314881430654, + 37.393898393584536 + ], + [ + -5.975285181668732, + 37.39565697942774 + ], + [ + -5.966630745640582, + 37.40714989057746 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 8 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.995965602200496, + 37.3834658156984 + ], + [ + -5.990194183397561, + 37.36441485314666 + ], + [ + -5.9792132087914, + 37.37663853816038 + ], + [ + -5.98303912784847, + 37.390051246464104 + ], + [ + -5.995965602200496, + 37.3834658156984 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 4 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.990194183397561, + 37.36441485314666 + ], + [ + -5.991667436508618, + 37.359093048817556 + ], + [ + -5.975220574710803, + 37.358725234526325 + ], + [ + -5.9677328204994975, + 37.36945244319422 + ], + [ + -5.969424709766254, + 37.37478267183256 + ], + [ + -5.9792132087914, + 37.37663853816038 + ], + [ + -5.990194183397561, + 37.36441485314666 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 9 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.98303912784847, + 37.390051246464104 + ], + [ + -5.981314881430654, + 37.393898393584536 + ], + [ + -5.975285181668732, + 37.39565697942774 + ], + [ + -5.963175863543545, + 37.38403450765386 + ], + [ + -5.963298496465492, + 37.38284229005861 + ], + [ + -5.969424709766254, + 37.37478267183256 + ], + [ + -5.9792132087914, + 37.37663853816038 + ], + [ + -5.98303912784847, + 37.390051246464104 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 7 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.995965602200496, + 37.3834658156984 + ], + [ + -5.999699040810209, + 37.38464226854157 + ], + [ + -6.056335292289438, + 37.30350028295088 + ], + [ + -6.042673351034033, + 37.31235131332692 + ], + [ + -5.991667436508618, + 37.359093048817556 + ], + [ + -5.990194183397561, + 37.36441485314666 + ], + [ + -5.995965602200496, + 37.3834658156984 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "groupId": 11 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.995965602200496, + 37.3834658156984 + ], + [ + -5.999699040810209, + 37.38464226854157 + ], + [ + -6.006210657955016, + 37.39204750434759 + ], + [ + -5.9941297848131985, + 37.406630612305094 + ], + [ + -5.981314881430654, + 37.393898393584536 + ], + [ + -5.98303912784847, + 37.390051246464104 + ], + [ + -5.995965602200496, + 37.3834658156984 + ] + ] + ] + } + } + ] +} \ No newline at end of file diff --git a/frontend/src/components/PollutionMap.jsx b/frontend/src/components/PollutionMap.jsx index 94a2815..73b3d3f 100644 --- a/frontend/src/components/PollutionMap.jsx +++ b/frontend/src/components/PollutionMap.jsx @@ -8,7 +8,7 @@ import { useDataContext } from '@/hooks/useDataContext'; import L from "leaflet"; import "leaflet.heat"; -import { useEffect } from 'react'; +import { useEffect, useState, useRef } from 'react'; const PollutionMap = ({ groupId, deviceId }) => { const { config, configLoading, configError } = useConfig(); @@ -39,13 +39,16 @@ const PollutionMapContent = () => { const { config, configLoading, configError } = useConfig(); const { data, dataLoading, dataError } = useDataContext(); + const mapRef = useRef(null); // Referencia al mapa + const voronoiLayerRef = useRef(null); // Referencia al layer de Voronoi + const [showVoronoi, setShowVoronoi] = useState(true); + useEffect(() => { if (!data || !config) return; const isToday = (timestamp) => { const today = new Date(); - const date = new Date(timestamp * 1000); // si viene en segundos - + const date = new Date(timestamp * 1000); return ( today.getFullYear() === date.getFullYear() && today.getMonth() === date.getMonth() && @@ -53,19 +56,55 @@ const PollutionMapContent = () => { ); }; - const mapContainer = document.getElementById("map"); if (!mapContainer) return; + const getFillColor = (feature) => { + const index = feature.properties.index || Math.floor(Math.random() * 10); + const colors = [ + "#FFCDD2", "#F8BBD0", "#E1BEE7", "#D1C4E9", + "#C5CAE9", "#B3E5FC", "#B2DFDB", "#DCEDC8", + "#FFF9C4", "#FFE0B2" + ]; + return colors[index % colors.length]; + }; + + const SEVILLA = config.userConfig.city; const map = L.map(mapContainer).setView(SEVILLA, 12); + mapRef.current = map; L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { attribution: '© OpenStreetMap contributors' }).addTo(map); + fetch("/voronoi_sevilla_geovoronoi.geojson") + .then(res => res.json()) + .then(geojson => { + const voronoiLayer = L.geoJSON(geojson, { + style: (feature) => ({ + color: "#007946", + weight: 1.0, + opacity: 0.8, + fillOpacity: 0.3, + fillColor: getFillColor(feature), + dashArray: '5, 5' + }) + + }); + + voronoiLayerRef.current = voronoiLayer; + + if (showVoronoi) { + voronoiLayer.addTo(map); + } + }) + .catch(err => { + console.error("Error cargando el GeoJSON:", err); + }); + const points = data .filter(p => isToday(p.timestamp)) .map(p => [p.lat, p.lon, p.carbonMonoxide]); @@ -77,18 +116,69 @@ const PollutionMapContent = () => { }; }, [data, config]); + // Esta parte gestiona el toggle del voronoi + const toggleVoronoi = () => { + const map = mapRef.current; + const voronoiLayer = voronoiLayerRef.current; + + if (!map || !voronoiLayer) return; + + if (map.hasLayer(voronoiLayer)) { + map.removeLayer(voronoiLayer); + setShowVoronoi(false); + } else { + voronoiLayer.addTo(map); + setShowVoronoi(true); + } + }; + if (configLoading) return

Cargando configuración...

; if (configError) return

Error al cargar configuración: {configError}

; if (!config) return

Configuración no disponible.

; if (dataLoading) return

Cargando datos...

; - if (dataError) return

Error al cargar datos: {configError}

; + if (dataError) return

Error al cargar datos: {dataError}

; if (!data) return

Datos no disponibles.

; return ( -
+
+
+
+ +
+
); -} +}; PollutionMap.propTypes = { groupId: PropTypes.number.isRequired,