2023-08-07 23:25:35 +03:00
|
|
|
|
import { Component, createEffect, createSignal, onMount } from "solid-js";
|
2023-08-08 17:22:52 +03:00
|
|
|
|
|
2023-08-07 23:25:35 +03:00
|
|
|
|
import { ResultKeyVal } from "../../consts";
|
2023-08-08 17:22:52 +03:00
|
|
|
|
import { WindInputs, WindSignals } from "./WindInputs";
|
|
|
|
|
|
|
2023-08-08 19:00:22 +03:00
|
|
|
|
import arrowUrl from "../../assets/arrow.webp";
|
2025-03-14 11:31:47 +02:00
|
|
|
|
import { cityCoords } from "../cityCoords";
|
2024-02-11 21:44:29 +02:00
|
|
|
|
import { MapResolution, resolutionProps, windProps, WindProps } from "./mapConsts";
|
|
|
|
|
|
import { CityData, drawOnMap } from "./canvasDraw";
|
2025-03-14 11:31:47 +02:00
|
|
|
|
import { IconInputs } from "../weatherIcons/IconInputs";
|
2026-08-18 21:44:09 +03:00
|
|
|
|
import { download } from '../../helpers/download'
|
2024-01-23 22:58:26 +02:00
|
|
|
|
|
|
|
|
|
|
export const MapView: Component<{ type: MapResolution, data: () => ResultKeyVal[] }> = ({ type, data }) => {
|
2023-08-07 23:25:35 +03:00
|
|
|
|
const [getCanvas, setCanvas] = createSignal<HTMLCanvasElement>();
|
|
|
|
|
|
const [getImg, setImg] = createSignal(new Image());
|
2024-02-11 21:44:29 +02:00
|
|
|
|
const weatherIconsSingal = createSignal<{[key: string]: string;}>({});
|
|
|
|
|
|
const [getWeatherIcons] = weatherIconsSingal;
|
|
|
|
|
|
|
2025-03-12 13:16:35 +02:00
|
|
|
|
let lastCoords: CityData[] = [];
|
2024-01-23 22:58:26 +02:00
|
|
|
|
const props = resolutionProps[type];
|
2023-08-07 23:25:35 +03:00
|
|
|
|
|
2023-08-08 19:00:22 +03:00
|
|
|
|
const arrowImg = new Image();
|
|
|
|
|
|
arrowImg.src = arrowUrl;
|
|
|
|
|
|
|
2023-08-08 17:22:52 +03:00
|
|
|
|
const windSignals: WindSignals = {
|
|
|
|
|
|
direction: createSignal(""),
|
|
|
|
|
|
speed: createSignal(""),
|
|
|
|
|
|
gusts: createSignal(""),
|
2023-08-08 23:05:55 +03:00
|
|
|
|
roundValues: createSignal(false),
|
2023-08-08 17:22:52 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-02-08 15:25:51 +02:00
|
|
|
|
function getWindData(): [string, string, string, boolean, WindProps] {
|
2023-08-08 17:22:52 +03:00
|
|
|
|
return [
|
|
|
|
|
|
windSignals.direction[0](),
|
|
|
|
|
|
windSignals.speed[0](),
|
|
|
|
|
|
windSignals.gusts[0](),
|
2023-08-08 23:05:55 +03:00
|
|
|
|
windSignals.roundValues[0](),
|
2024-02-08 15:25:51 +02:00
|
|
|
|
windProps[type],
|
2023-08-08 17:22:52 +03:00
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-07 23:25:35 +03:00
|
|
|
|
onMount(() => {
|
|
|
|
|
|
const ctx = getCanvas()!.getContext("2d")!;
|
|
|
|
|
|
const img = new Image();
|
|
|
|
|
|
img.onload = () => {
|
|
|
|
|
|
setImg(img);
|
2023-08-08 23:05:55 +03:00
|
|
|
|
drawOnMap(
|
|
|
|
|
|
ctx,
|
|
|
|
|
|
[img, arrowImg],
|
|
|
|
|
|
lastCoords,
|
|
|
|
|
|
getWindData(),
|
2024-01-23 22:58:26 +02:00
|
|
|
|
props,
|
2023-08-08 23:05:55 +03:00
|
|
|
|
);
|
2023-08-07 23:25:35 +03:00
|
|
|
|
};
|
2024-01-23 22:58:26 +02:00
|
|
|
|
img.src = props.map;
|
2023-08-07 23:25:35 +03:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
createEffect(() => {
|
|
|
|
|
|
const ctx = getCanvas()!.getContext("2d")!;
|
2024-02-11 21:44:29 +02:00
|
|
|
|
const weatherIcons = getWeatherIcons();
|
2025-03-12 13:16:35 +02:00
|
|
|
|
const coordsAndData: CityData[] = data()
|
2023-08-07 23:25:35 +03:00
|
|
|
|
.map(([city, value]) => [
|
|
|
|
|
|
city,
|
|
|
|
|
|
cityCoords[city as keyof typeof cityCoords],
|
|
|
|
|
|
typeof value === "number" ? value : -99,
|
2024-02-11 21:44:29 +02:00
|
|
|
|
weatherIcons[city],
|
2023-08-07 23:25:35 +03:00
|
|
|
|
]);
|
|
|
|
|
|
lastCoords = coordsAndData;
|
2023-08-08 23:05:55 +03:00
|
|
|
|
drawOnMap(
|
|
|
|
|
|
ctx,
|
2024-02-11 21:44:29 +02:00
|
|
|
|
[getImg(), arrowImg],
|
2023-08-08 23:05:55 +03:00
|
|
|
|
coordsAndData,
|
|
|
|
|
|
getWindData(),
|
2024-01-23 22:58:26 +02:00
|
|
|
|
props,
|
2023-08-08 23:05:55 +03:00
|
|
|
|
);
|
2023-08-07 23:25:35 +03:00
|
|
|
|
});
|
|
|
|
|
|
|
2024-02-08 15:42:36 +02:00
|
|
|
|
const getStyleSize = () => {
|
2026-08-18 21:44:09 +03:00
|
|
|
|
return { "width": "100%", "height": "auto" };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function downloadPng() {
|
|
|
|
|
|
getCanvas()!.toBlob(blob => blob && download(blob, `weather-${type}.png`), 'image/png')
|
2024-02-08 15:42:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-07 23:25:35 +03:00
|
|
|
|
return (
|
2026-08-18 21:44:09 +03:00
|
|
|
|
<div class="mapWorkspace">
|
|
|
|
|
|
<div class="mapToolbar"><div><strong>Map preview</strong><span>{props.width} × {props.height} px export</span></div><button class="primary" onClick={downloadPng}>Download PNG</button></div>
|
|
|
|
|
|
<details class="broadcastControls">
|
|
|
|
|
|
<summary><span><strong>Broadcast overlay controls</strong><small>Optional manual wind and weather-symbol overrides</small></span></summary>
|
|
|
|
|
|
<p class="controlHelp">These settings do not change the queried city values. Use them only when preparing a finished broadcast graphic.</p>
|
2024-02-11 21:44:29 +02:00
|
|
|
|
<div class="grid-1-2">
|
|
|
|
|
|
<div>
|
2025-03-20 22:17:28 +02:00
|
|
|
|
<div>
|
|
|
|
|
|
<label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="checkbox"
|
|
|
|
|
|
checked={windSignals.roundValues[0]()}
|
|
|
|
|
|
onChange={e => windSignals.roundValues[1](e.target.checked)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
Round values
|
|
|
|
|
|
</label>
|
|
|
|
|
|
</div>
|
2024-02-11 21:44:29 +02:00
|
|
|
|
{ props.showWind && <WindInputs signals={windSignals} /> }
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div><IconInputs weatherIconsSingal={weatherIconsSingal} /></div>
|
|
|
|
|
|
</div>
|
2026-08-18 21:44:09 +03:00
|
|
|
|
</details>
|
|
|
|
|
|
<div class="canvasFrame">
|
2024-02-06 16:56:31 +02:00
|
|
|
|
<canvas
|
|
|
|
|
|
ref={setCanvas}
|
2026-08-18 21:44:09 +03:00
|
|
|
|
width={props.width}
|
|
|
|
|
|
height={props.height}
|
2024-02-08 15:42:36 +02:00
|
|
|
|
style={getStyleSize()}
|
2024-02-06 16:56:31 +02:00
|
|
|
|
/>
|
2026-08-18 21:44:09 +03:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2023-08-07 23:25:35 +03:00
|
|
|
|
);
|
|
|
|
|
|
}
|