1
0

Enhance PollutionMap component with Voronoi layer toggle; add GeoJSON data for visualization

This commit is contained in:
Jose
2025-05-30 20:53:02 +02:00
parent fc130cc92d
commit 1e5a8f7ee7
3 changed files with 824 additions and 7 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -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
]
]
]
}
}
]
}

View File

@@ -8,7 +8,7 @@ import { useDataContext } from '@/hooks/useDataContext';
import L from "leaflet"; import L from "leaflet";
import "leaflet.heat"; import "leaflet.heat";
import { useEffect } from 'react'; import { useEffect, useState, useRef } from 'react';
const PollutionMap = ({ groupId, deviceId }) => { const PollutionMap = ({ groupId, deviceId }) => {
const { config, configLoading, configError } = useConfig(); const { config, configLoading, configError } = useConfig();
@@ -39,13 +39,16 @@ const PollutionMapContent = () => {
const { config, configLoading, configError } = useConfig(); const { config, configLoading, configError } = useConfig();
const { data, dataLoading, dataError } = useDataContext(); 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(() => { useEffect(() => {
if (!data || !config) return; if (!data || !config) return;
const isToday = (timestamp) => { const isToday = (timestamp) => {
const today = new Date(); const today = new Date();
const date = new Date(timestamp * 1000); // si viene en segundos const date = new Date(timestamp * 1000);
return ( return (
today.getFullYear() === date.getFullYear() && today.getFullYear() === date.getFullYear() &&
today.getMonth() === date.getMonth() && today.getMonth() === date.getMonth() &&
@@ -53,19 +56,55 @@ const PollutionMapContent = () => {
); );
}; };
const mapContainer = document.getElementById("map"); const mapContainer = document.getElementById("map");
if (!mapContainer) return; 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 SEVILLA = config.userConfig.city;
const map = L.map(mapContainer).setView(SEVILLA, 12); const map = L.map(mapContainer).setView(SEVILLA, 12);
mapRef.current = map;
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: attribution:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map); }).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 const points = data
.filter(p => isToday(p.timestamp)) .filter(p => isToday(p.timestamp))
.map(p => [p.lat, p.lon, p.carbonMonoxide]); .map(p => [p.lat, p.lon, p.carbonMonoxide]);
@@ -77,18 +116,69 @@ const PollutionMapContent = () => {
}; };
}, [data, config]); }, [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 <p>Cargando configuración...</p>; if (configLoading) return <p>Cargando configuración...</p>;
if (configError) return <p>Error al cargar configuración: {configError}</p>; if (configError) return <p>Error al cargar configuración: {configError}</p>;
if (!config) return <p>Configuración no disponible.</p>; if (!config) return <p>Configuración no disponible.</p>;
if (dataLoading) return <p>Cargando datos...</p>; if (dataLoading) return <p>Cargando datos...</p>;
if (dataError) return <p>Error al cargar datos: {configError}</p>; if (dataError) return <p>Error al cargar datos: {dataError}</p>;
if (!data) return <p>Datos no disponibles.</p>; if (!data) return <p>Datos no disponibles.</p>;
return ( return (
<div id="map" className='rounded-4' style={{ height: "60vh" }}></div> <div style={{ position: "relative" }}>
<div id="map" className='rounded-4' style={{ height: "60vh" }}></div>
<div
style={{
position: "absolute",
top: "80px",
left: "10px",
zIndex: 1000,
border: "2px solid rgba(0,0,0,0.2)",
padding: "0",
cursor: "pointer",
backgroundColor: "transparent",
borderRadius: "4px",
backgroundClip: "padding-box",
}}
>
<button
onClick={toggleVoronoi}
style={{
zIndex: 1000,
width: "30px",
height: "30px",
padding: "0",
cursor: "pointer",
backgroundColor: "#ffffff",
borderRadius: "2px",
border: "none",
}}
onMouseOver={(e) => e.currentTarget.style.backgroundColor = "#f4f4f4"}
onMouseOut={(e) => e.currentTarget.style.backgroundColor = "#ffffff"}
>
<img src='/images/voro.png' width={30} height={30} />
</button>
</div>
</div>
); );
} };
PollutionMap.propTypes = { PollutionMap.propTypes = {
groupId: PropTypes.number.isRequired, groupId: PropTypes.number.isRequired,