import type { Meters } from '../units'; import { roundTo } from '../units'; import type { Arbeitsbereich, Haltlinie, Lageplan } from './lageplan'; import { massstabVon } from './lageplan'; import { abstand, inMeter, type Massstab, type Punkt } from './vermessung'; /** * Die Arbeitsstelle im Lageplan - Laenge und Lage der Haltlinien dazu. * * WOFUER (gewuenscht am 11.09.2026 aus dem Betrieb): An einer einstreifigen * Verkehrsfuehrung steht die Arbeitsstelle zuerst fest, und Haltlinien, * Aufstellung und Signalgeber richten sich nach ihr. Bisher liess sich das im * Programm nicht zeichnen; der Bearbeiter setzte die Haltlinien nach Augenmass * ins Luftbild und trug die Zahlen daneben von Hand ein. * * WAS DIESE DATEI AUSDRUECKLICH NICHT TUT: rechnen. Der Arbeitsbereich geht in * keine Zwischenzeit, keine Freigabezeit und keine Umlaufzeit ein. Er ist eine * Zeichnung mit zwei abgeleiteten Auskuenften, und die Auskuenfte werden * angezeigt und gedruckt, nicht uebernommen. Das ist kein Versaeumnis: Die * Engstellenlaenge ist in diesem Programm der groesste erfasste Raeumweg der * Konfliktbeziehungen, also der Abstand der Haltlinien - und ein von Hand * gezeichnetes Band ist keine bessere Auskunft darueber als die Haltlinien * selbst, die derselbe Bearbeiter in dieselbe Zeichnung gesetzt hat. * * WARUM DER ABSTAND ZU EINEM ENDPUNKT UND NICHT ZUR BANDKANTE. Gefragt ist * "wie weit steht meine Haltlinie vor der Arbeitsstelle". Das ist der Abstand * zum naechsten ENDE des Bandes, gemessen von der Mitte der Haltlinie. Die * seitliche Kante des Bandes beantwortet eine andere Frage und ist an einer * laengs liegenden Arbeitsstelle ohne Aussage. * * KEIN ENDE WIRD BENANNT. Liegt eine Haltlinie gleich weit von beiden Enden * entfernt - an einer Baustellenampel mit mittiger Zufahrt kein seltener * Fall -, duerfte nicht der Feldindex des Linienzugs entscheiden, welches Ende * im Bericht steht. Deshalb nennt diese Auskunft nur die Entfernung und ob die * Haltlinie IM Band liegt; welches Ende naeher ist, ist keine Angabe, die * jemand braucht. */ /** Wie eine Haltlinie zum Arbeitsbereich liegt. */ export interface HaltlinienlageZumBereich { readonly haltlinieId: string; readonly haltlinienName: string; /** Abstand der Haltlinienmitte zum naechsten Ende des Bandes. */ readonly abstandMeter: Meters; /** * Liegt die Haltlinienmitte innerhalb des Bandes? * * Dann steht sie mitten in der Arbeitsstelle, und der Abstand sagt, wie weit * es von dort bis zu deren naechstem Ende ist. */ readonly imBereich: boolean; } /** Ein vermessener Arbeitsbereich samt der Lage aller Haltlinien dazu. */ export interface Arbeitsbereichslage { readonly id: string; readonly name: string; readonly laengeMeter: Meters; readonly breiteMeter: Meters; readonly haltlinien: readonly HaltlinienlageZumBereich[]; } /** Mitte einer Haltlinie in Bildpunkten. */ function mitte(h: Haltlinie): Punkt { return { x: (h.von.x + h.bis.x) / 2, y: (h.von.y + h.bis.y) / 2 }; } /** Laenge eines Linienzugs in Metern. */ function zugLaenge(punkte: readonly Punkt[], massstab: Massstab): Meters { let summe = 0; for (let i = 1; i < punkte.length; i += 1) { summe += abstand(inMeter(punkte[i - 1]!, massstab), inMeter(punkte[i]!, massstab)); } return summe; } /** * Kleinster Abstand eines Punktes zu einem Linienzug - und ob er auf ihm liegt. * * Gemessen wird gegen die Strecken, nicht nur gegen die Stuetzpunkte: Eine * Haltlinie neben der Mitte eines langen Bandes waere sonst weit entfernt. */ function abstandZumZug(p: Punkt, punkte: readonly Punkt[]): Meters { let kleinster = Number.POSITIVE_INFINITY; for (let i = 1; i < punkte.length; i += 1) { const a = punkte[i - 1]!; const b = punkte[i]!; const dx = b.x - a.x; const dy = b.y - a.y; const quadrat = dx * dx + dy * dy; // Entartete Strecke: Dann zaehlt der Stuetzpunkt. const t = quadrat === 0 ? 0 : Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / quadrat)); const fuss = { x: a.x + dx * t, y: a.y + dy * t }; const d = abstand(p, fuss); if (d < kleinster) kleinster = d; } return kleinster; } /** * Vermisst einen Arbeitsbereich. `null`, solange der Massstab fehlt oder das * Band aus weniger als zwei Punkten besteht - dann gibt es nichts zu messen, * und eine Null waere eine Zahl, die niemand erhoben hat. */ export function arbeitsbereichslage( lageplan: Lageplan, bereich: Arbeitsbereich, ): Arbeitsbereichslage | null { const massstab = massstabVon(lageplan); if (massstab === null) return null; if (bereich.punkte.length < 2) return null; const erster = bereich.punkte[0]!; const letzter = bereich.punkte[bereich.punkte.length - 1]!; const halbeBreite = bereich.breiteMeter / 2; const haltlinien = lageplan.haltlinien.map((h) => { const m = mitte(h); const inMetern = inMeter(m, massstab); const zuErsterm = abstand(inMetern, inMeter(erster, massstab)); const zuLetztem = abstand(inMetern, inMeter(letzter, massstab)); const seitlich = abstandZumZug(m, bereich.punkte) * massstab.meterJeBildpunkt; return { haltlinieId: h.id, haltlinienName: h.name, abstandMeter: roundTo(Math.min(zuErsterm, zuLetztem), 2), // Im Band liegt, wer seitlich innerhalb der halben Breite liegt und // dabei naeher am Zug als an beiden Enden ist. imBereich: seitlich <= halbeBreite, }; }); return { id: bereich.id, name: bereich.name, laengeMeter: roundTo(zugLaenge(bereich.punkte, massstab), 2), breiteMeter: bereich.breiteMeter, haltlinien, }; } /** Alle vermessenen Arbeitsbereiche eines Lageplans, in der Reihenfolge der Zeichnung. */ export function arbeitsbereichslagen(lageplan: Lageplan): Arbeitsbereichslage[] { const ergebnis: Arbeitsbereichslage[] = []; for (const bereich of lageplan.arbeitsbereiche) { const lage = arbeitsbereichslage(lageplan, bereich); if (lage !== null) ergebnis.push(lage); } return ergebnis; }