Prioritaet gestapelter Geofences
Wenn staedtische Policy-Geofences mit Betreiberzonen ueberlappen, muss eine gewinnen. Levy verwendet eine 6-stufige Prioritaetsleiter, materialisiert als Integer-priority-Spalte auf jeder Zone und beim Lesen ueber ein PostGIS-RPC aufgeloest, das alle Zonen an einem Punkt absteigend nach Prioritaet zurueckgibt.
Die 6-stufige Leiter
Hoehere Prioritaet gewinnt. Die erste nicht-null-Regel pro rule_type wird zur aktiven Regel am Punkt.
| Stufe | Prioritaet | Quelle | Beispiele |
|---|---|---|---|
| 1 | 1000 | Stadt-Policy prohibited_areas + speed_limit | "Kein Fahren in der Pearl Street", "5 km/h auf dem Platz" |
| 2 | 950 | Stadt-Policy parking_zones + equity_zones | "Parken nur in markierten Korridoren", "Einsatz >= 20% in Zone X" |
| 3 | 700 | Betreiber-definierte No-Ride-Zonen | Ihre eigene No-Go-Zone um Privatgrund |
| 4 | 500 | Betreiber-definierte Slow-Zonen | Ihre eigene Fussgaengerzonen-Geschwindigkeitsbegrenzung |
| 5 | 300 | Betreiber-definierte Parkzonen | Ihre eigenen Parkstellplatz-Definitionen |
| 6 | 100 | Systemstandards | Fallback Subaccount-Standards fuer Geschwindigkeit und Parken |
Die numerischen Werte sind absichtlich 50-200 auseinander gesetzt, sodass Betreiber bei Bedarf eine individuelle Zwischenstufen-Zone einfuegen koennen (z.B. eine temporaere VIP-Event-Zone bei Prioritaet 800). Heute zeigt die UI keine eigene Prioritaetsbearbeitung -- jede Betreiberzone erhaelt ihren Stufenstandard --, aber die Spalte ermoeglicht es.
Der Konfliktloeser
src/lib/compliance/conflict-resolver.ts exportiert zwei Funktionen:
| Funktion | Was sie tut |
|---|---|
resolveStack(zones) | Nimmt eine Liste von Zonen an einem Punkt, gibt die aktive Regel pro rule_type zurueck (ein Tempolimit, ein No-Ride-Flag, eine Parkregel). |
detectOperatorOverrides(operatorZones, policyGeofences) | Gibt die Betreiberzonen zurueck, die derzeit von einer hoeher priorisierten Policy ueberdeckt werden. |
resolveStack() ist die Funktion, die current_speed_limit_kph in MDS-Antworten und die GBFS-geofencing_zones.json-Prioritaetssortierung antreibt. Auch die Zone-Crossing-Engine ruft sie bei jeder GPS-Telemetrieaktualisierung waehrend einer aktiven Fahrt auf.
detectOperatorOverrides() treibt das Konflikt-Banner auf dem Compliance-Dashboard an.
Das zones_containing_point-RPC
Migration 20270601004000_07_zones_priority_source.sql liefert eine Postgres-Funktion:
CREATE FUNCTION zones_containing_point(
p_subaccount_id uuid,
p_lat double precision,
p_lng double precision
)
RETURNS SETOF zones
LANGUAGE sql STABLE
AS $$
SELECT *
FROM zones
WHERE subaccount_id = p_subaccount_id
AND deleted_at IS NULL
AND ST_Contains(geom, ST_SetSRID(ST_MakePoint(p_lng, p_lat), 4326))
ORDER BY priority DESC, created_at ASC;
$$;
Die geom-Spalte wird via Trigger automatisch aus der geojson-Spalte gefuellt. Jede Zone, ob betreiber-authored oder von einer Policy gespiegelt, hat eine nicht-null geom, sodass die PostGIS-Abfrage die kanonische Antwort auf "welche Zonen enthalten diesen Punkt?" ist.
stackForPoint(point)
In JS umhuellt src/lib/compliance/zone-stack.ts das RPC und konsultiert zusaetzlich policy_geofences (das derzeit In-JS-pointInPolygon nutzt, weil die PostGIS-Spalte auf dieser Tabelle bis zur ST_GeomFromGeoJSON-Migration null ist). Die kombinierte Liste wird dann an resolveStack() uebergeben.
const stack = await stackForPoint({
subaccountId,
lat: vehicle.last_lat,
lng: vehicle.last_lng,
});
// stack: [
// { source: 'city', priority: 1000, rule_type: 'speed', rule_value: 8, ... },
// { source: 'operator', priority: 500, rule_type: 'speed', rule_value: 10, ... },
// { source: 'operator', priority: 300, rule_type: 'parking', ... },
// ]
const active = resolveStack(stack);
// active.speed.rule_value === 8 // Stadt gewinnt
// active.parking existiert // Betreiber-Parken gilt (Stadt hat hier keine Parkregel)
Konflikterkennung
detectOperatorOverrides() wird vom Betreiber-Dashboard aufgerufen, um das Hinweisbanner anzuzeigen. Es gibt fuer den gegebenen Subaccount alle Betreiberzonen zurueck, deren Geometrie eine aktive policy_geofences-Zeile hoeherer Prioritaet und gleichen rule_type schneidet.
| Feld im Ergebnis | Bedeutung |
|---|---|
operator_zone | Die ueberdeckte Zone |
shadowing_rule | Die MDS-Policy-Regel, die sie ueberlagert |
affected_area | Das Polygonschnittgebiet (fuer die Kartenvorschau) |
rule_type | Der Regeltyp, in dem der Konflikt besteht (speed, no_ride, parking) |
In der UI kann der Betreiber:
- Die ueberdeckte Zone loeschen -- wenn sie jetzt redundant mit der Stadt-Policy ist.
- Bearbeiten, um nebenher zu funktionieren -- wenn die Betreiberzone aus einem anderen Grund existiert (z.B. eine Slow-Zone, die die No-Go-Zone der Stadt umgibt und Kontext fuer Fahrer liefert).
- Bestaetigen -- den Hinweis verwerfen ohne etwas zu aendern. Die Ueberdeckung bleibt; das Banner hoert nur auf, zu noergeln.
Lesen der Leiter in mobilen Clients
Die mobile Kunden-App liest GBFS 3.0 geofencing_zones.json (sortiert nach Prioritaet absteigend) und behandelt das erste passende Feature als aktive Regel. Das erwarten GBFS-Validatoren, und es passt zur Leiter-Semantik: die staedtische Regel kommt zuerst, weil sie die hoehere Prioritaet hat.
Wenn Ihr mobiler Client eigene Zonenaufloesung implementiert, gilt dieselbe Regel -- nach Prioritaet absteigend sortieren, das erste passende Feature pro rule_type nehmen.
Szenarien
Szenario 1: Stadt-Slow-Zone ueberlappt Betreiber-Slow-Zone
| Zone | Quelle | Prioritaet | Tempolimit |
|---|---|---|---|
| Stadt "Innenstadt Fussgaengerzone" | Stadt | 1000 | 8 km/h |
| Betreiber "Plaza" | Betreiber | 500 | 10 km/h |
Ergebnis: 8 km/h der Stadt gewinnt im Ueberlappungsbereich. Ausserhalb der Stadtzone, aber innerhalb der Betreiberzone gilt 10 km/h. Die Betreiberzone erscheint im Konflikt-Banner als "ueberdeckt durch Stadt Innenstadt Fussgaengerzone".
Szenario 2: Stadt-No-Go innerhalb Betreiber-Parkzone
| Zone | Quelle | Prioritaet | Typ |
|---|---|---|---|
| Stadt "Baustelle" | Stadt | 1000 | no_ride |
| Betreiber "Main Street Korridor" | Betreiber | 300 | parking |
Ergebnis: Die Regeln sind unterschiedlicher Typ, also gelten beide. Innerhalb der Baustelle darf das Fahrzeug nicht fahren (Stadt-no_ride gewinnt fuer no_ride-rule_type). Ausserhalb der Baustelle, aber innerhalb der Betreiberzone ist Parken erlaubt (Betreiber-Parken ist die einzige parking-Regel).
Szenario 3: Stadt-Policy laeuft ab
Wenn policy_geofences.is_active auf false flippt (weil active_until verstrichen ist), liefert das zones_containing_point-RPC diese Zeilen nicht mehr. Die Betreiberzone darunter kommt automatisch wieder an die Spitze der Leiter. Kein manuelles Aufraeumen; kein Audit-Log-Update; das Konflikt-Banner verschwindet beim naechsten Seitenaufruf.
Szenario 4: Zwei Stadt-Policies ueberlappen
Staedte veroeffentlichen manchmal zwei Policies mit ueberlappender Geometrie (z.B. eine dauerhafte Slow-Zone plus eine temporaere Veranstaltungs-No-Ride-Zone). Beide stehen bei Prioritaet 1000 (Stadtstufe), aber der Resolver sortiert Gleichstaende nach rule_type-Schaerfe: no_ride schlaegt speed, speed schlaegt parking. Die strengste Regel gilt.
Wenn derselbe rule_type bei gleicher Prioritaet kollidiert, sortieren wir nach start_date absteigend -- die zuletzt aktivierte Policy gewinnt. Das handhabt "Stadt hat ihr Slow-Zonen-Tempolimit von 10 auf 8 km/h aktualisiert" sauber.
Was Sie aendern koennen
| Knopf | Standard | Hinweise |
|---|---|---|
| Betreiberzonen-Prioritaet | Stufenstandard (700/500/300) | Eigene Werte erfordern heute eine direkte DB-Aktualisierung. |
| Konflikt bestaetigen | n/a | Dashboard-Aktion; die Ueberdeckung bleibt, aber das Banner hoert auf zu noergeln. |
| Ueberdeckte Zone loeschen | n/a | Dashboard-Aktion; gespiegelte Stadt-Zonen sind nicht aus der Betreiber-UI loeschbar. |
Stadt-Zonen koennen vom Betreiber nicht bearbeitet werden. Sie gehoeren dem Policy-Feed; der einzige Weg, sie zu aendern, ist die Stadt zu bitten, ihren Feed zu aktualisieren.
Was kommt als naechstes?
- Echtzeit-Geschwindigkeitsdurchsetzung -- was mit Fahrzeugen passiert, die bereits in einer Zone sind, wenn sich die Leiter verschiebt.
- Policy-Aufnahme von Staedten -- woher die Stadt-Stufe-Zonen kommen.
- Wie Zonen funktionieren -- die urspruengliche Betreiberzonen-Implementierung, auf der die Leiter aufbaut.