diff --git a/package-lock.json b/package-lock.json index c48f9a5..6bb8971 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "bootstrap-icons": "^1.13.1", "js-cookie": "^3.0.5", "leaflet": "^1.9.4", + "leaflet-heatmap": "^1.0.0", "leaflet-velocity": "^2.1.4", "leaflet.heat": "^0.2.0" }, @@ -1078,6 +1079,11 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/heatmap.js": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/heatmap.js/-/heatmap.js-2.0.5.tgz", + "integrity": "sha512-CG2gYFP5Cv9IQCXEg3ZRxnJDyAilhWnQlAuHYGuWVzv6mFtQelS1bR9iN80IyDmFECbFPbg6I0LR5uAFHgCthw==" + }, "node_modules/import-meta-resolve": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", @@ -1118,6 +1124,15 @@ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" }, + "node_modules/leaflet-heatmap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/leaflet-heatmap/-/leaflet-heatmap-1.0.0.tgz", + "integrity": "sha512-WP/emZYwjWaEnWMcE2dftuJvtjp53zmJcHtVTHUqPN7AQEowHxDTLH5j1BJjE4uL1K5dJclBLX4oLpnOGS/qTw==", + "dependencies": { + "heatmap.js": "*", + "leaflet": "*" + } + }, "node_modules/leaflet-velocity": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/leaflet-velocity/-/leaflet-velocity-2.1.4.tgz", diff --git a/package.json b/package.json index 905bfba..cb7085e 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "bootstrap-icons": "^1.13.1", "js-cookie": "^3.0.5", "leaflet": "^1.9.4", + "leaflet-heatmap": "^1.0.0", "leaflet-velocity": "^2.1.4", "leaflet.heat": "^0.2.0" } diff --git a/src/lib/components/Map.svelte b/src/lib/components/Map.svelte index 5e6cc7a..5ce3f55 100644 --- a/src/lib/components/Map.svelte +++ b/src/lib/components/Map.svelte @@ -25,7 +25,7 @@ onMount(async () => { if (!mapContainer) return; - map = L.map(mapContainer, { zoomControl: false }).setView([51.505, -0.09], 13); + map = L.map(mapContainer, { zoomControl: false }).setView([51.505, -0.09], 13); L.control.zoom({ position: "bottomleft" }).addTo(map); plotLayerGroup = L.layerGroup().addTo(map); diff --git a/src/lib/components/WindVisualisation.svelte b/src/lib/components/WindVisualisation.svelte index 386c503..3cd67bb 100644 --- a/src/lib/components/WindVisualisation.svelte +++ b/src/lib/components/WindVisualisation.svelte @@ -20,36 +20,54 @@ // Функция для нормализации данных тепловой карты const prepareHeatData = (windData) => { - if (!windData || !windData.header || !windData.data) { - console.warn("Wind data is missing or incomplete"); - return []; + if (!windData || windData.length < 2) { + console.warn("Invalid wind data structure"); + return []; } - const { lo1, la1, dx, dy, nx, ny } = windData.header; + // Получаем U и V компоненты + const uComponent = windData.find(item => item.header.parameterNumber === 2); + const vComponent = windData.find(item => item.header.parameterNumber === 3); + + if (!uComponent || !vComponent) { + console.warn("Missing wind components"); + return []; + } + + const header = uComponent.header; // Используем header из U компоненты + const { lo1, la1, dx, dy, nx, ny } = header; const heatData = []; let maxSpeed = 0; + // Проверяем совпадение размеров данных + if (uComponent.data.length !== vComponent.data.length) { + console.warn("U and V components have different lengths"); + return []; + } + // Собираем данные и находим максимальную скорость - for (let y = 0; y < ny; y++) { - for (let x = 0; x < nx; x++) { - const u = windData.data[y][x * 2]; - const v = windData.data[y][x * 2 + 1]; - const speed = Math.sqrt(u * u + v * v); + for (let i = 0; i < uComponent.data.length; i++) { + const u = uComponent.data[i]; + const v = vComponent.data[i]; + const speed = Math.sqrt(u * u + v * v); - if (!isNaN(speed)) { - const lat = la1 - y * dy; - const lng = lo1 + x * dx; - heatData.push([lat, lng, speed]); - maxSpeed = Math.max(maxSpeed, speed); - } + if (!isNaN(speed)) { + // Вычисляем координаты для текущей точки + const y = Math.floor(i / nx); + const x = i % nx; + const lat = la1 - y * dy; + const lng = lo1 + x * dx; + + heatData.push([lat, lng, speed]); + maxSpeed = Math.max(maxSpeed, speed); } } - console.log('Prepared heat data: ${heatData.length} points, max speed: ${maxSpeed}'); + console.log(`Prepared heat data: ${heatData.length} points, max speed: ${maxSpeed}`); // Нормализуем значения интенсивности от 0 до 1 if (maxSpeed > 0) { - return heatData.map(([lat, lng, intensity]) => [lat, lng, intensity / maxSpeed]); + return heatData.map(([lat, lng, intensity]) => [lat, lng, intensity / maxSpeed]); } return heatData; @@ -58,27 +76,27 @@ // Создание тепловой карты const createHeatLayer = (data) => { if (!data || data.length === 0) { - console.warn("No valid heat data provided"); - return null; + console.warn("No valid heat data provided"); + return null; } try { - return L.heatLayer(data, { - radius: 15, - blur: 20, - maxZoom: 17, - minOpacity: 0.5, - gradient: { - 0.1: 'blue', - 0.3: 'cyan', - 0.5: 'lime', - 0.7: 'yellow', - 1.0: 'red' - } - }); + return L.heatLayer(data, { + radius: 20, // Увеличьте радиус для глобальной карты + blur: 15, + maxZoom: 10, + minOpacity: 0.7, + gradient: { + 0.1: 'blue', + 0.3: 'cyan', + 0.5: 'lime', + 0.7: 'yellow', + 1.0: 'red' + } + }); } catch (e) { - console.error("Failed to create heat layer:", e); - return null; + console.error("Failed to create heat layer:", e); + return null; } }; @@ -92,28 +110,53 @@ if (legend) map.removeControl(legend); // Создаем слой векторов ветра - velocityLayer = L.velocityLayer({ - displayValues: true, - displayOptions: { - velocityType: 'Wind Speed', - position: 'bottomright', - emptyString: 'No wind data', - }, - data: windData - }).addTo(map); + if (showVectors) { + velocityLayer = L.velocityLayer({ + displayValues: true, + displayOptions: { + velocityType: 'Wind Speed', + position: 'bottomright', + emptyString: 'No wind data', + }, + data: windData + }).addTo(map); + } // Создаем тепловую карту - const heatData = prepareHeatData(windData); - heatLayer = createHeatLayer(heatData); - - if (heatLayer) { - heatLayer.addTo(map); - createLegend(Math.max(...heatData.map(point => point[2]))); - } else { - console.warn("Heat layer was not created"); + if (showHeatmap) { + const heatData = prepareHeatData(windData); + heatLayer = createHeatLayer(heatData); + + if (heatLayer) { + heatLayer.addTo(map); + createLegend(Math.max(...heatData.map(point => point[2]))); + } } + + // Обновляем контроль слоев + updateLayerControl(); }; + const updateLayerControl = () => { + if (layerControl) { + map.removeControl(layerControl); + } + + const overlays = {}; + + if (velocityLayer) { + overlays['Векторы ветра'] = velocityLayer; + } + + if (heatLayer) { + overlays['Тепловая карта'] = heatLayer; + } + + layerControl = L.control.layers(null, overlays, { + collapsed: false, + position: 'topright' + }).addTo(map); + }; // Создание легенды с учетом максимальной скорости const createLegend = (maxSpeed) => { if (!map) return; @@ -162,8 +205,42 @@ updateLayers(); }; +
+
+ + +
+
\ No newline at end of file