diff --git a/web/src/cities/map/MapView.tsx b/web/src/cities/map/MapView.tsx index b2cb0ff..3cb43c5 100644 --- a/web/src/cities/map/MapView.tsx +++ b/web/src/cities/map/MapView.tsx @@ -15,7 +15,7 @@ export const MapView: Component<{ type: MapResolution, data: () => ResultKeyVal[ const weatherIconsSingal = createSignal<{[key: string]: string;}>({}); const [getWeatherIcons] = weatherIconsSingal; - let lastCoords: CityData = []; + let lastCoords: CityData[] = []; const props = resolutionProps[type]; const arrowImg = new Image(); @@ -57,7 +57,7 @@ export const MapView: Component<{ type: MapResolution, data: () => ResultKeyVal[ createEffect(() => { const ctx = getCanvas()!.getContext("2d")!; const weatherIcons = getWeatherIcons(); - const coordsAndData: CityData = data() + const coordsAndData: CityData[] = data() .map(([city, value]) => [ city, cityCoords[city as keyof typeof cityCoords], diff --git a/web/src/cities/map/canvasDraw.ts b/web/src/cities/map/canvasDraw.ts index 9210912..634dc22 100644 --- a/web/src/cities/map/canvasDraw.ts +++ b/web/src/cities/map/canvasDraw.ts @@ -2,12 +2,12 @@ import { ResolutionPropsValue, WindProps, windProps } from "./mapConsts"; import { drawRotatedImage, getAngleFromString } from "./windAngles"; // cityName, {x, y}, weatherValue, icon -export type CityData = [string, {x: number, y: number }, number, string | undefined][]; +export type CityData = [cityName: string, {x: number, y: number }, weatherValue: number, icon: string | undefined] export function drawOnMap( ctx: CanvasRenderingContext2D, imgArr: [HTMLImageElement, HTMLImageElement], - cityData: CityData, + cityData: CityData[], windData: [string, string, string, boolean, WindProps], props: ResolutionPropsValue, ): void { diff --git a/web/src/components/cityCoords.ts b/web/src/components/cityCoords.ts index 80a15e6..2dfd4ac 100644 --- a/web/src/components/cityCoords.ts +++ b/web/src/components/cityCoords.ts @@ -32,4 +32,10 @@ export const cityCoords: {[key: string]: { x: number, y: number}} = { "Vičaki": { x: 155, y: 142}, "Zīlāni": { x: 690, y: 370 }, "Zosēni": { x: 650, y: 250 }, -}; \ No newline at end of file + + // rest of cities (forecasts) + "Cēsis": { x: 590, y: 195 }, + "Valmiera": { x: 600, y: 140 }, + "Talsi": { x: 250, y: 235 }, + "Jēkabpils": { x: 690, y: 370 }, +} \ No newline at end of file diff --git a/web/src/components/weatherIcons/iconConsts.ts b/web/src/components/weatherIcons/iconConsts.ts index bc1650f..1e33d6a 100644 --- a/web/src/components/weatherIcons/iconConsts.ts +++ b/web/src/components/weatherIcons/iconConsts.ts @@ -1,3 +1,182 @@ /* font-family: 'Daira by LTV grafika'; */ -export const weatherIcons = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i"]; -export const weatherIconCities = ['Ainaži', 'Alūksne', 'Bauska', 'Daugavpils', 'Liepāja', 'Madona', 'Priekuļi', 'Rēzekne', 'Rīga', 'Saldus', 'Stende', 'Ventspils', 'Zīlāni']; \ No newline at end of file +export const weatherIcons = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i"] +export const weatherIconCities = ['Ainaži', 'Alūksne', 'Bauska', 'Daugavpils', 'Liepāja', 'Madona', 'Priekuļi', 'Rēzekne', 'Rīga', 'Saldus', 'Stende', 'Ventspils', 'Zīlāni'] + +export function codeToIcon(code: number): string { + const letter = codeIconMap[code] + if (!letter) throw new Error(`Did not found corresponding weather icon for code: ${code}`) + return letter +} + +export const codeIconMap: { [key: number]: string } = { + 1101: 'b', + 1102: 'B', + 1103: 'T', + 1104: 'S', + 1105: 'S', + 1201: 'G', + 1202: 'G', + 1203: 'H', + 1204: 'G', + 1205: 'G', + 1206: 'H', + 1207: 'G', + 1208: 'h', + 1301: 'J', + 1302: 'J', + 1303: 'K', + 1304: 'P', + 1305: 'P', + 1306: 'Q', + 1307: 'Q', + 1308: 'P', + 1309: 'Q', + 1310: 'P', + 1311: 'P', + 1312: 'Q', + 1313: 'd', + 1314: 'e', + 1315: 'd', + 1316: 'e', + 1317: 'M', + 1318: 'M', + 1319: 'N', + 1320: 'M', + 1321: 'M', + 1322: 'N', + 1323: 'D', + 1324: 'D', + 1325: 'E', + 1401: 'S', + 1402: 'S', + 1403: 'T', + 1404: 'B', + 1405: 'G', + 1406: 'H', + 1407: 'G', + 1408: 'H', + 1409: 'S', + 1410: 'S', + 1411: 'B', + 1412: 'T', + 1413: 'V', + 1414: 'W', + 1415: 'g', + 1416: 'h', + 1501: 'G', + 1502: 'G', + 1503: 'H', + 1504: 'G', + 1505: 'G', + 1506: 'H', + 1507: 'M', + 1508: 'M', + 1509: 'N', + 1510: 'G', + 1511: 'G', + 1512: 'H', + 1601: 'V', + 1602: 'V', + 1603: 'W', + 1604: 'Y', + 1605: 'Y', + 1606: 'Z', + 1607: 'Y', + 1608: 'Z', + 1609: 'Y', + 1610: 'Z', + 1611: 'Y', + 1612: 'Y', + 1613: 'Z', + 1614: 'g', + 1615: 'g', + 1616: 'h', + 1617: 'd', + 1618: 'd', + 1619: 'e', + 2101: 'C', + 2102: 'C', + 2103: 'U', + 2104: 'S', + 2105: 'S', + 2201: 'G', + 2202: 'G', + 2203: 'I', + 2204: 'G', + 2205: 'G', + 2206: 'I', + 2207: 'G', + 2208: 'i', + 2301: 'J', + 2302: 'J', + 2303: 'L', + 2304: 'P', + 2305: 'P', + 2306: 'R', + 2307: 'R', + 2308: 'P', + 2309: 'R', + 2310: 'P', + 2311: 'P', + 2312: 'R', + 2313: 'd', + 2314: 'f', + 2315: 'd', + 2316: 'f', + 2317: 'M', + 2318: 'M', + 2319: 'O', + 2320: 'M', + 2321: 'M', + 2322: 'O', + 2323: 'D', + 2324: 'D', + 2325: 'F', + 2401: 'S', + 2402: 'S', + 2403: 'U', + 2404: 'C', + 2405: 'G', + 2406: 'I', + 2407: 'G', + 2408: 'I', + 2409: 'S', + 2410: 'S', + 2411: 'C', + 2412: 'U', + 2413: 'V', + 2414: 'X', + 2415: 'g', + 2416: 'i', + 2501: 'G', + 2502: 'G', + 2503: 'I', + 2504: 'G', + 2505: 'G', + 2506: 'I', + 2507: 'M', + 2508: 'M', + 2509: 'O', + 2510: 'G', + 2511: 'G', + 2512: 'I', + 2601: 'V', + 2602: 'V', + 2603: 'X', + 2604: 'Y', + 2605: 'Y', + 2606: 'a', + 2607: 'Y', + 2608: 'a', + 2609: 'Y', + 2610: 'a', + 2611: 'Y', + 2612: 'Y', + 2613: 'a', + 2614: 'g', + 2615: 'g', + 2616: 'i', + 2617: 'd', + 2618: 'd', + 2619: 'f', +} \ No newline at end of file diff --git a/web/src/lvgmc-forecast/LvgmcForecast.tsx b/web/src/lvgmc-forecast/LvgmcForecast.tsx index 9c23e1a..0d96156 100644 --- a/web/src/lvgmc-forecast/LvgmcForecast.tsx +++ b/web/src/lvgmc-forecast/LvgmcForecast.tsx @@ -1,6 +1,13 @@ -import { Component } from 'solid-js' +import { Component, createEffect, createSignal, onMount } from 'solid-js' import { apiHost } from '../consts' import { fetchText } from '../helpers/fetch' +import { codeToIcon } from '../components/weatherIcons/iconConsts' +import { cityCoords } from '../components/cityCoords' +import { CityData, drawOnMap } from '../cities/map/canvasDraw' + +import arrowUrl from '../assets/arrow.webp' +import bgMap from '../assets/map_3840x1440_wind.webp' +import { resolutionProps, WindProps, windProps } from '../cities/map/mapConsts' // Eiropa_LTV_pilsetas_nakama_dn.csv // Eiropa_LTV_pilsetas_tekosa_dn.csv @@ -8,11 +15,74 @@ import { fetchText } from '../helpers/fetch' // Latvija_LTV_pilsetas_tekosa_dn.csv // Latvija_faktiskais_laiks.csv +export const csvCityListInOrder = ['Alūksne', 'Bauska', 'Cēsis', 'Daugavpils', 'Jelgava', 'Rīga', 'Liepāja', 'Madona', 'Rēzekne', 'Saldus', 'Valmiera', 'Ventspils', 'Ainaži', 'Dobele', 'Gulbene', 'Sigulda', 'Talsi', 'Jēkabpils'] +export const citiesToShow = ['Liepāja', 'Ventspils', 'Saldus', 'Talsi', 'Bauska', 'Rīga', 'Ainaži', 'Valmiera', 'Alūksne', 'Madona', 'Jēkabpils', 'Daugavpils'] + + export const LvgmcForecast: Component<{}> = () => { + const [getCityData, setCityData] = createSignal([]) + const [getWindData, setWindData] = createSignal<[string, string, string]>(['', '', '']) + fetchText(`${apiHost}/api/show/lvgmc-forecast/Latvija_LTV_pilsetas_tekosa_dn.csv`) - .then(console.log) + .then(csv => { + const csvLines = csv.split('\n') + console.log(csvLines) + const citiesOffset = 4 + const cityData: CityData[] = csvCityListInOrder + .map((city, i): CityData => { + const parts = csvLines[i + citiesOffset].split(';') + const temperature = Number(parts[1]) + const iconCode = Number(parts[2]) + const icon = codeToIcon(iconCode) + const coord = cityCoords[city] + if (!coord) throw new Error(`No coord found for city: ${city}`) + return [city, coord, temperature, icon] + }) + .filter(([city]) => citiesToShow.includes(city)) + + const windOffset = 30 + const windParts = csvLines[windOffset].split(';') + const windSpped = windParts[1] + const windGusts = windParts[3] + const windDirection = windParts[5] + setWindData([windDirection, windSpped, windGusts]) + + setCityData(cityData) + draw() + }) + + const arrowImg = new Image() + arrowImg.src = arrowUrl + + let canvas: HTMLCanvasElement + let ctx: CanvasRenderingContext2D + onMount(() => { + ctx = canvas!.getContext('2d')! + }) + + function draw(){ + const img = new Image() + const tmpWindData: [string, string, string, boolean, WindProps] = [...getWindData(), true, windProps.map_3840x1440_wind] + img.onload = () => { + drawOnMap( + ctx, + [img, arrowImg], + getCityData(), + tmpWindData, + resolutionProps.map_3840x1440_wind, + ) + } + img.src = bgMap + } return <> LvgmcForecast +
+ canvas=c} + width={'3840px'} + height={'1440px'} + style={{ 'width': '1000px', 'height': '374px' }} + /> }