import { describe, expect, it } from 'vitest'; import { ausschnittUm, kantenlaenge, nachGeo, unterAusschnitt, type Ausschnitt, } from '@/domain/geometrie/projektion'; /** * Teilausschnitte fuer den kachelweisen Abruf. * * Der Fehler, um den es hier geht, ist die senkrechte Spiegelung: Bildzeile 0 * liegt OBEN, also am noerdlichen Rand; die Nordwerte laufen der Bildzeile damit * entgegen. Wird das vertauscht, ist jede Kachel fuer sich richtig und das * Mosaik als Ganzes verkehrt. Ein Luftbild hat keine Leserichtung - es faellt * niemandem auf, und vermessen wird danach die falsche Zufahrt. */ /** Ausschnitt mit runden Zahlen: 900 m Kante, damit sich Drittel glatt teilen. */ const GESAMT: Ausschnitt = { minOst: 350000, minNord: 5750000, maxOst: 350900, maxNord: 5750900, zone: 32, }; describe('Teilausschnitt einer Kachel', () => { it('legt Zeile 0 an den NOERDLICHEN Rand', () => { // Der eigentliche Pruefpunkt. Eine vertauschte Achse liefert hier // minNord === GESAMT.minNord - das Mosaik waere senkrecht gespiegelt. const obenLinks = unterAusschnitt(GESAMT, 0, 0, 3, 3); expect(obenLinks.maxNord).toBe(GESAMT.maxNord); expect(obenLinks.minNord).toBeCloseTo(GESAMT.maxNord - 300, 9); expect(obenLinks.minNord).not.toBe(GESAMT.minNord); }); it('legt die letzte Zeile an den suedlichen Rand', () => { const untenLinks = unterAusschnitt(GESAMT, 0, 2, 3, 3); expect(untenLinks.minNord).toBe(GESAMT.minNord); expect(untenLinks.maxNord).toBeCloseTo(GESAMT.minNord + 300, 9); }); it('laesst den Nordwert mit wachsender Zeile fallen', () => { // Die Aussage in einem Satz: weiter unten im Bild heisst weiter suedlich. const nordwerte = [0, 1, 2, 3, 4].map( (zeile) => unterAusschnitt(GESAMT, 0, zeile, 5, 5).maxNord, ); for (let i = 1; i < nordwerte.length; i += 1) { expect(nordwerte[i]!).toBeLessThan(nordwerte[i - 1]!); } }); it('laesst den Ostwert mit wachsender Spalte steigen', () => { // Waagerecht laeuft es gleichsinnig: Spalte 0 liegt links, also im Westen. const ostwerte = [0, 1, 2, 3, 4].map( (spalte) => unterAusschnitt(GESAMT, spalte, 0, 5, 5).minOst, ); expect(ostwerte[0]).toBe(GESAMT.minOst); for (let i = 1; i < ostwerte.length; i += 1) { expect(ostwerte[i]!).toBeGreaterThan(ostwerte[i - 1]!); } }); it('faengt die Spiegelung auch geografisch', () => { // Dieselbe Pruefung ohne Kartenkoordinaten: Die obere Kachel muss eine // groessere geografische Breite haben als die untere - sie liegt noerdlicher. const gesamt = ausschnittUm({ breite: 50.9412784, laenge: 6.9582814 }, 800); const oben = unterAusschnitt(gesamt, 0, 0, 2, 2); const unten = unterAusschnitt(gesamt, 0, 1, 2, 2); const mitteOben = nachGeo({ ost: (oben.minOst + oben.maxOst) / 2, nord: (oben.minNord + oben.maxNord) / 2, zone: oben.zone, }); const mitteUnten = nachGeo({ ost: (unten.minOst + unten.maxOst) / 2, nord: (unten.minNord + unten.maxNord) / 2, zone: unten.zone, }); expect(mitteOben.breite).toBeGreaterThan(mitteUnten.breite); // Die beiden Mitten liegen eine halbe Kantenlaenge auseinander. expect(oben.minNord - unten.minNord).toBeCloseTo(400, 6); }); it('teilt in gleich grosse Kacheln', () => { for (let zeile = 0; zeile < 3; zeile += 1) { for (let spalte = 0; spalte < 3; spalte += 1) { const kachel = unterAusschnitt(GESAMT, spalte, zeile, 3, 3); expect(kantenlaenge(kachel)).toBeCloseTo(300, 9); expect(kachel.maxNord - kachel.minNord).toBeCloseTo(300, 9); } } }); it('setzt sich lueckenlos und ueberschneidungsfrei wieder zusammen', () => { // Ein Spalt zwischen zwei Kacheln waere im Mosaik ein Versatz - und der // legt sich als scheinbarer Zeichenfehler auf jeden dort gemessenen Weg. const spalten = 4; const zeilen = 3; for (let zeile = 0; zeile < zeilen; zeile += 1) { for (let spalte = 0; spalte < spalten; spalte += 1) { const kachel = unterAusschnitt(GESAMT, spalte, zeile, spalten, zeilen); const linkerNachbar = spalte === 0 ? null : unterAusschnitt(GESAMT, spalte - 1, zeile, spalten, zeilen); if (linkerNachbar === null) expect(kachel.minOst).toBe(GESAMT.minOst); else expect(kachel.minOst).toBe(linkerNachbar.maxOst); const obererNachbar = zeile === 0 ? null : unterAusschnitt(GESAMT, spalte, zeile - 1, spalten, zeilen); if (obererNachbar === null) expect(kachel.maxNord).toBe(GESAMT.maxNord); else expect(kachel.maxNord).toBe(obererNachbar.minNord); } } // Die aeusseren Kanten treffen den Gesamtausschnitt genau. const letzte = unterAusschnitt(GESAMT, spalten - 1, zeilen - 1, spalten, zeilen); expect(letzte.maxOst).toBe(GESAMT.maxOst); expect(letzte.minNord).toBe(GESAMT.minNord); }); it('trifft die Aussenkanten auch bei unglatter Teilung genau', () => { // 1000 m durch 7 Kacheln geht nicht auf. Aufsummierte Kachelbreiten liessen // am Rand einen Rundungsspalt stehen; als Anteil gerechnet trifft die // letzte Kante den Rand. const gesamt = ausschnittUm({ breite: 50.9412784, laenge: 6.9582814 }, 1000); const letzte = unterAusschnitt(gesamt, 6, 6, 7, 7); expect(letzte.maxOst).toBe(gesamt.maxOst); expect(letzte.minNord).toBe(gesamt.minNord); expect(unterAusschnitt(gesamt, 0, 0, 7, 7).minOst).toBe(gesamt.minOst); expect(unterAusschnitt(gesamt, 0, 0, 7, 7).maxNord).toBe(gesamt.maxNord); }); it('gibt bei einer einzigen Kachel den Gesamtausschnitt zurueck', () => { expect(unterAusschnitt(GESAMT, 0, 0, 1, 1)).toEqual(GESAMT); }); it('reicht die Zone durch', () => { const inZone33 = unterAusschnitt({ ...GESAMT, zone: 33 }, 1, 1, 2, 2); expect(inZone33.zone).toBe(33); }); it('begrenzt unsinnige Angaben auf das vorhandene Raster', () => { // Eine Kachel ausserhalb waere ein Bildteil aus einer fremden Gegend - im // Luftbild ebenso wenig zu erkennen wie die Spiegelung. Deshalb wird // begrenzt und nicht extrapoliert. const rasterErsteKachel = unterAusschnitt(GESAMT, 0, 0, 2, 2); const rasterLetzteKachel = unterAusschnitt(GESAMT, 1, 1, 2, 2); expect(unterAusschnitt(GESAMT, -3, -3, 2, 2)).toEqual(rasterErsteKachel); expect(unterAusschnitt(GESAMT, 9, 9, 2, 2)).toEqual(rasterLetzteKachel); expect(unterAusschnitt(GESAMT, Number.NaN, Number.NaN, 2, 2)).toEqual(rasterErsteKachel); // Weniger als eine Kachel je Richtung gibt es nicht. expect(unterAusschnitt(GESAMT, 0, 0, 0, 0)).toEqual(GESAMT); expect(unterAusschnitt(GESAMT, 0, 0, -5, Number.NaN)).toEqual(GESAMT); // Bruchteile werden abgeschnitten, nicht gerundet - 2,7 Kacheln gibt es nicht. expect(unterAusschnitt(GESAMT, 1.9, 0.4, 2.7, 3.8)).toEqual( unterAusschnitt(GESAMT, 1, 0, 2, 3), ); }); });