Skip to content

Commit be04592

Browse files
gutzbenjclaude
andauthored
feat(app): let the Explorer name the elevation of an interpolated point (#1899)
* feat(app): let the Explorer name the elevation of an interpolated point The backend answers for a point's elevation now, bringing each station's readings to it before using them, and the app had no way to say one -- so interpolating in the Alps blended a valley station and a summit one as though altitude did not exist, which is 15 K around Garmisch. The field sits beside the coordinates and is optional: left empty nothing is corrected, as before. Choosing the point by station fills it with that station's own height, a station naming its altitude as well as its position -- and that is also what the REST API does when a station names the point, so the two agree. Eleven locales, each in its own words. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011BMmz23eRedUH9VTYaMQzV * fix(app): let the elevation change the answer, and survive a station without one Four from review, two of which made the field unusable. The Explorer holds the parameters it last fetched with and disables the button while they are unchanged -- and it did not hold the elevation. Typing one after a fetch left the button dead and the uncorrected answer on screen, with no way to ask again and nothing said about why. `station?.height` is null for a station whose provider reports no height, and several report none for any station -- FMI, IPMA, LHMT, the Environment Agency, WSV and IMGW's hydrology, which is the same list the backend change turns away. Null is not undefined, so it reached `toString()` and took the download with it. The shared type says `number | null` now, so the compiler has the same say. The input was seeded once while the station watcher writes the model directly, so choosing a station and switching to manual showed an empty box against a query carrying that station's height -- a correction applied invisibly. The box follows the model. And a shared Explorer link carries the elevation, a link that reproduces a different answer being worse than one that reproduces none. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011BMmz23eRedUH9VTYaMQzV * fix(app): stop the elevation dragging the coordinates out of a chosen station The watcher I added to keep the elevation box in step with the model fed the watcher that rebuilds the model from the boxes, and those boxes hold only what was typed into them -- nothing, when the point came from a station. So choosing a station set its coordinates and then immediately cleared them, leaving the Fetch button dead for the whole station source. It survived only for stations whose provider reports no height, which made it look intermittent. The coordinates and the elevation are watched apart now: what fills the coordinates is the station, not the manual boxes. Confirmed by running the watchers, as the report did -- the station's 47.9/8.0 stay put and typing an elevation afterwards moves only the elevation. The fetch guard compared an elevation the fetch never recorded, so the button stayed enabled for the rest of the session once one was set. It records it. And the elevation went into the shared URL without anything reading it back, or watching the point to write it in the first place. The URL watcher follows the interpolation now, and the coordinates and elevation are read from the query on the way in, which the coordinates never were either. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011BMmz23eRedUH9VTYaMQzV * test(app): cover the wiring of the interpolated point Three bugs in three rounds, all in the same watchers, and no test could reach them: the wiring lived in `<script setup>`, where nothing can import it. It is a composable now, and the component calls it. Six tests over what broke -- a station's coordinates surviving its elevation, a station whose provider reports no height, a typed elevation leaving the coordinates alone, an empty box meaning no correction. Each was checked against the bug it describes by putting that bug back: watching the three inputs together fails two of them, and passing a null height through fails a third. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011BMmz23eRedUH9VTYaMQzV * fix(app): show the whole point, and let its boxes follow it The elevation field was drawn only for a point given by coordinates, so a point given by a station carried that station's height without showing it -- and with an elevation set the backend drops every neighbouring station whose own height is unknown, which for ECCC and met.no is some of them. A quietly different answer with nothing on screen to explain it. The field belongs to the point rather than to one way of naming it, so it is drawn for both. The coordinate boxes were seeded once and never followed the model, which the elevation sync made dangerous rather than merely untidy: after choosing a station and switching to manual entry, the boxes read empty while the model held the station's position, and typing a latitude read the still-empty longitude box and wiped a coordinate nobody had touched. All three boxes follow the model now, by one function, and a test says so -- it fails when the coordinate half is taken out again. `numberFromQuery` took `1e400` for a number and a repeated key for a string. A link that names its point by a station still restores nothing, the station having to be fetched before it can be selected. That is unchanged, and the comment says so rather than claiming a round trip that is half there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011BMmz23eRedUH9VTYaMQzV * fix(app): take only numbers an answer can be given for, from either side The explorer's query parser rejects `1e400` because Infinity would travel into a shared link and on to an API that can say nothing about it -- and the boxes it guards took the same value happily, `Number.isNaN` letting through what `Number.isFinite` was there to stop. One parser reads a box now, and a test types `1e400` into two of them. A `UInput` of type number writes a number back through a model its own props declare as a string, so the guard that keeps a box in step compared `'1000'` against `1000`, always found them different, and wrote on every model change -- each write firing the box's watcher again, and with the URL now following the point, twice the router calls per keystroke. It compares the box as text. The refs stay typed as the component declares them, since saying `string | number` there is what the template will not take. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011BMmz23eRedUH9VTYaMQzV * fix(app): let a station's height go when the point is described afresh Choosing a summit station filled the elevation with its 1000 m, and switching to typing coordinates kept it: a city at 34 m answered as though it stood on the mountain, six degrees of air temperature, with every neighbouring station of unknown height dropped for company. The height came from the point, so it goes when the point is described afresh. A shared link carries the elevation from either source now. The box is drawn for both and sent for both, and a height typed over a station's is the user's rather than the station's, so writing it only for hand-typed coordinates dropped what someone had chosen deliberately. And the composable's own docstring sat above the parser declared before it, which left it describing the wrong function and the composable describing nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011BMmz23eRedUH9VTYaMQzV * fix(app): change the source once, and only when it changes Clicking the source already in use was treated as a change, and dropped the height of a station that stayed selected: nothing moved on screen, and the next answer came back uncorrected -- six degrees of air temperature at 1000 m. It returns early now. Going back to the station source left the elevation empty, the station never having left the select and its watcher therefore staying silent. The source change names the station's height again. Writing that took two assignments in a tick, and the second spread the model the first had replaced, so the source change was undone by the elevation change that followed it -- which a component test caught, the composable tests being blind to `setSource`. A station's contribution to the point is given as a patch now, so one assignment carries both. `forgetPoint` said it forgot the point while forgetting only the elevation, and is named for what it does. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011BMmz23eRedUH9VTYaMQzV * fix(app): let the source button leave a typed point alone, and drop a stale station Switching to the station source took the empty answer of a select with nothing chosen in it and wrote that over the point -- so typing coordinates, clicking through to the station list and back left both boxes blank. A select with nothing in it has nothing to say about the point. The select itself never followed the model. A provider or dataset change replaces the model and clears the station, and the select went on holding the old one, ready to write it back -- coordinates, height and all -- for a dataset that may not have that station at all. Both have tests, and both tests fail with the bugs put back. `forgetElevation` went with them: the source change does that inline now, and the only thing left calling it was its own test. The component test waits on promises rather than on twenty milliseconds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011BMmz23eRedUH9VTYaMQzV --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 1154a7b commit be04592

20 files changed

Lines changed: 430 additions & 28 deletions

CHANGELOG_APP.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ Types of changes:
1616

1717
## [Unreleased]
1818

19+
### Added
20+
21+
- `[Explorer]` An elevation for the point an interpolation or summary answers for, in metres above
22+
sea level, beside the coordinates. Air temperature falls about 0.65 K per 100 m, so a point in a
23+
valley and one on the ridge above it get different answers from the same stations; left empty,
24+
the readings are used as they come. Choosing the point by station fills it with that station's
25+
own height, a station naming its altitude as well as its position, and the field is shown either
26+
way so that a height is never carried without being seen
27+
1928
### Fixed
2029

2130
- `[Meteogram]` Wind barbs convert metres per second to knots the way the API does, a knot being a

app/app/components/DataViewer.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const stationSelection = computed(() => {
2424
return {
2525
mode: 'station' as const,
2626
selection: { stations: [] as { station_id: string }[] },
27-
interpolation: { source: 'manual' as const, latitude: undefined, longitude: undefined, station: undefined },
27+
interpolation: { source: 'manual' as const, latitude: undefined, longitude: undefined, elevation: undefined, station: undefined },
2828
dateRange: { startDate: undefined, endDate: undefined },
2929
}
3030
}
@@ -149,6 +149,7 @@ const apiQuery = computed(() => {
149149
...base,
150150
latitude: interp?.latitude,
151151
longitude: interp?.longitude,
152+
elevation: interp?.elevation,
152153
use_nearby_station_distance: props.settings.useNearbyStationDistance,
153154
}
154155
// Add interpolation station distance if provided (filter out empty values)
@@ -171,6 +172,7 @@ const apiQuery = computed(() => {
171172
...base,
172173
latitude: interp?.latitude,
173174
longitude: interp?.longitude,
175+
elevation: interp?.elevation,
174176
use_nearby_station_distance: props.settings.useNearbyStationDistance,
175177
}
176178
// Add summary station distance if provided (filter out empty values)
@@ -431,6 +433,8 @@ async function downloadValues(format: string, extension: string) {
431433
params.set('latitude', interp.latitude.toString())
432434
if (interp?.longitude !== undefined)
433435
params.set('longitude', interp.longitude.toString())
436+
if (interp?.elevation !== undefined)
437+
params.set('elevation', interp.elevation.toString())
434438
// Add interpolation settings
435439
params.set('use_nearby_station_distance', props.settings.useNearbyStationDistance.toString())
436440
const stationDistance = Object.entries(props.settings.useStationDistancePerParameter)
@@ -456,6 +460,8 @@ async function downloadValues(format: string, extension: string) {
456460
params.set('latitude', interp.latitude.toString())
457461
if (interp?.longitude !== undefined)
458462
params.set('longitude', interp.longitude.toString())
463+
if (interp?.elevation !== undefined)
464+
params.set('elevation', interp.elevation.toString())
459465
// Add summary settings (uses same settings as interpolation)
460466
params.set('use_nearby_station_distance', props.settings.useNearbyStationDistance.toString())
461467
const stationDistance = Object.entries(props.settings.useStationDistancePerParameter)

app/app/components/InterpolationSummarySelection.vue

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,21 @@ const sourceOptions = computed(() => [
1515
{ value: 'station' as InterpolationSource, label: t('interpolation.fromStation'), icon: 'i-lucide-map-pin' },
1616
])
1717
18-
// For manual input
19-
const latitudeInput = ref<string>(modelValue.value.latitude?.toString() ?? '')
20-
const longitudeInput = ref<string>(modelValue.value.longitude?.toString() ?? '')
21-
22-
// Watch inputs and update model
23-
watch([latitudeInput, longitudeInput], ([lat, lon]) => {
24-
const latNum = Number.parseFloat(lat)
25-
const lonNum = Number.parseFloat(lon)
26-
modelValue.value = {
27-
...modelValue.value,
28-
latitude: Number.isNaN(latNum) ? undefined : latNum,
29-
longitude: Number.isNaN(lonNum) ? undefined : lonNum,
30-
}
31-
})
18+
// the point, and the boxes that describe it -- see `useInterpolationPoint` for why the coordinates
19+
// and the elevation are kept apart
20+
const { latitudeInput, longitudeInput, elevationInput, fromStation, pointFromStation } = useInterpolationPoint(modelValue)
3221
3322
// For station selection
3423
const selectedStation = ref<Station | undefined>(modelValue.value.station)
3524
36-
watch(selectedStation, (station) => {
37-
modelValue.value = {
38-
...modelValue.value,
39-
station,
40-
latitude: station?.latitude,
41-
longitude: station?.longitude,
42-
}
25+
watch(selectedStation, fromStation)
26+
27+
// the parent replaces the whole model when the provider or dataset changes, which clears the
28+
// station -- and a select still holding the old one would write it back, coordinates, height and
29+
// all, for a station the new dataset may not have
30+
watch(() => modelValue.value.station, (station) => {
31+
if (station !== selectedStation.value)
32+
selectedStation.value = station
4333
})
4434
4535
// Fetch stations for station source
@@ -85,10 +75,20 @@ watch(() => props.parameterSelection, () => {
8575
}, { deep: true, immediate: true })
8676
8777
function setSource(source: InterpolationSource) {
88-
modelValue.value = {
89-
...modelValue.value,
90-
source,
91-
}
78+
// clicking the source already in use is not a change, and treating it as one dropped the height
79+
// of a station that stayed selected -- the form unchanged on screen, the next answer
80+
// uncorrected, which at 1000 m is six degrees of air temperature
81+
if (source === modelValue.value.source)
82+
return
83+
// one assignment: a second write in the same tick spreads the model the first replaced, and the
84+
// source change was being undone by the elevation change that followed it
85+
modelValue.value = source === 'station'
86+
// back to the station still in the select: it names its height again, where the watcher below
87+
// stays silent, the selection itself not having changed. With nothing selected it says
88+
// nothing -- taking its empty answer would clear coordinates someone had just typed
89+
? { ...modelValue.value, source, ...(selectedStation.value ? pointFromStation(selectedStation.value) : {}) }
90+
// the elevation came from wherever the point did, so it goes with it
91+
: { ...modelValue.value, source, elevation: undefined }
9292
}
9393
9494
// Display coordinates
@@ -159,6 +159,18 @@ const displayCoords = computed(() => {
159159
</UFormField>
160160
</div>
161161

162+
<!-- part of the point whichever way the point was given: a station fills it with its own
163+
height, and leaving it filled in silently is what drops the neighbours that have none -->
164+
<UFormField :label="t('interpolation.elevation')" :hint="t('interpolation.elevationHint')">
165+
<UInput
166+
v-model="elevationInput"
167+
type="number"
168+
step="1"
169+
placeholder="e.g. 34"
170+
class="w-full"
171+
/>
172+
</UFormField>
173+
162174
<div v-if="displayCoords" class="text-sm text-gray-500">
163175
{{ t('interpolation.coords', { coords: displayCoords }) }}
164176
</div>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import type { Ref } from 'vue'
2+
import type { InterpolationSelection } from '~/types/station-selection-state.type'
3+
4+
/**
5+
* Read a box as the number it names, or nothing.
6+
*
7+
* Finite, not merely not-NaN: `1e400` parses to Infinity, which would travel into the model, into
8+
* a shared link, and on to an API that can give no answer for it.
9+
*/
10+
function numberFromBox(value: string | number): number | undefined {
11+
const parsed = Number.parseFloat(String(value))
12+
return Number.isFinite(parsed) ? parsed : undefined
13+
}
14+
15+
/**
16+
* Wire the point an interpolation or summary answers for to the boxes that describe it.
17+
*
18+
* Three values, filled from two places: the coordinates and the elevation are typed in manual
19+
* mode, while choosing a station writes all three at once. Keeping that straight is the whole of
20+
* this, and getting it wrong is quiet -- a query that carries a height the form does not show, or
21+
* a station whose coordinates are cleared by an elevation arriving after them.
22+
*/
23+
export function useInterpolationPoint(modelValue: Ref<InterpolationSelection>) {
24+
// `string`, which is what `UInput` declares its model to be -- though one of type number writes
25+
// a number back through it at runtime, so everything reading a box says which it wants rather
26+
// than trusting the annotation
27+
const latitudeInput = ref<string>(modelValue.value.latitude?.toString() ?? '')
28+
const longitudeInput = ref<string>(modelValue.value.longitude?.toString() ?? '')
29+
const elevationInput = ref<string>(modelValue.value.elevation?.toString() ?? '')
30+
31+
// choosing a station writes all three straight into the model, so the boxes follow it -- or they
32+
// show empty while the query carries a point the user cannot see. All three, not the elevation
33+
// alone: with the coordinate boxes left stale, typing one of them made the watcher below read
34+
// the other box, still empty, and wipe a coordinate nobody had touched
35+
function follow(box: Ref<string>, value: number | undefined) {
36+
const shown = value?.toString() ?? ''
37+
// against the box as text: it may hold a number, and `'1000' !== 1000` would write on every
38+
// change, each write firing the box's own watcher again
39+
if (shown !== String(box.value))
40+
box.value = shown
41+
}
42+
watch(() => modelValue.value.latitude, latitude => follow(latitudeInput, latitude))
43+
watch(() => modelValue.value.longitude, longitude => follow(longitudeInput, longitude))
44+
watch(() => modelValue.value.elevation, elevation => follow(elevationInput, elevation))
45+
46+
// the coordinates and the elevation are watched apart. These boxes hold only what was typed into
47+
// them, which in station mode is nothing, so writing all three together let an elevation arriving
48+
// from a station take that station's coordinates out with it
49+
watch([latitudeInput, longitudeInput], ([latitude, longitude]) => {
50+
modelValue.value = {
51+
...modelValue.value,
52+
latitude: numberFromBox(latitude),
53+
longitude: numberFromBox(longitude),
54+
}
55+
})
56+
57+
watch(elevationInput, (elevation) => {
58+
// optional: left empty, the readings are interpolated as they come
59+
const parsed = numberFromBox(elevation)
60+
if (parsed !== modelValue.value.elevation)
61+
modelValue.value = { ...modelValue.value, elevation: parsed }
62+
})
63+
64+
/**
65+
* What a station says about the point: its position, and its altitude where the provider
66+
* reports one.
67+
*
68+
* Given apart from the writing so that a caller changing something else at the same time can
69+
* put both into one assignment -- two writes in a tick have the second spreading a model the
70+
* first has already replaced, and the first is lost.
71+
*/
72+
function pointFromStation(station: Station | undefined): Partial<InterpolationSelection> {
73+
return {
74+
station,
75+
latitude: station?.latitude,
76+
longitude: station?.longitude,
77+
elevation: station?.height ?? undefined,
78+
}
79+
}
80+
81+
/** Take the point from a station. */
82+
function fromStation(station: Station | undefined) {
83+
modelValue.value = { ...modelValue.value, ...pointFromStation(station) }
84+
}
85+
86+
return { latitudeInput, longitudeInput, elevationInput, fromStation, pointFromStation }
87+
}

app/app/pages/explorer.vue

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ function toQuery(paramSel: ParameterSelectionState, stationSel: StationSelection
111111
else if (stationSel.interpolation.station) {
112112
q.interpolationStation = stationSel.interpolation.station.station_id
113113
}
114+
// outside the branch: the box is shown for either source and sent for either, and a height
115+
// the user typed over a station's is theirs rather than the station's. It survives the round
116+
// trip only for a point given by coordinates: picking the station again names its own height,
117+
// which is what choosing a station means
118+
if (stationSel.interpolation.elevation !== undefined)
119+
q.elevation = stationSel.interpolation.elevation.toString()
114120
}
115121
if (stationSel.dateRange.startDate)
116122
q.startDate = stationSel.dateRange.startDate
@@ -136,10 +142,27 @@ function dataSettingsToQuery(settings: DataSettings): Record<string, string> {
136142
137143
const showAbout = ref(false)
138144
const parameterSelectionState = ref<ParameterSelectionState>(fromQuery(route.query))
145+
function numberFromQuery(value: unknown): number | undefined {
146+
// a repeated key arrives as an array, and `1e400` parses to Infinity, which travels back into
147+
// the URL and on to the API as a number no answer can be given for
148+
const first = Array.isArray(value) ? value[0] : value
149+
const parsed = Number.parseFloat(String(first ?? ''))
150+
return Number.isFinite(parsed) ? parsed : undefined
151+
}
152+
139153
const stationSelectionState = ref<StationSelectionState>({
140154
mode: modeFromQuery(route.query),
141155
selection: { stations: [] },
142-
interpolation: { source: (route.query.interpolationSource as 'manual' | 'station') || 'manual' },
156+
interpolation: {
157+
source: (route.query.interpolationSource as 'manual' | 'station') || 'manual',
158+
// read back what `toQuery` writes for a point given by coordinates, so a shared link
159+
// reproduces the answer it was copied from. A point given by a station is written as an id
160+
// and not restored -- the station itself has to be fetched before it can be selected, which
161+
// `initialStationIds` does for station mode and nothing does for this one yet
162+
latitude: numberFromQuery(route.query.lat),
163+
longitude: numberFromQuery(route.query.lon),
164+
elevation: numberFromQuery(route.query.elevation),
165+
},
143166
dateRange: {
144167
startDate: route.query.startDate?.toString(),
145168
endDate: route.query.endDate?.toString(),
@@ -229,6 +252,7 @@ watch(
229252
[
230253
parameterSelectionState,
231254
() => stationSelectionState.value.selection.stations,
255+
() => stationSelectionState.value.interpolation,
232256
() => dataSettings.value.humanize,
233257
() => dataSettings.value.convertUnits,
234258
() => dataSettings.value.shape,
@@ -369,6 +393,7 @@ const lastFetchedParams = ref<{
369393
stations: string
370394
interpolationLat?: number
371395
interpolationLon?: number
396+
interpolationElevation?: number
372397
startDate?: string
373398
endDate?: string
374399
settings: string
@@ -402,6 +427,7 @@ const canFetch = computed(() => {
402427
: '',
403428
interpolationLat: ss.interpolation.latitude,
404429
interpolationLon: ss.interpolation.longitude,
430+
interpolationElevation: ss.interpolation.elevation,
405431
startDate: ss.dateRange.startDate,
406432
endDate: ss.dateRange.endDate,
407433
settings: JSON.stringify(dataSettings.value),
@@ -417,6 +443,7 @@ const canFetch = computed(() => {
417443
&& currentParams.stations === lastFetchedParams.value.stations
418444
&& currentParams.interpolationLat === lastFetchedParams.value.interpolationLat
419445
&& currentParams.interpolationLon === lastFetchedParams.value.interpolationLon
446+
&& currentParams.interpolationElevation === lastFetchedParams.value.interpolationElevation
420447
&& currentParams.startDate === lastFetchedParams.value.startDate
421448
&& currentParams.endDate === lastFetchedParams.value.endDate
422449
&& currentParams.settings === lastFetchedParams.value.settings
@@ -449,6 +476,7 @@ function fetchData() {
449476
: '',
450477
interpolationLat: ss.interpolation.latitude,
451478
interpolationLon: ss.interpolation.longitude,
479+
interpolationElevation: ss.interpolation.elevation,
452480
startDate: ss.dateRange.startDate,
453481
endDate: ss.dateRange.endDate,
454482
settings: JSON.stringify(dataSettings.value),

app/app/types/station-selection-state.type.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ export interface InterpolationSelection {
1010
source: InterpolationSource
1111
latitude?: number
1212
longitude?: number
13+
/**
14+
* Metres above sea level. The backend brings each station's readings to this height before
15+
* using them, for the quantities that fall with it -- air temperature, dew point.
16+
*/
17+
elevation?: number
1318
station?: Station
1419
}
1520

app/i18n/locales/cs.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,8 @@
512512
"fromStation": "Ze stanice",
513513
"latitude": "Zeměpisná šířka",
514514
"longitude": "Zeměpisná délka",
515+
"elevation": "Nadmořská výška",
516+
"elevationHint": "volitelné, m n. m.",
515517
"selectStationForCoords": "Vyberte stanici pro souřadnice",
516518
"selectStation": "Vyberte stanici",
517519
"loadingStations": "Načítání stanic …",

app/i18n/locales/da.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,8 @@
512512
"fromStation": "Fra en station",
513513
"latitude": "Breddegrad",
514514
"longitude": "Længdegrad",
515+
"elevation": "Højde",
516+
"elevationHint": "valgfri, m over havet",
515517
"selectStationForCoords": "Vælg en station til koordinater",
516518
"selectStation": "Vælg en station",
517519
"loadingStations": "Indlæser stationer …",

app/i18n/locales/de-hh.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,8 @@
512512
"fromStation": "Von Station",
513513
"latitude": "Breitengrad",
514514
"longitude": "Längengrad",
515+
"elevation": "Höhe",
516+
"elevationHint": "optional, m över NN",
515517
"selectStationForCoords": "Station für Koordinaten auswählen",
516518
"selectStation": "Station auswählen",
517519
"loadingStations": "Stationen werden geladen …",

app/i18n/locales/de.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,8 @@
512512
"fromStation": "Von Station",
513513
"latitude": "Breitengrad",
514514
"longitude": "Längengrad",
515+
"elevation": "Höhe",
516+
"elevationHint": "optional, m über NN",
515517
"selectStationForCoords": "Station für Koordinaten auswählen",
516518
"selectStation": "Station auswählen",
517519
"loadingStations": "Stationen werden geladen …",

0 commit comments

Comments
 (0)