diff --git a/web/src/pages/warnings/Warnings.tsx b/web/src/pages/warnings/Warnings.tsx index 24bab48..51e10ad 100644 --- a/web/src/pages/warnings/Warnings.tsx +++ b/web/src/pages/warnings/Warnings.tsx @@ -32,6 +32,8 @@ export function Warnings() { const phenomenonMatches = phenomenon() === 'all' || warning.phenomenon === phenomenon(); return dateMatches && phenomenonMatches; })); + const [selectedId, setSelectedId] = createSignal(); + const selectedWarning = createMemo(() => filteredWarnings().find(item => item.id === selectedId())); const loadArtwork = () => { const current = template(); @@ -50,16 +52,24 @@ export function Warnings() { }; createEffect(() => { output(); loadArtwork(); }); + createEffect(() => { + const list = filteredWarnings(); + if (!list.some(item => item.id === selectedId())) setSelectedId(list[0]?.id); + }); + createEffect(() => { + const warning = selectedWarning(); + setTitle(warning ? defaultTitle(warning.phenomenon) : ''); + }); createEffect(() => { const images = artwork(); const current = template(); - const warnings = filteredWarnings(); + const warning = selectedWarning(); const currentTitle = title(); if (!canvas || !images) return; if (canvas.width !== current.width) canvas.width = current.width; if (canvas.height !== current.height) canvas.height = current.height; void document.fonts.load(`700 ${current.title.fontSize}px Monda`).then(() => - drawWarningMap(canvas.getContext('2d')!, images.background, images.border, current, warnings, currentTitle), + drawWarningMap(canvas.getContext('2d')!, images.background, images.border, current, warning, currentTitle), ); }); @@ -67,7 +77,7 @@ export function Warnings() { return
-
ĒTERA GRAFIKA

Brīdinājumi

Aktuālie LVĢMC brīdinājumu poligoni un manuāli ievadāms virsraksts.

+
ĒTERA GRAFIKA

Brīdinājumi

Izvēlies vienu LVĢMC brīdinājumu, kas jārāda kartē; virsraksts aizpildās automātiski.

{filteredWarnings().length} {filteredWarnings().length === 1 ? 'brīdinājums' : 'brīdinājumi'}
@@ -79,7 +89,7 @@ export function Warnings() {

2. Izvēlies brīdinājumu

- +
@@ -87,12 +97,27 @@ export function Warnings() {
Brīdinājumu datus neizdevās ielādēt.
Izvēlētajam periodam nav aktīvu brīdinājumu. Karti joprojām var priekšskatīt, bet tajā nebūs iekrāsotu teritoriju.
- 0}>
{warning =>
{warning.phenomenon || 'Brīdinājums'}{warning.intensity} · {warning.regions}{formatPeriod(warning.validFrom, warning.validUntil)}
}
+ 0}>
{warning => { + const active = () => warning.id === selectedId(); + return ; + }}
3. Kartes priekšskatījums{template().width} × {template().height} px eksports
; } +function defaultTitle(phenomenon: string) { + return phenomenon ? `Brīdinājums: ${phenomenon}` : ''; +} + function datePart(value: string) { const match = value.trim().match(/^(\d{4}-\d{2}-\d{2})/); return match?.[1]; diff --git a/web/src/pages/warnings/warningDraw.ts b/web/src/pages/warnings/warningDraw.ts index ddcc992..e7f1274 100644 --- a/web/src/pages/warnings/warningDraw.ts +++ b/web/src/pages/warnings/warningDraw.ts @@ -16,9 +16,9 @@ export type WeatherWarning = { }; const severityStyles = { - yellow: { fill: 'rgba(255, 214, 0, .76)', stroke: '#6d5b00', label: 'DZELTENS' }, - orange: { fill: 'rgba(242, 126, 32, .78)', stroke: '#713300', label: 'ORANŽS' }, - red: { fill: 'rgba(204, 28, 32, .80)', stroke: '#650006', label: 'SARKANS' }, + yellow: { fill: 'rgba(255, 214, 0, .76)' }, + orange: { fill: 'rgba(242, 126, 32, .78)' }, + red: { fill: 'rgba(204, 28, 32, .80)' }, } as const; export type Severity = keyof typeof severityStyles; @@ -35,7 +35,7 @@ export function drawWarningMap( background: HTMLImageElement, border: HTMLImageElement, template: WarningTemplate, - warnings: WeatherWarning[], + warning: WeatherWarning | undefined, title: string, ) { ctx.clearRect(0, 0, template.width, template.height); @@ -46,9 +46,7 @@ export function drawWarningMap( warningLayer.height = template.height; const warningContext = warningLayer.getContext('2d')!; - [...warnings] - .sort((a, b) => severityRank(warningSeverity(a.intensity)) - severityRank(warningSeverity(b.intensity))) - .forEach(warning => drawWarning(warningContext, template, warning)); + if (warning) drawWarning(warningContext, template, warning); ctx.save(); ctx.filter = `blur(${template.feather}px)`; @@ -60,7 +58,6 @@ export function drawWarningMap( ctx.drawImage(border, 0, 0, template.width, template.height); drawTitle(ctx, template, title); - drawLegend(ctx, template, warnings); } function drawWarning(ctx: CanvasRenderingContext2D, template: WarningTemplate, warning: WeatherWarning) { @@ -118,32 +115,3 @@ function drawTitle(ctx: CanvasRenderingContext2D, template: WarningTemplate, val ctx.fillText('LVĢMC', x + width - sourceWidth / 2, layout.top + layout.height + sourceHeight / 2); } -function drawLegend(ctx: CanvasRenderingContext2D, template: WarningTemplate, warnings: WeatherWarning[]) { - const severities = [...new Set(warnings.map(item => warningSeverity(item.intensity)))] - .sort((a, b) => severityRank(b) - severityRank(a)); - if (!severities.length) return; - const layout = template.legend; - ctx.font = `700 ${layout.fontSize}px Monda`; - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - severities.forEach((severity, index) => { - const style = severityStyles[severity]; - const y = template.height - layout.bottom - index * (layout.swatch + layout.gap); - const labelWidth = ctx.measureText(style.label).width; - const swatchX = template.width - layout.right - labelWidth - layout.gap - layout.swatch; - ctx.fillStyle = style.fill; - ctx.fillRect(swatchX, y - layout.swatch / 2, layout.swatch, layout.swatch); - ctx.strokeStyle = style.stroke; - ctx.lineWidth = template.width === 1920 ? 2 : 3; - ctx.strokeRect(swatchX, y - layout.swatch / 2, layout.swatch, layout.swatch); - ctx.fillStyle = '#fff'; - ctx.shadowColor = 'rgba(0,0,0,.85)'; - ctx.shadowBlur = template.width === 1920 ? 4 : 7; - ctx.fillText(style.label, template.width - layout.right, y); - ctx.shadowBlur = 0; - }); -} - -function severityRank(severity: Severity) { - return severity === 'red' ? 3 : severity === 'orange' ? 2 : 1; -} diff --git a/web/src/pages/warnings/warningTemplates.ts b/web/src/pages/warnings/warningTemplates.ts index c9efd64..5ace1f3 100644 --- a/web/src/pages/warnings/warningTemplates.ts +++ b/web/src/pages/warnings/warningTemplates.ts @@ -13,7 +13,6 @@ export type WarningTemplate = { feather: number; geo: { a: number; b: number; c: number; d: number; e: number; f: number }; title: { right: number; top: number; height: number; fontSize: number; horizontalPadding: number; maximumWidth: number }; - legend: { right: number; bottom: number; swatch: number; gap: number; fontSize: number }; }; // `geo` maps (lon, lat) to pixel space as x = a*lon + b*lat + c, y = d*lon + e*lat + f. @@ -33,7 +32,6 @@ export const warningTemplates: Record = { feather: 16, geo: { a: 171.6971, b: -21.1156, c: -2040.5772, d: -5.3027, e: -306.1028, f: 18050.1672 }, title: { right: 88, top: 38, height: 62, fontSize: 30, horizontalPadding: 24, maximumWidth: 900 }, - legend: { right: 88, bottom: 58, swatch: 27, gap: 14, fontSize: 20 }, }, '3840x1440': { width: 3840, @@ -43,6 +41,5 @@ export const warningTemplates: Record = { feather: 28, geo: { a: 279.8026, b: -34.4106, c: -2997.9777, d: -8.6414, e: -498.8342, f: 29339.5317 }, title: { right: 376, top: 76, height: 124, fontSize: 60, horizontalPadding: 48, maximumWidth: 1700 }, - legend: { right: 376, bottom: 76, swatch: 42, gap: 22, fontSize: 30 }, }, }; diff --git a/web/src/pages/warnings/warnings.css b/web/src/pages/warnings/warnings.css index d0db4b5..b8ff389 100644 --- a/web/src/pages/warnings/warnings.css +++ b/web/src/pages/warnings/warnings.css @@ -1 +1 @@ -.warningsPage{max-width:1900px}.warningsHeading{margin-bottom:18px}.warningsSetup{display:grid;grid-template-columns:260px minmax(0,1fr);gap:28px;padding:18px 0 24px;border-top:1px solid var(--border);border-bottom:1px solid var(--border)}.warningsSetup h2{margin:0 0 14px;font-size:20px}.warningFilterGrid{display:grid;grid-template-columns:220px 260px minmax(320px,1fr);gap:12px}.warningFilterGrid label{display:grid;gap:5px}.warningFilterGrid label span{font-size:12px;color:var(--text-muted)}.warningTitle input{width:100%}.warningSummary{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:10px;margin:18px 0}.warningCard{display:grid;gap:3px;padding:12px 14px;border:1px solid var(--border);border-left-width:7px;border-radius:10px;background:rgba(255,255,255,.6)}.warningCard.yellow{border-left-color:#ffd600}.warningCard.orange{border-left-color:#f27e20}.warningCard.red{border-left-color:#cc1c20}.warningCard span,.warningCard small{color:var(--text-muted)}.warningsPreview{padding-top:24px}.warningsPreview canvas{display:block;width:100%;height:auto}.inlineNotice button{margin-left:14px}@media(max-width:1100px){.warningsSetup{grid-template-columns:1fr}.warningFilterGrid{grid-template-columns:repeat(2,minmax(220px,1fr))}.warningTitle{grid-column:1/-1}}@media(max-width:650px){.warningFilterGrid,.resolutionChoices{grid-template-columns:1fr}.warningTitle{grid-column:auto}} +.warningsPage{max-width:1900px}.warningsHeading{margin-bottom:18px}.warningsSetup{display:grid;grid-template-columns:260px minmax(0,1fr);gap:28px;padding:18px 0 24px;border-top:1px solid var(--border);border-bottom:1px solid var(--border)}.warningsSetup h2{margin:0 0 14px;font-size:20px}.warningFilterGrid{display:grid;grid-template-columns:220px 260px minmax(320px,1fr);gap:12px}.warningFilterGrid label{display:grid;gap:5px}.warningFilterGrid label span{font-size:12px;color:var(--text-muted)}.warningTitle input{width:100%}.warningSummary{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));align-items:start;gap:10px;margin:18px 0}.warningCard{display:grid;gap:3px;padding:12px 14px;border:1px solid var(--border);border-left-width:7px;border-radius:10px;background:rgba(255,255,255,.6);width:100%;appearance:none;font:inherit;text-align:left;cursor:pointer}.warningCard.yellow{border-left-color:#ffd600}.warningCard.orange{border-left-color:#f27e20}.warningCard.red{border-left-color:#cc1c20}.warningCard span,.warningCard small{color:var(--text-muted)}.warningCard.active{background:rgba(255,255,255,.95);border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-pale)}.warningCardDetails{display:grid;gap:4px;margin-top:6px}.warningCardDetails p{margin:0;font-size:13px;line-height:1.4;color:var(--text-muted)}.warningRisks{font-style:italic}.warningsPreview{padding-top:24px}.warningsPreview canvas{display:block;width:100%;height:auto}.inlineNotice button{margin-left:14px}@media(max-width:1100px){.warningsSetup{grid-template-columns:1fr}.warningFilterGrid{grid-template-columns:repeat(2,minmax(220px,1fr))}.warningTitle{grid-column:1/-1}}@media(max-width:650px){.warningFilterGrid,.resolutionChoices{grid-template-columns:1fr}.warningTitle{grid-column:auto}}