Merge branch 'components' of https://git.intra.yksa.space/mikhailov.aa/leaflet_svelte into components
This commit is contained in:
commit
87f0a53cb5
3 changed files with 202 additions and 45 deletions
197
src/routes/WindVisualisation.svelte
Normal file
197
src/routes/WindVisualisation.svelte
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
<script>
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import L from 'leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import 'leaflet-velocity/dist/leaflet-velocity.css';
|
||||
import 'leaflet-velocity/dist/leaflet-velocity';
|
||||
import 'leaflet.heat';
|
||||
|
||||
export let map; // принимаем карту из родительского компонента
|
||||
export let windData;
|
||||
|
||||
let velocityLayer;
|
||||
let heatLayer;
|
||||
let legend;
|
||||
|
||||
// Состояние переключателей
|
||||
let showHeatmap = true;
|
||||
let showVectors = true;
|
||||
let layerControl;
|
||||
|
||||
// Функция для нормализации данных тепловой карты
|
||||
const prepareHeatData = (windData) => {
|
||||
if (!windData || !windData.header || !windData.data) {
|
||||
console.warn("Wind data is missing or incomplete");
|
||||
return [];
|
||||
}
|
||||
|
||||
const { lo1, la1, dx, dy, nx, ny } = windData.header;
|
||||
const heatData = [];
|
||||
let maxSpeed = 0;
|
||||
|
||||
// Собираем данные и находим максимальную скорость
|
||||
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);
|
||||
|
||||
if (!isNaN(speed)) {
|
||||
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}');
|
||||
|
||||
// Нормализуем значения интенсивности от 0 до 1
|
||||
if (maxSpeed > 0) {
|
||||
return heatData.map(([lat, lng, intensity]) => [lat, lng, intensity / maxSpeed]);
|
||||
}
|
||||
|
||||
return heatData;
|
||||
};
|
||||
|
||||
// Создание тепловой карты
|
||||
const createHeatLayer = (data) => {
|
||||
if (!data || data.length === 0) {
|
||||
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'
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("Failed to create heat layer:", e);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// Обновление слоев
|
||||
const updateLayers = () => {
|
||||
if (!map || !windData) return;
|
||||
|
||||
// Удаляем старые слои
|
||||
if (velocityLayer) map.removeLayer(velocityLayer);
|
||||
if (heatLayer) map.removeLayer(heatLayer);
|
||||
if (legend) map.removeControl(legend);
|
||||
|
||||
// Создаем слой векторов ветра
|
||||
velocityLayer = L.velocityLayer({
|
||||
displayValues: true,
|
||||
displayOptions: {
|
||||
velocityType: 'Wind Speed',
|
||||
position: 'bottomleft',
|
||||
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");
|
||||
}
|
||||
};
|
||||
|
||||
// Создание легенды с учетом максимальной скорости
|
||||
const createLegend = (maxSpeed) => {
|
||||
if (!map) return;
|
||||
|
||||
legend = L.control({ position: 'bottomright' });
|
||||
|
||||
legend.onAdd = () => {
|
||||
const div = L.DomUtil.create('div', 'wind-heat-legend');
|
||||
div.innerHTML = `
|
||||
<h4>Wind Speed (m/s)</h4>
|
||||
<div class="legend-scale">
|
||||
<div class="legend-color" style="background: #0000FF;"></div>
|
||||
<div class="legend-color" style="background: #00FFFF;"></div>
|
||||
<div class="legend-color" style="background: #00FF00;"></div>
|
||||
<div class="legend-color" style="background: #FFFF00;"></div>
|
||||
<div class="legend-color" style="background: #FF0000;"></div>
|
||||
</div>
|
||||
<div class="legend-labels">
|
||||
<span>0</span>
|
||||
<span>${(maxSpeed * 0.25).toFixed(1)}</span>
|
||||
<span>${(maxSpeed * 0.5).toFixed(1)}</span>
|
||||
<span>${(maxSpeed * 0.75).toFixed(1)}</span>
|
||||
<span>${maxSpeed.toFixed(1)}</span>
|
||||
</div>
|
||||
`;
|
||||
return div;
|
||||
};
|
||||
|
||||
legend.addTo(map);
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
updateLayers();
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (map) {
|
||||
if (velocityLayer) map.removeLayer(velocityLayer);
|
||||
if (heatLayer) map.removeLayer(heatLayer);
|
||||
if (legend) map.removeControl(legend);
|
||||
}
|
||||
});
|
||||
|
||||
// Реактивность на изменение параметров
|
||||
$: if (map && windData) {
|
||||
updateLayers();
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.wind-heat-legend {
|
||||
padding: 8px 10px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 15px rgba(0,0,0,0.2);
|
||||
line-height: 1.2;
|
||||
color: #333;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
.wind-heat-legend h4 {
|
||||
margin: 0 0 5px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.legend-scale {
|
||||
display: flex;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.legend-color {
|
||||
height: 12px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.legend-labels {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 11px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
import * as L from "leaflet";
|
||||
import type { Map as LeafletMap, LayerGroup } from "leaflet";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import VelocityLayer from "./velocity.svelte";
|
||||
import WindVisualization from './WindVisualisation.svelte';
|
||||
import { distHaversine } from "../lib/mathutil.ts";
|
||||
import type { PredictionData, TelemetryData } from "../lib/types.ts";
|
||||
|
||||
|
|
@ -20,15 +20,7 @@
|
|||
let mouseLng = 0;
|
||||
let isSelecting = false;
|
||||
|
||||
let velocityOptions = {
|
||||
displayValues: true,
|
||||
displayOptions: {
|
||||
velocityType: "Global Wind",
|
||||
position: "bottomleft",
|
||||
emptyString: "No velocity data",
|
||||
},
|
||||
data: null,
|
||||
};
|
||||
let windData;
|
||||
|
||||
const dispatch = createEventDispatcher<{ coordinatesSelected: { lat: number; lng: number } }>();
|
||||
|
||||
|
|
@ -43,7 +35,7 @@
|
|||
}).addTo(map);
|
||||
|
||||
const response = await fetch("src/routes/testVelo.json");
|
||||
velocityOptions.data = await response.json();
|
||||
windData = await response.json();
|
||||
|
||||
map.on("mousemove", (e: any) => {
|
||||
mouseLat = e.latlng.lat;
|
||||
|
|
@ -152,7 +144,7 @@
|
|||
</p>
|
||||
</div>
|
||||
<slot />
|
||||
{#if map}
|
||||
<VelocityLayer {map} {velocityOptions} />
|
||||
{#if map && windData}
|
||||
<WindVisualization {map} windData={windData} />
|
||||
{/if}
|
||||
</div>
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<script>
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import L from 'leaflet';
|
||||
import 'leaflet-velocity/dist/leaflet-velocity.css';
|
||||
import 'leaflet-velocity/dist/leaflet-velocity';
|
||||
|
||||
export let map; // принимаем карту из родительского компонента
|
||||
export let velocityOptions = {}; // параметры для velocity
|
||||
|
||||
let velocityLayer;
|
||||
|
||||
onMount(() => {
|
||||
if (map && !velocityLayer) {
|
||||
// Создаем слой velocity
|
||||
velocityLayer = L.velocityLayer(velocityOptions);
|
||||
|
||||
// Добавляем слой на карту
|
||||
velocityLayer.addTo(map);
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (map && velocityLayer) {
|
||||
map.removeLayer(velocityLayer);
|
||||
}
|
||||
});
|
||||
|
||||
// Реактивность на изменение параметров
|
||||
$: if (velocityLayer && velocityOptions) {
|
||||
velocityLayer.setOptions(velocityOptions);
|
||||
}
|
||||
</script>
|
||||
Loading…
Add table
Add a link
Reference in a new issue