Draw csv data on a map
This commit is contained in:
@@ -15,7 +15,7 @@ export const MapView: Component<{ type: MapResolution, data: () => ResultKeyVal[
|
|||||||
const weatherIconsSingal = createSignal<{[key: string]: string;}>({});
|
const weatherIconsSingal = createSignal<{[key: string]: string;}>({});
|
||||||
const [getWeatherIcons] = weatherIconsSingal;
|
const [getWeatherIcons] = weatherIconsSingal;
|
||||||
|
|
||||||
let lastCoords: CityData = [];
|
let lastCoords: CityData[] = [];
|
||||||
const props = resolutionProps[type];
|
const props = resolutionProps[type];
|
||||||
|
|
||||||
const arrowImg = new Image();
|
const arrowImg = new Image();
|
||||||
@@ -57,7 +57,7 @@ export const MapView: Component<{ type: MapResolution, data: () => ResultKeyVal[
|
|||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const ctx = getCanvas()!.getContext("2d")!;
|
const ctx = getCanvas()!.getContext("2d")!;
|
||||||
const weatherIcons = getWeatherIcons();
|
const weatherIcons = getWeatherIcons();
|
||||||
const coordsAndData: CityData = data()
|
const coordsAndData: CityData[] = data()
|
||||||
.map(([city, value]) => [
|
.map(([city, value]) => [
|
||||||
city,
|
city,
|
||||||
cityCoords[city as keyof typeof cityCoords],
|
cityCoords[city as keyof typeof cityCoords],
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { ResolutionPropsValue, WindProps, windProps } from "./mapConsts";
|
|||||||
import { drawRotatedImage, getAngleFromString } from "./windAngles";
|
import { drawRotatedImage, getAngleFromString } from "./windAngles";
|
||||||
|
|
||||||
// cityName, {x, y}, weatherValue, icon
|
// 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(
|
export function drawOnMap(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
imgArr: [HTMLImageElement, HTMLImageElement],
|
imgArr: [HTMLImageElement, HTMLImageElement],
|
||||||
cityData: CityData,
|
cityData: CityData[],
|
||||||
windData: [string, string, string, boolean, WindProps],
|
windData: [string, string, string, boolean, WindProps],
|
||||||
props: ResolutionPropsValue,
|
props: ResolutionPropsValue,
|
||||||
): void {
|
): void {
|
||||||
|
|||||||
@@ -32,4 +32,10 @@ export const cityCoords: {[key: string]: { x: number, y: number}} = {
|
|||||||
"Vičaki": { x: 155, y: 142},
|
"Vičaki": { x: 155, y: 142},
|
||||||
"Zīlāni": { x: 690, y: 370 },
|
"Zīlāni": { x: 690, y: 370 },
|
||||||
"Zosēni": { x: 650, y: 250 },
|
"Zosēni": { x: 650, y: 250 },
|
||||||
};
|
|
||||||
|
// 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 },
|
||||||
|
}
|
||||||
@@ -1,3 +1,182 @@
|
|||||||
/* font-family: 'Daira by LTV grafika'; */
|
/* 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 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 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',
|
||||||
|
}
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
import { Component } from 'solid-js'
|
import { Component, createEffect, createSignal, onMount } from 'solid-js'
|
||||||
import { apiHost } from '../consts'
|
import { apiHost } from '../consts'
|
||||||
import { fetchText } from '../helpers/fetch'
|
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_nakama_dn.csv
|
||||||
// Eiropa_LTV_pilsetas_tekosa_dn.csv
|
// Eiropa_LTV_pilsetas_tekosa_dn.csv
|
||||||
@@ -8,11 +15,74 @@ import { fetchText } from '../helpers/fetch'
|
|||||||
// Latvija_LTV_pilsetas_tekosa_dn.csv
|
// Latvija_LTV_pilsetas_tekosa_dn.csv
|
||||||
// Latvija_faktiskais_laiks.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<{}> = () => {
|
export const LvgmcForecast: Component<{}> = () => {
|
||||||
|
const [getCityData, setCityData] = createSignal<CityData[]>([])
|
||||||
|
const [getWindData, setWindData] = createSignal<[string, string, string]>(['', '', ''])
|
||||||
|
|
||||||
fetchText(`${apiHost}/api/show/lvgmc-forecast/Latvija_LTV_pilsetas_tekosa_dn.csv`)
|
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 <>
|
return <>
|
||||||
LvgmcForecast
|
LvgmcForecast
|
||||||
|
<br/>
|
||||||
|
<canvas
|
||||||
|
ref={c =>canvas=c}
|
||||||
|
width={'3840px'}
|
||||||
|
height={'1440px'}
|
||||||
|
style={{ 'width': '1000px', 'height': '374px' }}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user