Planmodell
Einleitung
Das Modell zur Verwaltung von kommunalen Plänen orientiert sich am deutschen Standard XPlanung und am Leitfaden für die Bereitstellung kommunaler Pläne und Satzungen im Rahmen der Geodateninfrastruktur Rheinland-Pfalz (GDI-RP). Dieser Leitfaden wurde ab 2008 auf Basis von XPlanung 2.0 entwickelt. Neben den Vorgaben des Datenaustauschstandards wurden dabei auch Anforderungen aus der kommunalen Praxis übernommen und eine standardisierte Bereitstellung vorgegeben. Der Standard wurde mit den kommunalen Spitzenverbänden des Landes abgestimmt und wird vom Lenkungsausschuss für Geodateninfrastruktur Rheinland-Pfalz herausgegeben.
In einer ersten Version des Modells wird nur eine minimale Zahl von Attributen definiert. Für den proof of concept (POC) ist das zunächst ausreichend. Das abstrakte Grundmodell heißt XPlan und vererbt seine Attribute auf das konkrete Modell BPlan. Der Geltungsbereich ist als Geometry-Field modelliert und kann damit auch Multipolygone aufnehmen.
Modelldefinition
xplanung_light/models.py
"""
https://xleitstelle.de/releases/objektartenkatalog_6_0
"""
class XPlan(models.Model):
name = models.CharField(null=False, blank=False, max_length=2048, verbose_name='Name des Plans', help_text='Offizieller Name des raumbezogenen Plans')
#nummer [0..1]
nummer = models.CharField(max_length=5, verbose_name="Nummer des Plans.")
#internalId [0..1]
#beschreibung [0..1]
#kommentar [0..1]
#technHerstellDatum [0..1], Date
#genehmigungsDatum [0..1], Date
#untergangsDatum [0..1], Date
#aendertPlan [0..*], XP_VerbundenerPlan
#wurdeGeaendertVonPlan [0..*], XP_VerbundenerPlan
#aendertPlanBereich [0..*], Referenz, Testphase
#wurdeGeaendertVonPlanBereich [0..*], Referenz, Testphase
#erstellungsMassstab [0..1], Integer
#bezugshoehe [0..1], Length
#hoehenbezug [0..1]
#technischerPlanersteller, [0..1]
#raeumlicherGeltungsbereich [1], GM_Object
geltungsbereich = models.GeometryField(null=False, blank=False, verbose_name='Grenze des räumlichen Geltungsbereiches des Plans.')
#verfahrensMerkmale [0..*], XP_VerfahrensMerkmal
#hatGenerAttribut [0..*], XP_GenerAttribut
#externeReferenz, [0..*], XP_SpezExterneReferenz
#texte [0..*], XP_TextAbschnitt
#begruendungsTexte [0..*], XP_BegruendungAbschnitt
history = HistoricalRecords(inherit=True)
class Meta:
abstract = True
class BPlan(XPlan):
BPLAN = "1000"
EINFACHERBPLAN = "10000"
QUALIFIZIERTERBPLAN = "10001"
BEBAUUNGSPLANZURWOHNRAUMVERSORGUNG = "10002"
VORHABENBEZOGENERBPLAN = "3000"
VORHABENUNDERSCHLIESSUNGSPLAN = "3100"
INNENBEREICHSSATZUNG = "4000"
KLARSTELLUNGSSATZUNG = "40000"
ENTWICKLUNGSSATZUNG = "40001"
ERGAENZUNGSSATZUNG = "40002"
AUSSENBEREICHSSATZUNG = "5000"
OERTLICHEBAUVORSCHRIFT = "7000"
SONSTIGES = "9999"
BPLAN_TYPE_CHOICES = [
(BPLAN, "BPlan"),
(EINFACHERBPLAN, "EinfacherBPlan"),
(QUALIFIZIERTERBPLAN, "QualifizierterBPlan"),
(BEBAUUNGSPLANZURWOHNRAUMVERSORGUNG, "BebauungsplanZurWohnraumversorgung"),
(VORHABENBEZOGENERBPLAN, "VorhabenbezogenerBPlan"),
(VORHABENUNDERSCHLIESSUNGSPLAN, "VorhabenUndErschliessungsplan"),
(INNENBEREICHSSATZUNG, "InnenbereichsSatzung"),
(KLARSTELLUNGSSATZUNG, "KlarstellungsSatzung"),
(ENTWICKLUNGSSATZUNG, "EntwicklungsSatzung"),
(ERGAENZUNGSSATZUNG, "ErgaenzungsSatzung"),
(AUSSENBEREICHSSATZUNG, "AussenbereichsSatzung"),
(OERTLICHEBAUVORSCHRIFT, "OertlicheBauvorschrift"),
(SONSTIGES, "Sonstiges"),
]
#gemeinde [1], XP_Gemeinde
gemeinde = models.ForeignKey(AdministrativeOrganization, null=True, on_delete=models.SET_NULL)
#planaufstellendeGemeinde [0..*], XP_Gemeinde
#plangeber [0..*], XP_Plangeber
#planArt [1..*], BP_PlanArt
planart = models.CharField(null=False, blank=False, max_length=5, choices=BPLAN_TYPE_CHOICES, default='1000', verbose_name='Typ des vorliegenden Bebauungsplans.', db_index=True)
#sonstPlanArt [0..1], BP_SonstPlanArt
#rechtsstand [0..1], BP_Rechtsstand
#status [0..1], BP_Status
#aenderungenBisDatum [0..1], Date
#aufstellungsbeschlussDatum [0..1], Date
#veraenderungssperre [0..1], BP_VeraenderungssperreDaten
#auslegungsStartDatum [0..*], Date
#auslegungsEndDatum [0..*], Date
#traegerbeteiligungsStartDatum [0..*], Date
#traegerbeteiligungsEndDatum [0..*], Date
#satzungsbeschlussDatum [0..1], Date
#rechtsverordnungsDatum [0..1], Date
#inkrafttretensDatum [0..1], Date
#ausfertigungsDatum [0..1], Date
#staedtebaulicherVertrag [0..1], Boolean
#erschliessungsVertrag [0..1], Boolean
#durchfuehrungsVertrag [0..1], Boolean
#gruenordnungsplan [0..1], Boolean
#versionBauNVO [0..1], XP_GesetzlicheGrundlage
#versionBauGB [0..1], XP_GesetzlicheGrundlage
#versionSonstRechtsgrundlage [0..*], XP_GesetzlicheGrundlage
#bereich [0..*], BP_Bereich
def __str__(self):
"""Returns a string representation of a BPlan."""
return f"'{self.planart}': '{self.name}'"
Verwaltungsviews
Vorarbeiten
Um eine komfortable Verwaltung von Geometrien zu ermöglichen, bietet es sich an das Django Leaflet package (django-leaflet) einzusetzen. Standardmäßig ist bei django noch eine älterer OpenLayers-Client (2.13) integriert.
Installation des packages
python3 -m pip install django-leaflet
Aktivieren des packages in komserv/settings.py
#...
'leaflet',
#...
Ersatz des OpenLayer Client im Admin backend - xplanung_light/admin.py
#...
from leaflet.admin import LeafletGeoAdmin
from xplanung_light.models import BPlan
#...
admin.site.register(BPlan, LeafletGeoAdmin)
#...
Urls
xplanung_light/urls.py
#...
from xplanung_light.views import BPlanCreateView, BPlanUpdateView, BPlanDeleteView, BPlanListView
#...
# urls for bplan
path("bplan/", BPlanListView.as_view(), name="bplan-list"),
path("bplan/create/", BPlanCreateView.as_view(), name="bplan-create"),
path("bplan/<int:pk>/update/", BPlanUpdateView.as_view(), name="bplan-update"),
path("bplan/<int:pk>/delete/", BPlanDeleteView.as_view(), name="bplan-delete"),
]
Views
Nutzung von einfachen ClassBasedGenericViews
xplanung_light/views.py
#...
from django.views.generic import (ListView, CreateView, UpdateView, DeleteView)
from xplanung_light.models import AdministrativeOrganization, BPlan
from django.urls import reverse_lazy
#...
class BPlanCreateView(CreateView):
model = BPlan
fields = ["name", "nummer", "geltungsbereich", "gemeinde", "planart"]
success_url = reverse_lazy("bplan-list")
class BPlanUpdateView(UpdateView):
model = BPlan
fields = ["name", "nummer", "geltungsbereich", "gemeinde", "planart"]
success_url = reverse_lazy("bplan-list")
class BPlanDeleteView(DeleteView):
model = BPlan
def get_success_url(self):
return reverse_lazy("bplan-list")
class BPlanListView(ListView):
model = BPlan
success_url = reverse_lazy("bplan-list")
Templates
xplanung_light/templates/xplanung_light/*
List
xplanung_light/templates/xplanung_light/bplan_list.html
{% extends "xplanung_light/layout.html" %}
{% block title %}
Liste der Bebauungspläne
{% endblock %}
{% block content %}
<p>Treffer: {{ object_list.count }} Bebauungspläne</p>
{% endblock %}
Confirm Delete
xplanung_light/templates/xplanung_light/bplan_confirm_delete.html
<form method="post">{% csrf_token %}
<p>Wollen sie das Objekt wirklich löschen? "{{ object }}"?</p>
{{ form }}
<input type="submit" value="Bestätigung">
</form>
Create
xplanung_light/templates/xplanung_light/bplan_form.html
{% extends "xplanung_light/layout.html" %}
{% block title %}
Bebauungsplan anlegen
{% endblock %}
{% block content %}
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save">
</form>
{% endblock %}
Update
xplanung_light/templates/xplanung_light/bplan_form_update.html
{% block title %}
Bebauungsplan editieren
{% endblock %}
{% block content %}
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Aktualisieren">
</form>
{% endblock %}
Erfassung eines BPlans im Admin-Backend
http://127.0.0.1:8000/admin/xplanung_light/bplan/add/
