import type { Meters } from '../units'; import { roundTo } from '../units'; import type { Lageplan } from './lageplan'; import { massstabVon } from './lageplan'; import type { Planblatt } from './planblatt'; import { abstand, inMeter, type Punkt } from './vermessung'; /** * Massketten fuer das Lageplanblatt - was das Blatt nachmessbar macht. * * WOZU. Das Blatt traegt seit 5.32.0 einen Massstabsbalken; wer eine Laenge * wissen will, greift sie damit ab. Das ist der Nachweis, aber es ist Arbeit, * und es ist ungenau: Ein Abgriff am Balken traegt den Fehler des Ausdrucks * und den des Lineals. Eine angeschriebene Masskette nennt die Zahl, die das * Programm ohnehin kennt, an der Stelle, an der sie gilt. * * DIE REGEL DAHINTER, UND SIE IST DIE WICHTIGERE: Eine Masszahl auf diesem * Blatt darf keine zweite Rechnung sein. Sie kommt aus derselben Vermessung * wie jede andere Laenge des Programms (`inMeter`/`abstand` aus vermessung.ts, * ueber den Massstab des Lageplans) und wird hier nur beschriftet. Gaebe es * zwei Wege zu derselben Zahl, glaubte ein Pruefer der gezeichneten - und die * waere die ungeprueftere. * * WAS HIER AUSDRUECKLICH NICHT BEMASST WIRD, und warum: * * - DER ABSTAND EINER HALTLINIE ZUM ARBEITSBEREICH. Die Auskunft gibt es * (`arbeitsbereichslage` in arbeitsbereich.ts), und sie steht am Bildschirm * und im Ausdruck. Sie nennt aber bewusst nur die Entfernung zum NAECHSTEN * Ende und nicht, zu welchem - liegt eine Haltlinie gleich weit von beiden * Enden, duerfte nicht der Feldindex des Linienzugs entscheiden. Eine * gezeichnete Kette entscheidet es doch, denn sie zeigt auf ein Ende. Die * Zahl bleibt deshalb in der Tabelle, wo sie nichts zeigt, was sie nicht * weiss. * - DER ABSTAND ZWEIER HALTLINIEN. Er ist an der einstreifigen * Verkehrsfuehrung die Engstellenlaenge, und die ist in diesem Programm der * groesste erfasste RAEUMWEG der Konfliktbeziehungen. Beide Zahlen * beschreiben dasselbe und werden verschieden gebildet; nebeneinander auf * einem Blatt waeren sie eine Einladung, der falschen zu glauben. * - DIE LAENGE EINER HALTLINIE, und das ist die Berichtigung von 5.42.1. * Sie stand hier zuerst, mit der Begruendung, sie sei eindeutig messbar. * Eindeutig messbar heisst aber nicht gedeckt. AUS DEM BETRIEB GEMELDET: * Eine Haltlinie quert die Fahrbahn, ihre Laenge liest sich als * FAHRBAHNBREITE - und das Programm fuehrt eine zweite Breite, die wirklich * rechnet (`SignalGroup.fahrstreifenbreite`, ueber den Faktor fb in den * Zeitbedarfswert, unterhalb von 3,00 m). Auf dem Blatt stand damit eine * Breite neben einer anderen, verschieden gebildet, und die gezeichnete sah * nach Messung aus. An einer Arbeitsstelle kommt hinzu, dass die * verbleibende Fahrbahnbreite ein Anordnungskriterium ist: Wer "2,6 m" an * einer Haltlinie liest, zieht einen Schluss, den dieses Programm nicht * treffen wollte und nicht decken kann. * * Die Laenge gibt es weiterhin - aber dort, wo sie hingehoert: in der * Haltlinientabelle der Ansicht *Lageplan* und im Fenster, das die * Haltlinie anlegt. Dort ist sie eine Auskunft beim Zeichnen und keine * Zusicherung in einer Anordnungsunterlage. * - ALLES, WAS DAS PROGRAMM NICHT FUEHRT: Restfahrbahnbreiten, Abstaende zu * Signalgebern, Aufstellentfernungen von Verkehrszeichen. Wo es keine Zahl * hat, zeichnet es keine Kette. */ /** Eine Masskette auf dem Blatt - Endpunkte in Blattkoordinaten. */ export interface Masskette { /** Wofuer die Kette steht; die Zeichnung unterscheidet danach nicht. */ readonly art: 'bereich-laenge' | 'bereich-breite'; /** Der Zug, dem die Kette folgt. Mindestens zwei Punkte. */ readonly punkte: readonly Punkt[]; /** Die angeschriebene Laenge in Metern. */ readonly meter: Meters; /** Wohin die Beschriftung gehoert - die Mitte des Zuges. */ readonly beschriftung: Punkt; } /** Laenge eines Zuges in Blattpunkten. */ function zugLaengeBlatt(punkte: readonly Punkt[]): number { let summe = 0; for (let i = 1; i < punkte.length; i += 1) { const a = punkte[i - 1]!; const b = punkte[i]!; summe += Math.hypot(b.x - a.x, b.y - a.y); } return summe; } /** * Der Punkt auf halber Zuglaenge - dorthin gehoert die Beschriftung. * * Nicht die Mitte zwischen Anfang und Ende: Bei einem geknickten Band laege * die neben dem Band, und die Zahl stuende dann auf der Fahrbahn statt auf der * Arbeitsstelle. */ function mitteAufZug(punkte: readonly Punkt[]): Punkt { const halbe = zugLaengeBlatt(punkte) / 2; let gegangen = 0; for (let i = 1; i < punkte.length; i += 1) { const a = punkte[i - 1]!; const b = punkte[i]!; const stueck = Math.hypot(b.x - a.x, b.y - a.y); if (gegangen + stueck >= halbe && stueck > 0) { const anteil = (halbe - gegangen) / stueck; return { x: a.x + (b.x - a.x) * anteil, y: a.y + (b.y - a.y) * anteil }; } gegangen += stueck; } return punkte[0] ?? { x: 0, y: 0 }; } /** Die Einheitsnormale des ersten Segments - fuer die Querkette der Breite. */ function normaleAmAnfang(punkte: readonly Punkt[]): Punkt | null { const a = punkte[0]; const b = punkte[1]; if (a === undefined || b === undefined) return null; const dx = b.x - a.x; const dy = b.y - a.y; const laenge = Math.hypot(dx, dy); if (laenge <= 1e-9) return null; return { x: -dy / laenge, y: dx / laenge }; } /** * Die Massketten zu einem Blatt. * * Leer, wo der Lageplan keinen Massstab hat - dann gibt es auch kein Blatt. * Die Zuordnung laeuft ueber die Kennungen: Das Blatt fuehrt dieselben * Objekte in Blattkoordinaten, und die Laenge wird an den URSPRUNGSpunkten in * Metern gemessen, nicht an den abgebildeten. Sonst haenge die Masszahl an der * Blattgroesse. */ export function massketten(lageplan: Lageplan, blatt: Planblatt): readonly Masskette[] { const massstab = massstabVon(lageplan); if (massstab === null) return []; const ketten: Masskette[] = []; for (const gezeichnet of blatt.arbeitsbereiche) { const quelle = lageplan.arbeitsbereiche.find((b) => b.id === gezeichnet.id); if (quelle === undefined) continue; let summe = 0; for (let i = 1; i < quelle.punkte.length; i += 1) { summe += abstand( inMeter(quelle.punkte[i - 1]!, massstab), inMeter(quelle.punkte[i]!, massstab), ); } const laenge = roundTo(summe, 1); if (laenge > 0 && gezeichnet.punkte.length >= 2) { ketten.push({ art: 'bereich-laenge', punkte: gezeichnet.punkte, meter: laenge, beschriftung: mitteAufZug(gezeichnet.punkte), }); } // Die Breite quer zum ersten Segment, auf die halbe Bandbreite nach beiden // Seiten: Sie IST die Ausdehnung des Bandes an dieser Stelle. const normale = normaleAmAnfang(gezeichnet.punkte); const anfang = gezeichnet.punkte[0]; const breite = roundTo(quelle.breiteMeter, 1); if (normale !== null && anfang !== undefined && breite > 0) { const halb = gezeichnet.breite / 2; const punkte = [ { x: anfang.x - normale.x * halb, y: anfang.y - normale.y * halb }, { x: anfang.x + normale.x * halb, y: anfang.y + normale.y * halb }, ]; ketten.push({ art: 'bereich-breite', punkte, meter: breite, beschriftung: mitteAufZug(punkte), }); } } return ketten; }