|
1 | 1 | <script lang="ts" context="module">
|
2 | 2 | interface SubjectValue {
|
3 |
| - humidity: number; |
4 |
| - issuedAt: string; // ISO-8601 date |
| 3 | + humidity: number |
| 4 | + issuedAt: string // ISO-8601 date |
5 | 5 | pressure: {
|
6 |
| - atSeaLevel: string; |
7 |
| - }; |
8 |
| - rainfall: string; |
9 |
| - temp: number; |
| 6 | + atSeaLevel: string |
| 7 | + } |
| 8 | + rainfall: string |
| 9 | + temp: number |
10 | 10 | wind: {
|
11 |
| - averageSpeed: 6; |
12 |
| - direction: string; // Compass direction |
13 |
| - strength: string; // Description of wind strength. |
14 |
| - }; |
| 11 | + averageSpeed: 6 |
| 12 | + direction: string // Compass direction |
| 13 | + strength: string // Description of wind strength. |
| 14 | + } |
15 | 15 | }
|
16 | 16 |
|
17 | 17 | interface MetServiceMapMarkerSubject {
|
18 |
| - asAt: string; // ISO-8601 date |
19 |
| - value: SubjectValue; |
20 |
| - type: "local" | "traffic-camera"; |
| 18 | + asAt: string // ISO-8601 date |
| 19 | + value: SubjectValue |
| 20 | + type: 'local' | 'traffic-camera' |
21 | 21 | }
|
22 | 22 |
|
23 | 23 | interface MetServiceMapMarker {
|
24 |
| - label: string; |
25 |
| - point: [number, number]; |
26 |
| - subject: MetServiceMapMarkerSubject[]; |
| 24 | + label: string |
| 25 | + point: [number, number] |
| 26 | + subject: MetServiceMapMarkerSubject[] |
27 | 27 | }
|
28 | 28 |
|
29 | 29 | interface MetServiceLiveWeatherResponse {
|
30 |
| - isSkiSeason: boolean; |
| 30 | + isSkiSeason: boolean |
31 | 31 | layout: {
|
32 | 32 | primary: {
|
33 | 33 | map: {
|
34 |
| - markers: MetServiceMapMarker[]; |
35 |
| - }; |
36 |
| - }; |
37 |
| - }; |
| 34 | + markers: MetServiceMapMarker[] |
| 35 | + } |
| 36 | + } |
| 37 | + } |
38 | 38 | }
|
39 | 39 | </script>
|
40 | 40 |
|
41 | 41 | <script lang="ts">
|
42 |
| - import Feature from "ol/Feature"; |
43 |
| - import type { FeatureLike } from "ol/Feature"; |
44 |
| - import { fromLonLat, get, toLonLat } from "ol/proj"; |
45 |
| - import Point from "ol/geom/Point"; |
46 |
| - import { corsFetch } from "../utils/cors"; |
47 |
| - import { Style, Circle, Stroke, Fill, Text } from "ol/style"; |
48 |
| - import VectorLayer from "../ol/VectorLayer.svelte"; |
49 |
| - import VectorSource from "ol/source/Vector"; |
50 |
| - import Popup from "../ol/Popup.svelte"; |
51 |
| - import type { Coordinate } from "ol/coordinate"; |
52 |
| -import RelativeTime from "../components/RelativeTime.svelte"; |
| 42 | + import Feature from 'ol/Feature' |
| 43 | + import { fromLonLat } from 'ol/proj' |
| 44 | + import Point from 'ol/geom/Point' |
| 45 | + import { Style, Circle, Stroke, Fill, Text } from 'ol/style' |
| 46 | + import VectorLayer from '../ol/VectorLayer.svelte' |
| 47 | + import VectorSource from 'ol/source/Vector' |
| 48 | + import Popup from '../ol/Popup.svelte' |
| 49 | + import type { Coordinate } from 'ol/coordinate' |
| 50 | + import RelativeTime from '../components/RelativeTime.svelte' |
53 | 51 |
|
54 | 52 | const getWeatherIcon = (value: SubjectValue): string => {
|
55 | 53 | const options = {
|
56 |
| - sunCloud: "🌤️", |
57 |
| - rain: "🌧️", |
58 |
| - cloud: "☁️", |
59 |
| - storm: "⛈️", |
60 |
| - snow: "🌨️", |
61 |
| - sun: "☀️", |
62 |
| - wind: "💨", |
63 |
| - }; |
| 54 | + sunCloud: '🌤️', |
| 55 | + rain: '🌧️', |
| 56 | + cloud: '☁️', |
| 57 | + storm: '⛈️', |
| 58 | + snow: '🌨️', |
| 59 | + sun: '☀️', |
| 60 | + wind: '💨', |
| 61 | + } |
64 | 62 |
|
65 |
| - const rain = parseFloat(value.rainfall); |
| 63 | + const rain = parseFloat(value.rainfall) |
66 | 64 |
|
67 | 65 | if (rain > 0) {
|
68 | 66 | // I guess it'll be snow/hail if its cold?
|
69 |
| - return value.temp < 0 ? options.snow : options.rain; |
| 67 | + return value.temp < 0 ? options.snow : options.rain |
70 | 68 | }
|
71 | 69 |
|
72 | 70 | // I guess wind greater than 20km/h is windy?
|
73 |
| - if (value.wind && value.wind.averageSpeed > 20) return options.wind; |
| 71 | + if (value.wind && value.wind.averageSpeed > 20) return options.wind |
74 | 72 |
|
75 | 73 | // I don't actually have the data to know if it's cloudy, so this seems safe.
|
76 |
| - return options.sunCloud; |
77 |
| - }; |
| 74 | + return options.sunCloud |
| 75 | + } |
78 | 76 |
|
79 | 77 | const getFeatures = async () => {
|
80 |
| - const url = "/api/weather"; |
81 |
| - const response = await fetch(url); |
82 |
| - const data = (await response.json()) as MetServiceLiveWeatherResponse; |
| 78 | + const url = '/api/weather' |
| 79 | + const response = await fetch(url) |
| 80 | + const data = (await response.json()) as MetServiceLiveWeatherResponse |
83 | 81 |
|
84 |
| - const observations = data?.layout?.primary?.map?.markers; |
85 |
| - if (!observations) return null; |
| 82 | + const observations = data?.layout?.primary?.map?.markers |
| 83 | + if (!observations) return null |
86 | 84 |
|
87 | 85 | return (
|
88 | 86 | observations
|
89 | 87 | // Filter out traffic cameras and stuff.
|
90 | 88 | .filter((marker) => {
|
91 |
| - if (!marker.subject.length) return false; |
| 89 | + if (!marker.subject.length) return false |
92 | 90 |
|
93 |
| - if (marker.subject[0].type === "traffic-camera") return false; |
| 91 | + if (marker.subject[0].type === 'traffic-camera') return false |
94 | 92 |
|
95 |
| - return true; |
| 93 | + return true |
96 | 94 | })
|
97 | 95 | .map((marker) => {
|
98 |
| - const coords = fromLonLat([marker.point[1], marker.point[0]]); |
99 |
| - const feature = new Feature(new Point(coords)); |
100 |
| - const subject = marker.subject[0]; |
101 |
| - const observation = subject?.value; |
| 96 | + const coords = fromLonLat([marker.point[1], marker.point[0]]) |
| 97 | + const feature = new Feature(new Point(coords)) |
| 98 | + const subject = marker.subject[0] |
| 99 | + const observation = subject?.value |
102 | 100 | if (observation && !observation.issuedAt)
|
103 |
| - observation.issuedAt = subject.asAt; |
| 101 | + observation.issuedAt = subject.asAt |
104 | 102 |
|
105 | 103 | feature.setStyle(
|
106 | 104 | new Style({
|
107 | 105 | image: new Circle({
|
108 | 106 | radius: 12,
|
109 |
| - fill: new Fill({ color: "blue" }), |
110 |
| - stroke: new Stroke({ color: "white" }), |
| 107 | + fill: new Fill({ color: 'blue' }), |
| 108 | + stroke: new Stroke({ color: 'white' }), |
111 | 109 | }),
|
112 | 110 | text: new Text({
|
113 |
| - fill: new Fill({ color: "white" }), |
| 111 | + fill: new Fill({ color: 'white' }), |
114 | 112 | text: getWeatherIcon(observation),
|
115 | 113 | }),
|
116 | 114 | })
|
117 |
| - ); |
118 |
| - feature.set("observation", observation); |
119 |
| - feature.set("subject", subject); |
| 115 | + ) |
| 116 | + feature.set('observation', observation) |
| 117 | + feature.set('subject', subject) |
120 | 118 |
|
121 |
| - return feature; |
| 119 | + return feature |
122 | 120 | })
|
123 |
| - ); |
124 |
| - }; |
| 121 | + ) |
| 122 | + } |
125 | 123 |
|
126 |
| - let currentObservation: SubjectValue; |
127 |
| - let coords: Coordinate; |
| 124 | + let currentObservation: SubjectValue |
| 125 | + let coords: Coordinate |
128 | 126 | </script>
|
129 | 127 |
|
130 |
| -<style> |
131 |
| - .weather { |
132 |
| - white-space: nowrap; |
133 |
| - } |
134 |
| -</style> |
135 |
| - |
136 | 128 | {#await getFeatures() then features}
|
137 | 129 | <VectorLayer
|
138 | 130 | id="weather"
|
139 | 131 | title="Live Weather"
|
140 | 132 | visible={false}
|
141 | 133 | on:featureClick={(event) => {
|
142 |
| - const feature = event.detail.feature; |
143 |
| - const geometry = feature.getGeometry(); |
144 |
| - currentObservation = feature.get('observation'); |
145 |
| - coords = geometry.getCoordinates(); |
| 134 | + const feature = event.detail.feature |
| 135 | + const geometry = feature.getGeometry() |
| 136 | + currentObservation = feature.get('observation') |
| 137 | + coords = geometry.getCoordinates() |
146 | 138 | }}
|
147 | 139 | source={new VectorSource({ features })} />
|
148 | 140 | {/await}
|
@@ -173,10 +165,17 @@ import RelativeTime from "../components/RelativeTime.svelte";
|
173 | 165 | <p>
|
174 | 166 | <b>💨 Wind:</b>
|
175 | 167 | {currentObservation.wind.strength}
|
176 |
| - {currentObservation.wind.averageSpeed}km/h {currentObservation.wind.direction} |
| 168 | + {currentObservation.wind.averageSpeed}km/h {currentObservation.wind |
| 169 | + .direction} |
177 | 170 | </p>
|
178 | 171 | {/if}
|
179 |
| - <i>Updated: <RelativeTime time={currentObservation.issuedAt}/></i> |
| 172 | + <i>Updated: <RelativeTime time={currentObservation.issuedAt} /></i> |
180 | 173 | </div>
|
181 | 174 | </Popup>
|
182 | 175 | {/if}
|
| 176 | + |
| 177 | +<style> |
| 178 | + .weather { |
| 179 | + white-space: nowrap; |
| 180 | + } |
| 181 | +</style> |
0 commit comments