lsa-planer

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

/ src domain geometrie arbeitsbereich.ts

5,9 KB Rohdatei
src/domain/geometrie/arbeitsbereich.ts — 151 Zeilen
1 import type { Meters } from '../units';
2 import { roundTo } from '../units';
3 import type { Arbeitsbereich, Haltlinie, Lageplan } from './lageplan';
4 import { massstabVon } from './lageplan';
5 import { abstand, inMeter, type Massstab, type Punkt } from './vermessung';
6
7 /**
8 * Die Arbeitsstelle im Lageplan - Laenge und Lage der Haltlinien dazu.
9 *
10 * WOFUER (gewuenscht am 11.09.2026 aus dem Betrieb): An einer einstreifigen
11 * Verkehrsfuehrung steht die Arbeitsstelle zuerst fest, und Haltlinien,
12 * Aufstellung und Signalgeber richten sich nach ihr. Bisher liess sich das im
13 * Programm nicht zeichnen; der Bearbeiter setzte die Haltlinien nach Augenmass
14 * ins Luftbild und trug die Zahlen daneben von Hand ein.
15 *
16 * WAS DIESE DATEI AUSDRUECKLICH NICHT TUT: rechnen. Der Arbeitsbereich geht in
17 * keine Zwischenzeit, keine Freigabezeit und keine Umlaufzeit ein. Er ist eine
18 * Zeichnung mit zwei abgeleiteten Auskuenften, und die Auskuenfte werden
19 * angezeigt und gedruckt, nicht uebernommen. Das ist kein Versaeumnis: Die
20 * Engstellenlaenge ist in diesem Programm der groesste erfasste Raeumweg der
21 * Konfliktbeziehungen, also der Abstand der Haltlinien - und ein von Hand
22 * gezeichnetes Band ist keine bessere Auskunft darueber als die Haltlinien
23 * selbst, die derselbe Bearbeiter in dieselbe Zeichnung gesetzt hat.
24 *
25 * WARUM DER ABSTAND ZU EINEM ENDPUNKT UND NICHT ZUR BANDKANTE. Gefragt ist
26 * "wie weit steht meine Haltlinie vor der Arbeitsstelle". Das ist der Abstand
27 * zum naechsten ENDE des Bandes, gemessen von der Mitte der Haltlinie. Die
28 * seitliche Kante des Bandes beantwortet eine andere Frage und ist an einer
29 * laengs liegenden Arbeitsstelle ohne Aussage.
30 *
31 * KEIN ENDE WIRD BENANNT. Liegt eine Haltlinie gleich weit von beiden Enden
32 * entfernt - an einer Baustellenampel mit mittiger Zufahrt kein seltener
33 * Fall -, duerfte nicht der Feldindex des Linienzugs entscheiden, welches Ende
34 * im Bericht steht. Deshalb nennt diese Auskunft nur die Entfernung und ob die
35 * Haltlinie IM Band liegt; welches Ende naeher ist, ist keine Angabe, die
36 * jemand braucht.
37 */
38
39 /** Wie eine Haltlinie zum Arbeitsbereich liegt. */
40 export interface HaltlinienlageZumBereich {
41 readonly haltlinieId: string;
42 readonly haltlinienName: string;
43 /** Abstand der Haltlinienmitte zum naechsten Ende des Bandes. */
44 readonly abstandMeter: Meters;
45 /**
46 * Liegt die Haltlinienmitte innerhalb des Bandes?
47 *
48 * Dann steht sie mitten in der Arbeitsstelle, und der Abstand sagt, wie weit
49 * es von dort bis zu deren naechstem Ende ist.
50 */
51 readonly imBereich: boolean;
52 }
53
54 /** Ein vermessener Arbeitsbereich samt der Lage aller Haltlinien dazu. */
55 export interface Arbeitsbereichslage {
56 readonly id: string;
57 readonly name: string;
58 readonly laengeMeter: Meters;
59 readonly breiteMeter: Meters;
60 readonly haltlinien: readonly HaltlinienlageZumBereich[];
61 }
62
63 /** Mitte einer Haltlinie in Bildpunkten. */
64 function mitte(h: Haltlinie): Punkt {
65 return { x: (h.von.x + h.bis.x) / 2, y: (h.von.y + h.bis.y) / 2 };
66 }
67
68 /** Laenge eines Linienzugs in Metern. */
69 function zugLaenge(punkte: readonly Punkt[], massstab: Massstab): Meters {
70 let summe = 0;
71 for (let i = 1; i < punkte.length; i += 1) {
72 summe += abstand(inMeter(punkte[i - 1]!, massstab), inMeter(punkte[i]!, massstab));
73 }
74 return summe;
75 }
76
77 /**
78 * Kleinster Abstand eines Punktes zu einem Linienzug - und ob er auf ihm liegt.
79 *
80 * Gemessen wird gegen die Strecken, nicht nur gegen die Stuetzpunkte: Eine
81 * Haltlinie neben der Mitte eines langen Bandes waere sonst weit entfernt.
82 */
83 function abstandZumZug(p: Punkt, punkte: readonly Punkt[]): Meters {
84 let kleinster = Number.POSITIVE_INFINITY;
85 for (let i = 1; i < punkte.length; i += 1) {
86 const a = punkte[i - 1]!;
87 const b = punkte[i]!;
88 const dx = b.x - a.x;
89 const dy = b.y - a.y;
90 const quadrat = dx * dx + dy * dy;
91 // Entartete Strecke: Dann zaehlt der Stuetzpunkt.
92 const t =
93 quadrat === 0 ? 0 : Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / quadrat));
94 const fuss = { x: a.x + dx * t, y: a.y + dy * t };
95 const d = abstand(p, fuss);
96 if (d < kleinster) kleinster = d;
97 }
98 return kleinster;
99 }
100
101 /**
102 * Vermisst einen Arbeitsbereich. `null`, solange der Massstab fehlt oder das
103 * Band aus weniger als zwei Punkten besteht - dann gibt es nichts zu messen,
104 * und eine Null waere eine Zahl, die niemand erhoben hat.
105 */
106 export function arbeitsbereichslage(
107 lageplan: Lageplan,
108 bereich: Arbeitsbereich,
109 ): Arbeitsbereichslage | null {
110 const massstab = massstabVon(lageplan);
111 if (massstab === null) return null;
112 if (bereich.punkte.length < 2) return null;
113
114 const erster = bereich.punkte[0]!;
115 const letzter = bereich.punkte[bereich.punkte.length - 1]!;
116 const halbeBreite = bereich.breiteMeter / 2;
117
118 const haltlinien = lageplan.haltlinien.map((h) => {
119 const m = mitte(h);
120 const inMetern = inMeter(m, massstab);
121 const zuErsterm = abstand(inMetern, inMeter(erster, massstab));
122 const zuLetztem = abstand(inMetern, inMeter(letzter, massstab));
123 const seitlich = abstandZumZug(m, bereich.punkte) * massstab.meterJeBildpunkt;
124 return {
125 haltlinieId: h.id,
126 haltlinienName: h.name,
127 abstandMeter: roundTo(Math.min(zuErsterm, zuLetztem), 2),
128 // Im Band liegt, wer seitlich innerhalb der halben Breite liegt und
129 // dabei naeher am Zug als an beiden Enden ist.
130 imBereich: seitlich <= halbeBreite,
131 };
132 });
133
134 return {
135 id: bereich.id,
136 name: bereich.name,
137 laengeMeter: roundTo(zugLaenge(bereich.punkte, massstab), 2),
138 breiteMeter: bereich.breiteMeter,
139 haltlinien,
140 };
141 }
142
143 /** Alle vermessenen Arbeitsbereiche eines Lageplans, in der Reihenfolge der Zeichnung. */
144 export function arbeitsbereichslagen(lageplan: Lageplan): Arbeitsbereichslage[] {
145 const ergebnis: Arbeitsbereichslage[] = [];
146 for (const bereich of lageplan.arbeitsbereiche) {
147 const lage = arbeitsbereichslage(lageplan, bereich);
148 if (lage !== null) ergebnis.push(lage);
149 }
150 return ergebnis;
151 }