lsa-planer

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

/ tests domain unterAusschnitt.test.ts

6,8 KB Rohdatei
tests/domain/unterAusschnitt.test.ts — 173 Zeilen
1 import { describe, expect, it } from 'vitest';
2 import {
3 ausschnittUm,
4 kantenlaenge,
5 nachGeo,
6 unterAusschnitt,
7 type Ausschnitt,
8 } from '@/domain/geometrie/projektion';
9
10 /**
11 * Teilausschnitte fuer den kachelweisen Abruf.
12 *
13 * Der Fehler, um den es hier geht, ist die senkrechte Spiegelung: Bildzeile 0
14 * liegt OBEN, also am noerdlichen Rand; die Nordwerte laufen der Bildzeile damit
15 * entgegen. Wird das vertauscht, ist jede Kachel fuer sich richtig und das
16 * Mosaik als Ganzes verkehrt. Ein Luftbild hat keine Leserichtung - es faellt
17 * niemandem auf, und vermessen wird danach die falsche Zufahrt.
18 */
19
20 /** Ausschnitt mit runden Zahlen: 900 m Kante, damit sich Drittel glatt teilen. */
21 const GESAMT: Ausschnitt = {
22 minOst: 350000,
23 minNord: 5750000,
24 maxOst: 350900,
25 maxNord: 5750900,
26 zone: 32,
27 };
28
29 describe('Teilausschnitt einer Kachel', () => {
30 it('legt Zeile 0 an den NOERDLICHEN Rand', () => {
31 // Der eigentliche Pruefpunkt. Eine vertauschte Achse liefert hier
32 // minNord === GESAMT.minNord - das Mosaik waere senkrecht gespiegelt.
33 const obenLinks = unterAusschnitt(GESAMT, 0, 0, 3, 3);
34
35 expect(obenLinks.maxNord).toBe(GESAMT.maxNord);
36 expect(obenLinks.minNord).toBeCloseTo(GESAMT.maxNord - 300, 9);
37 expect(obenLinks.minNord).not.toBe(GESAMT.minNord);
38 });
39
40 it('legt die letzte Zeile an den suedlichen Rand', () => {
41 const untenLinks = unterAusschnitt(GESAMT, 0, 2, 3, 3);
42
43 expect(untenLinks.minNord).toBe(GESAMT.minNord);
44 expect(untenLinks.maxNord).toBeCloseTo(GESAMT.minNord + 300, 9);
45 });
46
47 it('laesst den Nordwert mit wachsender Zeile fallen', () => {
48 // Die Aussage in einem Satz: weiter unten im Bild heisst weiter suedlich.
49 const nordwerte = [0, 1, 2, 3, 4].map(
50 (zeile) => unterAusschnitt(GESAMT, 0, zeile, 5, 5).maxNord,
51 );
52
53 for (let i = 1; i < nordwerte.length; i += 1) {
54 expect(nordwerte[i]!).toBeLessThan(nordwerte[i - 1]!);
55 }
56 });
57
58 it('laesst den Ostwert mit wachsender Spalte steigen', () => {
59 // Waagerecht laeuft es gleichsinnig: Spalte 0 liegt links, also im Westen.
60 const ostwerte = [0, 1, 2, 3, 4].map(
61 (spalte) => unterAusschnitt(GESAMT, spalte, 0, 5, 5).minOst,
62 );
63
64 expect(ostwerte[0]).toBe(GESAMT.minOst);
65 for (let i = 1; i < ostwerte.length; i += 1) {
66 expect(ostwerte[i]!).toBeGreaterThan(ostwerte[i - 1]!);
67 }
68 });
69
70 it('faengt die Spiegelung auch geografisch', () => {
71 // Dieselbe Pruefung ohne Kartenkoordinaten: Die obere Kachel muss eine
72 // groessere geografische Breite haben als die untere - sie liegt noerdlicher.
73 const gesamt = ausschnittUm({ breite: 50.9412784, laenge: 6.9582814 }, 800);
74 const oben = unterAusschnitt(gesamt, 0, 0, 2, 2);
75 const unten = unterAusschnitt(gesamt, 0, 1, 2, 2);
76
77 const mitteOben = nachGeo({
78 ost: (oben.minOst + oben.maxOst) / 2,
79 nord: (oben.minNord + oben.maxNord) / 2,
80 zone: oben.zone,
81 });
82 const mitteUnten = nachGeo({
83 ost: (unten.minOst + unten.maxOst) / 2,
84 nord: (unten.minNord + unten.maxNord) / 2,
85 zone: unten.zone,
86 });
87
88 expect(mitteOben.breite).toBeGreaterThan(mitteUnten.breite);
89 // Die beiden Mitten liegen eine halbe Kantenlaenge auseinander.
90 expect(oben.minNord - unten.minNord).toBeCloseTo(400, 6);
91 });
92
93 it('teilt in gleich grosse Kacheln', () => {
94 for (let zeile = 0; zeile < 3; zeile += 1) {
95 for (let spalte = 0; spalte < 3; spalte += 1) {
96 const kachel = unterAusschnitt(GESAMT, spalte, zeile, 3, 3);
97 expect(kantenlaenge(kachel)).toBeCloseTo(300, 9);
98 expect(kachel.maxNord - kachel.minNord).toBeCloseTo(300, 9);
99 }
100 }
101 });
102
103 it('setzt sich lueckenlos und ueberschneidungsfrei wieder zusammen', () => {
104 // Ein Spalt zwischen zwei Kacheln waere im Mosaik ein Versatz - und der
105 // legt sich als scheinbarer Zeichenfehler auf jeden dort gemessenen Weg.
106 const spalten = 4;
107 const zeilen = 3;
108
109 for (let zeile = 0; zeile < zeilen; zeile += 1) {
110 for (let spalte = 0; spalte < spalten; spalte += 1) {
111 const kachel = unterAusschnitt(GESAMT, spalte, zeile, spalten, zeilen);
112
113 const linkerNachbar =
114 spalte === 0 ? null : unterAusschnitt(GESAMT, spalte - 1, zeile, spalten, zeilen);
115 if (linkerNachbar === null) expect(kachel.minOst).toBe(GESAMT.minOst);
116 else expect(kachel.minOst).toBe(linkerNachbar.maxOst);
117
118 const obererNachbar =
119 zeile === 0 ? null : unterAusschnitt(GESAMT, spalte, zeile - 1, spalten, zeilen);
120 if (obererNachbar === null) expect(kachel.maxNord).toBe(GESAMT.maxNord);
121 else expect(kachel.maxNord).toBe(obererNachbar.minNord);
122 }
123 }
124
125 // Die aeusseren Kanten treffen den Gesamtausschnitt genau.
126 const letzte = unterAusschnitt(GESAMT, spalten - 1, zeilen - 1, spalten, zeilen);
127 expect(letzte.maxOst).toBe(GESAMT.maxOst);
128 expect(letzte.minNord).toBe(GESAMT.minNord);
129 });
130
131 it('trifft die Aussenkanten auch bei unglatter Teilung genau', () => {
132 // 1000 m durch 7 Kacheln geht nicht auf. Aufsummierte Kachelbreiten liessen
133 // am Rand einen Rundungsspalt stehen; als Anteil gerechnet trifft die
134 // letzte Kante den Rand.
135 const gesamt = ausschnittUm({ breite: 50.9412784, laenge: 6.9582814 }, 1000);
136 const letzte = unterAusschnitt(gesamt, 6, 6, 7, 7);
137
138 expect(letzte.maxOst).toBe(gesamt.maxOst);
139 expect(letzte.minNord).toBe(gesamt.minNord);
140 expect(unterAusschnitt(gesamt, 0, 0, 7, 7).minOst).toBe(gesamt.minOst);
141 expect(unterAusschnitt(gesamt, 0, 0, 7, 7).maxNord).toBe(gesamt.maxNord);
142 });
143
144 it('gibt bei einer einzigen Kachel den Gesamtausschnitt zurueck', () => {
145 expect(unterAusschnitt(GESAMT, 0, 0, 1, 1)).toEqual(GESAMT);
146 });
147
148 it('reicht die Zone durch', () => {
149 const inZone33 = unterAusschnitt({ ...GESAMT, zone: 33 }, 1, 1, 2, 2);
150 expect(inZone33.zone).toBe(33);
151 });
152
153 it('begrenzt unsinnige Angaben auf das vorhandene Raster', () => {
154 // Eine Kachel ausserhalb waere ein Bildteil aus einer fremden Gegend - im
155 // Luftbild ebenso wenig zu erkennen wie die Spiegelung. Deshalb wird
156 // begrenzt und nicht extrapoliert.
157 const rasterErsteKachel = unterAusschnitt(GESAMT, 0, 0, 2, 2);
158 const rasterLetzteKachel = unterAusschnitt(GESAMT, 1, 1, 2, 2);
159
160 expect(unterAusschnitt(GESAMT, -3, -3, 2, 2)).toEqual(rasterErsteKachel);
161 expect(unterAusschnitt(GESAMT, 9, 9, 2, 2)).toEqual(rasterLetzteKachel);
162 expect(unterAusschnitt(GESAMT, Number.NaN, Number.NaN, 2, 2)).toEqual(rasterErsteKachel);
163
164 // Weniger als eine Kachel je Richtung gibt es nicht.
165 expect(unterAusschnitt(GESAMT, 0, 0, 0, 0)).toEqual(GESAMT);
166 expect(unterAusschnitt(GESAMT, 0, 0, -5, Number.NaN)).toEqual(GESAMT);
167
168 // Bruchteile werden abgeschnitten, nicht gerundet - 2,7 Kacheln gibt es nicht.
169 expect(unterAusschnitt(GESAMT, 1.9, 0.4, 2.7, 3.8)).toEqual(
170 unterAusschnitt(GESAMT, 1, 0, 2, 3),
171 );
172 });
173 });