From 1e5a8f7ee798dc852ff811ba0e2cbec6f8fa546b Mon Sep 17 00:00:00 2001 From: Jose Date: Fri, 30 May 2025 20:53:02 +0200 Subject: [PATCH] Enhance PollutionMap component with Voronoi layer toggle; add GeoJSON data for visualization --- frontend/public/images/voro.png | Bin 0 -> 1918 bytes .../public/voronoi_sevilla_geovoronoi.geojson | 727 ++++++++++++++++++ frontend/src/components/PollutionMap.jsx | 104 ++- 3 files changed, 824 insertions(+), 7 deletions(-) create mode 100644 frontend/public/images/voro.png create mode 100644 frontend/public/voronoi_sevilla_geovoronoi.geojson diff --git a/frontend/public/images/voro.png b/frontend/public/images/voro.png new file mode 100644 index 0000000000000000000000000000000000000000..3231ed72d7a20fc359dd9cb34fcce1545dbf125e GIT binary patch literal 1918 zcmX|?dpr|*AIHZNxy8t(M`%Vjm)&SLwsWwCv*xnM$VtP>9vd@bvzTzGQPCt_&?Dry zj49XDQ?6ZVQ|oo1;*=uTse`)uoMFEhnT9ufup!wHWmuy@purP6C{>% z916!^Fi;o*iajrrbHBC=%$=x5=k^M`ceZpWmAdsxCZQ=l?O1%t?emBalK4ceI zQ|%)P`PrLsMJ7_PnGcrG%Q8iYi}F>xeh$GJV%e>p_-$dmCRdnRtjO?vm+CsXT(Qzg zgK37E)r1~EZXTW-s$O|JG-75jhHF%|oantaGNKozC?MI_Zhh$bXl~D5M%>9O!z-ex zrg3@y6u$M|Ze>2lSW$zsY0+ydG1Izlm-%QKC^?3OCrtOeJ+YPEPsR(!pUlaAN%;Kd z2$B9T$=K>mptzCpsWtl|vg%F6dw!t9!2lZ(`WL`sv5C^_B${{$4TA+G#y3m!~>< zZq4ueGh-KW0?SRpd++r(f*iW)Tb~&h<_Ys>ybcQCPhG5Ft;;91I?5C|jiau#bR~2gIml7?evsTeOB`DTeJ*&PBCSaVeFDZKaI!Cdo>+5|eY!dZIYd=#iiFC5tU$4LN zW?)=akoanbPoY^OSG{%7y?OP>_tit9g08tm%^SU0N%EZ0q{z6EvN02VlN0E}kHv~n zJ=^e5hr+;Y{MfXCeWk2)d*~wS?zo|zWQ-vZhMrFSoMxi#Iex$0KH*rCXH4?RSmQ%q z4plv_Kfa1Vyy)X%G%%;0Hg_xp;U`{6I~%V>3n=T~5+lZ(^b$UW`tPoynw_Nvd8kv* z?kp4K)wd>4NfSGredlj?j-0x|7b?Nm6x9}O%hsdU%?A;gWkX@VQS2?Z3Zb{qoKn{1 zAq~?5;wyo+nY(PljZwnK8F%5WrXlL?8Z5IHxnExQ2mdG4FfW%w`{m$L;Eg>jM`njIs-Ix|$M(shQyF*t z^GlRgltj&%n+pl!(*^=6F`(Em@U`_6g_~b=W`JAResuO}qJ!?~w5w{S`0T1CjTvWZ zL`R%?r++2WqW&K*mU1HI^R2({h9TbIi`=dJx} zruYM0+Z|XOCwsOm^Ga8f5AJA>tQFY*Gni63lT4{4*3`X_9I!A&_CkN_xXf!V;;$U% z&B?8_#3NRuW`I0vLSE?FZ`mhZX`oNfx2b1Xxu5JYB3koUYflJ4Sn{?OmyFKF=RPq5 z=#hgy+!Pe}d-A`DN`rHiC8F#?p_K;Yoc)scVyS=LEqzZj(@Uc%1w}q-h5=pX+uJpF zA+CPSg$9$DgyXp>sotEFHSDpl z^O7l73aPfKoSW_`Xx{PON!!lN#CL78A7FqO)v{B)0I<4mq$Yg<0H@J73F11&uZ1V-~GvasDgj{;}b6Y0B@^Gi%;eAgm-d!Bhzr}-;w!n=`oGiHweJjdif;gho^O1c-urzBb?!&g{|5lX BYW4sC literal 0 HcmV?d00001 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,