409 lines
9.6 KiB
Markdown
409 lines
9.6 KiB
Markdown
# 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+
|