Files
WeatherTool/web/src/components/map/MapView.tsx
T

129 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Component, createEffect, createSignal, onMount } from "solid-js";
import { ResultKeyVal } from "../../consts";
import { WindInputs, WindSignals } from "./WindInputs";
import arrowUrl from "../../assets/arrow.webp";
import { cityCoords } from "../cityCoords";
import { MapResolution, resolutionProps, windProps, WindProps } from "./mapConsts";
import { CityData, drawOnMap } from "./canvasDraw";
import { IconInputs } from "../weatherIcons/IconInputs";
import { download } from '../../helpers/download'
export const MapView: Component<{ type: MapResolution, data: () => ResultKeyVal[], mode?: "temperature" | "faktiska" }> = ({ type, data, mode = "temperature" }) => {
const [getCanvas, setCanvas] = createSignal<HTMLCanvasElement>();
const [getImg, setImg] = createSignal(new Image());
const weatherIconsSignal = createSignal<{[key: string]: string;}>({});
const [getWeatherIcons] = weatherIconsSignal;
const [getTitle, setTitle] = createSignal("");
const [getSource, setSource] = createSignal("");
let lastCoords: CityData[] = [];
const props = resolutionProps[type];
const arrowImg = new Image();
arrowImg.src = arrowUrl;
const windSignals: WindSignals = {
direction: createSignal(""),
speed: createSignal(""),
gusts: createSignal(""),
roundValues: createSignal(false),
}
function getWindData(): [string, string, string, boolean, WindProps] {
return [
windSignals.direction[0](),
windSignals.speed[0](),
windSignals.gusts[0](),
windSignals.roundValues[0](),
windProps[type],
];
}
onMount(() => {
const ctx = getCanvas()!.getContext("2d")!;
const img = new Image();
img.onload = () => {
setImg(img);
drawOnMap(
ctx,
[img, arrowImg],
lastCoords,
getWindData(),
[getTitle(), getSource()],
props,
);
};
img.src = props.map;
});
createEffect(() => {
const ctx = getCanvas()!.getContext("2d")!;
const weatherIcons = getWeatherIcons();
const coordsAndData: CityData[] = data()
.map(([city, value]) => [
city,
cityCoords[city as keyof typeof cityCoords],
typeof value === "number" ? value : -99,
weatherIcons[city],
]);
lastCoords = coordsAndData;
drawOnMap(
ctx,
[getImg(), arrowImg],
coordsAndData,
getWindData(),
[getTitle(), getSource()],
props,
);
});
const getStyleSize = () => {
return { "width": "100%", "height": "auto" };
}
function downloadPng() {
getCanvas()!.toBlob(blob => blob && download(blob, `weather-${type}.png`), 'image/png')
}
return (
<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" open>
<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>
<div class="overlayFields">
<label><span>Title</span><input type="text" placeholder="19. APRĪĻA GAISA TEMPERATŪRAS REKORDI" value={getTitle()} onInput={e => setTitle(e.target.value)} /></label>
<label><span>Source</span><input type="text" placeholder="LVĢMC" value={getSource()} onInput={e => setSource(e.target.value)} /></label>
</div>
<div class="mapControlSections">
<section class="valueControls">
<strong>Value and wind settings</strong>
<div>
<label>
<input
type="checkbox"
checked={windSignals.roundValues[0]()}
onChange={e => windSignals.roundValues[1](e.target.checked)}
/>
Round values
</label>
</div>
{ props.showWind && <WindInputs signals={windSignals} /> }
</section>
{mode === "faktiska" && <IconInputs cities={data().map(([city]) => city)} weatherIconsSignal={weatherIconsSignal} />}
</div>
</details>
<div class="canvasFrame">
<canvas
ref={setCanvas}
width={props.width}
height={props.height}
style={getStyleSize()}
/>
</div>
</div>
);
}