pek
This commit is contained in:
parent
588e4170ac
commit
15f0e51bd0
14 changed files with 2647 additions and 0 deletions
558
mandates/pek/datenmodell/PEK Datenmodell.md
Normal file
558
mandates/pek/datenmodell/PEK Datenmodell.md
Normal file
|
|
@ -0,0 +1,558 @@
|
||||||
|
# Datenmodell Architektur-Planungs-App
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
|
||||||
|
Dieses Datenmodell bildet die Grundlage für eine Schweizer Architektur-Planungs-App zur Verwaltung von Bauprojekten, Parzellen, Dokumenten und regulatorischen Kontextinformationen.
|
||||||
|
|
||||||
|
## Datenfluss-Diagramm
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
---
|
||||||
|
title: Hauptflüsse - Architektur-Planungs-App
|
||||||
|
---
|
||||||
|
flowchart TD
|
||||||
|
Start([Datenmodell Start])
|
||||||
|
|
||||||
|
subgraph Admin[Administrative Ebene]
|
||||||
|
Land[LAND<br/>Schweiz]
|
||||||
|
Kanton[KANTON<br/>z.B. Zürich]
|
||||||
|
Gemeinde[GEMEINDE<br/>z.B. Zürich Stadt]
|
||||||
|
Land --> Kanton
|
||||||
|
Kanton --> Gemeinde
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Geo[Geografische Daten]
|
||||||
|
Parzelle[PARZELLE<br/>Grundstück mit<br/>Bauparametern]
|
||||||
|
GeoPunkt[GEO_PUNKT<br/>Koordinaten]
|
||||||
|
Gemeinde --> Parzelle
|
||||||
|
Parzelle --> GeoPunkt
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Core[Kern-Business-Logik]
|
||||||
|
Projekt[PROJEKT<br/>Bauprojekt]
|
||||||
|
Dokument[DOKUMENT<br/>Dateien & URLs]
|
||||||
|
Projekt -.Perimeter.-> Parzelle
|
||||||
|
Projekt -.Dokumente.-> Dokument
|
||||||
|
Parzelle -.Dokumente.-> Dokument
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Support[Unterstützende Daten]
|
||||||
|
Kontext[KONTEXT<br/>Zusatzinfos]
|
||||||
|
Projekt --> Kontext
|
||||||
|
Parzelle --> Kontext
|
||||||
|
end
|
||||||
|
|
||||||
|
Start --> Admin
|
||||||
|
|
||||||
|
style Land fill:#50C878,stroke:#2D7A4A,stroke-width:2px,color:#fff
|
||||||
|
style Kanton fill:#50C878,stroke:#2D7A4A,stroke-width:2px,color:#fff
|
||||||
|
style Gemeinde fill:#50C878,stroke:#2D7A4A,stroke-width:2px,color:#fff
|
||||||
|
style Parzelle fill:#4A90E2,stroke:#2E5C8A,stroke-width:3px,color:#fff
|
||||||
|
style Projekt fill:#4A90E2,stroke:#2E5C8A,stroke-width:3px,color:#fff
|
||||||
|
style Dokument fill:#4A90E2,stroke:#2E5C8A,stroke-width:3px,color:#fff
|
||||||
|
style GeoPunkt fill:#F5A623,stroke:#C17D11,stroke-width:2px,color:#fff
|
||||||
|
style Kontext fill:#F5A623,stroke:#C17D11,stroke-width:2px,color:#fff
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Alle Datenobjekte als Tabellen
|
||||||
|
|
||||||
|
### Übersichtstabelle
|
||||||
|
|
||||||
|
| Objekt | Typ | Beschreibung | Hauptfelder |
|
||||||
|
|--------|-----|--------------|-------------|
|
||||||
|
| **Projekt** | Hauptentität | Bauprojekt mit Status und Perimeter | id, label, statusProzess |
|
||||||
|
| **Parzelle** | Hauptentität | Grundstück mit Bauparametern | id, label, bauzone, AZ, BZ |
|
||||||
|
| **Dokument** | Hauptentität | Dateien und URLs mit Versionierung | id, label, typ, format |
|
||||||
|
| **Land** | Admin | Nationale Ebene | id, label |
|
||||||
|
| **Kanton** | Admin | Kantonale Ebene mit Baurecht | id, label, Baureglement |
|
||||||
|
| **Gemeinde** | Admin | Gemeinde-Ebene mit BZO | id, label, plz, BZO |
|
||||||
|
| **GeoPunkt** | Hilfsobjekt | 3D-Koordinate im LV95 | x, y, z, referenzen |
|
||||||
|
| **Kontext** | Hilfsobjekt | Flexible Zusatzinformationen | id, thema, inhalt |
|
||||||
|
| **Tag** | Enum | Dokumentkategorien | - |
|
||||||
|
| **GeoTag** | Enum | Geopunkt-Kategorien | - |
|
||||||
|
| **JaNein** | Enum | Drei-wertiger Status | "", "Ja", "Nein" |
|
||||||
|
| **StatusProzess** | Enum | Projektstatus | 7 Werte |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Zentrale Entitäten
|
||||||
|
|
||||||
|
### 1. Projekt
|
||||||
|
**Das Kernobjekt, das ein Bauprojekt repräsentiert.**
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||||
|
| `label` | String | ✓ | Projektbezeichnung |
|
||||||
|
| `statusProzess` | Array[Enum] | - | Projektstatus: Eingang, Analyse, Studie, Planung, Baurechtsverfahren, Umsetzung, Archiv |
|
||||||
|
| `perimeter` | Array[Parzelle] | - | Betroffene Parzellen (n:m Beziehung) |
|
||||||
|
| `dokumenteBauherrschaft` | Array[Dokument] | - | Dokumente vom Bauherrn (n:m Beziehung) |
|
||||||
|
| `dokumentePlanung` | Array[Dokument] | - | Planungsdokumente (n:m Beziehung) |
|
||||||
|
| `geoBaulinie` | Array[GeoPunkt] | - | Baulinie als Polygonzug (1:n Beziehung) |
|
||||||
|
| `kontextInformationen` | Array[Kontext] | - | Projektspezifische Kontextinfos (1:n Beziehung) |
|
||||||
|
|
||||||
|
**Beziehungen:**
|
||||||
|
- **n:m** zu Parzelle (Perimeter über `projekt_parzelle`)
|
||||||
|
- **n:m** zu Dokument (Bauherrschaft über `projekt_dokument_bauherrschaft`)
|
||||||
|
- **n:m** zu Dokument (Planung über `projekt_dokument_planung`)
|
||||||
|
- **1:n** zu GeoPunkt (Baulinie)
|
||||||
|
- **1:n** zu Kontext
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Parzelle
|
||||||
|
**Repräsentiert ein Grundstück mit allen baurechtlichen Eigenschaften.**
|
||||||
|
|
||||||
|
#### Grunddaten
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||||
|
| `label` | String | ✓ | Parzellenbezeichnung |
|
||||||
|
| `parzellenNummern` | Array[String] | - | Offizielle Parzellennummern |
|
||||||
|
| `eigentuemerschaaft` | String | - | Eigentümer der Parzelle |
|
||||||
|
| `strasseNr` | String | - | Straße und Hausnummer |
|
||||||
|
|
||||||
|
#### Geografischer Kontext
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `kontextLand` | Land | - | Land der Parzelle (n:1 Beziehung) |
|
||||||
|
| `kontextKanton` | Kanton | - | Kanton der Parzelle (n:1 Beziehung) |
|
||||||
|
| `kontextGemeinde` | Gemeinde | - | Gemeinde der Parzelle (n:1 Beziehung) |
|
||||||
|
| `geoUmfang` | Array[GeoPunkt] | - | Parzellengrenze als Polygon |
|
||||||
|
|
||||||
|
#### Nachbarschaft
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `nachbarEigentuemer` | Array[Parzelle] | - | Selbstreferenz zu angrenzenden Parzellen (n:m Beziehung) |
|
||||||
|
|
||||||
|
#### Schutzzonen
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `hochwasserschutzzone` | String | - | Hochwasserschutzzone (falls zutreffend) |
|
||||||
|
| `laermschutzzone` | String | - | Lärmschutzzone |
|
||||||
|
| `grundwasserschutzzone` | String | - | Grundwasserschutzzone (falls zutreffend) |
|
||||||
|
|
||||||
|
#### Bebauungsparameter
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `bauzone` | String | - | Bauzonenbezeichnung (z.B. W3, WG2, etc.) |
|
||||||
|
| `az` | Float | - | Ausnützungsziffer |
|
||||||
|
| `bz` | Float | - | Bebauungsziffer |
|
||||||
|
| `vollgeschossZahl` | Integer | - | Anzahl zulässiger Vollgeschosse |
|
||||||
|
| `anrechenbarDachgeschoss` | Float | - | Anrechenbarer Anteil Dachgeschoss (0.0 - 1.0) |
|
||||||
|
| `anrechenbarUntergeschoss` | Float | - | Anrechenbarer Anteil Untergeschoss (0.0 - 1.0) |
|
||||||
|
| `gebaeudehoehe_max` | Float | - | Maximale Gebäudehöhe in Metern |
|
||||||
|
|
||||||
|
#### Abstandsregelungen
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `regelnGrenzabstand` | String | - | Regelungen zum Grenzabstand |
|
||||||
|
| `regelnMehrlaengenzuschlag` | String | - | Regelungen zum Mehrlängenzuschlag |
|
||||||
|
| `regelnMehrhoehenzuschlag` | String | - | Regelungen zum Mehrhöhenzuschlag |
|
||||||
|
|
||||||
|
#### Eigenschaften (Ja/Nein)
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `parzelleBebaut` | JaNein | - | Ist die Parzelle bebaut? ("", "Ja", "Nein") |
|
||||||
|
| `parzelleErschlossen` | JaNein | - | Ist die Parzelle erschlossen? ("", "Ja", "Nein") |
|
||||||
|
| `hanglage` | JaNein | - | Liegt die Parzelle in Hanglage? ("", "Ja", "Nein") |
|
||||||
|
|
||||||
|
#### Weitere Informationen
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `spezifischeDokumente` | Array[Dokument] | - | Parzellenspezifische Dokumente (n:m Beziehung) |
|
||||||
|
| `kontextInformationen` | Array[Kontext] | - | Parzellenspezifische Kontextinfos (1:n Beziehung) |
|
||||||
|
|
||||||
|
**Beziehungen:**
|
||||||
|
- **n:1** zu Land, Kanton, Gemeinde (geografischer Kontext)
|
||||||
|
- **n:m** zu Parzelle (Nachbarn über `parzelle_nachbar`)
|
||||||
|
- **n:m** zu Dokument (über `parzelle_dokument`)
|
||||||
|
- **1:n** zu GeoPunkt (Umfang als Polygon)
|
||||||
|
- **1:n** zu Kontext
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Dokument
|
||||||
|
**Verwaltet Dateien und URLs mit Versionierung.**
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||||
|
| `label` | String | ✓ | Dokumentbezeichnung |
|
||||||
|
| `versionsbezeichnung` | String | - | Versionsnummer oder -bezeichnung (z.B. "v1.0", "Rev. A") |
|
||||||
|
| `typ` | Enum | ✓ | Art des Dokuments: `Datei` oder `Url` |
|
||||||
|
| `format` | String | - | Dateiformat (z.B. "PDF", "DWG", "IFC", "URL") |
|
||||||
|
| `dokumentReferenz` | String | ✓ | Dateipfad oder URL |
|
||||||
|
| `tags` | Array[Tag] | - | Kategorisierung (siehe Tag-Enum) |
|
||||||
|
|
||||||
|
#### Tag-Enum (Dokumentkategorien)
|
||||||
|
|
||||||
|
| Tag | Beschreibung |
|
||||||
|
|-----|--------------|
|
||||||
|
| `Kataster Objekte` | Amtliche Vermessung |
|
||||||
|
| `Kataster Werkeleitungen` | Leitungskataster |
|
||||||
|
| `Kataster Belastete Standorte` | Altlasten |
|
||||||
|
| `Kataster Bäume` | Baumkataster |
|
||||||
|
| `Zonenplan` | Zonenpläne |
|
||||||
|
| `Planungs- und Baugesetz (PGB)` | Kantonale Baugesetze |
|
||||||
|
| `Bau- und Zonenordnung (BZO)` | Gemeinde BZO |
|
||||||
|
| `Parkplatzverordnung` | Parkplatzregelungen |
|
||||||
|
| `Eigentümerauskunft` | Grundbuch-Auszüge Eigentümer |
|
||||||
|
| `Grundbuchauszug` | Vollständige Grundbuch-Auszüge |
|
||||||
|
|
||||||
|
**Beziehungen:**
|
||||||
|
- **n:m** zu allen Entitäten, die Dokumente referenzieren (Projekt, Parzelle, Land, Kanton, Gemeinde)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. Geografische Entitäten
|
||||||
|
|
||||||
|
#### GeoPunkt
|
||||||
|
**Repräsentiert einen 3D-Punkt im Schweizer Koordinatensystem LV95 (EPSG:2056).**
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `x` | Float | ✓ | LV95 Ostwert (E), typisch 2'480'000 - 2'840'000 |
|
||||||
|
| `y` | Float | ✓ | LV95 Nordwert (N), typisch 1'070'000 - 1'300'000 |
|
||||||
|
| `z` | Float | - | Höhe über Meer in Metern |
|
||||||
|
| `referenzen` | Array[GeoTag] | - | Kategorisierung des Punktes |
|
||||||
|
|
||||||
|
**Verwendung:**
|
||||||
|
- Parzellenumfang (Polygon)
|
||||||
|
- Baulinie (Linienzug)
|
||||||
|
- Einzelne Referenzpunkte
|
||||||
|
|
||||||
|
#### GeoTag (Enum)
|
||||||
|
|
||||||
|
| Kategorie | Beschreibung |
|
||||||
|
|-----------|--------------|
|
||||||
|
| `Referenzpunkt Kat. 1` | Fixpunkt höchster Genauigkeit |
|
||||||
|
| `Referenzpunkt Kat. 2` | Fixpunkt mittlerer Genauigkeit |
|
||||||
|
| `Referenzpunkt Kat. 3` | Fixpunkt niedriger Genauigkeit |
|
||||||
|
| `Geometeraufnahme` | Vom Geometer vermessener Punkt |
|
||||||
|
|
||||||
|
**Koordinatensystem-Beispiel (Zürich Hauptbahnhof):**
|
||||||
|
- X (Ost): 2'683'140
|
||||||
|
- Y (Nord): 1'247'850
|
||||||
|
- Z (Höhe): 408 m ü. M.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Administrative Hierarchie
|
||||||
|
|
||||||
|
#### Land
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||||
|
| `label` | String | ✓ | Landesname (z.B. "Schweiz") |
|
||||||
|
| `dokumente` | Array[Dokument] | - | Nationale Gesetze (1:n Beziehung) |
|
||||||
|
| `kontextInformationen` | Array[Kontext] | - | Nationale Kontextinformationen (1:n Beziehung) |
|
||||||
|
|
||||||
|
**Beziehungen:**
|
||||||
|
- **1:n** zu Kanton
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Kanton
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||||
|
| `label` | String | ✓ | Kantonsname (z.B. "Zürich") |
|
||||||
|
| `dokumente` | Array[Dokument] | - | Kantonale Dokumente (1:n Beziehung) |
|
||||||
|
| `kontextInformationen` | Array[Kontext] | - | Kantonsspezifische Kontextinfos (1:n Beziehung) |
|
||||||
|
| `baureglementAktuell` | Dokument | - | Aktuelles Baureglement |
|
||||||
|
| `baureglementRevision` | Dokument | - | Baureglement in Revision |
|
||||||
|
| `bauverordnungAktuell` | Dokument | - | Aktuelle Bauverordnung |
|
||||||
|
| `bauverordnungRevision` | Dokument | - | Bauverordnung in Revision |
|
||||||
|
|
||||||
|
**Beziehungen:**
|
||||||
|
- **n:1** zu Land
|
||||||
|
- **1:n** zu Gemeinde
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Gemeinde
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||||
|
| `label` | String | ✓ | Gemeindename (z.B. "Zürich") |
|
||||||
|
| `plz` | String | - | Postleitzahl |
|
||||||
|
| `dokumente` | Array[Dokument] | - | Gemeindedokumente (1:n Beziehung) |
|
||||||
|
| `kontextInformationen` | Array[Kontext] | - | Gemeindespezifische Kontextinfos (1:n Beziehung) |
|
||||||
|
| `bzoAktuell` | Dokument | - | Aktuelle Bau- und Zonenordnung (BZO) |
|
||||||
|
| `bzoRevision` | Dokument | - | BZO in Revision |
|
||||||
|
|
||||||
|
**Beziehungen:**
|
||||||
|
- **n:1** zu Kanton
|
||||||
|
- **1:n** zu Parzelle
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. Kontext
|
||||||
|
**Flexibles System für spezifische Informationen und Hinweise.**
|
||||||
|
|
||||||
|
| Feld | Datentyp | Pflicht | Beschreibung |
|
||||||
|
|------|----------|---------|--------------|
|
||||||
|
| `id` | UUID | ✓ | Eindeutiger Identifier |
|
||||||
|
| `thema` | String | ✓ | Bezeichnung des Themas |
|
||||||
|
| `inhalt` | String | ✓ | Detaillierte Information (Text) |
|
||||||
|
|
||||||
|
**Polymorphe Beziehung** - Kontext kann gehören zu:
|
||||||
|
- Projekt (n:1)
|
||||||
|
- Parzelle (n:1)
|
||||||
|
- Land (n:1)
|
||||||
|
- Kanton (n:1)
|
||||||
|
- Gemeinde (n:1)
|
||||||
|
|
||||||
|
#### Beispielthemen (nicht abschliessend)
|
||||||
|
|
||||||
|
| Themenbereich | Beispiele |
|
||||||
|
|---------------|-----------|
|
||||||
|
| **Nutzung** | Vorgaben zur Erdgeschossnutzung (Wohnen erlaubt oder Pflicht für Gewerbe) |
|
||||||
|
| **Rechte** | Dienstbarkeiten (Wegrechte, Nähebaurechte, etc.) |
|
||||||
|
| **Parkierung** | Anforderung Parkplätze (Berechnung / Reduktionsfaktoren) |
|
||||||
|
| **Ausnützung** | Ausnützungsübertragungen |
|
||||||
|
| **Umwelt** | Schadstoffbelastungen auf Parzellen |
|
||||||
|
| **Planung** | Aktive Gestaltungspläne |
|
||||||
|
| **Lärm** | Lärmempfindlichkeitsstufen |
|
||||||
|
| **Energie** | Mögliche Wärmenutzung (Wärmeverbundnetze; Fernwärme, Anergie) |
|
||||||
|
| **Natur** | Baumbestand auf privaten Grundstücken |
|
||||||
|
| **Schutz** | Isos (Ortsbild, Schutzstatus, Denkmalschutz, Weilergebiet, etc.) |
|
||||||
|
| **Gefahren** | Naturgefahren (z.B. Objektschutzmassnahmen (Hochwasser)) |
|
||||||
|
| **Revision** | Verweis auf aktuell in oder zukünftig in Revision befindlichen Normen/Gesetze (z.B. Revision PBG mit aktuell negativer Vorwirkung) |
|
||||||
|
|
||||||
|
**Design-Rationale:**
|
||||||
|
Das Kontext-Objekt ermöglicht flexibles Hinzufügen von projektspezifischen, parzellen-spezifischen oder regionalen Informationen ohne Schemaänderungen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7. Hilfsentitäten & Enumerationen
|
||||||
|
|
||||||
|
#### JaNein (Enum)
|
||||||
|
**Drei-wertiger Zustand für optionale Ja/Nein-Fragen.**
|
||||||
|
|
||||||
|
| Wert | Bedeutung |
|
||||||
|
|------|-----------|
|
||||||
|
| `""` (leer) | Unbekannt / Nicht erfasst |
|
||||||
|
| `"Ja"` | Ja / Zutreffend |
|
||||||
|
| `"Nein"` | Nein / Nicht zutreffend |
|
||||||
|
|
||||||
|
**Verwendung:**
|
||||||
|
- `parzelleBebaut`: Ist die Parzelle bebaut?
|
||||||
|
- `parzelleErschlossen`: Ist die Parzelle erschlossen?
|
||||||
|
- `hanglage`: Liegt die Parzelle in Hanglage?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### StatusProzess (Enum)
|
||||||
|
**Projektstatus zur Nachverfolgung des Projektfortschritts.**
|
||||||
|
|
||||||
|
| Wert | Beschreibung |
|
||||||
|
|------|--------------|
|
||||||
|
| `Eingang` | Projekt wurde eingereicht |
|
||||||
|
| `Analyse` | Projekt wird analysiert |
|
||||||
|
| `Studie` | Machbarkeitsstudie läuft |
|
||||||
|
| `Planung` | Planungsphase |
|
||||||
|
| `Baurechtsverfahren` | Baubewilligung läuft |
|
||||||
|
| `Umsetzung` | Bauprojekt in Umsetzung |
|
||||||
|
| `Archiv` | Projekt abgeschlossen |
|
||||||
|
|
||||||
|
**Besonderheit:**
|
||||||
|
Ein Projekt kann mehrere Status gleichzeitig haben (z.B. "Analyse" und "Studie").
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### DokumentTyp (Enum)
|
||||||
|
|
||||||
|
| Wert | Beschreibung |
|
||||||
|
|------|--------------|
|
||||||
|
| `Datei` | Physische Datei (PDF, DWG, etc.) |
|
||||||
|
| `Url` | Externer Link / URL |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Beziehungsdiagramm
|
||||||
|
|
||||||
|
```
|
||||||
|
PROJEKT
|
||||||
|
├── perimeter [1:n] ──────────> PARZELLE
|
||||||
|
│ ├── dokumenteBauherrschaft [1:n] ──> DOKUMENT
|
||||||
|
│ ├── dokumentePlanung [1:n] ────────> DOKUMENT
|
||||||
|
│ ├── geoBaulinie [1:n] ─────────────> GEO_PUNKT
|
||||||
|
│ └── kontextInformationen [1:n] ───> KONTEXT
|
||||||
|
|
||||||
|
PARZELLE
|
||||||
|
├── nachbarEigentuemer [n:n] ──> PARZELLE (selbst)
|
||||||
|
├── kontextLand [n:1] ──────────> LAND
|
||||||
|
├── kontextKanton [n:1] ────────> KANTON
|
||||||
|
├── kontextGemeinde [n:1] ──────> GEMEINDE
|
||||||
|
├── geoUmfang [1:n] ────────────> GEO_PUNKT
|
||||||
|
├── spezifischeDokumente [1:n] ─> DOKUMENT
|
||||||
|
└── kontextInformationen [1:n] ─> KONTEXT
|
||||||
|
|
||||||
|
DOKUMENT
|
||||||
|
└── tags [n:n] ─────────────────> TAG
|
||||||
|
|
||||||
|
GEO_PUNKT
|
||||||
|
└── referenzen [1:n] ───────────> GEO_TAG
|
||||||
|
|
||||||
|
LAND / KANTON / GEMEINDE
|
||||||
|
├── dokumente [1:n] ────────────> DOKUMENT
|
||||||
|
└── kontextInformationen [1:n] ─> KONTEXT
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Junction Tables (Many-to-Many Beziehungen)
|
||||||
|
|
||||||
|
**Zwischentabellen zur Auflösung von n:m Beziehungen:**
|
||||||
|
|
||||||
|
| Tabelle | Verbindet | Felder | Beschreibung |
|
||||||
|
|---------|-----------|--------|--------------|
|
||||||
|
| `projekt_parzelle` | Projekt ↔ Parzelle | `projekt_id`, `parzelle_id` | Projektperimeter |
|
||||||
|
| `projekt_dokument_bauherrschaft` | Projekt ↔ Dokument | `projekt_id`, `dokument_id` | Dokumente der Bauherrschaft |
|
||||||
|
| `projekt_dokument_planung` | Projekt ↔ Dokument | `projekt_id`, `dokument_id` | Planungsdokumente |
|
||||||
|
| `parzelle_nachbar` | Parzelle ↔ Parzelle | `parzelle_id`, `nachbar_id` | Nachbarschaftsbeziehungen |
|
||||||
|
| `parzelle_dokument` | Parzelle ↔ Dokument | `parzelle_id`, `dokument_id` | Parzellenspezifische Dokumente |
|
||||||
|
|
||||||
|
**Besonderheiten:**
|
||||||
|
- `parzelle_nachbar`: Selbstreferenzierende Tabelle mit Constraint `parzelle_id != nachbar_id`
|
||||||
|
- Alle Junction Tables haben zusammengesetzte Primärschlüssel aus beiden Foreign Keys
|
||||||
|
- CASCADE DELETE empfohlen für automatische Bereinigung
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementierungshinweise
|
||||||
|
|
||||||
|
### Datenbank-Design
|
||||||
|
|
||||||
|
#### Empfehlung: Hybrid-Ansatz
|
||||||
|
|
||||||
|
**Relationale Datenbank (PostgreSQL mit PostGIS):**
|
||||||
|
- Stammdaten: Projekt, Parzelle, Land, Kanton, Gemeinde
|
||||||
|
- Geografische Daten: GeoPunkt mit PostGIS-Geometrie-Typen
|
||||||
|
- Strukturierte Queries und Joins
|
||||||
|
|
||||||
|
**Dokumenten-Datenbank oder Blob Storage:**
|
||||||
|
- Dokumente: S3, Azure Blob Storage oder MinIO
|
||||||
|
- Metadaten in relationaler DB, Binärdaten extern
|
||||||
|
|
||||||
|
#### Schema-Überlegungen
|
||||||
|
|
||||||
|
**Normalisierung:**
|
||||||
|
1. Land, Kanton, Gemeinde als separate Tabellen mit Referenzen
|
||||||
|
2. Dokument als zentrale Tabelle, referenziert von mehreren Entitäten (Polymorphic Associations oder Junction Tables)
|
||||||
|
3. GeoPunkt entweder embedded (JSON) oder separate Tabelle mit Foreign Keys
|
||||||
|
|
||||||
|
**Denormalisierung für Performance:**
|
||||||
|
- Häufig abgefragte Parzellendaten können gecached werden
|
||||||
|
- Gemeinde.plz könnte redundant in Parzelle gespeichert werden
|
||||||
|
|
||||||
|
### Geografische Daten
|
||||||
|
|
||||||
|
**Koordinatensystem:**
|
||||||
|
Schweizer Landessystem LV95 (EPSG:2056):
|
||||||
|
- X (Ost): 2'480'000 - 2'840'000
|
||||||
|
- Y (Nord): 1'070'000 - 1'300'000
|
||||||
|
- Z (Höhe): Meter über Meer
|
||||||
|
|
||||||
|
### Validierung
|
||||||
|
|
||||||
|
**Pflichtfelder:**
|
||||||
|
- Alle IDs (UUID)
|
||||||
|
- Alle Labels
|
||||||
|
- Dokument: typ, dokumentReferenz
|
||||||
|
- GeoPunkt: x, y (z optional)
|
||||||
|
- Kontext: thema, inhalt
|
||||||
|
|
||||||
|
**Geschäftslogik-Validierungen:**
|
||||||
|
- AZ und BZ müssen > 0 sein
|
||||||
|
- VollgeschossZahl muss ≥ 0 sein
|
||||||
|
- Geo-Koordinaten müssen in gültigem CH-Bereich liegen
|
||||||
|
- Parzelle muss mindestens 3 GeoPunkte für gültiges Polygon haben
|
||||||
|
|
||||||
|
### Sicherheit & Zugriffskontrolle
|
||||||
|
|
||||||
|
**Überlegungen:**
|
||||||
|
- Dokumente: Zugriffskontrolle nach Projekt-/Benutzerrolle
|
||||||
|
- Eigentümerdaten: DSGVO-konforme Behandlung
|
||||||
|
- Audit-Log für Änderungen an Bebauungsparametern
|
||||||
|
- Versionierung von Dokumenten (via versionsbezeichnung)
|
||||||
|
|
||||||
|
### Erweiterbarkeit
|
||||||
|
|
||||||
|
**Flexible Bereiche:**
|
||||||
|
1. **Kontext-Objekt**: Neue Themen können ohne Schema-Änderung hinzugefügt werden
|
||||||
|
2. **Tag-System**: Erweiterbar um neue Dokumentkategorien
|
||||||
|
3. **StatusProzess**: Kann projektspezifisch angepasst werden
|
||||||
|
4. **GeoTag**: Neue Kategorien für Vermessungspunkte möglich
|
||||||
|
|
||||||
|
**Migration-Strategy:**
|
||||||
|
- Verwende Datenbank-Migrationen (z.B. Alembic für Python, Flyway für Java)
|
||||||
|
- Behalte alte Enums bei, füge neue hinzu
|
||||||
|
- Nutze nullable Felder für neue Eigenschaften
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Anwendungsfälle
|
||||||
|
|
||||||
|
### Use Case 1: Neues Projekt anlegen
|
||||||
|
1. Erstelle Projekt mit Label und Status
|
||||||
|
2. Füge Parzellen zum Perimeter hinzu
|
||||||
|
3. Lade Dokumente der Bauherrschaft hoch
|
||||||
|
4. Verknüpfe Kontext-Informationen
|
||||||
|
|
||||||
|
### Use Case 2: Bebaubarkeit prüfen
|
||||||
|
1. Lade Parzelle mit allen Eigenschaften
|
||||||
|
2. Prüfe AZ, BZ, Vollgeschosszahl
|
||||||
|
3. Berücksichtige Hanglage, Schutzzonen
|
||||||
|
4. Lade BZO der Gemeinde
|
||||||
|
5. Prüfe Kontext-Informationen (Dienstbarkeiten, etc.)
|
||||||
|
|
||||||
|
### Use Case 3: Nachbaranalyse
|
||||||
|
1. Lade Parzelle
|
||||||
|
2. Folge nachbarEigentuemer-Referenzen
|
||||||
|
3. Zeige Eigentümer und Bebauung der Nachbarparzellen
|
||||||
|
|
||||||
|
### Use Case 4: Dokumentensuche
|
||||||
|
1. Suche über Tags (z.B. "Zonenplan")
|
||||||
|
2. Filtere nach Format (z.B. PDF)
|
||||||
|
3. Gruppiere nach Projekt/Parzelle/Gemeinde
|
||||||
|
|
||||||
|
### Use Case 5: Revisionen verfolgen
|
||||||
|
1. Prüfe Gemeinde.bzoRevision
|
||||||
|
2. Prüfe Kanton.baureglementRevision
|
||||||
|
3. Erstelle Kontext-Eintrag mit Hinweis auf negative Vorwirkung
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Offene Fragen / Zu klären
|
||||||
|
|
||||||
|
1. **Versionierung**: Sollen Änderungen an Parzellen historisiert werden?
|
||||||
|
2. **Mehrsprachigkeit**: Labels in DE/FR/IT?
|
||||||
|
3. **Benutzer & Rollen**: Wer darf was bearbeiten?
|
||||||
|
4. **Workflow-Engine**: Für Statusübergänge und Genehmigungen?
|
||||||
|
5. **Integration**: Anbindung an amtliche Geodaten (z.B. Swisstopo API)?
|
||||||
|
6. **Berechnungen**: Sollen Ausnützungsberechnungen automatisiert werden?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Nächste Schritte
|
||||||
|
|
||||||
|
1. **Validierung**: Review mit PEK
|
||||||
|
2. **Prototyp**: Implementierung der Datenmodell-Klassen
|
||||||
|
3. **GIS-Integration**: PostGIS aufsetzen, Test-Geodaten importieren
|
||||||
|
4. **API-Design**: RESTful API (FastAPI) mit OpenAPI-Dokumentation
|
||||||
409
mandates/pek/datenmodell/README.md
Normal file
409
mandates/pek/datenmodell/README.md
Normal file
|
|
@ -0,0 +1,409 @@
|
||||||
|
# Architektur-Planungs-App - Datenmodell
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
|
||||||
|
Dieses Repository enthält das vollständige Datenmodell für eine Schweizer Architektur-Planungs-Applikation zur Verwaltung von Bauprojekten, Parzellen, Dokumenten und regulatorischen Informationen.
|
||||||
|
|
||||||
|
## 📁 Dateien
|
||||||
|
|
||||||
|
### 1. **DATENMODELL_DOKUMENTATION.md**
|
||||||
|
Umfassende Dokumentation mit:
|
||||||
|
- Detaillierte Beschreibung aller Entitäten
|
||||||
|
- Beziehungsdiagramme
|
||||||
|
- Implementierungshinweise
|
||||||
|
- Use Cases
|
||||||
|
- Best Practices
|
||||||
|
- Offene Fragen
|
||||||
|
|
||||||
|
**Empfohlene Lesereihenfolge: Zuerst diese Datei lesen!**
|
||||||
|
|
||||||
|
### 2. **datenmodell.mermaid**
|
||||||
|
Visuelles ER-Diagramm zur Darstellung der Entitäten und Beziehungen.
|
||||||
|
|
||||||
|
**Verwendung:**
|
||||||
|
```bash
|
||||||
|
# In Visual Studio Code mit Mermaid Extension
|
||||||
|
# Oder online: https://mermaid.live/
|
||||||
|
|
||||||
|
# Datei öffnen und als Diagramm anzeigen
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. **datenmodell-schema.json**
|
||||||
|
JSON Schema Definition im JSON Schema Draft-07 Format.
|
||||||
|
|
||||||
|
**Verwendung:**
|
||||||
|
- API-Dokumentation mit Swagger/OpenAPI
|
||||||
|
- Validierung von JSON-Payloads
|
||||||
|
- Code-Generierung für verschiedene Sprachen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# JSON Schema validieren
|
||||||
|
npm install -g ajv-cli
|
||||||
|
ajv validate -s datenmodell-schema.json -d beispiel-daten.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. **models.py**
|
||||||
|
Python SQLAlchemy Implementation mit PostGIS-Unterstützung.
|
||||||
|
|
||||||
|
**Verwendung:**
|
||||||
|
```bash
|
||||||
|
# Installation
|
||||||
|
pip install sqlalchemy geoalchemy2 psycopg2-binary --break-system-packages
|
||||||
|
|
||||||
|
# Datenbank erstellen
|
||||||
|
python models.py
|
||||||
|
|
||||||
|
# In eigener Anwendung verwenden
|
||||||
|
from models import Projekt, Parzelle, Dokument
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tech Stack:**
|
||||||
|
- Python 3.10+
|
||||||
|
- SQLAlchemy 2.0+
|
||||||
|
- PostgreSQL 15+ mit PostGIS 3.4+
|
||||||
|
- GeoAlchemy2
|
||||||
|
|
||||||
|
### 5. **schema.prisma**
|
||||||
|
Prisma Schema für TypeScript/JavaScript Backend.
|
||||||
|
|
||||||
|
**Verwendung:**
|
||||||
|
```bash
|
||||||
|
# Installation
|
||||||
|
npm install prisma @prisma/client
|
||||||
|
|
||||||
|
# Datenbank migrieren
|
||||||
|
npx prisma migrate dev --name init
|
||||||
|
|
||||||
|
# Prisma Client generieren
|
||||||
|
npx prisma generate
|
||||||
|
|
||||||
|
# Prisma Studio öffnen
|
||||||
|
npx prisma studio
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tech Stack:**
|
||||||
|
- Node.js 18+
|
||||||
|
- Prisma 5+
|
||||||
|
- PostgreSQL 15+ mit PostGIS
|
||||||
|
|
||||||
|
### 6. **migration_001_initial_schema.sql**
|
||||||
|
SQL-Migrationsskript für direkte PostgreSQL-Verwendung.
|
||||||
|
|
||||||
|
**Verwendung:**
|
||||||
|
```bash
|
||||||
|
# PostgreSQL Datenbank erstellen
|
||||||
|
createdb architektur_app
|
||||||
|
|
||||||
|
# Migration ausführen
|
||||||
|
psql -d architektur_app -f migration_001_initial_schema.sql
|
||||||
|
|
||||||
|
# Verbinden und testen
|
||||||
|
psql architektur_app
|
||||||
|
\dt # Tabellen anzeigen
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### Option 1: Python mit SQLAlchemy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. PostgreSQL mit PostGIS aufsetzen
|
||||||
|
docker run --name postgis -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgis/postgis:15-3.4
|
||||||
|
|
||||||
|
# 2. Dependencies installieren
|
||||||
|
pip install sqlalchemy geoalchemy2 psycopg2-binary --break-system-packages
|
||||||
|
|
||||||
|
# 3. Models verwenden
|
||||||
|
python models.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: TypeScript mit Prisma
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Projekt initialisieren
|
||||||
|
npm init -y
|
||||||
|
npm install prisma @prisma/client
|
||||||
|
|
||||||
|
# 2. Prisma konfigurieren
|
||||||
|
cp schema.prisma prisma/schema.prisma
|
||||||
|
|
||||||
|
# 3. Database URL setzen
|
||||||
|
echo "DATABASE_URL=\"postgresql://user:password@localhost:5432/architektur_app\"" > .env
|
||||||
|
|
||||||
|
# 4. Migration ausführen
|
||||||
|
npx prisma migrate dev --name init
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: Direkt mit SQL
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Datenbank erstellen
|
||||||
|
createdb architektur_app
|
||||||
|
|
||||||
|
# 2. Migration ausführen
|
||||||
|
psql -d architektur_app -f migration_001_initial_schema.sql
|
||||||
|
|
||||||
|
# 3. Daten einfügen und abfragen
|
||||||
|
psql architektur_app
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 Datenmodell-Struktur
|
||||||
|
|
||||||
|
### Kern-Entitäten
|
||||||
|
|
||||||
|
```
|
||||||
|
PROJEKT (Bauprojekt)
|
||||||
|
├── Perimeter (Parzellen)
|
||||||
|
├── Dokumente (Bauherrschaft & Planung)
|
||||||
|
├── Baulinie (geografisch)
|
||||||
|
└── Kontext-Informationen
|
||||||
|
|
||||||
|
PARZELLE (Grundstück)
|
||||||
|
├── Geografischer Kontext (Land, Kanton, Gemeinde)
|
||||||
|
├── Bauliche Parameter (AZ, BZ, Vollgeschosse, etc.)
|
||||||
|
├── Schutzzonen
|
||||||
|
├── Nachbarparzellen
|
||||||
|
└── Dokumente & Kontext
|
||||||
|
|
||||||
|
DOKUMENT (Datei oder URL)
|
||||||
|
├── Versionierung
|
||||||
|
├── Tags
|
||||||
|
└── Format
|
||||||
|
|
||||||
|
Administrative Hierarchie:
|
||||||
|
LAND → KANTON → GEMEINDE
|
||||||
|
```
|
||||||
|
|
||||||
|
### Geografische Daten
|
||||||
|
|
||||||
|
Das Modell verwendet das **Schweizer Landessystem LV95 (EPSG:2056)**:
|
||||||
|
- Ostwert (X): 2'480'000 - 2'840'000
|
||||||
|
- Nordwert (Y): 1'070'000 - 1'300'000
|
||||||
|
- Höhe (Z): Meter über Meer
|
||||||
|
|
||||||
|
## 🗺️ Koordinatensystem
|
||||||
|
|
||||||
|
Alle geografischen Daten verwenden **LV95 (Swiss LV95 / EPSG:2056)**.
|
||||||
|
|
||||||
|
**Beispiel-Koordinaten (Zürich Hauptbahnhof):**
|
||||||
|
```
|
||||||
|
X (Ost): 2'683'140
|
||||||
|
Y (Nord): 1'247'850
|
||||||
|
Z (Höhe): 408 m ü. M.
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔍 Wichtige Entscheidungen
|
||||||
|
|
||||||
|
### 1. Polymorphe Beziehungen
|
||||||
|
Das `Kontext`-Objekt kann zu verschiedenen Entitäten gehören (Projekt, Parzelle, Land, Kanton, Gemeinde). Dies ermöglicht flexible Erweiterungen ohne Schema-Änderungen.
|
||||||
|
|
||||||
|
### 2. Array-Felder
|
||||||
|
- `statusProzess`: Ein Projekt kann mehrere Status gleichzeitig haben
|
||||||
|
- `tags`: Dokumente können mehrere Tags haben
|
||||||
|
- `parzellenNummern`: Parzellen können mehrere offizielle Nummern haben
|
||||||
|
|
||||||
|
### 3. Selbstreferenzierende Beziehungen
|
||||||
|
Parzellen referenzieren sich gegenseitig als Nachbarn (n:m Beziehung).
|
||||||
|
|
||||||
|
### 4. Versionierung
|
||||||
|
Dokumente haben eine `versionsbezeichnung` für manuelle Versionskontrolle.
|
||||||
|
|
||||||
|
### 5. Drei-wertiger Zustand
|
||||||
|
`JaNein` Enum erlaubt "", "Ja", "Nein" für unbekannte Zustände.
|
||||||
|
|
||||||
|
## 📋 Anwendungsfälle
|
||||||
|
|
||||||
|
### UC1: Neues Projekt erstellen
|
||||||
|
```python
|
||||||
|
# Python Beispiel
|
||||||
|
projekt = Projekt(
|
||||||
|
label="Neubau Mehrfamilienhaus",
|
||||||
|
status_prozess=[StatusProzess.EINGANG]
|
||||||
|
)
|
||||||
|
projekt.perimeter.append(parzelle_1)
|
||||||
|
session.add(projekt)
|
||||||
|
session.commit()
|
||||||
|
```
|
||||||
|
|
||||||
|
### UC2: Bebaubarkeit prüfen
|
||||||
|
```sql
|
||||||
|
-- SQL Beispiel
|
||||||
|
SELECT
|
||||||
|
p.label,
|
||||||
|
p.bauzone,
|
||||||
|
p.az,
|
||||||
|
p.bz,
|
||||||
|
p.vollgeschoss_zahl,
|
||||||
|
p.gebaeudehoehe_max,
|
||||||
|
ST_Area(p.geo_umfang) as flaeche_m2
|
||||||
|
FROM v_parzelle_vollstaendig p
|
||||||
|
WHERE p.id = 'UUID';
|
||||||
|
```
|
||||||
|
|
||||||
|
### UC3: Nachbaranalyse
|
||||||
|
```typescript
|
||||||
|
// TypeScript/Prisma Beispiel
|
||||||
|
const parzelle = await prisma.parzelle.findUnique({
|
||||||
|
where: { id: parzelleId },
|
||||||
|
include: {
|
||||||
|
nachbarEigentuemer_von: {
|
||||||
|
include: { nachbar: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 Best Practices
|
||||||
|
|
||||||
|
### Geometrie-Handling
|
||||||
|
```python
|
||||||
|
# PostGIS: Parzelle mit Polygon erstellen
|
||||||
|
from geoalchemy2.shape import from_shape
|
||||||
|
from shapely.geometry import Polygon
|
||||||
|
|
||||||
|
polygon = Polygon([
|
||||||
|
(2683140, 1247850),
|
||||||
|
(2683200, 1247850),
|
||||||
|
(2683200, 1247900),
|
||||||
|
(2683140, 1247900),
|
||||||
|
(2683140, 1247850)
|
||||||
|
])
|
||||||
|
|
||||||
|
parzelle.geo_umfang = from_shape(polygon, srid=2056)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kontext-Informationen
|
||||||
|
```python
|
||||||
|
# Flexibles Hinzufügen von Kontextinformationen
|
||||||
|
kontext = Kontext(
|
||||||
|
thema="Dienstbarkeiten",
|
||||||
|
inhalt="Wegrecht zugunsten Parzelle 1235 entlang Ostgrenze, eingetragen am 15.03.2020",
|
||||||
|
parzelle_id=parzelle.id
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dokumenten-Management
|
||||||
|
```python
|
||||||
|
# Dokument mit Tags
|
||||||
|
dokument = Dokument(
|
||||||
|
label="Zonenplan Gemeinde Zürich",
|
||||||
|
versionsbezeichnung="2024-v1",
|
||||||
|
typ=DokumentTyp.DATEI,
|
||||||
|
format="PDF",
|
||||||
|
dokument_referenz="/storage/docs/zonenplan-zh-2024.pdf",
|
||||||
|
tags=[TagTyp.ZONENPLAN, TagTyp.BZO]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔐 Sicherheit
|
||||||
|
|
||||||
|
### Zu beachten:
|
||||||
|
- Eigentümerdaten sind personenbezogene Daten (DSGVO/DSG)
|
||||||
|
- Dokumente benötigen Zugriffskontrolle
|
||||||
|
- Audit-Logging für Änderungen empfohlen
|
||||||
|
- Geometriedaten sollten validiert werden
|
||||||
|
|
||||||
|
### Empfohlene Maßnahmen:
|
||||||
|
```sql
|
||||||
|
-- Audit-Log Tabelle hinzufügen
|
||||||
|
CREATE TABLE audit_log (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
table_name VARCHAR(100) NOT NULL,
|
||||||
|
record_id UUID NOT NULL,
|
||||||
|
action VARCHAR(20) NOT NULL,
|
||||||
|
changed_by UUID NOT NULL,
|
||||||
|
changed_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
old_values JSONB,
|
||||||
|
new_values JSONB
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📈 Performance-Optimierung
|
||||||
|
|
||||||
|
### Indices sind bereits erstellt für:
|
||||||
|
- Geografische Suchen (GIST Index auf Geometrien)
|
||||||
|
- Array-Suchen (GIN Index auf Arrays)
|
||||||
|
- Foreign Key Joins
|
||||||
|
|
||||||
|
### Zusätzliche Optimierungen:
|
||||||
|
```sql
|
||||||
|
-- Materialized View für häufige Reports
|
||||||
|
CREATE MATERIALIZED VIEW mv_projekt_statistik AS
|
||||||
|
SELECT
|
||||||
|
k.label as kanton,
|
||||||
|
COUNT(DISTINCT pr.id) as anzahl_projekte,
|
||||||
|
COUNT(DISTINCT pa.id) as anzahl_parzellen,
|
||||||
|
SUM(ST_Area(pa.geo_umfang)) as gesamtflaeche_m2
|
||||||
|
FROM projekt pr
|
||||||
|
JOIN projekt_parzelle pp ON pr.id = pp.projekt_id
|
||||||
|
JOIN parzelle pa ON pp.parzelle_id = pa.id
|
||||||
|
JOIN kanton k ON pa.kanton_id = k.id
|
||||||
|
GROUP BY k.label;
|
||||||
|
|
||||||
|
-- Refresh periodisch
|
||||||
|
REFRESH MATERIALIZED VIEW mv_projekt_statistik;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🧪 Testing
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
```python
|
||||||
|
# pytest Beispiel
|
||||||
|
def test_parzelle_creation():
|
||||||
|
parzelle = Parzelle(
|
||||||
|
label="Test Parzelle",
|
||||||
|
parzellen_nummern=["1234"],
|
||||||
|
az=1.5,
|
||||||
|
bz=0.4
|
||||||
|
)
|
||||||
|
assert parzelle.label == "Test Parzelle"
|
||||||
|
assert parzelle.az == 1.5
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
```typescript
|
||||||
|
// Jest Beispiel
|
||||||
|
describe('Projekt API', () => {
|
||||||
|
it('should create projekt with parzellen', async () => {
|
||||||
|
const projekt = await createProjekt({
|
||||||
|
label: 'Test Projekt',
|
||||||
|
perimeter: [parzelle1.id, parzelle2.id]
|
||||||
|
});
|
||||||
|
expect(projekt.perimeter).toHaveLength(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 Weitere Ressourcen
|
||||||
|
|
||||||
|
- [PostGIS Dokumentation](https://postgis.net/docs/)
|
||||||
|
- [SQLAlchemy ORM](https://docs.sqlalchemy.org/)
|
||||||
|
- [Prisma Dokumentation](https://www.prisma.io/docs/)
|
||||||
|
- [Swisstopo - Schweizer Koordinatensysteme](https://www.swisstopo.admin.ch/de/wissen-fakten/geodaesie-vermessung/bezugsrahmen/lokal/lv95.html)
|
||||||
|
|
||||||
|
## 🤝 Nächste Schritte
|
||||||
|
|
||||||
|
1. **Validierung**: Review mit Architekten und Fachexperten
|
||||||
|
2. **API-Design**: RESTful oder GraphQL API implementieren
|
||||||
|
3. **Frontend-Prototyp**: Kartenansicht mit Leaflet/MapLibre
|
||||||
|
4. **GIS-Integration**: Anbindung an Swisstopo-APIs
|
||||||
|
5. **Workflow-Engine**: Statusübergänge und Genehmigungen
|
||||||
|
6. **Benutzer-Management**: Rollen und Berechtigungen
|
||||||
|
|
||||||
|
## 📞 Support
|
||||||
|
|
||||||
|
Bei Fragen zum Datenmodell:
|
||||||
|
- Öffne ein Issue im Repository
|
||||||
|
- Konsultiere die `DATENMODELL_DOKUMENTATION.md`
|
||||||
|
- Prüfe die Beispiel-Implementierungen in `models.py` oder `schema.prisma`
|
||||||
|
|
||||||
|
## 📝 License
|
||||||
|
|
||||||
|
[Lizenz hier einfügen]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Version:** 1.0
|
||||||
|
**Letzte Aktualisierung:** 2025-10-24
|
||||||
|
**Koordinatensystem:** LV95 (EPSG:2056)
|
||||||
|
**Datenbank:** PostgreSQL 15+ mit PostGIS 3.4+
|
||||||
462
mandates/pek/datenmodell/datenmodell-schema.json
Normal file
462
mandates/pek/datenmodell/datenmodell-schema.json
Normal file
|
|
@ -0,0 +1,462 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Architektur-Planungs-App Datenmodell",
|
||||||
|
"version": "1.0",
|
||||||
|
|
||||||
|
"definitions": {
|
||||||
|
"Projekt": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"description": "Eindeutige Projekt-ID"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Projektbezeichnung"
|
||||||
|
},
|
||||||
|
"statusProzess": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Eingang",
|
||||||
|
"Analyse",
|
||||||
|
"Studie",
|
||||||
|
"Planung",
|
||||||
|
"Baurechtsverfahren",
|
||||||
|
"Umsetzung",
|
||||||
|
"Archiv"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"description": "Aktuelle(r) Projektstatus/-stati"
|
||||||
|
},
|
||||||
|
"perimeter": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Parzelle"
|
||||||
|
},
|
||||||
|
"description": "Parzellen im Projektperimeter"
|
||||||
|
},
|
||||||
|
"dokumenteBauherrschaft": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Dokument"
|
||||||
|
},
|
||||||
|
"description": "Dokumente der Bauherrschaft"
|
||||||
|
},
|
||||||
|
"dokumentePlanung": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Dokument"
|
||||||
|
},
|
||||||
|
"description": "Planungsdokumente"
|
||||||
|
},
|
||||||
|
"geoBaulinie": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/GeoPunkt"
|
||||||
|
},
|
||||||
|
"description": "Geografische Punkte der Baulinie"
|
||||||
|
},
|
||||||
|
"kontextInformationen": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Kontext"
|
||||||
|
},
|
||||||
|
"description": "Kontextuelle Projektinformationen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "label"]
|
||||||
|
},
|
||||||
|
|
||||||
|
"Dokument": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"description": "Eindeutige Dokument-ID"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Dokumentbezeichnung"
|
||||||
|
},
|
||||||
|
"versionsbezeichnung": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Versionsnummer oder -bezeichnung"
|
||||||
|
},
|
||||||
|
"typ": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["Datei", "Url"],
|
||||||
|
"description": "Art des Dokuments"
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Dateiformat (z.B. PDF, DWG, URL)",
|
||||||
|
"examples": ["PDF", "DWG", "IFC", "DXF", "URL"]
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Tag"
|
||||||
|
},
|
||||||
|
"description": "Kategorisierungs-Tags"
|
||||||
|
},
|
||||||
|
"dokumentReferenz": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Pfad oder URL zum Dokument"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "label", "typ", "dokumentReferenz"]
|
||||||
|
},
|
||||||
|
|
||||||
|
"Tag": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Kataster Objekte",
|
||||||
|
"Kataster Werkeleitungen",
|
||||||
|
"Kataster Belastete Standorte",
|
||||||
|
"Kataster Bäume",
|
||||||
|
"Zonenplan",
|
||||||
|
"Planungs- und Baugesetz (PGB)",
|
||||||
|
"Bau- und Zonenordnung (BZO)",
|
||||||
|
"Parkplatzverordnung",
|
||||||
|
"Eigentümerauskunft",
|
||||||
|
"Grundbuchauszug"
|
||||||
|
],
|
||||||
|
"description": "Vordefinierte Dokumentkategorien"
|
||||||
|
},
|
||||||
|
|
||||||
|
"Parzelle": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"description": "Eindeutige Parzellen-ID"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Parzellenbezeichnung"
|
||||||
|
},
|
||||||
|
"parzellenNummern": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "Offizielle Parzellennummern"
|
||||||
|
},
|
||||||
|
"eigentuemerschaaft": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Eigentümer der Parzelle"
|
||||||
|
},
|
||||||
|
"nachbarEigentuemer": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Parzelle"
|
||||||
|
},
|
||||||
|
"description": "Angrenzende Parzellen"
|
||||||
|
},
|
||||||
|
"kontextLand": {
|
||||||
|
"$ref": "#/definitions/Land",
|
||||||
|
"description": "Land der Parzelle"
|
||||||
|
},
|
||||||
|
"kontextKanton": {
|
||||||
|
"$ref": "#/definitions/Kanton",
|
||||||
|
"description": "Kanton der Parzelle"
|
||||||
|
},
|
||||||
|
"kontextGemeinde": {
|
||||||
|
"$ref": "#/definitions/Gemeinde",
|
||||||
|
"description": "Gemeinde der Parzelle"
|
||||||
|
},
|
||||||
|
"kontextInformationen": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Kontext"
|
||||||
|
},
|
||||||
|
"description": "Parzellenspezifische Kontextinformationen"
|
||||||
|
},
|
||||||
|
"strasseNr": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Straße und Hausnummer"
|
||||||
|
},
|
||||||
|
"geoUmfang": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/GeoPunkt"
|
||||||
|
},
|
||||||
|
"description": "Geografische Umfangspunkte der Parzelle"
|
||||||
|
},
|
||||||
|
"bauzone": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Bauzonenbezeichnung"
|
||||||
|
},
|
||||||
|
"spezifischeDokumente": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Dokument"
|
||||||
|
},
|
||||||
|
"description": "Parzellenspezifische Dokumente"
|
||||||
|
},
|
||||||
|
"hochwasserschutzzone": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Hochwasserschutzzone (falls zutreffend)"
|
||||||
|
},
|
||||||
|
"laermschutzzone": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Lärmschutzzone"
|
||||||
|
},
|
||||||
|
"grundwasserschutzzone": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Grundwasserschutzzone (falls zutreffend)"
|
||||||
|
},
|
||||||
|
"parzelleBebaut": {
|
||||||
|
"$ref": "#/definitions/JaNein",
|
||||||
|
"description": "Ist die Parzelle bebaut?"
|
||||||
|
},
|
||||||
|
"parzelleErschlossen": {
|
||||||
|
"$ref": "#/definitions/JaNein",
|
||||||
|
"description": "Ist die Parzelle erschlossen?"
|
||||||
|
},
|
||||||
|
"hanglage": {
|
||||||
|
"$ref": "#/definitions/JaNein",
|
||||||
|
"description": "Liegt die Parzelle in Hanglage?"
|
||||||
|
},
|
||||||
|
"az": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Ausnützungsziffer"
|
||||||
|
},
|
||||||
|
"bz": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Bebauungsziffer"
|
||||||
|
},
|
||||||
|
"vollgeschossZahl": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Anzahl zulässiger Vollgeschosse"
|
||||||
|
},
|
||||||
|
"anrechenbarDachgeschoss": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Anrechenbarer Anteil Dachgeschoss"
|
||||||
|
},
|
||||||
|
"anrechenbarUntergeschoss": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Anrechenbarer Anteil Untergeschoss"
|
||||||
|
},
|
||||||
|
"gebaeudehoehe_max": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Maximale Gebäudehöhe in Metern"
|
||||||
|
},
|
||||||
|
"regelnGrenzabstand": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Regelungen zum Grenzabstand"
|
||||||
|
},
|
||||||
|
"regelnMehrlaengenzuschlag": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Regelungen zum Mehrlängenzuschlag"
|
||||||
|
},
|
||||||
|
"regelnMehrhoehenzuschlag": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Regelungen zum Mehrhöhenzuschlag"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "label"]
|
||||||
|
},
|
||||||
|
|
||||||
|
"Gemeinde": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"description": "Eindeutige Gemeinde-ID"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Gemeindename"
|
||||||
|
},
|
||||||
|
"plz": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Postleitzahl"
|
||||||
|
},
|
||||||
|
"dokumente": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Dokument"
|
||||||
|
},
|
||||||
|
"description": "Gemeindedokumente"
|
||||||
|
},
|
||||||
|
"kontextInformationen": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Kontext"
|
||||||
|
},
|
||||||
|
"description": "Gemeindespezifische Kontextinformationen"
|
||||||
|
},
|
||||||
|
"bzoAktuell": {
|
||||||
|
"$ref": "#/definitions/Dokument",
|
||||||
|
"description": "Aktuelle Bau- und Zonenordnung"
|
||||||
|
},
|
||||||
|
"bzoRevision": {
|
||||||
|
"$ref": "#/definitions/Dokument",
|
||||||
|
"description": "BZO in Revision"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "label"]
|
||||||
|
},
|
||||||
|
|
||||||
|
"Kanton": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"description": "Eindeutige Kanton-ID"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Kantonsname"
|
||||||
|
},
|
||||||
|
"dokumente": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Dokument"
|
||||||
|
},
|
||||||
|
"description": "Kantonale Dokumente"
|
||||||
|
},
|
||||||
|
"kontextInformationen": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Kontext"
|
||||||
|
},
|
||||||
|
"description": "Kantonsspezifische Kontextinformationen"
|
||||||
|
},
|
||||||
|
"baureglementAktuell": {
|
||||||
|
"$ref": "#/definitions/Dokument",
|
||||||
|
"description": "Aktuelles Baureglement"
|
||||||
|
},
|
||||||
|
"baureglementRevision": {
|
||||||
|
"$ref": "#/definitions/Dokument",
|
||||||
|
"description": "Baureglement in Revision"
|
||||||
|
},
|
||||||
|
"bauverordnungAktuell": {
|
||||||
|
"$ref": "#/definitions/Dokument",
|
||||||
|
"description": "Aktuelle Bauverordnung"
|
||||||
|
},
|
||||||
|
"bauverordnungRevision": {
|
||||||
|
"$ref": "#/definitions/Dokument",
|
||||||
|
"description": "Bauverordnung in Revision"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "label"]
|
||||||
|
},
|
||||||
|
|
||||||
|
"Land": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"description": "Eindeutige Land-ID"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Landesname"
|
||||||
|
},
|
||||||
|
"dokumente": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Dokument"
|
||||||
|
},
|
||||||
|
"description": "Nationale Dokumente"
|
||||||
|
},
|
||||||
|
"kontextInformationen": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Kontext"
|
||||||
|
},
|
||||||
|
"description": "Nationale Kontextinformationen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "label"]
|
||||||
|
},
|
||||||
|
|
||||||
|
"GeoPunkt": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"x": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "X-Koordinate (Ost)"
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Y-Koordinate (Nord)"
|
||||||
|
},
|
||||||
|
"z": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Z-Koordinate (Höhe)"
|
||||||
|
},
|
||||||
|
"referenzen": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/GeoTag"
|
||||||
|
},
|
||||||
|
"description": "Kategorisierung des Geopunkts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["x", "y"]
|
||||||
|
},
|
||||||
|
|
||||||
|
"GeoTag": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Referenzpunkt Kat. 1",
|
||||||
|
"Referenzpunkt Kat. 2",
|
||||||
|
"Referenzpunkt Kat. 3",
|
||||||
|
"Geometeraufnahme"
|
||||||
|
],
|
||||||
|
"description": "Kategorien für Geopunkte"
|
||||||
|
},
|
||||||
|
|
||||||
|
"JaNein": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["", "Ja", "Nein"],
|
||||||
|
"description": "Ja/Nein/Leer Wert"
|
||||||
|
},
|
||||||
|
|
||||||
|
"Kontext": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uuid",
|
||||||
|
"description": "Eindeutige Kontext-ID"
|
||||||
|
},
|
||||||
|
"thema": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Thema der Kontextinformation",
|
||||||
|
"examples": [
|
||||||
|
"Vorgaben zur Erdgeschossnutzung",
|
||||||
|
"Dienstbarkeiten",
|
||||||
|
"Anforderung Parkplätze",
|
||||||
|
"Ausnützungsübertragungen",
|
||||||
|
"Schadstoffbelastungen auf Parzellen",
|
||||||
|
"Aktive Gestaltungspläne",
|
||||||
|
"Lärmempfindlichkeitsstufen",
|
||||||
|
"Mögliche Wärmenutzung",
|
||||||
|
"Baumbestand auf privaten Grundstücken",
|
||||||
|
"Isos (Ortsbild, Schutzstatus, Denkmalschutz, Weilergebiet, etc.)",
|
||||||
|
"Naturgefahren",
|
||||||
|
"Verweis auf Revisionen"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"inhalt": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Detaillierter Inhalt der Kontextinformation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "thema", "inhalt"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
mandates/pek/datenmodell/datenmodell.mermaid
Normal file
45
mandates/pek/datenmodell/datenmodell.mermaid
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
title: Hauptflüsse - Architektur-Planungs-App
|
||||||
|
---
|
||||||
|
flowchart TD
|
||||||
|
Start([Datenmodell Start])
|
||||||
|
|
||||||
|
subgraph Admin[Administrative Ebene]
|
||||||
|
Land[LAND<br/>Schweiz]
|
||||||
|
Kanton[KANTON<br/>z.B. Zürich]
|
||||||
|
Gemeinde[GEMEINDE<br/>z.B. Zürich Stadt]
|
||||||
|
Land --> Kanton
|
||||||
|
Kanton --> Gemeinde
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Geo[Geografische Daten]
|
||||||
|
Parzelle[PARZELLE<br/>Grundstück mit<br/>Bauparametern]
|
||||||
|
GeoPunkt[GEO_PUNKT<br/>Koordinaten]
|
||||||
|
Gemeinde --> Parzelle
|
||||||
|
Parzelle --> GeoPunkt
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Core[Kern-Business-Logik]
|
||||||
|
Projekt[PROJEKT<br/>Bauprojekt]
|
||||||
|
Dokument[DOKUMENT<br/>Dateien & URLs]
|
||||||
|
Projekt -.Perimeter.-> Parzelle
|
||||||
|
Projekt -.Dokumente.-> Dokument
|
||||||
|
Parzelle -.Dokumente.-> Dokument
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Support[Unterstützende Daten]
|
||||||
|
Kontext[KONTEXT<br/>Zusatzinfos]
|
||||||
|
Projekt --> Kontext
|
||||||
|
Parzelle --> Kontext
|
||||||
|
end
|
||||||
|
|
||||||
|
Start --> Admin
|
||||||
|
|
||||||
|
style Land fill:#50C878,stroke:#2D7A4A,stroke-width:2px,color:#fff
|
||||||
|
style Kanton fill:#50C878,stroke:#2D7A4A,stroke-width:2px,color:#fff
|
||||||
|
style Gemeinde fill:#50C878,stroke:#2D7A4A,stroke-width:2px,color:#fff
|
||||||
|
style Parzelle fill:#4A90E2,stroke:#2E5C8A,stroke-width:3px,color:#fff
|
||||||
|
style Projekt fill:#4A90E2,stroke:#2E5C8A,stroke-width:3px,color:#fff
|
||||||
|
style Dokument fill:#4A90E2,stroke:#2E5C8A,stroke-width:3px,color:#fff
|
||||||
|
style GeoPunkt fill:#F5A623,stroke:#C17D11,stroke-width:2px,color:#fff
|
||||||
|
style Kontext fill:#F5A623,stroke:#C17D11,stroke-width:2px,color:#fff
|
||||||
393
mandates/pek/datenmodell/migration_001_initial_schema.sql
Normal file
393
mandates/pek/datenmodell/migration_001_initial_schema.sql
Normal file
|
|
@ -0,0 +1,393 @@
|
||||||
|
-- ============================================================================
|
||||||
|
-- Architektur-Planungs-App Datenbank Schema
|
||||||
|
-- PostgreSQL 15+ mit PostGIS 3.4+
|
||||||
|
-- Schweizer Koordinatensystem: LV95 (EPSG:2056)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- PostGIS Extension aktivieren
|
||||||
|
CREATE EXTENSION IF NOT EXISTS postgis;
|
||||||
|
|
||||||
|
-- UUID Extension für uuid_generate_v4()
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- ENUMS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE TYPE status_prozess AS ENUM (
|
||||||
|
'Eingang',
|
||||||
|
'Analyse',
|
||||||
|
'Studie',
|
||||||
|
'Planung',
|
||||||
|
'Baurechtsverfahren',
|
||||||
|
'Umsetzung',
|
||||||
|
'Archiv'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TYPE dokument_typ AS ENUM (
|
||||||
|
'Datei',
|
||||||
|
'Url'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TYPE tag_typ AS ENUM (
|
||||||
|
'Kataster Objekte',
|
||||||
|
'Kataster Werkeleitungen',
|
||||||
|
'Kataster Belastete Standorte',
|
||||||
|
'Kataster Bäume',
|
||||||
|
'Zonenplan',
|
||||||
|
'Planungs- und Baugesetz (PGB)',
|
||||||
|
'Bau- und Zonenordnung (BZO)',
|
||||||
|
'Parkplatzverordnung',
|
||||||
|
'Eigentümerauskunft',
|
||||||
|
'Grundbuchauszug'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TYPE geo_tag_typ AS ENUM (
|
||||||
|
'Referenzpunkt Kat. 1',
|
||||||
|
'Referenzpunkt Kat. 2',
|
||||||
|
'Referenzpunkt Kat. 3',
|
||||||
|
'Geometeraufnahme'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TYPE ja_nein AS ENUM (
|
||||||
|
'',
|
||||||
|
'Ja',
|
||||||
|
'Nein'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- HAUPTTABELLEN
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Land
|
||||||
|
CREATE TABLE land (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
label VARCHAR(255) NOT NULL,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Kanton
|
||||||
|
CREATE TABLE kanton (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
label VARCHAR(255) NOT NULL,
|
||||||
|
baureglement_aktuell_id UUID,
|
||||||
|
baureglement_revision_id UUID,
|
||||||
|
bauverordnung_aktuell_id UUID,
|
||||||
|
bauverordnung_revision_id UUID,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Gemeinde
|
||||||
|
CREATE TABLE gemeinde (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
label VARCHAR(255) NOT NULL,
|
||||||
|
plz VARCHAR(10),
|
||||||
|
bzo_aktuell_id UUID,
|
||||||
|
bzo_revision_id UUID,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Dokument
|
||||||
|
CREATE TABLE dokument (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
label VARCHAR(255) NOT NULL,
|
||||||
|
versionsbezeichnung VARCHAR(100),
|
||||||
|
typ dokument_typ NOT NULL,
|
||||||
|
format VARCHAR(50),
|
||||||
|
dokument_referenz TEXT NOT NULL,
|
||||||
|
tags tag_typ[],
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Projekt
|
||||||
|
CREATE TABLE projekt (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
label VARCHAR(255) NOT NULL,
|
||||||
|
status_prozess status_prozess[],
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Parzelle
|
||||||
|
CREATE TABLE parzelle (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
label VARCHAR(255) NOT NULL,
|
||||||
|
parzellen_nummern VARCHAR(50)[],
|
||||||
|
eigentuemerschaaft VARCHAR(255),
|
||||||
|
strasse_nr VARCHAR(255),
|
||||||
|
|
||||||
|
-- Geografischer Kontext
|
||||||
|
land_id UUID REFERENCES land(id),
|
||||||
|
kanton_id UUID REFERENCES kanton(id),
|
||||||
|
gemeinde_id UUID REFERENCES gemeinde(id),
|
||||||
|
|
||||||
|
-- Geometrie (PostGIS)
|
||||||
|
geo_umfang GEOMETRY(POLYGON, 2056),
|
||||||
|
|
||||||
|
-- Bauliche Parameter
|
||||||
|
bauzone VARCHAR(50),
|
||||||
|
az DECIMAL(5,2),
|
||||||
|
bz DECIMAL(5,2),
|
||||||
|
vollgeschoss_zahl INTEGER,
|
||||||
|
anrechenbar_dachgeschoss DECIMAL(3,2),
|
||||||
|
anrechenbar_untergeschoss DECIMAL(3,2),
|
||||||
|
gebaeudehoehe_max DECIMAL(6,2),
|
||||||
|
|
||||||
|
-- Regelungen
|
||||||
|
regeln_grenzabstand TEXT,
|
||||||
|
regeln_mehrlaengenzuschlag TEXT,
|
||||||
|
regeln_mehrhoehenzuschlag TEXT,
|
||||||
|
|
||||||
|
-- Schutzzonen
|
||||||
|
hochwasserschutzzone VARCHAR(100),
|
||||||
|
laermschutzzone VARCHAR(100),
|
||||||
|
grundwasserschutzzone VARCHAR(100),
|
||||||
|
|
||||||
|
-- Eigenschaften
|
||||||
|
parzelle_bebaut ja_nein,
|
||||||
|
parzelle_erschlossen ja_nein,
|
||||||
|
hanglage ja_nein,
|
||||||
|
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- GeoPunkt
|
||||||
|
CREATE TABLE geo_punkt (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
x DECIMAL(12,3) NOT NULL, -- LV95 Ostwert
|
||||||
|
y DECIMAL(12,3) NOT NULL, -- LV95 Nordwert
|
||||||
|
z DECIMAL(8,3), -- Höhe über Meer
|
||||||
|
referenzen geo_tag_typ[],
|
||||||
|
projekt_id UUID REFERENCES projekt(id) ON DELETE CASCADE,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Kontext (Polymorphe Beziehung)
|
||||||
|
CREATE TABLE kontext (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
thema VARCHAR(255) NOT NULL,
|
||||||
|
inhalt TEXT NOT NULL,
|
||||||
|
|
||||||
|
-- Polymorphe Foreign Keys
|
||||||
|
projekt_id UUID REFERENCES projekt(id) ON DELETE CASCADE,
|
||||||
|
parzelle_id UUID REFERENCES parzelle(id) ON DELETE CASCADE,
|
||||||
|
land_id UUID REFERENCES land(id) ON DELETE CASCADE,
|
||||||
|
kanton_id UUID REFERENCES kanton(id) ON DELETE CASCADE,
|
||||||
|
gemeinde_id UUID REFERENCES gemeinde(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
-- Constraint: Kontext muss zu genau einer Entität gehören
|
||||||
|
CONSTRAINT kontext_single_parent CHECK (
|
||||||
|
(projekt_id IS NOT NULL)::INTEGER +
|
||||||
|
(parzelle_id IS NOT NULL)::INTEGER +
|
||||||
|
(land_id IS NOT NULL)::INTEGER +
|
||||||
|
(kanton_id IS NOT NULL)::INTEGER +
|
||||||
|
(gemeinde_id IS NOT NULL)::INTEGER = 1
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- JUNCTION TABLES (Many-to-Many Beziehungen)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Projekt <-> Parzelle
|
||||||
|
CREATE TABLE projekt_parzelle (
|
||||||
|
projekt_id UUID NOT NULL REFERENCES projekt(id) ON DELETE CASCADE,
|
||||||
|
parzelle_id UUID NOT NULL REFERENCES parzelle(id) ON DELETE CASCADE,
|
||||||
|
PRIMARY KEY (projekt_id, parzelle_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Projekt <-> Dokument (Bauherrschaft)
|
||||||
|
CREATE TABLE projekt_dokument_bauherrschaft (
|
||||||
|
projekt_id UUID NOT NULL REFERENCES projekt(id) ON DELETE CASCADE,
|
||||||
|
dokument_id UUID NOT NULL REFERENCES dokument(id) ON DELETE CASCADE,
|
||||||
|
PRIMARY KEY (projekt_id, dokument_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Projekt <-> Dokument (Planung)
|
||||||
|
CREATE TABLE projekt_dokument_planung (
|
||||||
|
projekt_id UUID NOT NULL REFERENCES projekt(id) ON DELETE CASCADE,
|
||||||
|
dokument_id UUID NOT NULL REFERENCES dokument(id) ON DELETE CASCADE,
|
||||||
|
PRIMARY KEY (projekt_id, dokument_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Parzelle <-> Parzelle (Nachbarn)
|
||||||
|
CREATE TABLE parzelle_nachbar (
|
||||||
|
parzelle_id UUID NOT NULL REFERENCES parzelle(id) ON DELETE CASCADE,
|
||||||
|
nachbar_id UUID NOT NULL REFERENCES parzelle(id) ON DELETE CASCADE,
|
||||||
|
PRIMARY KEY (parzelle_id, nachbar_id),
|
||||||
|
CHECK (parzelle_id != nachbar_id) -- Parzelle kann nicht ihr eigener Nachbar sein
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Parzelle <-> Dokument
|
||||||
|
CREATE TABLE parzelle_dokument (
|
||||||
|
parzelle_id UUID NOT NULL REFERENCES parzelle(id) ON DELETE CASCADE,
|
||||||
|
dokument_id UUID NOT NULL REFERENCES dokument(id) ON DELETE CASCADE,
|
||||||
|
PRIMARY KEY (parzelle_id, dokument_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FOREIGN KEY CONSTRAINTS (nachträglich für Kanton/Gemeinde Dokumente)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
ALTER TABLE kanton
|
||||||
|
ADD CONSTRAINT fk_kanton_baureglement_aktuell
|
||||||
|
FOREIGN KEY (baureglement_aktuell_id) REFERENCES dokument(id),
|
||||||
|
ADD CONSTRAINT fk_kanton_baureglement_revision
|
||||||
|
FOREIGN KEY (baureglement_revision_id) REFERENCES dokument(id),
|
||||||
|
ADD CONSTRAINT fk_kanton_bauverordnung_aktuell
|
||||||
|
FOREIGN KEY (bauverordnung_aktuell_id) REFERENCES dokument(id),
|
||||||
|
ADD CONSTRAINT fk_kanton_bauverordnung_revision
|
||||||
|
FOREIGN KEY (bauverordnung_revision_id) REFERENCES dokument(id);
|
||||||
|
|
||||||
|
ALTER TABLE gemeinde
|
||||||
|
ADD CONSTRAINT fk_gemeinde_bzo_aktuell
|
||||||
|
FOREIGN KEY (bzo_aktuell_id) REFERENCES dokument(id),
|
||||||
|
ADD CONSTRAINT fk_gemeinde_bzo_revision
|
||||||
|
FOREIGN KEY (bzo_revision_id) REFERENCES dokument(id);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- INDICES für Performance
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Projekt Indices
|
||||||
|
CREATE INDEX idx_projekt_status ON projekt USING GIN (status_prozess);
|
||||||
|
|
||||||
|
-- Parzelle Indices
|
||||||
|
CREATE INDEX idx_parzelle_land ON parzelle(land_id);
|
||||||
|
CREATE INDEX idx_parzelle_kanton ON parzelle(kanton_id);
|
||||||
|
CREATE INDEX idx_parzelle_gemeinde ON parzelle(gemeinde_id);
|
||||||
|
CREATE INDEX idx_parzelle_bauzone ON parzelle(bauzone);
|
||||||
|
CREATE INDEX idx_parzelle_geo_umfang ON parzelle USING GIST(geo_umfang);
|
||||||
|
|
||||||
|
-- Dokument Indices
|
||||||
|
CREATE INDEX idx_dokument_typ ON dokument(typ);
|
||||||
|
CREATE INDEX idx_dokument_tags ON dokument USING GIN (tags);
|
||||||
|
|
||||||
|
-- GeoPunkt Indices
|
||||||
|
CREATE INDEX idx_geopunkt_projekt ON geo_punkt(projekt_id);
|
||||||
|
CREATE INDEX idx_geopunkt_referenzen ON geo_punkt USING GIN (referenzen);
|
||||||
|
|
||||||
|
-- Kontext Indices
|
||||||
|
CREATE INDEX idx_kontext_projekt ON kontext(projekt_id);
|
||||||
|
CREATE INDEX idx_kontext_parzelle ON kontext(parzelle_id);
|
||||||
|
CREATE INDEX idx_kontext_land ON kontext(land_id);
|
||||||
|
CREATE INDEX idx_kontext_kanton ON kontext(kanton_id);
|
||||||
|
CREATE INDEX idx_kontext_gemeinde ON kontext(gemeinde_id);
|
||||||
|
CREATE INDEX idx_kontext_thema ON kontext(thema);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TRIGGER für updated_at
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ language 'plpgsql';
|
||||||
|
|
||||||
|
CREATE TRIGGER update_land_updated_at BEFORE UPDATE ON land
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
CREATE TRIGGER update_kanton_updated_at BEFORE UPDATE ON kanton
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
CREATE TRIGGER update_gemeinde_updated_at BEFORE UPDATE ON gemeinde
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
CREATE TRIGGER update_dokument_updated_at BEFORE UPDATE ON dokument
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
CREATE TRIGGER update_projekt_updated_at BEFORE UPDATE ON projekt
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
CREATE TRIGGER update_parzelle_updated_at BEFORE UPDATE ON parzelle
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
CREATE TRIGGER update_geo_punkt_updated_at BEFORE UPDATE ON geo_punkt
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
CREATE TRIGGER update_kontext_updated_at BEFORE UPDATE ON kontext
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- VIEWS für häufige Abfragen
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- View: Parzellen mit vollständigem geografischem Kontext
|
||||||
|
CREATE VIEW v_parzelle_vollstaendig AS
|
||||||
|
SELECT
|
||||||
|
p.*,
|
||||||
|
l.label as land_name,
|
||||||
|
k.label as kanton_name,
|
||||||
|
g.label as gemeinde_name,
|
||||||
|
g.plz as gemeinde_plz,
|
||||||
|
ST_AsGeoJSON(p.geo_umfang) as geo_umfang_geojson,
|
||||||
|
ST_Area(p.geo_umfang) as flaeche_m2
|
||||||
|
FROM parzelle p
|
||||||
|
LEFT JOIN land l ON p.land_id = l.id
|
||||||
|
LEFT JOIN kanton k ON p.kanton_id = k.id
|
||||||
|
LEFT JOIN gemeinde g ON p.gemeinde_id = g.id;
|
||||||
|
|
||||||
|
-- View: Projekte mit Perimeter-Information
|
||||||
|
CREATE VIEW v_projekt_mit_perimeter AS
|
||||||
|
SELECT
|
||||||
|
pr.id,
|
||||||
|
pr.label,
|
||||||
|
pr.status_prozess,
|
||||||
|
COUNT(DISTINCT pp.parzelle_id) as anzahl_parzellen,
|
||||||
|
STRING_AGG(DISTINCT pa.label, ', ') as parzellen_labels
|
||||||
|
FROM projekt pr
|
||||||
|
LEFT JOIN projekt_parzelle pp ON pr.id = pp.projekt_id
|
||||||
|
LEFT JOIN parzelle pa ON pp.parzelle_id = pa.id
|
||||||
|
GROUP BY pr.id, pr.label, pr.status_prozess;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- BEISPIELDATEN
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Land Schweiz
|
||||||
|
INSERT INTO land (label) VALUES ('Schweiz');
|
||||||
|
|
||||||
|
-- Kantone (Beispiele)
|
||||||
|
INSERT INTO kanton (label) VALUES
|
||||||
|
('Zürich'),
|
||||||
|
('Bern'),
|
||||||
|
('Luzern');
|
||||||
|
|
||||||
|
-- Gemeinden (Beispiele für Zürich)
|
||||||
|
INSERT INTO gemeinde (label, plz) VALUES
|
||||||
|
('Zürich', '8000'),
|
||||||
|
('Winterthur', '8400'),
|
||||||
|
('Uster', '8610');
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- KOMMENTARE
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
COMMENT ON TABLE projekt IS 'Bauprojekte mit Status und Perimeter';
|
||||||
|
COMMENT ON TABLE parzelle IS 'Grundstücke mit baulichen und rechtlichen Eigenschaften';
|
||||||
|
COMMENT ON TABLE dokument IS 'Dokumente und URLs mit Versionierung';
|
||||||
|
COMMENT ON TABLE geo_punkt IS '3D-Punkte im LV95-Koordinatensystem';
|
||||||
|
COMMENT ON TABLE kontext IS 'Flexible Kontextinformationen für verschiedene Entitäten';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN parzelle.geo_umfang IS 'Parzellengrenze als PostGIS Polygon im LV95 (EPSG:2056)';
|
||||||
|
COMMENT ON COLUMN parzelle.az IS 'Ausnützungsziffer';
|
||||||
|
COMMENT ON COLUMN parzelle.bz IS 'Bebauungsziffer';
|
||||||
|
COMMENT ON COLUMN geo_punkt.x IS 'LV95 Ostwert (E), typisch 2480000-2840000';
|
||||||
|
COMMENT ON COLUMN geo_punkt.y IS 'LV95 Nordwert (N), typisch 1070000-1300000';
|
||||||
|
COMMENT ON COLUMN geo_punkt.z IS 'Höhe über Meer in Metern';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- Ende der Migration
|
||||||
|
-- ============================================================================
|
||||||
457
mandates/pek/datenmodell/models.py
Normal file
457
mandates/pek/datenmodell/models.py
Normal file
|
|
@ -0,0 +1,457 @@
|
||||||
|
"""
|
||||||
|
SQLAlchemy Datenmodell für Architektur-Planungs-App
|
||||||
|
Verwendet PostgreSQL mit PostGIS Extension
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sqlalchemy import (
|
||||||
|
Column, String, Integer, Float, Enum as SQLEnum,
|
||||||
|
ForeignKey, Table, Text, ARRAY
|
||||||
|
)
|
||||||
|
from sqlalchemy.dialects.postgresql import UUID, ENUM
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
from geoalchemy2 import Geometry
|
||||||
|
import uuid
|
||||||
|
import enum
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ENUMS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
class StatusProzess(enum.Enum):
|
||||||
|
EINGANG = "Eingang"
|
||||||
|
ANALYSE = "Analyse"
|
||||||
|
STUDIE = "Studie"
|
||||||
|
PLANUNG = "Planung"
|
||||||
|
BAURECHTSVERFAHREN = "Baurechtsverfahren"
|
||||||
|
UMSETZUNG = "Umsetzung"
|
||||||
|
ARCHIV = "Archiv"
|
||||||
|
|
||||||
|
|
||||||
|
class DokumentTyp(enum.Enum):
|
||||||
|
DATEI = "Datei"
|
||||||
|
URL = "Url"
|
||||||
|
|
||||||
|
|
||||||
|
class TagTyp(enum.Enum):
|
||||||
|
KATASTER_OBJEKTE = "Kataster Objekte"
|
||||||
|
KATASTER_WERKELEITUNGEN = "Kataster Werkeleitungen"
|
||||||
|
KATASTER_BELASTETE_STANDORTE = "Kataster Belastete Standorte"
|
||||||
|
KATASTER_BAEUME = "Kataster Bäume"
|
||||||
|
ZONENPLAN = "Zonenplan"
|
||||||
|
PGB = "Planungs- und Baugesetz (PGB)"
|
||||||
|
BZO = "Bau- und Zonenordnung (BZO)"
|
||||||
|
PARKPLATZVERORDNUNG = "Parkplatzverordnung"
|
||||||
|
EIGENTUEMER_AUSKUNFT = "Eigentümerauskunft"
|
||||||
|
GRUNDBUCHAUSZUG = "Grundbuchauszug"
|
||||||
|
|
||||||
|
|
||||||
|
class GeoTagTyp(enum.Enum):
|
||||||
|
REFERENZPUNKT_KAT1 = "Referenzpunkt Kat. 1"
|
||||||
|
REFERENZPUNKT_KAT2 = "Referenzpunkt Kat. 2"
|
||||||
|
REFERENZPUNKT_KAT3 = "Referenzpunkt Kat. 3"
|
||||||
|
GEOMETER_AUFNAHME = "Geometeraufnahme"
|
||||||
|
|
||||||
|
|
||||||
|
class JaNein(enum.Enum):
|
||||||
|
LEER = ""
|
||||||
|
JA = "Ja"
|
||||||
|
NEIN = "Nein"
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# JUNCTION TABLES (Many-to-Many Beziehungen)
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Projekt <-> Dokument (Bauherrschaft)
|
||||||
|
projekt_dokumente_bauherrschaft = Table(
|
||||||
|
'projekt_dokumente_bauherrschaft',
|
||||||
|
Base.metadata,
|
||||||
|
Column('projekt_id', UUID(as_uuid=True), ForeignKey('projekt.id')),
|
||||||
|
Column('dokument_id', UUID(as_uuid=True), ForeignKey('dokument.id'))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Projekt <-> Dokument (Planung)
|
||||||
|
projekt_dokumente_planung = Table(
|
||||||
|
'projekt_dokumente_planung',
|
||||||
|
Base.metadata,
|
||||||
|
Column('projekt_id', UUID(as_uuid=True), ForeignKey('projekt.id')),
|
||||||
|
Column('dokument_id', UUID(as_uuid=True), ForeignKey('dokument.id'))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Projekt <-> Parzelle
|
||||||
|
projekt_parzelle = Table(
|
||||||
|
'projekt_parzelle',
|
||||||
|
Base.metadata,
|
||||||
|
Column('projekt_id', UUID(as_uuid=True), ForeignKey('projekt.id')),
|
||||||
|
Column('parzelle_id', UUID(as_uuid=True), ForeignKey('parzelle.id'))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Parzelle <-> Parzelle (Nachbarn)
|
||||||
|
parzelle_nachbar = Table(
|
||||||
|
'parzelle_nachbar',
|
||||||
|
Base.metadata,
|
||||||
|
Column('parzelle_id', UUID(as_uuid=True), ForeignKey('parzelle.id')),
|
||||||
|
Column('nachbar_id', UUID(as_uuid=True), ForeignKey('parzelle.id'))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Parzelle <-> Dokument
|
||||||
|
parzelle_dokument = Table(
|
||||||
|
'parzelle_dokument',
|
||||||
|
Base.metadata,
|
||||||
|
Column('parzelle_id', UUID(as_uuid=True), ForeignKey('parzelle.id')),
|
||||||
|
Column('dokument_id', UUID(as_uuid=True), ForeignKey('dokument.id'))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Dokument <-> Tag (Many-to-Many, da Tags wiederverwendbar)
|
||||||
|
dokument_tag = Table(
|
||||||
|
'dokument_tag',
|
||||||
|
Base.metadata,
|
||||||
|
Column('dokument_id', UUID(as_uuid=True), ForeignKey('dokument.id')),
|
||||||
|
Column('tag', ENUM(TagTyp, name='tag_typ'))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# MODELS
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
class Projekt(Base):
|
||||||
|
__tablename__ = 'projekt'
|
||||||
|
|
||||||
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||||
|
label = Column(String, nullable=False)
|
||||||
|
status_prozess = Column(ARRAY(ENUM(StatusProzess, name='status_prozess')))
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
perimeter = relationship(
|
||||||
|
'Parzelle',
|
||||||
|
secondary=projekt_parzelle,
|
||||||
|
back_populates='projekte'
|
||||||
|
)
|
||||||
|
|
||||||
|
dokumente_bauherrschaft = relationship(
|
||||||
|
'Dokument',
|
||||||
|
secondary=projekt_dokumente_bauherrschaft
|
||||||
|
)
|
||||||
|
|
||||||
|
dokumente_planung = relationship(
|
||||||
|
'Dokument',
|
||||||
|
secondary=projekt_dokumente_planung
|
||||||
|
)
|
||||||
|
|
||||||
|
geo_baulinie = relationship('GeoPunkt', back_populates='projekt_baulinie')
|
||||||
|
|
||||||
|
kontext_informationen = relationship(
|
||||||
|
'Kontext',
|
||||||
|
foreign_keys='Kontext.projekt_id',
|
||||||
|
back_populates='projekt'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Parzelle(Base):
|
||||||
|
__tablename__ = 'parzelle'
|
||||||
|
|
||||||
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||||
|
label = Column(String, nullable=False)
|
||||||
|
parzellen_nummern = Column(ARRAY(String))
|
||||||
|
eigentuemerschaaft = Column(String)
|
||||||
|
strasse_nr = Column(String)
|
||||||
|
|
||||||
|
# Geografischer Kontext
|
||||||
|
land_id = Column(UUID(as_uuid=True), ForeignKey('land.id'))
|
||||||
|
kanton_id = Column(UUID(as_uuid=True), ForeignKey('kanton.id'))
|
||||||
|
gemeinde_id = Column(UUID(as_uuid=True), ForeignKey('gemeinde.id'))
|
||||||
|
|
||||||
|
# Geometrie (PostGIS)
|
||||||
|
geo_umfang = Column(Geometry('POLYGON', srid=2056)) # LV95 Koordinatensystem
|
||||||
|
|
||||||
|
# Bauliche Parameter
|
||||||
|
bauzone = Column(String)
|
||||||
|
az = Column(Float) # Ausnützungsziffer
|
||||||
|
bz = Column(Float) # Bebauungsziffer
|
||||||
|
vollgeschoss_zahl = Column(Integer)
|
||||||
|
anrechenbar_dachgeschoss = Column(Float)
|
||||||
|
anrechenbar_untergeschoss = Column(Float)
|
||||||
|
gebaeudehoehe_max = Column(Float)
|
||||||
|
|
||||||
|
# Regelungen
|
||||||
|
regeln_grenzabstand = Column(Text)
|
||||||
|
regeln_mehrlaengenzuschlag = Column(Text)
|
||||||
|
regeln_mehrhoehenzuschlag = Column(Text)
|
||||||
|
|
||||||
|
# Schutzzonen
|
||||||
|
hochwasserschutzzone = Column(String)
|
||||||
|
laermschutzzone = Column(String)
|
||||||
|
grundwasserschutzzone = Column(String)
|
||||||
|
|
||||||
|
# Eigenschaften
|
||||||
|
parzelle_bebaut = Column(ENUM(JaNein, name='ja_nein'))
|
||||||
|
parzelle_erschlossen = Column(ENUM(JaNein, name='ja_nein'))
|
||||||
|
hanglage = Column(ENUM(JaNein, name='ja_nein'))
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
projekte = relationship(
|
||||||
|
'Projekt',
|
||||||
|
secondary=projekt_parzelle,
|
||||||
|
back_populates='perimeter'
|
||||||
|
)
|
||||||
|
|
||||||
|
nachbar_eigentuemer = relationship(
|
||||||
|
'Parzelle',
|
||||||
|
secondary=parzelle_nachbar,
|
||||||
|
primaryjoin=id == parzelle_nachbar.c.parzelle_id,
|
||||||
|
secondaryjoin=id == parzelle_nachbar.c.nachbar_id
|
||||||
|
)
|
||||||
|
|
||||||
|
kontext_land = relationship('Land', back_populates='parzellen')
|
||||||
|
kontext_kanton = relationship('Kanton', back_populates='parzellen')
|
||||||
|
kontext_gemeinde = relationship('Gemeinde', back_populates='parzellen')
|
||||||
|
|
||||||
|
spezifische_dokumente = relationship(
|
||||||
|
'Dokument',
|
||||||
|
secondary=parzelle_dokument
|
||||||
|
)
|
||||||
|
|
||||||
|
kontext_informationen = relationship(
|
||||||
|
'Kontext',
|
||||||
|
foreign_keys='Kontext.parzelle_id',
|
||||||
|
back_populates='parzelle'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Dokument(Base):
|
||||||
|
__tablename__ = 'dokument'
|
||||||
|
|
||||||
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||||
|
label = Column(String, nullable=False)
|
||||||
|
versionsbezeichnung = Column(String)
|
||||||
|
typ = Column(ENUM(DokumentTyp, name='dokument_typ'), nullable=False)
|
||||||
|
format = Column(String)
|
||||||
|
dokument_referenz = Column(String, nullable=False) # Pfad oder URL
|
||||||
|
|
||||||
|
# Tags als Array (einfache Variante)
|
||||||
|
# Alternative: Many-to-Many über Junction Table
|
||||||
|
tags = Column(ARRAY(ENUM(TagTyp, name='tag_typ')))
|
||||||
|
|
||||||
|
|
||||||
|
class GeoPunkt(Base):
|
||||||
|
__tablename__ = 'geo_punkt'
|
||||||
|
|
||||||
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||||
|
|
||||||
|
# Koordinaten (einzeln gespeichert für Flexibilität)
|
||||||
|
x = Column(Float, nullable=False)
|
||||||
|
y = Column(Float, nullable=False)
|
||||||
|
z = Column(Float) # Optional
|
||||||
|
|
||||||
|
# Alternative: PostGIS Point
|
||||||
|
# koordinaten = Column(Geometry('POINTZ', srid=2056))
|
||||||
|
|
||||||
|
# Kategorisierung
|
||||||
|
referenzen = Column(ARRAY(ENUM(GeoTagTyp, name='geo_tag_typ')))
|
||||||
|
|
||||||
|
# Foreign Keys (je nach Verwendung)
|
||||||
|
projekt_id = Column(UUID(as_uuid=True), ForeignKey('projekt.id'))
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
projekt_baulinie = relationship('Projekt', back_populates='geo_baulinie')
|
||||||
|
|
||||||
|
|
||||||
|
class Kontext(Base):
|
||||||
|
__tablename__ = 'kontext'
|
||||||
|
|
||||||
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||||
|
thema = Column(String, nullable=False)
|
||||||
|
inhalt = Column(Text, nullable=False)
|
||||||
|
|
||||||
|
# Polymorphe Beziehung (kann zu verschiedenen Entitäten gehören)
|
||||||
|
projekt_id = Column(UUID(as_uuid=True), ForeignKey('projekt.id'))
|
||||||
|
parzelle_id = Column(UUID(as_uuid=True), ForeignKey('parzelle.id'))
|
||||||
|
land_id = Column(UUID(as_uuid=True), ForeignKey('land.id'))
|
||||||
|
kanton_id = Column(UUID(as_uuid=True), ForeignKey('kanton.id'))
|
||||||
|
gemeinde_id = Column(UUID(as_uuid=True), ForeignKey('gemeinde.id'))
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
projekt = relationship('Projekt', back_populates='kontext_informationen')
|
||||||
|
parzelle = relationship('Parzelle', back_populates='kontext_informationen')
|
||||||
|
land = relationship('Land', back_populates='kontext_informationen')
|
||||||
|
kanton = relationship('Kanton', back_populates='kontext_informationen')
|
||||||
|
gemeinde = relationship('Gemeinde', back_populates='kontext_informationen')
|
||||||
|
|
||||||
|
|
||||||
|
class Gemeinde(Base):
|
||||||
|
__tablename__ = 'gemeinde'
|
||||||
|
|
||||||
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||||
|
label = Column(String, nullable=False)
|
||||||
|
plz = Column(String)
|
||||||
|
|
||||||
|
# BZO Dokumente
|
||||||
|
bzo_aktuell_id = Column(UUID(as_uuid=True), ForeignKey('dokument.id'))
|
||||||
|
bzo_revision_id = Column(UUID(as_uuid=True), ForeignKey('dokument.id'))
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
parzellen = relationship('Parzelle', back_populates='kontext_gemeinde')
|
||||||
|
kontext_informationen = relationship(
|
||||||
|
'Kontext',
|
||||||
|
foreign_keys='Kontext.gemeinde_id',
|
||||||
|
back_populates='gemeinde'
|
||||||
|
)
|
||||||
|
|
||||||
|
bzo_aktuell = relationship('Dokument', foreign_keys=[bzo_aktuell_id])
|
||||||
|
bzo_revision = relationship('Dokument', foreign_keys=[bzo_revision_id])
|
||||||
|
|
||||||
|
|
||||||
|
class Kanton(Base):
|
||||||
|
__tablename__ = 'kanton'
|
||||||
|
|
||||||
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||||
|
label = Column(String, nullable=False)
|
||||||
|
|
||||||
|
# Regelwerke
|
||||||
|
baureglement_aktuell_id = Column(UUID(as_uuid=True), ForeignKey('dokument.id'))
|
||||||
|
baureglement_revision_id = Column(UUID(as_uuid=True), ForeignKey('dokument.id'))
|
||||||
|
bauverordnung_aktuell_id = Column(UUID(as_uuid=True), ForeignKey('dokument.id'))
|
||||||
|
bauverordnung_revision_id = Column(UUID(as_uuid=True), ForeignKey('dokument.id'))
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
parzellen = relationship('Parzelle', back_populates='kontext_kanton')
|
||||||
|
kontext_informationen = relationship(
|
||||||
|
'Kontext',
|
||||||
|
foreign_keys='Kontext.kanton_id',
|
||||||
|
back_populates='kanton'
|
||||||
|
)
|
||||||
|
|
||||||
|
baureglement_aktuell = relationship('Dokument', foreign_keys=[baureglement_aktuell_id])
|
||||||
|
baureglement_revision = relationship('Dokument', foreign_keys=[baureglement_revision_id])
|
||||||
|
bauverordnung_aktuell = relationship('Dokument', foreign_keys=[bauverordnung_aktuell_id])
|
||||||
|
bauverordnung_revision = relationship('Dokument', foreign_keys=[bauverordnung_revision_id])
|
||||||
|
|
||||||
|
|
||||||
|
class Land(Base):
|
||||||
|
__tablename__ = 'land'
|
||||||
|
|
||||||
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||||
|
label = Column(String, nullable=False)
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
parzellen = relationship('Parzelle', back_populates='kontext_land')
|
||||||
|
kontext_informationen = relationship(
|
||||||
|
'Kontext',
|
||||||
|
foreign_keys='Kontext.land_id',
|
||||||
|
back_populates='land'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# DATENBANK-SETUP
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
def create_database_engine():
|
||||||
|
"""
|
||||||
|
Erstellt die Database Engine mit PostGIS Support
|
||||||
|
"""
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
|
||||||
|
# Beispiel Connection String
|
||||||
|
DATABASE_URL = "postgresql://user:password@localhost:5432/architektur_app"
|
||||||
|
|
||||||
|
engine = create_engine(
|
||||||
|
DATABASE_URL,
|
||||||
|
echo=True # SQL Logging für Development
|
||||||
|
)
|
||||||
|
|
||||||
|
return engine
|
||||||
|
|
||||||
|
|
||||||
|
def init_database(engine):
|
||||||
|
"""
|
||||||
|
Initialisiert die Datenbank und erstellt alle Tabellen
|
||||||
|
"""
|
||||||
|
# PostGIS Extension aktivieren (manuell oder via SQL)
|
||||||
|
with engine.connect() as conn:
|
||||||
|
conn.execute("CREATE EXTENSION IF NOT EXISTS postgis;")
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
# Tabellen erstellen
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# BEISPIEL USAGE
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
# Engine erstellen
|
||||||
|
engine = create_database_engine()
|
||||||
|
|
||||||
|
# Datenbank initialisieren
|
||||||
|
init_database(engine)
|
||||||
|
|
||||||
|
# Session erstellen
|
||||||
|
Session = sessionmaker(bind=engine)
|
||||||
|
session = Session()
|
||||||
|
|
||||||
|
# Beispiel: Schweiz erstellen
|
||||||
|
schweiz = Land(
|
||||||
|
label="Schweiz"
|
||||||
|
)
|
||||||
|
session.add(schweiz)
|
||||||
|
|
||||||
|
# Beispiel: Kanton Zürich erstellen
|
||||||
|
kanton_zh = Kanton(
|
||||||
|
label="Zürich"
|
||||||
|
)
|
||||||
|
session.add(kanton_zh)
|
||||||
|
|
||||||
|
# Beispiel: Gemeinde Zürich erstellen
|
||||||
|
gemeinde_zh = Gemeinde(
|
||||||
|
label="Zürich",
|
||||||
|
plz="8000"
|
||||||
|
)
|
||||||
|
session.add(gemeinde_zh)
|
||||||
|
|
||||||
|
# Beispiel: Parzelle erstellen
|
||||||
|
parzelle = Parzelle(
|
||||||
|
label="Bahnhofstrasse 1",
|
||||||
|
parzellen_nummern=["1234"],
|
||||||
|
eigentuemerschaaft="Mustermann AG",
|
||||||
|
strasse_nr="Bahnhofstrasse 1",
|
||||||
|
land_id=schweiz.id,
|
||||||
|
kanton_id=kanton_zh.id,
|
||||||
|
gemeinde_id=gemeinde_zh.id,
|
||||||
|
bauzone="W3",
|
||||||
|
az=1.5,
|
||||||
|
bz=0.4,
|
||||||
|
vollgeschoss_zahl=4,
|
||||||
|
parzelle_bebaut=JaNein.NEIN,
|
||||||
|
parzelle_erschlossen=JaNein.JA
|
||||||
|
)
|
||||||
|
session.add(parzelle)
|
||||||
|
|
||||||
|
# Beispiel: Projekt erstellen
|
||||||
|
projekt = Projekt(
|
||||||
|
label="Neubau Wohnhaus",
|
||||||
|
status_prozess=[StatusProzess.EINGANG, StatusProzess.ANALYSE]
|
||||||
|
)
|
||||||
|
projekt.perimeter.append(parzelle)
|
||||||
|
session.add(projekt)
|
||||||
|
|
||||||
|
# Beispiel: Kontext hinzufügen
|
||||||
|
kontext = Kontext(
|
||||||
|
thema="Dienstbarkeiten",
|
||||||
|
inhalt="Wegrecht zugunsten Parzelle 1235 entlang Ostgrenze",
|
||||||
|
parzelle_id=parzelle.id
|
||||||
|
)
|
||||||
|
session.add(kontext)
|
||||||
|
|
||||||
|
# Speichern
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
print("Datenbank erfolgreich initialisiert und Beispieldaten eingefügt!")
|
||||||
323
mandates/pek/datenmodell/schema.prisma
Normal file
323
mandates/pek/datenmodell/schema.prisma
Normal file
|
|
@ -0,0 +1,323 @@
|
||||||
|
// Prisma Schema für Architektur-Planungs-App
|
||||||
|
// Unterstützt PostgreSQL mit PostGIS
|
||||||
|
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
previewFeatures = ["postgresqlExtensions"]
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
extensions = [postgis]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// ENUMS
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
enum StatusProzess {
|
||||||
|
EINGANG
|
||||||
|
ANALYSE
|
||||||
|
STUDIE
|
||||||
|
PLANUNG
|
||||||
|
BAURECHTSVERFAHREN
|
||||||
|
UMSETZUNG
|
||||||
|
ARCHIV
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DokumentTyp {
|
||||||
|
DATEI
|
||||||
|
URL
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TagTyp {
|
||||||
|
KATASTER_OBJEKTE
|
||||||
|
KATASTER_WERKELEITUNGEN
|
||||||
|
KATASTER_BELASTETE_STANDORTE
|
||||||
|
KATASTER_BAEUME
|
||||||
|
ZONENPLAN
|
||||||
|
PGB
|
||||||
|
BZO
|
||||||
|
PARKPLATZVERORDNUNG
|
||||||
|
EIGENTUEMER_AUSKUNFT
|
||||||
|
GRUNDBUCHAUSZUG
|
||||||
|
}
|
||||||
|
|
||||||
|
enum GeoTagTyp {
|
||||||
|
REFERENZPUNKT_KAT1
|
||||||
|
REFERENZPUNKT_KAT2
|
||||||
|
REFERENZPUNKT_KAT3
|
||||||
|
GEOMETER_AUFNAHME
|
||||||
|
}
|
||||||
|
|
||||||
|
enum JaNein {
|
||||||
|
LEER
|
||||||
|
JA
|
||||||
|
NEIN
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// MODELS
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
model Projekt {
|
||||||
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
label String
|
||||||
|
statusProzess StatusProzess[]
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
perimeter ParzelleInProjekt[]
|
||||||
|
dokumenteBauherrschaft ProjektDokumentBauherrschaft[]
|
||||||
|
dokumentePlanung ProjektDokumentPlanung[]
|
||||||
|
geoBaulinie GeoPunkt[]
|
||||||
|
kontextInformationen Kontext[]
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@map("projekt")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Parzelle {
|
||||||
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
label String
|
||||||
|
parzellenNummern String[]
|
||||||
|
eigentuemerschaaft String?
|
||||||
|
strasseNr String?
|
||||||
|
|
||||||
|
// Geografischer Kontext
|
||||||
|
landId String? @db.Uuid
|
||||||
|
kantonId String? @db.Uuid
|
||||||
|
gemeindeId String? @db.Uuid
|
||||||
|
|
||||||
|
// Geometrie (als GeoJSON oder WKT gespeichert)
|
||||||
|
// Für echte PostGIS Integration: Unsupported.("geometry(POLYGON, 2056)")
|
||||||
|
geoUmfangJson Json? // Alternative: GeoJSON Format
|
||||||
|
|
||||||
|
// Bauliche Parameter
|
||||||
|
bauzone String?
|
||||||
|
az Float?
|
||||||
|
bz Float?
|
||||||
|
vollgeschossZahl Int?
|
||||||
|
anrechenbarDachgeschoss Float?
|
||||||
|
anrechenbarUntergeschoss Float?
|
||||||
|
gebaeudehoehe_max Float?
|
||||||
|
|
||||||
|
// Regelungen
|
||||||
|
regelnGrenzabstand String?
|
||||||
|
regelnMehrlaengenzuschlag String?
|
||||||
|
regelnMehrhoehenzuschlag String?
|
||||||
|
|
||||||
|
// Schutzzonen
|
||||||
|
hochwasserschutzzone String?
|
||||||
|
laermschutzzone String?
|
||||||
|
grundwasserschutzzone String?
|
||||||
|
|
||||||
|
// Eigenschaften
|
||||||
|
parzelleBebaut JaNein?
|
||||||
|
parzelleErschlossen JaNein?
|
||||||
|
hanglage JaNein?
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
projekte ParzelleInProjekt[]
|
||||||
|
nachbarEigentuemer_von ParzelleNachbar[] @relation("ParzelleNachbarVon")
|
||||||
|
nachbarEigentuemer_zu ParzelleNachbar[] @relation("ParzelleNachbarZu")
|
||||||
|
|
||||||
|
kontextLand Land? @relation(fields: [landId], references: [id])
|
||||||
|
kontextKanton Kanton? @relation(fields: [kantonId], references: [id])
|
||||||
|
kontextGemeinde Gemeinde? @relation(fields: [gemeindeId], references: [id])
|
||||||
|
|
||||||
|
spezifischeDokumente ParzelleDokument[]
|
||||||
|
kontextInformationen Kontext[]
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@map("parzelle")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Dokument {
|
||||||
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
label String
|
||||||
|
versionsbezeichnung String?
|
||||||
|
typ DokumentTyp
|
||||||
|
format String?
|
||||||
|
dokumentReferenz String
|
||||||
|
tags TagTyp[]
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
projekteBauherrschaft ProjektDokumentBauherrschaft[]
|
||||||
|
projektePlanung ProjektDokumentPlanung[]
|
||||||
|
parzellen ParzelleDokument[]
|
||||||
|
|
||||||
|
// Spezifische Dokumente für Gemeinde/Kanton
|
||||||
|
gemeindeBzoAktuell Gemeinde[] @relation("GemeindeBZOAktuell")
|
||||||
|
gemeindeBzoRevision Gemeinde[] @relation("GemeindeBZORevision")
|
||||||
|
kantonBaureglementAktuell Kanton[] @relation("KantonBaureglementAktuell")
|
||||||
|
kantonBaureglementRevision Kanton[] @relation("KantonBaureglementRevision")
|
||||||
|
kantonBauverordnungAktuell Kanton[] @relation("KantonBauverordnungAktuell")
|
||||||
|
kantonBauverordnungRevision Kanton[] @relation("KantonBauverordnungRevision")
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@map("dokument")
|
||||||
|
}
|
||||||
|
|
||||||
|
model GeoPunkt {
|
||||||
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
x Float
|
||||||
|
y Float
|
||||||
|
z Float?
|
||||||
|
referenzen GeoTagTyp[]
|
||||||
|
|
||||||
|
// Foreign Keys
|
||||||
|
projektId String? @db.Uuid
|
||||||
|
projekt Projekt? @relation(fields: [projektId], references: [id])
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@map("geo_punkt")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Kontext {
|
||||||
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
thema String
|
||||||
|
inhalt String @db.Text
|
||||||
|
|
||||||
|
// Polymorphe Beziehung (kann zu verschiedenen Entitäten gehören)
|
||||||
|
projektId String? @db.Uuid
|
||||||
|
parzelleId String? @db.Uuid
|
||||||
|
landId String? @db.Uuid
|
||||||
|
kantonId String? @db.Uuid
|
||||||
|
gemeindeId String? @db.Uuid
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
projekt Projekt? @relation(fields: [projektId], references: [id], onDelete: Cascade)
|
||||||
|
parzelle Parzelle? @relation(fields: [parzelleId], references: [id], onDelete: Cascade)
|
||||||
|
land Land? @relation(fields: [landId], references: [id], onDelete: Cascade)
|
||||||
|
kanton Kanton? @relation(fields: [kantonId], references: [id], onDelete: Cascade)
|
||||||
|
gemeinde Gemeinde? @relation(fields: [gemeindeId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@map("kontext")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Gemeinde {
|
||||||
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
label String
|
||||||
|
plz String?
|
||||||
|
|
||||||
|
// BZO Dokumente
|
||||||
|
bzoAktuellId String? @db.Uuid
|
||||||
|
bzoRevisionId String? @db.Uuid
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
parzellen Parzelle[]
|
||||||
|
kontextInformationen Kontext[]
|
||||||
|
|
||||||
|
bzoAktuell Dokument? @relation("GemeindeBZOAktuell", fields: [bzoAktuellId], references: [id])
|
||||||
|
bzoRevision Dokument? @relation("GemeindeBZORevision", fields: [bzoRevisionId], references: [id])
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@map("gemeinde")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Kanton {
|
||||||
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
label String
|
||||||
|
|
||||||
|
// Regelwerke
|
||||||
|
baureglementAktuellId String? @db.Uuid
|
||||||
|
baureglementRevisionId String? @db.Uuid
|
||||||
|
bauverordnungAktuellId String? @db.Uuid
|
||||||
|
bauverordnungRevisionId String? @db.Uuid
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
parzellen Parzelle[]
|
||||||
|
kontextInformationen Kontext[]
|
||||||
|
|
||||||
|
baureglementAktuell Dokument? @relation("KantonBaureglementAktuell", fields: [baureglementAktuellId], references: [id])
|
||||||
|
baureglementRevision Dokument? @relation("KantonBaureglementRevision", fields: [baureglementRevisionId], references: [id])
|
||||||
|
bauverordnungAktuell Dokument? @relation("KantonBauverordnungAktuell", fields: [bauverordnungAktuellId], references: [id])
|
||||||
|
bauverordnungRevision Dokument? @relation("KantonBauverordnungRevision", fields: [bauverordnungRevisionId], references: [id])
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@map("kanton")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Land {
|
||||||
|
id String @id @default(uuid()) @db.Uuid
|
||||||
|
label String
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
parzellen Parzelle[]
|
||||||
|
kontextInformationen Kontext[]
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@map("land")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// JUNCTION TABLES (Many-to-Many)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
model ParzelleInProjekt {
|
||||||
|
projektId String @db.Uuid
|
||||||
|
parzelleId String @db.Uuid
|
||||||
|
projekt Projekt @relation(fields: [projektId], references: [id], onDelete: Cascade)
|
||||||
|
parzelle Parzelle @relation(fields: [parzelleId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@id([projektId, parzelleId])
|
||||||
|
@@map("projekt_parzelle")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ParzelleNachbar {
|
||||||
|
parzelleId String @db.Uuid
|
||||||
|
nachbarId String @db.Uuid
|
||||||
|
parzelle Parzelle @relation("ParzelleNachbarVon", fields: [parzelleId], references: [id], onDelete: Cascade)
|
||||||
|
nachbar Parzelle @relation("ParzelleNachbarZu", fields: [nachbarId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@id([parzelleId, nachbarId])
|
||||||
|
@@map("parzelle_nachbar")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ProjektDokumentBauherrschaft {
|
||||||
|
projektId String @db.Uuid
|
||||||
|
dokumentId String @db.Uuid
|
||||||
|
projekt Projekt @relation(fields: [projektId], references: [id], onDelete: Cascade)
|
||||||
|
dokument Dokument @relation(fields: [dokumentId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@id([projektId, dokumentId])
|
||||||
|
@@map("projekt_dokument_bauherrschaft")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ProjektDokumentPlanung {
|
||||||
|
projektId String @db.Uuid
|
||||||
|
dokumentId String @db.Uuid
|
||||||
|
projekt Projekt @relation(fields: [projektId], references: [id], onDelete: Cascade)
|
||||||
|
dokument Dokument @relation(fields: [dokumentId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@id([projektId, dokumentId])
|
||||||
|
@@map("projekt_dokument_planung")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ParzelleDokument {
|
||||||
|
parzelleId String @db.Uuid
|
||||||
|
dokumentId String @db.Uuid
|
||||||
|
parzelle Parzelle @relation(fields: [parzelleId], references: [id], onDelete: Cascade)
|
||||||
|
dokument Dokument @relation(fields: [dokumentId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@id([parzelleId, dokumentId])
|
||||||
|
@@map("parzelle_dokument")
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 715 KiB After Width: | Height: | Size: 715 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 559 KiB After Width: | Height: | Size: 559 KiB |
Loading…
Reference in a new issue