lsa-planer

LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.

/ src services kartendienste.ts

17,9 KB Rohdatei
src/services/kartendienste.ts — 469 Zeilen
1 import type { Meters } from '@/domain/units';
2 import {
3 ausschnittUm,
4 bboxParameter,
5 epsgFuerZone,
6 unterAusschnitt,
7 type Ausschnitt,
8 type GeoPunkt,
9 } from '@/domain/geometrie/projektion';
10
11 /**
12 * Amtliche Kartendienste als Grundlage des Lageplans.
13 *
14 * WARUM AMTLICHE DIENSTE UND KEIN BEKANNTER KARTENANBIETER
15 *
16 * Die Kacheln der grossen Kartenanbieter duerfen ausserhalb ihrer eigenen
17 * kostenpflichtigen Schnittstellen nicht verwendet, nicht zwischengespeichert
18 * und erst recht nicht in eine fremde Datei eingebettet werden. Genau das waere
19 * hier aber noetig: Ein Signalzeitenplan muss Jahre spaeter noch aus der
20 * Projektdatei allein nachvollziehbar sein.
21 *
22 * Die Landesvermessungsverwaltungen stellen dieselbe Grundlage in besserer
23 * Qualitaet und mit klarer Rechtslage bereit: digitale Orthophotos, also
24 * entzerrte und lagerichtige Luftbilder mit 20 cm Bodenaufloesung oder besser.
25 * "Lagerichtig" ist der entscheidende Punkt - in einem Orthophoto darf gemessen
26 * werden, in einer Schraegaufnahme nicht.
27 *
28 * PRUEFSTAND DER EINTRAEGE
29 *
30 * Jeder Eintrag wurde mit einem echten Bildabruf ueber der jeweiligen
31 * Landeshauptstadt geprueft. Die Angaben zu Nutzungsrecht und Quellenvermerk
32 * stammen aus der Selbstauskunft des Dienstes (GetCapabilities, Felder Fees und
33 * AccessConstraints). Ein Quellenvermerk ist nur dort eingetragen, wo der
34 * Dienst ihn woertlich vorgibt; sonst bleibt das Feld leer, und es wird der
35 * Herausgeber genannt.
36 *
37 * BEZUGSSYSTEM
38 *
39 * Angefordert wird durchweg in ETRS89/UTM (EPSG:258xx) - dem Bezugssystem der
40 * deutschen Landesvermessung. Dessen Karteneinheiten sind Meter; ein daraus
41 * abgegriffener Raeumweg ist damit unmittelbar der Raeumweg in der Wirklichkeit.
42 * Die ausfuehrliche Begruendung steht in domain/geometrie/projektion.ts.
43 */
44
45 export type Kartenart = 'luftbild' | 'karte';
46
47 export interface Kartendienst {
48 readonly id: string;
49 /** Bezeichnung in der Auswahlliste. */
50 readonly name: string;
51 readonly art: Kartenart;
52 /** Herausgeber - erscheint als Quellenangabe im Plan und im Ausdruck. */
53 readonly herausgeber: string;
54 /** Gebiet, fuer das der Dienst Daten liefert. */
55 readonly gebiet: string;
56 readonly basisUrl: string;
57 readonly ebene: string;
58 readonly bildformat: 'image/jpeg' | 'image/png';
59 /** Bodenaufloesung in Metern je Bildpunkt. */
60 readonly aufloesung: Meters;
61 /** Groesste vom Dienst zugelassene Bildkante. */
62 readonly maxKante: number;
63 /** Nutzungsrecht laut Selbstauskunft des Dienstes. */
64 readonly nutzungsrecht: string;
65 /** Woertlich vorgegebener Quellenvermerk, sofern der Dienst einen nennt. */
66 readonly namensnennung: string | null;
67 }
68
69 /**
70 * Bodenaufloesung: 0,1 m bedeutet, dass ein Bildpunkt 10 cm am Boden
71 * entspricht. Feiner anzufordern bringt nichts - das Bild wird dadurch nur
72 * groesser, nicht schaerfer.
73 *
74 * Die Liste ist nach Gebiet geordnet; der Bundesdienst steht vorn, weil er
75 * ueberall gilt.
76 */
77 export const KARTENDIENSTE: readonly Kartendienst[] = [
78 {
79 id: 'de-topplus',
80 name: 'Deutschland · Amtliche Stadtkarte (TopPlusOpen)',
81 art: 'karte',
82 herausgeber: 'Bundesamt für Kartographie und Geodäsie',
83 gebiet: 'Deutschland und Europa',
84 basisUrl: 'https://sgx.geodatenzentrum.de/wms_topplus_open',
85 ebene: 'web',
86 bildformat: 'image/png',
87 aufloesung: 0.2,
88 maxKante: 6000,
89 nutzungsrecht: 'Datenlizenz Deutschland Namensnennung 2.0, geldleistungsfrei',
90 namensnennung: null,
91 },
92 {
93 id: 'bw-dop',
94 name: 'Baden-Württemberg · Luftbild 20 cm',
95 art: 'luftbild',
96 herausgeber: 'Landesamt für Geoinformation und Landentwicklung Baden-Württemberg',
97 gebiet: 'Baden-Württemberg',
98 basisUrl: 'https://owsproxy.lgl-bw.de/owsproxy/ows/WMS_LGL-BW_ATKIS_DOP_20_C',
99 ebene: 'IMAGES_DOP_20_RGB',
100 bildformat: 'image/jpeg',
101 aufloesung: 0.2,
102 maxKante: 15000,
103 nutzungsrecht: 'Datenlizenz Deutschland Namensnennung 2.0, unentgeltlich',
104 namensnennung: 'LGL-BW (Jahr des Datenbezugs), dl-de/by-2-0, www.lgl-bw.de',
105 },
106 {
107 id: 'by-dop',
108 name: 'Bayern · Luftbild 40 cm',
109 art: 'luftbild',
110 herausgeber: 'Bayerische Vermessungsverwaltung',
111 gebiet: 'Bayern',
112 basisUrl: 'https://geoservices.bayern.de/od/wms/dop/v1/dop40',
113 ebene: 'by_dop40c',
114 bildformat: 'image/jpeg',
115 aufloesung: 0.4,
116 maxKante: 6000,
117 nutzungsrecht: 'Creative Commons Namensnennung 4.0 (CC BY 4.0), kostenfrei',
118 namensnennung: null,
119 },
120 {
121 id: 'bebb-dop',
122 name: 'Berlin und Brandenburg · Luftbild 20 cm',
123 art: 'luftbild',
124 herausgeber: 'Landesvermessung und Geobasisinformation Brandenburg',
125 gebiet: 'Berlin und Brandenburg',
126 basisUrl: 'https://isk.geobasis-bb.de/mapproxy/dop20c/service/wms',
127 ebene: 'bebb_dop20c',
128 bildformat: 'image/jpeg',
129 aufloesung: 0.2,
130 maxKante: 4000,
131 nutzungsrecht: 'Datenlizenz Deutschland Namensnennung 2.0, kostenfrei',
132 namensnennung: '© GeoBasis-DE/LGB, dl-de/by-2-0; © Geoportal Berlin, dl-de/by-2-0',
133 },
134 {
135 id: 'he-dop',
136 name: 'Hessen · Luftbild 20 cm',
137 art: 'luftbild',
138 herausgeber: 'Hessische Verwaltung für Bodenmanagement und Geoinformation',
139 gebiet: 'Hessen',
140 basisUrl: 'https://www.gds-srv.hessen.de/cgi-bin/lika-services/ogc-free-images.ows',
141 ebene: 'he_dop20_rgb',
142 bildformat: 'image/jpeg',
143 aufloesung: 0.2,
144 maxKante: 3000,
145 nutzungsrecht: 'Ohne Einschränkung nutzbar, kostenfrei (§ 24 HVGG)',
146 namensnennung: null,
147 },
148 {
149 id: 'mv-dop',
150 name: 'Mecklenburg-Vorpommern · Luftbild 20 cm',
151 art: 'luftbild',
152 herausgeber: 'Amt für Geoinformation, Vermessungs- und Katasterwesen M-V',
153 gebiet: 'Mecklenburg-Vorpommern',
154 basisUrl: 'https://www.geodaten-mv.de/dienste/adv_dop',
155 ebene: 'mv_dop',
156 bildformat: 'image/jpeg',
157 aufloesung: 0.2,
158 maxKante: 4192,
159 nutzungsrecht: 'Es gelten keine Bedingungen; sichtbarer Quellenvermerk verlangt',
160 namensnennung: '© GeoBasis-DE/M-V (Jahr der Datenlieferung)',
161 },
162 {
163 id: 'ni-dop',
164 name: 'Niedersachsen · Luftbild 20 cm',
165 art: 'luftbild',
166 herausgeber: 'Landesamt für Geoinformation und Landesvermessung Niedersachsen',
167 gebiet: 'Niedersachsen',
168 basisUrl: 'https://opendata.lgln.niedersachsen.de/doorman/noauth/dop_wms',
169 ebene: 'ni_dop20',
170 bildformat: 'image/jpeg',
171 aufloesung: 0.2,
172 maxKante: 15000,
173 nutzungsrecht: 'Creative Commons Namensnennung 4.0 (CC BY 4.0)',
174 namensnennung: 'LGLN (Jahr des Datenbezugs), CC BY 4.0',
175 },
176 {
177 id: 'nw-dop',
178 name: 'Nordrhein-Westfalen · Luftbild 10 cm',
179 art: 'luftbild',
180 herausgeber: 'Land NRW, Bezirksregierung Köln - Geobasis NRW',
181 gebiet: 'Nordrhein-Westfalen',
182 basisUrl: 'https://www.wms.nrw.de/geobasis/wms_nw_dop',
183 ebene: 'nw_dop_rgb',
184 bildformat: 'image/jpeg',
185 aufloesung: 0.1,
186 maxKante: 5000,
187 nutzungsrecht: 'Datenlizenz Deutschland Zero 2.0 - Nutzung ohne Einschränkungen',
188 namensnennung: null,
189 },
190 {
191 id: 'rp-dop',
192 name: 'Rheinland-Pfalz · Luftbild 20 cm',
193 art: 'luftbild',
194 herausgeber: 'Vermessungs- und Katasterverwaltung Rheinland-Pfalz',
195 gebiet: 'Rheinland-Pfalz',
196 basisUrl: 'https://geo4.service24.rlp.de/wms/rp_dop20.fcgi',
197 ebene: 'rp_dop20',
198 bildformat: 'image/jpeg',
199 aufloesung: 0.2,
200 maxKante: 10000,
201 nutzungsrecht: 'Datenlizenz Deutschland Namensnennung 2.0, geldleistungsfrei',
202 namensnennung: '© GeoBasis-DE / LVermGeoRP (Jahr des Datenbezugs), dl-de/by-2-0',
203 },
204 {
205 id: 'sn-dop',
206 name: 'Sachsen · Luftbild 20 cm',
207 art: 'luftbild',
208 herausgeber: 'Staatsbetrieb Geobasisinformation und Vermessung Sachsen',
209 gebiet: 'Sachsen',
210 basisUrl: 'https://geodienste.sachsen.de/wms_geosn_dop-rgb/guest',
211 ebene: 'sn_dop_020',
212 bildformat: 'image/jpeg',
213 aufloesung: 0.2,
214 maxKante: 4096,
215 nutzungsrecht: 'Kostenfrei nach den Nutzungsbedingungen des Geoportals Sachsen',
216 namensnennung: null,
217 },
218 {
219 id: 'st-dop',
220 name: 'Sachsen-Anhalt · Luftbild 20 cm',
221 art: 'luftbild',
222 herausgeber: 'Landesamt für Vermessung und Geoinformation Sachsen-Anhalt',
223 gebiet: 'Sachsen-Anhalt',
224 basisUrl:
225 'https://www.geodatenportal.sachsen-anhalt.de/wss/service/ST_LVermGeo_DOP_WMS_OpenData/guest',
226 ebene: 'lsa_lvermgeo_dop20_2',
227 bildformat: 'image/jpeg',
228 aufloesung: 0.2,
229 maxKante: 4096,
230 nutzungsrecht: 'Offene Daten des Landes Sachsen-Anhalt',
231 namensnennung: null,
232 },
233 {
234 id: 'sh-dop',
235 name: 'Schleswig-Holstein · Luftbild 20 cm',
236 art: 'luftbild',
237 herausgeber: 'Landesamt für Vermessung und Geoinformation Schleswig-Holstein',
238 gebiet: 'Schleswig-Holstein',
239 basisUrl: 'https://dienste.gdi-sh.de/WMS_SH_DOP20col_OpenGBD',
240 ebene: 'sh_dop20_rgb',
241 bildformat: 'image/jpeg',
242 aufloesung: 0.2,
243 maxKante: 5000,
244 nutzungsrecht: 'Creative Commons Namensnennung 4.0 (CC BY 4.0)',
245 namensnennung: '© GeoBasis-DE/LVermGeo SH/CC BY 4.0',
246 },
247 ];
248
249 /** Vorbelegung der Auswahl. */
250 export const STANDARD_DIENST = 'nw-dop';
251
252 /** Dienst zu einer Kennung; ohne Treffer der erste Eintrag. */
253 export function dienstMit(id: string): Kartendienst {
254 return KARTENDIENSTE.find((d) => d.id === id) ?? (KARTENDIENSTE[0] as Kartendienst);
255 }
256
257 /** Rechnernamen aller eingetragenen Dienste - fuer die Freigabeliste im Hauptprozess. */
258 export function rechnernamen(): readonly string[] {
259 return [...new Set(KARTENDIENSTE.map((d) => new URL(d.basisUrl).hostname))];
260 }
261
262 // --- Bildanforderung ---------------------------------------------------------
263
264 /*
265 * Hier stand ein zweiter Weg: baueAbruf() holte den Ausschnitt in EINEM Bild,
266 * begrenzt auf 1600 Bildpunkte (MAX_ANGEFORDERTE_KANTE). Seit der Kachelung
267 * ging ihn niemand mehr - der Luftbilddialog ruft ausschliesslich
268 * baueKachelplan -, nur die Pruefungen hielten ihn noch am Leben. Ein Weg, den
269 * das Programm nicht geht, driftet unbemerkt von dem ab, der benutzt wird:
270 * Beide bauten dieselbe Adresse, rechneten dieselbe Bodenaufloesung und
271 * schrieben denselben Herkunftstext, aber nur einer davon wurde beim naechsten
272 * Fehler mitgeaendert. Entfernt, mitsamt seinen Pruefungen.
273 */
274
275 /** Kleinste sinnvolle Bildkante. */
276 const MIN_KANTE = 512;
277
278 export interface Kartenausschnitt {
279 readonly dienst: Kartendienst;
280 readonly mitte: GeoPunkt;
281 /** Kantenlaenge des quadratischen Ausschnitts am Boden. */
282 readonly kantenlaenge: Meters;
283 }
284
285 /**
286 * Baut die Adresse EINES Bildabrufs.
287 *
288 * Einziger Aufrufer ist der Kachelplan. Bis 5.3.0 ging auch ein zweiter Weg
289 * hier durch - ein Einzelabruf des ganzen Ausschnitts -, und die gemeinsame
290 * Stelle war der Grund: Wichen die beiden Wege voneinander ab, saehe man das
291 * dem Bild nicht an, aber jede daraus abgegriffene Laenge waere falsch. Der
292 * zweite Weg ist entfallen (siehe oben); die Funktion bleibt getrennt, weil
293 * sie die Achsfolge und die Zonenwahl an EINER Stelle festhaelt.
294 *
295 * Die Zone wird dem Ausschnitt entnommen und nicht erneut aus der geografischen
296 * Laenge bestimmt: Eine Kachel liegt in derselben Zone wie das Gesamtbild, auch
297 * wenn ihr Rand rechnerisch knapp in die Nachbarzone reichte. Zwei Kacheln in
298 * verschiedenen Zonen liessen sich nicht zusammensetzen.
299 */
300 function abrufAdresse(dienst: Kartendienst, ausschnitt: Ausschnitt, kante: number): string {
301 // WMS 1.3.0: Bei den UTM-Systemen ist die Achsfolge Ost/Nord, die BBOX also
302 // minOst,minNord,maxOst,maxNord. (Nur bei EPSG:4326 waere sie vertauscht -
303 // ein haeufiger Grund fuer scheinbar leere Bilder.)
304 const parameter = new URLSearchParams({
305 SERVICE: 'WMS',
306 VERSION: '1.3.0',
307 REQUEST: 'GetMap',
308 LAYERS: dienst.ebene,
309 STYLES: '',
310 CRS: `EPSG:${epsgFuerZone(ausschnitt.zone)}`,
311 BBOX: bboxParameter(ausschnitt),
312 WIDTH: String(kante),
313 HEIGHT: String(kante),
314 FORMAT: dienst.bildformat,
315 TRANSPARENT: 'FALSE',
316 });
317 return `${dienst.basisUrl}?${parameter.toString()}`;
318 }
319
320 /**
321 * Quellenangabe, die im Plan und im Ausdruck erscheint.
322 *
323 * Sie enthaelt Herausgeber, Ausschnittsmitte und Kantenlaenge - damit laesst
324 * sich der Ausschnitt spaeter Punkt fuer Punkt nachstellen. Ein Pruefer kann
325 * die Grundlage der Vermassung dadurch selbst nachvollziehen.
326 */
327 export function herkunftstext(dienst: Kartendienst, mitte: GeoPunkt, kantenlaenge: Meters): string {
328 const koordinate = `${mitte.breite.toFixed(6)}, ${mitte.laenge.toFixed(6)}`;
329 const vermerk = dienst.namensnennung === null ? '' : ` · ${dienst.namensnennung}`;
330 return `${dienst.name} · ${dienst.herausgeber} · Mitte ${koordinate} · Kante ${Math.round(kantenlaenge)} m${vermerk}`;
331 }
332
333 // --- Kachelplan --------------------------------------------------------------
334
335 /**
336 * Groesste Kante EINER Kachel.
337 *
338 * Begrenzt, was einem Dienst in einem Zug zugemutet wird: Der Kartendienst
339 * rechnet das Bild erst aus, und der Aufwand waechst mit der Flaeche. Gemessen
340 * am Dienst Nordrhein-Westfalens dauerte ein Bild mit 2000 Bildpunkten ueber
341 * 40 Sekunden, eines mit 1600 rund zwei Drittel davon.
342 *
343 * Fachlich kostet die Grenze nichts, weil groessere Ausschnitte aus mehreren
344 * Kacheln entstehen: Die Bodenaufloesung des fertigen Bildes haengt an
345 * MAX_MOSAIK_KANTE, nicht hieran.
346 */
347 export const KACHEL_KANTE = 1600;
348
349 /**
350 * Groesste Kante des zusammengesetzten Bildes.
351 *
352 * Daran haengt die Genauigkeit, mit der spaeter gemessen wird: 7000 Bildpunkte
353 * reichen, damit ein 20-cm-Dienst bis 1400 m Kantenlaenge seine volle
354 * Bodenaufloesung liefert. Der feinste eingetragene Dienst (Nordrhein-Westfalen,
355 * 10 cm) schoepft sie nur bis 700 m aus; bei 1000 m liefert er 14,3 cm je
356 * Bildpunkt statt 10. Bei 2000 m sind es 28,6 cm - ein Uebersichtsbild.
357 *
358 * Hier stand "damit JEDER Dienst bis 1000 m seine volle Bodenaufloesung
359 * liefert". Das trifft ausgerechnet auf den voreingestellten Dienst nicht zu.
360 * Die Kappung ist trotzdem richtig: Ein Bild mit 10000 Bildpunkten Kante
361 * sprengt die Projektdatei, und die gewonnene Genauigkeit braucht bei einem
362 * 1000-m-Ausschnitt niemand. Ein Bildpunkt Zeichenfehler kostet dort bereits 0,029 s
363 * Zwischenzeit beim Kfz und 0,286 s beim mobilitaetseingeschraenkten
364 * Fussgaenger. Die Bodenaufloesung muss der Anwendung deshalb angezeigt werden,
365 * bevor er auf einem solchen Bild eine Furt abgreift.
366 *
367 * Nach oben begrenzt sie ausserdem die Projektdatei - das Mosaik wird
368 * eingebettet, damit der Plan Jahre spaeter aus der Datei allein
369 * nachvollziehbar bleibt.
370 */
371 export const MAX_MOSAIK_KANTE = 7000;
372
373 /** Ein einzelner Abruf innerhalb des Mosaiks. */
374 export interface Kachel {
375 readonly url: string;
376 /** Spalte im Mosaik, 0 ist die westlichste. */
377 readonly spalte: number;
378 /** Zeile im Mosaik, 0 ist die oberste Bildzeile - also die noerdlichste. */
379 readonly zeile: number;
380 /** Einsetzpunkt im Mosaik in Bildpunkten, von links. */
381 readonly x: number;
382 /** Einsetzpunkt im Mosaik in Bildpunkten, von oben. */
383 readonly y: number;
384 /** Kantenlaenge der Kachel in Bildpunkten - fuer jede Kachel dieselbe. */
385 readonly kante: number;
386 }
387
388 export interface Kachelplan {
389 /** Kantenlaenge des fertigen Mosaiks in Bildpunkten. */
390 readonly bildkante: number;
391 readonly spalten: number;
392 readonly zeilen: number;
393 /** Bodenaufloesung des Mosaiks - in jeder Kachel dieselbe. */
394 readonly meterJeBildpunkt: Meters;
395 /** Quellenangabe fuer den Plan. */
396 readonly herkunft: string;
397 readonly kacheln: readonly Kachel[];
398 }
399
400 /**
401 * Zerlegt einen Ausschnitt in Kacheln, die einzeln abgerufen und zu einem Bild
402 * zusammengesetzt werden.
403 *
404 * WARUM UEBERHAUPT KACHELN
405 *
406 * Eine Ortsdurchfahrt mit mehreren Knotenpunkten braucht bis zu 2000 m
407 * Kantenlaenge. Als ein Abruf waeren das 7000 Bildpunkte - das rechnet ein
408 * Kartendienst entweder gar nicht oder so lange, dass der Abruf in eine
409 * Zeitgrenze laeuft. Mehrere kleine Abrufe liefern dasselbe Bild und lassen
410 * sich nebenbei einzeln wiederholen und im Fortschritt anzeigen.
411 *
412 * WARUM ALLE KACHELN GLEICH GROSS SIND
413 *
414 * Die Bildkante wird nach oben auf ein Vielfaches der Kachelkante angeglichen.
415 * Die Alternative - eine schmalere Randkachel - erzeugt zwei Fehler: eine
416 * sichtbare Nahtstelle und, ungleich schlimmer, einen ortsabhaengigen Massstab.
417 * Ein Raeumweg, der ueber eine solche Naht hinweg abgegriffen wird, waere je
418 * nach Lage im Bild zu lang oder zu kurz - und die Zwischenzeit damit falsch,
419 * ohne dass irgendetwas darauf hindeutete. Der Preis sind hoechstens
420 * (spalten - 1) Bildpunkte mehr als angefordert; das sind bei 7000 vier.
421 */
422 export function baueKachelplan(anfrage: Kartenausschnitt): Kachelplan {
423 const { dienst, mitte } = anfrage;
424 const kantenlaenge = Math.max(20, Math.min(2000, anfrage.kantenlaenge));
425
426 // Die Bodenaufloesung des Dienstes ist die Untergrenze: Bayern liefert 40 cm,
427 // ein feiner angefordertes Bild waere nur leeres Vergroessern - es wiegt eine
428 // Genauigkeit vor, die in den Daten nicht steckt, und blaeht die Projektdatei.
429 const gewuenscht = Math.round(
430 Math.max(MIN_KANTE, Math.min(MAX_MOSAIK_KANTE, kantenlaenge / dienst.aufloesung)),
431 );
432
433 const kachelKante = Math.min(KACHEL_KANTE, dienst.maxKante);
434 const spalten = Math.max(1, Math.ceil(gewuenscht / kachelKante));
435 const zeilen = spalten; // Der Ausschnitt ist quadratisch.
436 const kachelPixel = Math.min(
437 Math.ceil(gewuenscht / spalten),
438 // Das Aufrunden darf die Obergrenze des Mosaiks nicht ueberschreiten ...
439 Math.floor(MAX_MOSAIK_KANTE / spalten),
440 // ... und keine Kachel darf groesser werden, als der Dienst zulaesst.
441 kachelKante,
442 );
443 const bildkante = kachelPixel * spalten;
444
445 const ausschnitt = ausschnittUm(mitte, kantenlaenge);
446 const kacheln: Kachel[] = [];
447 for (let zeile = 0; zeile < zeilen; zeile += 1) {
448 for (let spalte = 0; spalte < spalten; spalte += 1) {
449 const teil = unterAusschnitt(ausschnitt, spalte, zeile, spalten, zeilen);
450 kacheln.push({
451 url: abrufAdresse(dienst, teil, kachelPixel),
452 spalte,
453 zeile,
454 x: spalte * kachelPixel,
455 y: zeile * kachelPixel,
456 kante: kachelPixel,
457 });
458 }
459 }
460
461 return {
462 bildkante,
463 spalten,
464 zeilen,
465 meterJeBildpunkt: kantenlaenge / bildkante,
466 herkunft: herkunftstext(dienst, mitte, kantenlaenge),
467 kacheln,
468 };
469 }