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-20 10:46:26 +03:00
import { preloadWeatherIcons } from "../weatherIcons/weatherIconAssets" ;
2026-08-18 21:44:09 +03:00
import { download } from '../../helpers/download'
2024-01-23 22:58:26 +02:00
2026-08-19 17:11:58 +03:00
export const MapView : Component < { type : MapResolution , data : () => ResultKeyVal [], mode ?: "temperature" | "faktiska" , productionTemplate? : boolean } > = ({ type , data , mode = "temperature" , productionTemplate = false }) => {
2023-08-07 23:25:35 +03:00
const [ getCanvas , setCanvas ] = createSignal < HTMLCanvasElement >();
const [ getImg , setImg ] = createSignal ( new Image ());
2026-08-19 22:07:16 +03:00
const [ fontReady , setFontReady ] = createSignal ( false );
2026-08-20 10:46:26 +03:00
const [ iconsReady , setIconsReady ] = createSignal ( mode !== "faktiska" );
2026-08-18 22:31:56 +03:00
const weatherIconsSignal = createSignal < {[ key : string ] : string ;} > ({});
const [ getWeatherIcons ] = weatherIconsSignal ;
2026-08-18 22:14:40 +03:00
const [ getTitle , setTitle ] = createSignal ( "" );
const [ getSource , setSource ] = createSignal ( "" );
2024-02-11 21:44:29 +02:00
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 ( "" ),
2026-08-19 17:11:58 +03:00
roundValues : createSignal ( mode === "faktiska" ),
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 (() => {
2026-08-19 22:07:16 +03:00
void document . fonts . load ( `700 ${ props . fontSize } px Monda` ). then (() => setFontReady ( true ));
2026-08-20 10:46:26 +03:00
if ( mode === "faktiska" ) void preloadWeatherIcons (). then (() => setIconsReady ( true ));
2023-08-07 23:25:35 +03:00
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 (),
2026-08-18 22:14:40 +03:00
[ getTitle (), getSource ()],
2024-01-23 22:58:26 +02:00
props ,
2026-08-19 17:44:46 +03:00
productionTemplate ,
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 (() => {
2026-08-19 22:07:16 +03:00
fontReady ();
2026-08-20 10:46:26 +03:00
iconsReady ();
2023-08-07 23:25:35 +03:00
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 (),
2026-08-18 22:14:40 +03:00
[ getTitle (), getSource ()],
2024-01-23 22:58:26 +02:00
props ,
2026-08-19 17:44:46 +03:00
productionTemplate ,
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() {
2026-08-19 17:23:07 +03:00
const canvas = getCanvas () ! ;
2026-08-20 22:23:23 +03:00
if ( productionTemplate && ( canvas . width !== props . width || canvas . height !== props . height )) {
console . error ( `Faktiskā export blocked: expected ${ props . width } × ${ props . height } , received ${ canvas . width } × ${ canvas . height } ` );
2026-08-19 17:23:07 +03:00
return ;
}
2026-08-20 22:23:23 +03:00
const filename = productionTemplate ? `faktiska- ${ props . width } x ${ props . height } .png` : `weather- ${ type } .png` ;
2026-08-19 17:23:07 +03:00
canvas . toBlob ( blob => blob && download ( blob , filename ), '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" >
2026-08-22 14:48:16 +03:00
< div class = "mapToolbar" >< div >< strong >{ productionTemplate ? "Kartes priekšskatījums" : "Map preview" }</ strong >< span >{ props . width } × { props . height } px { productionTemplate ? "eksports" : "export" }</ span ></ div >< button class = "primary" onClick = { downloadPng }>{ productionTemplate ? "Lejupielādēt PNG" : "Download PNG" }</ button ></ div >
2026-08-23 13:31:12 +03:00
< details class = "broadcastControls" open = { ! productionTemplate }>
2026-08-22 14:48:16 +03:00
< summary >< span >< strong >{ productionTemplate ? "Kartes noformējums" : "Broadcast overlay controls" }</ strong >{ ! productionTemplate && < small > Optional manual wind and weather - symbol overrides </ small >}</ span ></ summary >
{ ! productionTemplate && < p class = "controlHelp" > These settings do not change the queried city values . Use them only when preparing a finished broadcast graphic .</ p >}
2026-08-19 17:11:58 +03:00
{ ! productionTemplate && < div class = "overlayFields" >
2026-08-18 22:14:40 +03:00
< 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 >
2026-08-19 17:11:58 +03:00
</ div >}
2026-08-18 22:31:56 +03:00
< div class = "mapControlSections" >
< section class = "valueControls" >
2026-08-22 14:48:16 +03:00
< strong >{ productionTemplate ? "Temperatūra un vējš" : "Value and wind settings" }</ strong >
2026-08-19 17:34:04 +03:00
< div class = "windControlRow" >
< label class = "roundControl" >
2025-03-20 22:17:28 +02:00
< input
type = "checkbox"
checked = { windSignals . roundValues [ 0 ]()}
onChange = { e => windSignals . roundValues [ 1 ]( e . target . checked )}
/>
2026-08-22 14:48:16 +03:00
< span >{ productionTemplate ? "Noapaļot temperatūras" : "Round temperatures" }</ span >
2025-03-20 22:17:28 +02:00
</ label >
2026-08-23 13:41:02 +03:00
{ props . showWind && < WindInputs signals = { windSignals } productionTemplate = { productionTemplate } /> }
2025-03-20 22:17:28 +02:00
</ div >
2026-08-18 22:31:56 +03:00
</ section >
{ mode === "faktiska" && < IconInputs cities = { data (). map (([ city ]) => city )} weatherIconsSignal = { weatherIconsSignal } />}
2024-02-11 21:44:29 +02:00
</ 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
);
}