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