Compare commits

..

9 Commits

Author SHA1 Message Date
afbdf221c3 # User view refactoring
* refactors majority of user views into class based views
* introduces BaseModalFormView and BaseView for even more generic usage
* renames url identifier user:index into user:detail for more clarity
2025-10-15 17:09:40 +02:00
be9f6f1b7e # Identifier Generator View EcoAccount refactoring
* refactors identifier generator view for ecoaccount
* simplifies base identifier generator view even further
2025-10-15 16:46:07 +02:00
80e8925a63 # Identifier Generator View Compensation refactoring
* refactors identifier generator view for compensation
2025-10-15 16:42:42 +02:00
c597e1934b # Identifier Generator View refactoring
* refactors identifier generator view for interventions
* simplifies same view for ema
2025-10-15 16:40:35 +02:00
a44d8658d4 # NewId Generator Ema refactoring
* introduces BaseNewIdentifierGeneratorView class
* refactors new identifier generator view for ema
2025-10-15 16:29:05 +02:00
bb71c0fcc8 # Index Ema refactoring
* refactors index view for ema
2025-10-15 16:14:03 +02:00
67acddf701 # Index EcoAccount refactoring
* refactors index view for eco account
2025-10-15 16:12:21 +02:00
21bb988d86 # Index Compensation refactoring
* refactors index view for compensations
2025-10-15 16:03:53 +02:00
1ceffccd40 # Index Intervention refactoring
* introduces BaseIndexView class
* refactors index view for interventions
2025-10-15 16:00:51 +02:00
72 changed files with 1695 additions and 2356 deletions

View File

@@ -10,6 +10,6 @@ from analysis.views import *
app_name = "analysis"
urlpatterns = [
path("reports/", ReportIndexView.as_view(), name="reports"),
path("reports/<id>", ReportDetailView.as_view(), name="report-detail"),
path("reports/", index_reports_view, name="reports"),
path("reports/<id>", detail_report_view, name="report-detail"),
]

View File

@@ -1,12 +1,8 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render, redirect, get_object_or_404
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.views import View
from django.views.generic import DetailView
from analysis.forms import TimespanReportForm
from analysis.utils.excel.excel import TempExcelFile
@@ -46,112 +42,57 @@ def index_reports_view(request: HttpRequest):
context = BaseContext(request, context).context
return render(request, template, context)
class ReportIndexView(LoginRequiredMixin, View):
@method_decorator(conservation_office_group_required)
def get(self, request: HttpRequest) -> HttpResponse:
"""
@login_required
@conservation_office_group_required
def detail_report_view(request: HttpRequest, id: str):
""" Renders the detailed report for a conservation office
Args:
request (HttpRequest): The incoming request
Args:
request (HttpRequest): The incoming request
id (str): The conservation_office KonovaCode id
Returns:
Returns:
"""
template = "analysis/reports/index.html"
form = TimespanReportForm(None)
context = {
"form": form
}
context = BaseContext(request, context).context
return render(request, template, context)
@method_decorator(conservation_office_group_required)
def post(self, request: HttpRequest) -> HttpResponse:
"""
Args:
request (HttpRequest): The incoming request
Returns:
"""
template = "analysis/reports/index.html"
form = TimespanReportForm(request.POST or None)
if form.is_valid():
redirect_url = form.save()
return redirect(redirect_url)
else:
messages.error(
request,
FORM_INVALID,
extra_tags="danger",
)
context = {
"form": form
}
context = BaseContext(request, context).context
return render(request, template, context)
class ReportDetailView(LoginRequiredMixin, DetailView):
@method_decorator(conservation_office_group_required)
def get(self, request: HttpRequest, id: str):
""" Renders the detailed report for a conservation office
Args:
request (HttpRequest): The incoming request
id (str): The conservation_office KonovaCode id
Returns:
"""
# Try to resolve the requested office id
cons_office = get_object_or_404(
KonovaCode,
id=id
"""
# Try to resolve the requested office id
cons_office = get_object_or_404(
KonovaCode,
id=id
)
# Try to resolve the date parameters into Date objects -> redirect if this fails
try:
df = request.GET.get("df", None)
dt = request.GET.get("dt", None)
date_from = timezone.make_aware(timezone.datetime.fromisoformat(df))
date_to = timezone.make_aware(timezone.datetime.fromisoformat(dt))
except ValueError:
messages.error(
request,
PARAMS_INVALID,
extra_tags="danger",
)
# Try to resolve the date parameters into Date objects -> redirect if this fails
try:
df = request.GET.get("df", None)
dt = request.GET.get("dt", None)
date_from = timezone.make_aware(timezone.datetime.fromisoformat(df))
date_to = timezone.make_aware(timezone.datetime.fromisoformat(dt))
except ValueError:
messages.error(
request,
PARAMS_INVALID,
extra_tags="danger",
)
return redirect("analysis:reports")
return redirect("analysis:reports")
# Check whether the html default rendering is requested or an alternative
format_param = request.GET.get("format", "html")
report = TimespanReport(id, date_from, date_to)
# Check whether the html default rendering is requested or an alternative
format_param = request.GET.get("format", "html")
report = TimespanReport(id, date_from, date_to)
if format_param == "html":
return self.__handle_html_format(request, report, cons_office)
elif format_param == "excel":
return self.__handle_excel_format(report, cons_office, df, dt)
else:
raise NotImplementedError
def __handle_html_format(self, request, report: TimespanReport, office: KonovaCode):
if format_param == "html":
template = "analysis/reports/detail.html"
context = {
"office": office,
"office": cons_office,
"report": report,
}
context = BaseContext(request, context).context
return render(request, template, context)
def __handle_excel_format(self, report: TimespanReport, office: KonovaCode, df: str, dt: str):
elif format_param == "excel":
file = TempExcelFile(report.excel_template_path, report.excel_map)
response = HttpResponse(
content=file.stream,
content_type="application/ms-excel",
)
response['Content-Disposition'] = f'attachment; filename={office.long_name}_{df}_{dt}.xlsx'
response['Content-Disposition'] = f'attachment; filename={cons_office.long_name}_{df}_{dt}.xlsx'
return response
else:
raise NotImplementedError

View File

@@ -71,7 +71,7 @@ class APIV1CreateTestCase(BaseAPIV1TestCase):
# Expect this first request to fail, since user has no shared access on the intervention, we want to create
# a compensation for
response = self._run_create_request(url, post_body)
self.assertEqual(response.status_code, 400, msg=response.content)
self.assertEqual(response.status_code, 500, msg=response.content)
content = json.loads(response.content)
self.assertGreater(len(content.get("errors", [])), 0, msg=response.content)

View File

@@ -7,8 +7,11 @@ Created on: 21.01.22
"""
from django.urls import path, include
from api.views.method_views import generate_new_token_view
app_name = "api"
urlpatterns = [
path("v1/", include("api.urls.v1.urls", namespace="v1")),
path("token/generate", generate_new_token_view, name="generate-new-token"),
]

35
api/views/method_views.py Normal file
View File

@@ -0,0 +1,35 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 27.01.22
"""
from django.contrib.auth.decorators import login_required
from django.http import HttpRequest, JsonResponse
from api.models import APIUserToken
@login_required
def generate_new_token_view(request: HttpRequest):
""" Handles request for fetching
Args:
request (HttpRequest): The incoming request
Returns:
"""
if request.method == "GET":
token = APIUserToken()
while APIUserToken.objects.filter(token=token.token).exists():
token = APIUserToken()
return JsonResponse(
data={
"gen_data": token.token
}
)
else:
raise NotImplementedError

View File

@@ -6,9 +6,7 @@ Created on: 21.01.22
"""
import json
from json import JSONDecodeError
from django.core.exceptions import ObjectDoesNotExist
from django.http import JsonResponse, HttpRequest
from api.utils.serializer.v1.compensation import CompensationAPISerializerV1
@@ -68,12 +66,8 @@ class AbstractAPIViewV1(AbstractAPIView):
body = request.body.decode("utf-8")
body = json.loads(body)
created_id = self.serializer.create_model_from_json(body, self.user)
except (JSONDecodeError,
AssertionError,
ValueError,
PermissionError,
ObjectDoesNotExist) as e:
return self._return_error_response(e, 400)
except Exception as e:
return self._return_error_response(e, 500)
return JsonResponse({"id": created_id})
def put(self, request: HttpRequest, id=None):

View File

@@ -81,7 +81,9 @@ class AbstractAPIView(View):
Returns:
"""
content = [f"{error.__class__.__name__}: {str(error)}"]
content = [error.__str__()]
if hasattr(error, "messages"):
content = error.messages
return JsonResponse(
{
"errors": content

View File

@@ -45,14 +45,6 @@ class AbstractCompensationAdmin(BaseObjectAdmin):
states = "\n".join(states)
return states
def get_actions(self, request):
DELETE_ACTION_IDENTIFIER = "delete_selected"
actions = super().get_actions(request)
if DELETE_ACTION_IDENTIFIER in actions:
del actions[DELETE_ACTION_IDENTIFIER]
return actions
class CompensationAdmin(AbstractCompensationAdmin):
autocomplete_fields = [

View File

@@ -15,7 +15,6 @@ from compensation.models import EcoAccount
from intervention.models import Handler, Responsibility, Legal
from konova.forms import SimpleGeomForm
from konova.forms.modals import RemoveModalForm
from konova.settings import ETS_GROUP
from konova.utils import validators
from user.models import User, UserActionLogEntry
@@ -247,13 +246,4 @@ class RemoveEcoAccountModalForm(RemoveModalForm):
"confirm",
_("The account can not be removed, since there are still deductions.")
)
# If there are deductions but the performing user is not part of an ETS group, we assume this poor
# fella does not know what he/she does -> give a hint that they should contact someone in charge...
user_is_ets_user = self.user.in_group(ETS_GROUP)
if not user_is_ets_user:
self.add_error(
"confirm",
_("Please contact the responsible conservation office to find a solution!")
)
return super_valid and not has_deductions

View File

@@ -53,7 +53,7 @@
</td>
<td class="align-middle">
{% if deduction.intervention.recorded %}
<em title="{% trans 'Recorded on' %} {{deduction.intervention.recorded.timestamp}} {% trans 'by' %} {{deduction.intervention.recorded.user}}" class='fas fa-bookmark registered-bookmark'></em>
<em title="{% trans 'Recorded on' %} {{obj.recorded.timestamp}} {% trans 'by' %} {{obj.recorded.user}}" class='fas fa-bookmark registered-bookmark'></em>
{% else %}
<em title="{% trans 'Not recorded yet' %}" class='far fa-bookmark'></em>
{% endif %}

View File

@@ -7,32 +7,30 @@ Created on: 24.08.21
"""
from django.urls import path
from compensation.views.compensation.detail import DetailCompensationView
from compensation.views.compensation.document import EditCompensationDocumentView, NewCompensationDocumentView, \
GetCompensationDocumentView, RemoveCompensationDocumentView
from compensation.views.compensation.remove import RemoveCompensationView
from compensation.views.compensation.resubmission import CompensationResubmissionView
from compensation.views.compensation.report import CompensationPublicReportView
from compensation.views.compensation.report import report_view
from compensation.views.compensation.deadline import NewCompensationDeadlineView, EditCompensationDeadlineView, \
RemoveCompensationDeadlineView
from compensation.views.compensation.action import NewCompensationActionView, EditCompensationActionView, \
RemoveCompensationActionView
from compensation.views.compensation.state import NewCompensationStateView, EditCompensationStateView, \
RemoveCompensationStateView
from compensation.views.compensation.compensation import IndexCompensationView, CompensationIdentifierGeneratorView, \
EditCompensationView, NewCompensationView
from compensation.views.compensation.compensation import new_view, detail_view, edit_view, \
remove_view, CompensationIndexView, CompensationIdentifierGeneratorView
from compensation.views.compensation.log import CompensationLogView
urlpatterns = [
# Main compensation
path("", IndexCompensationView.as_view(), name="index"),
path("", CompensationIndexView.as_view(), name="index"),
path('new/id', CompensationIdentifierGeneratorView.as_view(), name='new-id'),
path('new/<intervention_id>', NewCompensationView.as_view(), name='new'),
path('new', NewCompensationView.as_view(), name='new'),
path('<id>', DetailCompensationView.as_view(), name='detail'),
path('new/<intervention_id>', new_view, name='new'),
path('new', new_view, name='new'),
path('<id>', detail_view, name='detail'),
path('<id>/log', CompensationLogView.as_view(), name='log'),
path('<id>/edit', EditCompensationView.as_view(), name='edit'),
path('<id>/remove', RemoveCompensationView.as_view(), name='remove'),
path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'),
path('<id>/state/new', NewCompensationStateView.as_view(), name='new-state'),
path('<id>/state/<state_id>/edit', EditCompensationStateView.as_view(), name='state-edit'),
@@ -45,7 +43,7 @@ urlpatterns = [
path('<id>/deadline/new', NewCompensationDeadlineView.as_view(), name="new-deadline"),
path('<id>/deadline/<deadline_id>/edit', EditCompensationDeadlineView.as_view(), name='deadline-edit'),
path('<id>/deadline/<deadline_id>/remove', RemoveCompensationDeadlineView.as_view(), name='deadline-remove'),
path('<id>/report', CompensationPublicReportView.as_view(), name='report'),
path('<id>/report', report_view, name='report'),
path('<id>/resub', CompensationResubmissionView.as_view(), name='resubmission-create'),
# Documents

View File

@@ -8,13 +8,11 @@ Created on: 24.08.21
from django.urls import path
from compensation.autocomplete.eco_account import EcoAccountAutocomplete
from compensation.views.eco_account.detail import DetailEcoAccountView
from compensation.views.eco_account.eco_account import IndexEcoAccountView, EcoAccountIdentifierGeneratorView, \
NewEcoAccountView, EditEcoAccountView
from compensation.views.eco_account.eco_account import new_view, edit_view, remove_view, \
detail_view, EcoAccountIndexView, EcoAccountIdentifierGeneratorView
from compensation.views.eco_account.log import EcoAccountLogView
from compensation.views.eco_account.record import EcoAccountRecordView
from compensation.views.eco_account.remove import RemoveEcoAccountView
from compensation.views.eco_account.report import EcoAccountPublicReportView
from compensation.views.eco_account.report import report_view
from compensation.views.eco_account.resubmission import EcoAccountResubmissionView
from compensation.views.eco_account.state import NewEcoAccountStateView, EditEcoAccountStateView, \
RemoveEcoAccountStateView
@@ -30,15 +28,15 @@ from compensation.views.eco_account.deduction import NewEcoAccountDeductionView,
app_name = "acc"
urlpatterns = [
path("", IndexEcoAccountView.as_view(), name="index"),
path('new/', NewEcoAccountView.as_view(), name='new'),
path("", EcoAccountIndexView.as_view(), name="index"),
path('new/', new_view, name='new'),
path('new/id', EcoAccountIdentifierGeneratorView.as_view(), name='new-id'),
path('<id>', DetailEcoAccountView.as_view(), name='detail'),
path('<id>', detail_view, name='detail'),
path('<id>/log', EcoAccountLogView.as_view(), name='log'),
path('<id>/record', EcoAccountRecordView.as_view(), name='record'),
path('<id>/report', EcoAccountPublicReportView.as_view(), name='report'),
path('<id>/edit', EditEcoAccountView.as_view(), name='edit'),
path('<id>/remove', RemoveEcoAccountView.as_view(), name='remove'),
path('<id>/report', report_view, name='report'),
path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'),
path('<id>/resub', EcoAccountResubmissionView.as_view(), name='resubmission-create'),
path('<id>/state/new', NewEcoAccountStateView.as_view(), name='new-state'),

View File

@@ -10,7 +10,7 @@ from compensation.views.payment import *
app_name = "pay"
urlpatterns = [
path('<id>/new', NewPaymentView.as_view(), name='new'),
path('<id>/remove/<payment_id>', RemovePaymentView.as_view(), name='remove'),
path('<id>/edit/<payment_id>', EditPaymentView.as_view(), name='edit'),
path('<id>/new', new_payment_view, name='new'),
path('<id>/remove/<payment_id>', payment_remove_view, name='remove'),
path('<id>/edit/<payment_id>', payment_edit_view, name='edit'),
]

View File

@@ -6,127 +6,75 @@ Created on: 19.08.22
"""
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpRequest, JsonResponse
from django.shortcuts import get_object_or_404, render, redirect
from django.utils.decorators import method_decorator
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views import View
from compensation.forms.compensation import EditCompensationForm, NewCompensationForm
from compensation.models import Compensation
from compensation.tables.compensation import CompensationTable
from intervention.models import Intervention
from konova.contexts import BaseContext
from konova.decorators import shared_access_required, default_group_required
from konova.decorators import shared_access_required, default_group_required, any_group_check, login_required_modal, \
uuid_required
from konova.forms import SimpleGeomForm
from konova.forms.modals import RemoveModalForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, CHECK_STATE_RESET, FORM_INVALID, PARAMS_INVALID, \
IDENTIFIER_REPLACED, COMPENSATION_ADDED_TEMPLATE, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
from konova.views.identifier import AbstractIdentifierGeneratorView
from konova.views.index import AbstractIndexView
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE, \
RECORDED_BLOCKS_EDIT, CHECK_STATE_RESET, FORM_INVALID, PARAMS_INVALID, IDENTIFIER_REPLACED, \
COMPENSATION_ADDED_TEMPLATE, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView
class IndexCompensationView(AbstractIndexView):
def get(self, request, *args, **kwargs) -> HttpResponse:
"""
Renders the index view for compensation
class CompensationIndexView(LoginRequiredMixin, BaseIndexView):
_TAB_TITLE = _("Compensations - Overview")
_INDEX_TABLE_CLS = CompensationTable
Args:
request (HttpRequest): The incoming request
Returns:
A rendered view
"""
compensations = Compensation.objects.filter(
def _get_queryset(self):
qs = Compensation.objects.filter(
deleted=None, # only show those which are not deleted individually
intervention__deleted=None, # and don't show the ones whose intervention has been deleted
).order_by(
"-modified__timestamp"
)
table = CompensationTable(
request=request,
queryset=compensations
)
context = {
"table": table,
TAB_TITLE_IDENTIFIER: _("Compensations - Overview"),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
return qs
class NewCompensationView(LoginRequiredMixin, View):
_TEMPLATE = "compensation/form/view.html"
@login_required
@default_group_required
@shared_access_required(Intervention, "intervention_id")
def new_view(request: HttpRequest, intervention_id: str = None):
"""
Renders a view for a new compensation creation
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "intervention_id"))
def get(self, request: HttpRequest, intervention_id: str = None, *args, **kwargs) -> HttpResponse:
"""
Renders a view for new compensation
Args:
request (HttpRequest): The incoming request
A compensation creation may be called directly from the parent-intervention object. If so - we may take
the intervention's id and directly link the compensation to it.
Returns:
Args:
request (HttpRequest): The incoming request
intervention_id (str): The intervention identifier
Returns:
"""
if intervention_id:
# If the parent-intervention is recorded, we are not allowed to change anything on it's data.
# Not even adding new child elements like compensations!
intervention = get_object_or_404(Intervention, id=intervention_id)
recording_state_blocks_actions = intervention.is_recorded
if recording_state_blocks_actions:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("intervention:detail", id=intervention_id)
data_form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New compensation"),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "intervention_id"))
def post(self, request: HttpRequest, intervention_id: str = None, *args, **kwargs) -> HttpResponse:
"""
Renders a view for a new compensation creation
Args:
request (HttpRequest): The incoming request
Returns:
"""
if intervention_id:
# If the parent-intervention is recorded, we are not allowed to change anything on it's data.
# Not even adding new child elements like compensations!
intervention = get_object_or_404(Intervention, id=intervention_id)
recording_state_blocks_actions = intervention.is_recorded
if recording_state_blocks_actions:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("intervention:detail", id=intervention_id)
data_form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
"""
template = "compensation/form/view.html"
if intervention_id is not None:
try:
intervention = Intervention.objects.get(id=intervention_id)
except ObjectDoesNotExist:
messages.error(request, PARAMS_INVALID)
return redirect("home")
if intervention.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("intervention:detail", id=intervention_id)
data_form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
generated_identifier = data_form.cleaned_data.get("identifier", None)
comp = data_form.save(request.user, geom_form)
@@ -144,97 +92,66 @@ class NewCompensationView(LoginRequiredMixin, View):
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("compensation:detail", id=comp.id)
else:
messages.error(request, FORM_INVALID, extra_tags="danger", )
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New compensation"),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
messages.error(request, FORM_INVALID, extra_tags="danger",)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New compensation"),
}
context = BaseContext(request, context).context
return render(request, template, context)
class CompensationIdentifierGeneratorView(AbstractIdentifierGeneratorView):
_MODEL = Compensation
class CompensationIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
_MODEL_CLS = Compensation
_REDIRECT_URL_NAME = "compensation:index"
class EditCompensationView(LoginRequiredMixin, View):
_TEMPLATE = "compensation/form/view.html"
@login_required
@default_group_required
@shared_access_required(Compensation, "id")
def edit_view(request: HttpRequest, id: str):
"""
Renders a view for editing compensations
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Compensation, "id"))
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
Args:
request (HttpRequest): The incoming request
"""
Renders a view for editing compensations
Returns:
Args:
request (HttpRequest): The incoming request
"""
template = "compensation/form/view.html"
# Get object from db
comp = get_object_or_404(Compensation, id=id)
if comp.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("compensation:detail", id=id)
Returns:
"""
# Get object from db
comp = get_object_or_404(Compensation, id=id)
if comp.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("compensation:detail", id=id)
# Create forms, initialize with values from db/from POST request
data_form = EditCompensationForm(request.POST or None, instance=comp)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp)
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(comp.identifier),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Compensation, "id"))
def post(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
"""
Renders a view for editing compensations
Args:
request (HttpRequest): The incoming request
Returns:
"""
# Get object from db
comp = get_object_or_404(Compensation, id=id)
if comp.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("compensation:detail", id=id)
# Create forms, initialize with values from db/from POST request
data_form = EditCompensationForm(request.POST or None, instance=comp)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp)
# Create forms, initialize with values from db/from POST request
data_form = EditCompensationForm(request.POST or None, instance=comp)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
# Preserve state of intervention checked to determine whether the user must be informed or not
# about a change of the check state
intervention_is_checked = comp.intervention.checked is not None
# The data form takes the geom form for processing, as well as the performing user
comp = data_form.save(request.user, geom_form)
if intervention_is_checked:
@@ -245,21 +162,126 @@ class EditCompensationView(LoginRequiredMixin, View):
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("compensation:detail", id=comp.id)
else:
messages.error(request, FORM_INVALID, extra_tags="danger", )
messages.error(request, FORM_INVALID, extra_tags="danger",)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(comp.identifier),
}
context = BaseContext(request, context).context
return render(request, template, context)
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(comp.identifier),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@login_required
@any_group_check
@uuid_required
def detail_view(request: HttpRequest, id: str):
""" Renders a detail view for a compensation
Args:
request (HttpRequest): The incoming request
id (str): The compensation's id
Returns:
"""
template = "compensation/detail/compensation/view.html"
comp = get_object_or_404(
Compensation.objects.select_related(
"modified",
"created",
"geometry"
),
id=id,
deleted=None,
intervention__deleted=None,
)
geom_form = SimpleGeomForm(instance=comp)
parcels = comp.get_underlying_parcels()
_user = request.user
is_data_shared = comp.intervention.is_shared_with(_user)
# Order states according to surface
before_states = comp.before_states.all().prefetch_related("biotope_type").order_by("-surface")
after_states = comp.after_states.all().prefetch_related("biotope_type").order_by("-surface")
actions = comp.actions.all().prefetch_related("action_type")
# Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = comp.get_surface_before_states()
sum_after_states = comp.get_surface_after_states()
diff_states = abs(sum_before_states - sum_after_states)
request = comp.set_status_messages(request)
last_checked = comp.intervention.get_last_checked_action()
last_checked_tooltip = ""
if last_checked:
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(last_checked.get_timestamp_str_formatted(), last_checked.user)
requesting_user_is_only_shared_user = comp.is_only_shared_with(_user)
if requesting_user_is_only_shared_user:
messages.info(
request,
DO_NOT_FORGET_TO_SHARE
)
context = {
"obj": comp,
"last_checked": last_checked,
"last_checked_tooltip": last_checked_tooltip,
"geom_form": geom_form,
"parcels": parcels,
"is_entry_shared": is_data_shared,
"actions": actions,
"before_states": before_states,
"after_states": after_states,
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": comp.get_LANIS_link(),
TAB_TITLE_IDENTIFIER: f"{comp.identifier} - {comp.title}",
"has_finished_deadlines": comp.get_finished_deadlines().exists(),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required_modal
@login_required
@default_group_required
@shared_access_required(Compensation, "id")
def remove_view(request: HttpRequest, id: str):
""" Renders a modal view for removing the compensation
Args:
request (HttpRequest): The incoming request
id (str): The compensation's id
Returns:
"""
comp = get_object_or_404(Compensation, id=id)
form = RemoveModalForm(request.POST or None, instance=comp, request=request)
return form.process_request(
request=request,
msg_success=COMPENSATION_REMOVED_TEMPLATE.format(comp.identifier),
redirect_url=reverse("compensation:index"),
)

View File

@@ -1,97 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from django.contrib import messages
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render, get_object_or_404
from compensation.models import Compensation
from konova.contexts import BaseContext
from konova.forms import SimpleGeomForm
from konova.settings import ETS_GROUP, ZB_GROUP, DEFAULT_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import DO_NOT_FORGET_TO_SHARE, DATA_CHECKED_PREVIOUSLY_TEMPLATE
from konova.views.detail import AbstractDetailView
class DetailCompensationView(AbstractDetailView):
_TEMPLATE = "compensation/detail/compensation/view.html"
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
""" Renders a detail view for a compensation
Args:
request (HttpRequest): The incoming request
id (str): The compensation's id
Returns:
"""
comp = get_object_or_404(
Compensation.objects.select_related(
"modified",
"created",
"geometry"
),
id=id,
deleted=None,
intervention__deleted=None,
)
geom_form = SimpleGeomForm(instance=comp)
parcels = comp.get_underlying_parcels()
_user = request.user
is_data_shared = comp.intervention.is_shared_with(_user)
# Order states according to surface
before_states = comp.before_states.all().prefetch_related("biotope_type").order_by("-surface")
after_states = comp.after_states.all().prefetch_related("biotope_type").order_by("-surface")
actions = comp.actions.all().prefetch_related("action_type")
# Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = comp.get_surface_before_states()
sum_after_states = comp.get_surface_after_states()
diff_states = abs(sum_before_states - sum_after_states)
request = comp.set_status_messages(request)
last_checked = comp.intervention.get_last_checked_action()
last_checked_tooltip = ""
if last_checked:
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(
last_checked.get_timestamp_str_formatted(),
last_checked.user
)
requesting_user_is_only_shared_user = comp.is_only_shared_with(_user)
if requesting_user_is_only_shared_user:
messages.info(
request,
DO_NOT_FORGET_TO_SHARE
)
context = {
"obj": comp,
"last_checked": last_checked,
"last_checked_tooltip": last_checked_tooltip,
"geom_form": geom_form,
"parcels": parcels,
"is_entry_shared": is_data_shared,
"actions": actions,
"before_states": before_states,
"after_states": after_states,
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": comp.get_LANIS_link(),
TAB_TITLE_IDENTIFIER: f"{comp.identifier} - {comp.title}",
"has_finished_deadlines": comp.get_finished_deadlines().exists(),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)

View File

@@ -1,20 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from django.http import HttpRequest, HttpResponse
from django.utils.decorators import method_decorator
from compensation.models import Compensation
from konova.decorators import shared_access_required
from konova.views.remove import AbstractRemoveView
class RemoveCompensationView(AbstractRemoveView):
_MODEL = Compensation
_REDIRECT_URL = "compensation:index"
@method_decorator(shared_access_required(Compensation, "id"))
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
return super().get(request, *args, **kwargs)

View File

@@ -5,81 +5,77 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22
"""
from django.http import HttpRequest, HttpResponse
from django.http import HttpRequest
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from compensation.models import Compensation
from konova.contexts import BaseContext
from konova.decorators import uuid_required
from konova.forms import SimpleGeomForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.qrcode import QrCode
from konova.views.report import AbstractPublicReportView
from konova.utils.generators import generate_qr_code
@uuid_required
def report_view(request: HttpRequest, id: str):
""" Renders the public report view
class CompensationPublicReportView(AbstractPublicReportView):
_TEMPLATE = "compensation/report/compensation/report.html"
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
""" Renders the public report view
Returns:
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
Returns:
"""
comp = get_object_or_404(Compensation, id=id)
tab_title = _("Report {}").format(comp.identifier)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not comp.is_ready_for_publish():
template = "report/unavailable.html"
context = {
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=comp
)
parcels = comp.get_underlying_parcels()
qrcode = QrCode(
content=request.build_absolute_uri(reverse("compensation:report", args=(id,))),
size=10
)
qrcode_lanis = QrCode(
content=comp.get_LANIS_link(),
size=7
)
# Order states by surface
before_states = comp.before_states.all().order_by("-surface").prefetch_related("biotope_type")
after_states = comp.after_states.all().order_by("-surface").prefetch_related("biotope_type")
actions = comp.actions.all().prefetch_related("action_type")
"""
# Reuse the compensation report template since compensations are structurally identical
template = "compensation/report/compensation/report.html"
comp = get_object_or_404(Compensation, id=id)
tab_title = _("Report {}").format(comp.identifier)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not comp.is_ready_for_publish():
template = "report/unavailable.html"
context = {
"obj": comp,
"qrcode": {
"img": qrcode.get_img(),
"url": qrcode.get_content(),
},
"qrcode_lanis": {
"img": qrcode_lanis.get_img(),
"url": qrcode_lanis.get_content(),
},
"is_entry_shared": False, # disables action buttons during rendering
"before_states": before_states,
"after_states": after_states,
"geom_form": geom_form,
"parcels": parcels,
"actions": actions,
"tables_scrollable": False,
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
return render(request, template, context)
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=comp
)
parcels = comp.get_underlying_parcels()
qrcode_url = request.build_absolute_uri(reverse("compensation:report", args=(id,)))
qrcode_img = generate_qr_code(qrcode_url, 10)
qrcode_lanis_url = comp.get_LANIS_link()
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
# Order states by surface
before_states = comp.before_states.all().order_by("-surface").prefetch_related("biotope_type")
after_states = comp.after_states.all().order_by("-surface").prefetch_related("biotope_type")
actions = comp.actions.all().prefetch_related("action_type")
context = {
"obj": comp,
"qrcode": {
"img": qrcode_img,
"url": qrcode_url,
},
"qrcode_lanis": {
"img": qrcode_img_lanis,
"url": qrcode_lanis_url,
},
"is_entry_shared": False, # disables action buttons during rendering
"before_states": before_states,
"after_states": after_states,
"geom_form": geom_form,
"parcels": parcels,
"actions": actions,
"tables_scrollable": False,
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)

View File

@@ -1,97 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from django.contrib import messages
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render, get_object_or_404
from compensation.models import EcoAccount
from konova.contexts import BaseContext
from konova.forms import SimpleGeomForm
from konova.settings import ETS_GROUP, ZB_GROUP, DEFAULT_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import DO_NOT_FORGET_TO_SHARE
from konova.views.detail import AbstractDetailView
class DetailEcoAccountView(AbstractDetailView):
_TEMPLATE = "compensation/detail/eco_account/view.html"
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
""" Renders a detail view for a compensation
Args:
request (HttpRequest): The incoming request
id (str): The compensation's id
Returns:
"""
acc = get_object_or_404(
EcoAccount.objects.prefetch_related(
"deadlines",
).select_related(
'geometry',
'responsible',
),
id=id,
deleted=None,
)
geom_form = SimpleGeomForm(instance=acc)
parcels = acc.get_underlying_parcels()
_user = request.user
is_data_shared = acc.is_shared_with(_user)
# Order states according to surface
before_states = acc.before_states.order_by("-surface")
after_states = acc.after_states.order_by("-surface")
# Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = acc.get_surface_before_states()
sum_after_states = acc.get_surface_after_states()
diff_states = abs(sum_before_states - sum_after_states)
# Calculate rest of available surface for deductions
available_total = acc.deductable_rest
available_relative = acc.get_deductable_rest_relative()
# Prefetch related data to decrease the amount of db connections
deductions = acc.deductions.filter(
intervention__deleted=None,
)
actions = acc.actions.all()
request = acc.set_status_messages(request)
requesting_user_is_only_shared_user = acc.is_only_shared_with(_user)
if requesting_user_is_only_shared_user:
messages.info(
request,
DO_NOT_FORGET_TO_SHARE
)
context = {
"obj": acc,
"geom_form": geom_form,
"parcels": parcels,
"is_entry_shared": is_data_shared,
"before_states": before_states,
"after_states": after_states,
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"available": available_relative,
"available_total": available_total,
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": acc.get_LANIS_link(),
"deductions": deductions,
"actions": actions,
TAB_TITLE_IDENTIFIER: f"{acc.identifier} - {acc.title}",
"has_finished_deadlines": acc.get_finished_deadlines().exists(),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)

View File

@@ -6,94 +6,56 @@ Created on: 19.08.22
"""
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse
from django.http import HttpRequest, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.decorators import method_decorator
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views import View
from compensation.forms.eco_account import EditEcoAccountForm, NewEcoAccountForm
from compensation.forms.eco_account import EditEcoAccountForm, NewEcoAccountForm, RemoveEcoAccountModalForm
from compensation.models import EcoAccount
from compensation.tables.eco_account import EcoAccountTable
from konova.contexts import BaseContext
from konova.decorators import shared_access_required, default_group_required
from konova.decorators import shared_access_required, default_group_required, any_group_check, login_required_modal, \
uuid_required
from konova.forms import SimpleGeomForm
from konova.settings import ETS_GROUP, DEFAULT_GROUP, ZB_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, FORM_INVALID, \
IDENTIFIER_REPLACED, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
from konova.views.identifier import AbstractIdentifierGeneratorView
from konova.views.index import AbstractIndexView
from konova.utils.message_templates import CANCEL_ACC_RECORDED_OR_DEDUCTED, RECORDED_BLOCKS_EDIT, FORM_INVALID, \
IDENTIFIER_REPLACED, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView
class IndexEcoAccountView(AbstractIndexView):
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""
Renders the index view for eco accounts
class EcoAccountIndexView(LoginRequiredMixin, BaseIndexView):
_INDEX_TABLE_CLS = EcoAccountTable
_TAB_TITLE = _("Eco-account - Overview")
Args:
request (HttpRequest): The incoming request
Returns:
A rendered view
"""
eco_accounts = EcoAccount.objects.filter(
def _get_queryset(self):
qs = EcoAccount.objects.filter(
deleted=None,
).order_by(
"-modified__timestamp"
)
table = EcoAccountTable(
request=request,
queryset=eco_accounts
)
context = {
"table": table,
TAB_TITLE_IDENTIFIER: _("Eco-account - Overview"),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
return qs
class NewEcoAccountView(LoginRequiredMixin, View):
_TEMPLATE = "compensation/form/view.html"
@login_required
@default_group_required
def new_view(request: HttpRequest):
"""
Renders a view for a new eco account creation
@method_decorator(default_group_required)
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""
Renders a view for a new eco account creation
Args:
request (HttpRequest): The incoming request
Args:
request (HttpRequest): The incoming request
Returns:
Returns:
"""
data_form = NewEcoAccountForm(request.POST or None)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New Eco-Account"),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@method_decorator(default_group_required)
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""
Renders a view for a new eco account creation
Args:
request (HttpRequest): The incoming request
Returns:
"""
data_form = NewEcoAccountForm(request.POST or None)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
"""
template = "compensation/form/view.html"
data_form = NewEcoAccountForm(request.POST or None)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
generated_identifier = data_form.cleaned_data.get("identifier", None)
acc = data_form.save(request.user, geom_form)
@@ -111,92 +73,61 @@ class NewEcoAccountView(LoginRequiredMixin, View):
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("compensation:acc:detail", id=acc.id)
else:
messages.error(request, FORM_INVALID, extra_tags="danger", )
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New Eco-Account"),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
class EcoAccountIdentifierGeneratorView(AbstractIdentifierGeneratorView):
_MODEL = EcoAccount
messages.error(request, FORM_INVALID, extra_tags="danger",)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New Eco-Account"),
}
context = BaseContext(request, context).context
return render(request, template, context)
class EditEcoAccountView(LoginRequiredMixin, View):
_TEMPLATE = "compensation/form/view.html"
class EcoAccountIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
_MODEL_CLS = EcoAccount
_REDIRECT_URL_NAME = "compensation:acc:index"
@method_decorator(default_group_required)
@method_decorator(shared_access_required(EcoAccount, "id"))
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
"""
Renders a view for editing compensations
@login_required
@default_group_required
@shared_access_required(EcoAccount, "id")
def edit_view(request: HttpRequest, id: str):
"""
Renders a view for editing compensations
Args:
request (HttpRequest): The incoming request
Args:
request (HttpRequest): The incoming request
Returns:
Returns:
"""
# Get object from db
acc = get_object_or_404(EcoAccount, id=id)
if acc.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("compensation:acc:detail", id=id)
# Create forms, initialize with values from db/from POST request
data_form = EditEcoAccountForm(request.POST or None, instance=acc)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=acc)
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(acc.identifier),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(EcoAccount, "id"))
def post(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
"""
Renders a view for editing compensations
Args:
request (HttpRequest): The incoming request
Returns:
"""
# Get object from db
acc = get_object_or_404(EcoAccount, id=id)
if acc.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("compensation:acc:detail", id=id)
# Create forms, initialize with values from db/from POST request
data_form = EditEcoAccountForm(request.POST or None, instance=acc)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=acc)
"""
template = "compensation/form/view.html"
# Get object from db
acc = get_object_or_404(EcoAccount, id=id)
if acc.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("compensation:acc:detail", id=id)
# Create forms, initialize with values from db/from POST request
data_form = EditEcoAccountForm(request.POST or None, instance=acc)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=acc)
if request.method == "POST":
data_form_valid = data_form.is_valid()
geom_form_valid = geom_form.is_valid()
if data_form_valid and geom_form_valid:
@@ -208,21 +139,139 @@ class EditEcoAccountView(LoginRequiredMixin, View):
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("compensation:acc:detail", id=acc.id)
else:
messages.error(request, FORM_INVALID, extra_tags="danger", )
messages.error(request, FORM_INVALID, extra_tags="danger",)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(acc.identifier),
}
context = BaseContext(request, context).context
return render(request, template, context)
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(acc.identifier),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@login_required
@any_group_check
@uuid_required
def detail_view(request: HttpRequest, id: str):
""" Renders a detail view for a compensation
Args:
request (HttpRequest): The incoming request
id (str): The compensation's id
Returns:
"""
template = "compensation/detail/eco_account/view.html"
acc = get_object_or_404(
EcoAccount.objects.prefetch_related(
"deadlines",
).select_related(
'geometry',
'responsible',
),
id=id,
deleted=None,
)
geom_form = SimpleGeomForm(instance=acc)
parcels = acc.get_underlying_parcels()
_user = request.user
is_data_shared = acc.is_shared_with(_user)
# Order states according to surface
before_states = acc.before_states.order_by("-surface")
after_states = acc.after_states.order_by("-surface")
# Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = acc.get_surface_before_states()
sum_after_states = acc.get_surface_after_states()
diff_states = abs(sum_before_states - sum_after_states)
# Calculate rest of available surface for deductions
available_total = acc.deductable_rest
available_relative = acc.get_deductable_rest_relative()
# Prefetch related data to decrease the amount of db connections
deductions = acc.deductions.filter(
intervention__deleted=None,
)
actions = acc.actions.all()
request = acc.set_status_messages(request)
requesting_user_is_only_shared_user = acc.is_only_shared_with(_user)
if requesting_user_is_only_shared_user:
messages.info(
request,
DO_NOT_FORGET_TO_SHARE
)
context = {
"obj": acc,
"geom_form": geom_form,
"parcels": parcels,
"is_entry_shared": is_data_shared,
"before_states": before_states,
"after_states": after_states,
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"available": available_relative,
"available_total": available_total,
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": acc.get_LANIS_link(),
"deductions": deductions,
"actions": actions,
TAB_TITLE_IDENTIFIER: f"{acc.identifier} - {acc.title}",
"has_finished_deadlines": acc.get_finished_deadlines().exists(),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required_modal
@login_required
@default_group_required
@shared_access_required(EcoAccount, "id")
def remove_view(request: HttpRequest, id: str):
""" Renders a modal view for removing the eco account
Args:
request (HttpRequest): The incoming request
id (str): The account's id
Returns:
"""
acc = get_object_or_404(EcoAccount, id=id)
# If the eco account has already been recorded OR there are already deductions, it can not be deleted by a regular
# default group user
if acc.recorded is not None or acc.deductions.exists():
user = request.user
if not user.in_group(ETS_GROUP):
messages.info(request, CANCEL_ACC_RECORDED_OR_DEDUCTED)
return redirect("compensation:acc:detail", id=id)
form = RemoveEcoAccountModalForm(request.POST or None, instance=acc, request=request)
return form.process_request(
request=request,
msg_success=_("Eco-account removed"),
redirect_url=reverse("compensation:acc:index"),
)

View File

@@ -1,22 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from django.http import HttpRequest, HttpResponse
from django.utils.decorators import method_decorator
from compensation.forms.eco_account import RemoveEcoAccountModalForm
from compensation.models import EcoAccount
from konova.decorators import shared_access_required
from konova.views.remove import AbstractRemoveView
class RemoveEcoAccountView(AbstractRemoveView):
_MODEL = EcoAccount
_REDIRECT_URL = "compensation:acc:index"
_FORM = RemoveEcoAccountModalForm
@method_decorator(shared_access_required(EcoAccount, "id"))
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
return super().get(request, *args, **kwargs)

View File

@@ -5,88 +5,85 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22
"""
from django.http import HttpRequest, HttpResponse
from django.http import HttpRequest
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from compensation.models import EcoAccount
from konova.contexts import BaseContext
from konova.decorators import uuid_required
from konova.forms import SimpleGeomForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.qrcode import QrCode
from konova.views.report import AbstractPublicReportView
from konova.utils.generators import generate_qr_code
class EcoAccountPublicReportView(AbstractPublicReportView):
_TEMPLATE = "compensation/report/eco_account/report.html"
@uuid_required
def report_view(request: HttpRequest, id: str):
""" Renders the public report view
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
""" Renders the public report view
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
Returns:
Returns:
"""
acc = get_object_or_404(EcoAccount, id=id)
tab_title = _("Report {}").format(acc.identifier)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not acc.is_ready_for_publish():
template = "report/unavailable.html"
context = {
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=acc
)
parcels = acc.get_underlying_parcels()
qrcode = QrCode(
content=request.build_absolute_uri(reverse("compensation:acc:report", args=(id,))),
size=10
)
qrcode_lanis = QrCode(
content=acc.get_LANIS_link(),
size=7
)
# Order states by surface
before_states = acc.before_states.all().order_by("-surface").select_related("biotope_type__parent")
after_states = acc.after_states.all().order_by("-surface").select_related("biotope_type__parent")
actions = acc.actions.all().prefetch_related("action_type__parent")
# Reduce amount of db fetched data to the bare minimum we need in the template (deduction's intervention id and identifier)
deductions = acc.deductions.all() \
.distinct("intervention") \
.select_related("intervention") \
.values_list("intervention__id", "intervention__identifier", "intervention__title", named=True)
"""
# Reuse the compensation report template since EcoAccounts are structurally identical
template = "compensation/report/eco_account/report.html"
acc = get_object_or_404(EcoAccount, id=id)
tab_title = _("Report {}").format(acc.identifier)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not acc.is_ready_for_publish():
template = "report/unavailable.html"
context = {
"obj": acc,
"qrcode": {
"img": qrcode.get_img(),
"url": qrcode.get_content(),
},
"qrcode_lanis": {
"img": qrcode_lanis.get_img(),
"url": qrcode_lanis.get_content(),
},
"is_entry_shared": False, # disables action buttons during rendering
"before_states": before_states,
"after_states": after_states,
"geom_form": geom_form,
"parcels": parcels,
"actions": actions,
"deductions": deductions,
"tables_scrollable": False,
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
return render(request, template, context)
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=acc
)
parcels = acc.get_underlying_parcels()
qrcode_url = request.build_absolute_uri(reverse("compensation:acc:report", args=(id,)))
qrcode_img = generate_qr_code(qrcode_url, 10)
qrcode_lanis_url = acc.get_LANIS_link()
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
# Order states by surface
before_states = acc.before_states.all().order_by("-surface").select_related("biotope_type__parent")
after_states = acc.after_states.all().order_by("-surface").select_related("biotope_type__parent")
actions = acc.actions.all().prefetch_related("action_type__parent")
# Reduce amount of db fetched data to the bare minimum we need in the template (deduction's intervention id and identifier)
deductions = acc.deductions.all()\
.distinct("intervention")\
.select_related("intervention")\
.values_list("intervention__id", "intervention__identifier", "intervention__title", named=True)
context = {
"obj": acc,
"qrcode": {
"img": qrcode_img,
"url": qrcode_url,
},
"qrcode_lanis": {
"img": qrcode_img_lanis,
"url": qrcode_lanis_url,
},
"is_entry_shared": False, # disables action buttons during rendering
"before_states": before_states,
"after_states": after_states,
"geom_form": geom_form,
"parcels": parcels,
"actions": actions,
"deductions": deductions,
"tables_scrollable": False,
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)

View File

@@ -5,12 +5,10 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 09.08.21
"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from django.http import HttpRequest
from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator
from django.views import View
from compensation.forms.modals.payment import NewPaymentForm, RemovePaymentModalForm, EditPaymentModalForm
from compensation.models import Payment
@@ -19,97 +17,72 @@ from konova.decorators import default_group_required, shared_access_required
from konova.utils.message_templates import PAYMENT_ADDED, PAYMENT_REMOVED, PAYMENT_EDITED
class NewPaymentView(LoginRequiredMixin, View):
@login_required
@default_group_required
@shared_access_required(Intervention, "id")
def new_payment_view(request: HttpRequest, id: str):
""" Renders a modal view for adding new payments
def __process_request(self, request: HttpRequest, id: str):
""" Renders a modal view for adding new payments
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id for which a new payment shall be added
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id for which a new payment shall be added
Returns:
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
form = NewPaymentForm(request.POST or None, instance=intervention, request=request)
return form.process_request(
request,
msg_success=PAYMENT_ADDED,
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data"
)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def get(self, request: HttpRequest, id: str):
return self.__process_request(request, id=id)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def post(self, request: HttpRequest, id: str):
return self.__process_request(request, id=id)
"""
intervention = get_object_or_404(Intervention, id=id)
form = NewPaymentForm(request.POST or None, instance=intervention, request=request)
return form.process_request(
request,
msg_success=PAYMENT_ADDED,
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data"
)
class RemovePaymentView(LoginRequiredMixin, View):
@login_required
@default_group_required
@shared_access_required(Intervention, "id")
def payment_remove_view(request: HttpRequest, id: str, payment_id: str):
""" Renders a modal view for removing payments
def __process_request(self, request: HttpRequest, id: str, payment_id: str):
""" Renders a modal view for removing payments
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id
payment_id (str): The payment's id
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id
payment_id (str): The payment's id
Returns:
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
payment = get_object_or_404(Payment, id=payment_id)
form = RemovePaymentModalForm(request.POST or None, instance=intervention, payment=payment, request=request)
return form.process_request(
request=request,
msg_success=PAYMENT_REMOVED,
redirect_url=reverse("intervention:detail", args=(payment.intervention_id,)) + "#related_data"
)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def get(self, request: HttpRequest, id: str, payment_id: str):
return self.__process_request(request, id=id, payment_id=payment_id)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def post(self, request: HttpRequest, id: str, payment_id: str):
return self.__process_request(request, id=id, payment_id=payment_id)
"""
intervention = get_object_or_404(Intervention, id=id)
payment = get_object_or_404(Payment, id=payment_id)
form = RemovePaymentModalForm(request.POST or None, instance=intervention, payment=payment, request=request)
return form.process_request(
request=request,
msg_success=PAYMENT_REMOVED,
redirect_url=reverse("intervention:detail", args=(payment.intervention_id,)) + "#related_data"
)
class EditPaymentView(LoginRequiredMixin, View):
def __process_request(self, request: HttpRequest, id: str, payment_id: str):
""" Renders a modal view for editing payments
@login_required
@default_group_required
@shared_access_required(Intervention, "id")
def payment_edit_view(request: HttpRequest, id: str, payment_id: str):
""" Renders a modal view for editing payments
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id
payment_id (str): The payment's id
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id
payment_id (str): The payment's id
Returns:
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
payment = get_object_or_404(Payment, id=payment_id)
form = EditPaymentModalForm(request.POST or None, instance=intervention, payment=payment, request=request)
return form.process_request(
request=request,
msg_success=PAYMENT_EDITED,
redirect_url=reverse("intervention:detail", args=(payment.intervention_id,)) + "#related_data"
)
"""
intervention = get_object_or_404(Intervention, id=id)
payment = get_object_or_404(Payment, id=payment_id)
form = EditPaymentModalForm(request.POST or None, instance=intervention, payment=payment, request=request)
return form.process_request(
request=request,
msg_success=PAYMENT_EDITED,
redirect_url=reverse("intervention:detail", args=(payment.intervention_id,)) + "#related_data"
)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def get(self, request: HttpRequest, id: str, payment_id: str):
return self.__process_request(request, id=id, payment_id=payment_id)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def post(self, request: HttpRequest, id: str, payment_id: str):
return self.__process_request(request, id=id, payment_id=payment_id)

View File

@@ -15,10 +15,10 @@
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Resubmission' %}" data-form-url="{% url 'ema:resubmission-create' obj.id %}">
{% fa5_icon 'bell' %}
</button>
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'ema:share-form' obj.id %}">
{% fa5_icon 'share-alt' %}
</button>
{% if is_ets_member %}
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'ema:share-form' obj.id %}">
{% fa5_icon 'share-alt' %}
</button>
{% if obj.recorded %}
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Unrecord' %}" data-form-url="{% url 'ema:record' obj.id %}">
{% fa5_icon 'bookmark' 'far' %}
@@ -28,21 +28,19 @@
{% fa5_icon 'bookmark' %}
</button>
{% endif %}
<a href="{% url 'ema:edit' obj.id %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Edit' %}">
{% fa5_icon 'edit' %}
</button>
</a>
{% endif %}
{% if is_default_member %}
<button class="btn btn-default btn-modal mr-2" data-form-url="{% url 'ema:log' obj.id %}" title="{% trans 'Show log' %}">
{% fa5_icon 'history' %}
</button>
{% endif %}
{% if is_ets_member %}
<button class="btn btn-default btn-modal" data-form-url="{% url 'ema:remove' obj.id %}" title="{% trans 'Delete' %}">
{% fa5_icon 'trash' %}
<a href="{% url 'ema:edit' obj.id %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Edit' %}">
{% fa5_icon 'edit' %}
</button>
</a>
<button class="btn btn-default btn-modal mr-2" data-form-url="{% url 'ema:log' obj.id %}" title="{% trans 'Show log' %}">
{% fa5_icon 'history' %}
</button>
<button class="btn btn-default btn-modal" data-form-url="{% url 'ema:remove' obj.id %}" title="{% trans 'Delete' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}
{% endif %}
</div>

View File

@@ -118,7 +118,6 @@ class EmaViewTestCase(CompensationViewTestCase):
self.index_url,
self.detail_url,
self.report_url,
self.log_url,
]
fail_urls = [
self.new_url,
@@ -134,6 +133,7 @@ class EmaViewTestCase(CompensationViewTestCase):
self.action_remove_url,
self.action_new_url,
self.new_doc_url,
self.log_url,
self.remove_url,
]
self.assert_url_fail(client, fail_urls)

View File

@@ -9,28 +9,27 @@ from django.urls import path
from ema.views.action import NewEmaActionView, EditEmaActionView, RemoveEmaActionView
from ema.views.deadline import NewEmaDeadlineView, EditEmaDeadlineView, RemoveEmaDeadlineView
from ema.views.detail import DetailEmaView
from ema.views.document import NewEmaDocumentView, EditEmaDocumentView, RemoveEmaDocumentView, GetEmaDocumentView
from ema.views.ema import IndexEmaView, EmaIdentifierGeneratorView, EditEmaView, NewEmaView
from ema.views.ema import new_view, detail_view, edit_view, remove_view, EmaIndexView, \
EmaIdentifierGeneratorView
from ema.views.log import EmaLogView
from ema.views.record import EmaRecordView
from ema.views.remove import RemoveEmaView
from ema.views.report import EmaPublicReportView
from ema.views.report import report_view
from ema.views.resubmission import EmaResubmissionView
from ema.views.share import EmaShareFormView, EmaShareByTokenView
from ema.views.state import NewEmaStateView, EditEmaStateView, RemoveEmaStateView
app_name = "ema"
urlpatterns = [
path("", IndexEmaView.as_view(), name="index"),
path("new/", NewEmaView.as_view(), name="new"),
path("", EmaIndexView.as_view(), name="index"),
path("new/", new_view, name="new"),
path("new/id", EmaIdentifierGeneratorView.as_view(), name="new-id"),
path("<id>", DetailEmaView.as_view(), name="detail"),
path("<id>", detail_view, name="detail"),
path('<id>/log', EmaLogView.as_view(), name='log'),
path('<id>/edit', EditEmaView.as_view(), name='edit'),
path('<id>/remove', RemoveEmaView.as_view(), name='remove'),
path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'),
path('<id>/record', EmaRecordView.as_view(), name='record'),
path('<id>/report', EmaPublicReportView.as_view(), name='report'),
path('<id>/report', report_view, name='report'),
path('<id>/resub', EmaResubmissionView.as_view(), name='resubmission-create'),
path('<id>/state/new', NewEmaStateView.as_view(), name='new-state'),

View File

@@ -1,76 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from django.contrib import messages
from django.http import HttpResponse, HttpRequest
from django.shortcuts import get_object_or_404, render
from ema.models import Ema
from konova.contexts import BaseContext
from konova.forms import SimpleGeomForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import DO_NOT_FORGET_TO_SHARE
from konova.views.detail import AbstractDetailView
class DetailEmaView(AbstractDetailView):
_TEMPLATE = "ema/detail/view.html"
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
""" Renders the detail view of an EMA
Args:
request (HttpRequest): The incoming request
id (str): The EMA id
Returns:
"""
ema = get_object_or_404(Ema, id=id, deleted=None)
geom_form = SimpleGeomForm(instance=ema)
parcels = ema.get_underlying_parcels()
_user = request.user
is_entry_shared = ema.is_shared_with(_user)
# Order states according to surface
before_states = ema.before_states.all().order_by("-surface")
after_states = ema.after_states.all().order_by("-surface")
# Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = ema.get_surface_before_states()
sum_after_states = ema.get_surface_after_states()
diff_states = abs(sum_before_states - sum_after_states)
ema.set_status_messages(request)
requesting_user_is_only_shared_user = ema.is_only_shared_with(_user)
if requesting_user_is_only_shared_user:
messages.info(
request,
DO_NOT_FORGET_TO_SHARE
)
context = {
"obj": ema,
"geom_form": geom_form,
"parcels": parcels,
"is_entry_shared": is_entry_shared,
"before_states": before_states,
"after_states": after_states,
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": ema.get_LANIS_link(),
TAB_TITLE_IDENTIFIER: f"{ema.identifier} - {ema.title}",
"has_finished_deadlines": ema.get_finished_deadlines().exists(),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)

View File

@@ -8,95 +8,55 @@ Created on: 19.08.22
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse
from django.http import HttpRequest, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.decorators import method_decorator
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.generic.base import View
from ema.forms import NewEmaForm, EditEmaForm
from ema.models import Ema
from ema.tables import EmaTable
from konova.contexts import BaseContext
from konova.decorators import shared_access_required, conservation_office_group_required
from konova.decorators import shared_access_required, conservation_office_group_required, login_required_modal, \
uuid_required
from konova.forms import SimpleGeomForm
from konova.forms.modals import RemoveModalForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, IDENTIFIER_REPLACED, FORM_INVALID, \
GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
from konova.views.identifier import AbstractIdentifierGeneratorView
from konova.views.index import AbstractIndexView
DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE, MISSING_GROUP_PERMISSION
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView
class IndexEmaView(AbstractIndexView):
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
""" Renders the index view for EMAs
class EmaIndexView(LoginRequiredMixin, BaseIndexView):
_TAB_TITLE = _("EMAs - Overview")
_INDEX_TABLE_CLS = EmaTable
Args:
request (HttpRequest): The incoming request
Returns:
"""
emas = Ema.objects.filter(
def _get_queryset(self):
qs = Ema.objects.filter(
deleted=None,
).order_by(
"-modified__timestamp"
)
return qs
table = EmaTable(
request,
queryset=emas
)
context = {
"table": table,
TAB_TITLE_IDENTIFIER: _("EMAs - Overview"),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
class NewEmaView(LoginRequiredMixin, View):
_TEMPLATE = "ema/form/view.html"
@login_required
@conservation_office_group_required
def new_view(request: HttpRequest):
"""
Renders a view for a new eco account creation
@method_decorator(conservation_office_group_required)
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
""" GET endpoint
Args:
request (HttpRequest): The incoming request
Renders form for new EMA
Returns:
Args:
request (HttpRequest): The incoming request
*args ():
**kwargs ():
Returns:
"""
data_form = NewEmaForm(request.POST or None)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New EMA"),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@method_decorator(conservation_office_group_required)
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
""" POST endpoint
Processes submitted form
Args:
request (HttpRequest): The incoming request
*args ():
**kwargs ():
Returns:
"""
data_form = NewEmaForm(request.POST or None)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
"""
template = "ema/form/view.html"
data_form = NewEmaForm(request.POST or None)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
generated_identifier = data_form.cleaned_data.get("identifier", None)
ema = data_form.save(request.user, geom_form)
@@ -120,91 +80,117 @@ class NewEmaView(LoginRequiredMixin, View):
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("ema:detail", id=ema.id)
else:
messages.error(request, FORM_INVALID, extra_tags="danger",)
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New EMA"),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New EMA"),
}
context = BaseContext(request, context).context
return render(request, template, context)
class EmaIdentifierGeneratorView(AbstractIdentifierGeneratorView):
_MODEL = Ema
@method_decorator(conservation_office_group_required)
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
return super().get(request, *args, **kwargs)
class EmaIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
_MODEL_CLS = Ema
_REDIRECT_URL_NAME = "ema:index"
class EditEmaView(LoginRequiredMixin, View):
_TEMPLATE = "compensation/form/view.html"
def _user_has_permission(self, user):
return user.is_ets_user()
@method_decorator(conservation_office_group_required)
@method_decorator(shared_access_required(Ema, "id"))
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
""" GET endpoint
Renders form
@login_required
@uuid_required
def detail_view(request: HttpRequest, id: str):
""" Renders the detail view of an EMA
Args:
request (HttpRequest): The incoming request
id (str): The ema identifier
*args ():
**kwargs ():
Args:
request (HttpRequest): The incoming request
id (str): The EMA id
Returns:
Returns:
"""
# Get object from db
ema = get_object_or_404(Ema, id=id)
if ema.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("ema:detail", id=id)
"""
template = "ema/detail/view.html"
ema = get_object_or_404(Ema, id=id, deleted=None)
# Create forms, initialize with values from db/from POST request
data_form = EditEmaForm(instance=ema)
geom_form = SimpleGeomForm(read_only=False, instance=ema)
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(ema.identifier),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
geom_form = SimpleGeomForm(instance=ema)
parcels = ema.get_underlying_parcels()
_user = request.user
is_entry_shared = ema.is_shared_with(_user)
@method_decorator(conservation_office_group_required)
@method_decorator(shared_access_required(Ema, "id"))
def post(self, request: HttpRequest, id:str, *args, **kwargs) -> HttpResponse:
""" POST endpoint
# Order states according to surface
before_states = ema.before_states.all().order_by("-surface")
after_states = ema.after_states.all().order_by("-surface")
Process submitted forms
# Precalculate logical errors between before- and after-states
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
sum_before_states = ema.get_surface_before_states()
sum_after_states = ema.get_surface_after_states()
diff_states = abs(sum_before_states - sum_after_states)
Args:
request (HttpRequest): The incoming request
id (str): The id of the ema
*args ():
**kwargs ():
ema.set_status_messages(request)
Returns:
requesting_user_is_only_shared_user = ema.is_only_shared_with(_user)
if requesting_user_is_only_shared_user:
messages.info(
request,
DO_NOT_FORGET_TO_SHARE
)
"""
# Get object from db
ema = get_object_or_404(Ema, id=id)
if ema.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("ema:detail", id=id)
context = {
"obj": ema,
"geom_form": geom_form,
"parcels": parcels,
"is_entry_shared": is_entry_shared,
"before_states": before_states,
"after_states": after_states,
"sum_before_states": sum_before_states,
"sum_after_states": sum_after_states,
"diff_states": diff_states,
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": ema.get_LANIS_link(),
TAB_TITLE_IDENTIFIER: f"{ema.identifier} - {ema.title}",
"has_finished_deadlines": ema.get_finished_deadlines().exists(),
}
context = BaseContext(request, context).context
return render(request, template, context)
# Create forms, initialize with values from db/from POST request
data_form = EditEmaForm(request.POST or None, instance=ema)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=ema)
@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def edit_view(request: HttpRequest, id: str):
"""
Renders a view for editing compensations
Args:
request (HttpRequest): The incoming request
Returns:
"""
template = "compensation/form/view.html"
# Get object from db
ema = get_object_or_404(Ema, id=id)
if ema.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("ema:detail", id=id)
# Create forms, initialize with values from db/from POST request
data_form = EditEmaForm(request.POST or None, instance=ema)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=ema)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
# The data form takes the geom form for processing, as well as the performing user
ema = data_form.save(request.user, geom_form)
@@ -214,19 +200,48 @@ class EditEmaView(LoginRequiredMixin, View):
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("ema:detail", id=ema.id)
else:
messages.error(request, FORM_INVALID, extra_tags="danger", )
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(ema.identifier),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
messages.error(request, FORM_INVALID, extra_tags="danger",)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(ema.identifier),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required_modal
@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def remove_view(request: HttpRequest, id: str):
""" Renders a modal view for removing the EMA
Args:
request (HttpRequest): The incoming request
id (str): The EMA's id
Returns:
"""
ema = get_object_or_404(Ema, id=id)
form = RemoveModalForm(request.POST or None, instance=ema, request=request)
return form.process_request(
request=request,
msg_success=_("EMA removed"),
redirect_url=reverse("ema:index"),
)

View File

@@ -18,6 +18,7 @@ class EmaLogView(AbstractLogView):
@method_decorator(login_required_modal)
@method_decorator(login_required)
@method_decorator(conservation_office_group_required)
@method_decorator(shared_access_required(Ema, "id"))
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)

View File

@@ -1,21 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from django.http import HttpRequest, HttpResponse
from django.utils.decorators import method_decorator
from ema.models import Ema
from konova.decorators import shared_access_required, conservation_office_group_required
from konova.views.remove import AbstractRemoveView
class RemoveEmaView(AbstractRemoveView):
_MODEL = Ema
_REDIRECT_URL = "ema:index"
@method_decorator(conservation_office_group_required)
@method_decorator(shared_access_required(Ema, "id"))
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
return super().get(request, *args, **kwargs)

View File

@@ -5,81 +5,77 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22
"""
from django.http import HttpRequest, HttpResponse
from django.http import HttpRequest
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from ema.models import Ema
from konova.contexts import BaseContext
from konova.decorators import uuid_required
from konova.forms import SimpleGeomForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.qrcode import QrCode
from konova.views.report import AbstractPublicReportView
from konova.utils.generators import generate_qr_code
@uuid_required
def report_view(request:HttpRequest, id: str):
""" Renders the public report view
class EmaPublicReportView(AbstractPublicReportView):
_TEMPLATE = "ema/report/report.html"
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
""" Renders the public report view
Returns:
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
Returns:
"""
ema = get_object_or_404(Ema, id=id)
tab_title = _("Report {}").format(ema.identifier)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not ema.is_ready_for_publish():
template = "report/unavailable.html"
context = {
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=ema,
)
parcels = ema.get_underlying_parcels()
qrcode = QrCode(
content=request.build_absolute_uri(reverse("ema:report", args=(id,))),
size=10
)
qrcode_lanis = QrCode(
content=ema.get_LANIS_link(),
size=7
)
# Order states by surface
before_states = ema.before_states.all().order_by("-surface").prefetch_related("biotope_type")
after_states = ema.after_states.all().order_by("-surface").prefetch_related("biotope_type")
actions = ema.actions.all().prefetch_related("action_type")
"""
# Reuse the compensation report template since EMAs are structurally identical
template = "ema/report/report.html"
ema = get_object_or_404(Ema, id=id)
tab_title = _("Report {}").format(ema.identifier)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not ema.is_ready_for_publish():
template = "report/unavailable.html"
context = {
"obj": ema,
"qrcode": {
"img": qrcode.get_img(),
"url": qrcode.get_content(),
},
"qrcode_lanis": {
"img": qrcode_lanis.get_img(),
"url": qrcode_lanis.get_content(),
},
"is_entry_shared": False, # disables action buttons during rendering
"before_states": before_states,
"after_states": after_states,
"geom_form": geom_form,
"parcels": parcels,
"actions": actions,
"tables_scrollable": False,
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
return render(request, template, context)
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=ema,
)
parcels = ema.get_underlying_parcels()
qrcode_url = request.build_absolute_uri(reverse("ema:report", args=(id,)))
qrcode_img = generate_qr_code(qrcode_url, 10)
qrcode_lanis_url = ema.get_LANIS_link()
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
# Order states by surface
before_states = ema.before_states.all().order_by("-surface").prefetch_related("biotope_type")
after_states = ema.after_states.all().order_by("-surface").prefetch_related("biotope_type")
actions = ema.actions.all().prefetch_related("action_type")
context = {
"obj": ema,
"qrcode": {
"img": qrcode_img,
"url": qrcode_url
},
"qrcode_lanis": {
"img": qrcode_img_lanis,
"url": qrcode_lanis_url
},
"is_entry_shared": False, # disables action buttons during rendering
"before_states": before_states,
"after_states": after_states,
"geom_form": geom_form,
"parcels": parcels,
"actions": actions,
"tables_scrollable": False,
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)

View File

@@ -37,14 +37,6 @@ class InterventionAdmin(BaseObjectAdmin):
"geometry",
]
def get_actions(self, request):
DELETE_ACTION_IDENTIFIER = "delete_selected"
actions = super().get_actions(request)
if DELETE_ACTION_IDENTIFIER in actions:
del actions[DELETE_ACTION_IDENTIFIER]
return actions
class InterventionDocumentAdmin(AbstractDocumentAdmin):
pass

View File

@@ -8,42 +8,40 @@ Created on: 30.11.20
from django.urls import path
from intervention.autocomplete.intervention import InterventionAutocomplete
from intervention.views.check import InterventionCheckView
from intervention.views.compensation import RemoveCompensationFromInterventionView
from intervention.views.check import check_view
from intervention.views.compensation import remove_compensation_view
from intervention.views.deduction import NewInterventionDeductionView, EditInterventionDeductionView, \
RemoveInterventionDeductionView
from intervention.views.document import NewInterventionDocumentView, GetInterventionDocumentView, \
RemoveInterventionDocumentView, EditInterventionDocumentView
from intervention.views.intervention import IndexInterventionView, InterventionIdentifierGeneratorView, \
NewInterventionView, EditInterventionView
from intervention.views.remove import RemoveInterventionView
from intervention.views.detail import DetailInterventionView
from intervention.views.intervention import new_view, detail_view, edit_view, remove_view, \
InterventionIndexView, InterventionIdentifierGeneratorView
from intervention.views.log import InterventionLogView
from intervention.views.record import InterventionRecordView
from intervention.views.report import InterventionPublicReportView
from intervention.views.report import report_view
from intervention.views.resubmission import InterventionResubmissionView
from intervention.views.revocation import NewInterventionRevocationView, GetInterventionRevocationView, \
EditInterventionRevocationView, RemoveInterventionRevocationView
from intervention.views.revocation import new_revocation_view, edit_revocation_view, remove_revocation_view, \
get_revocation_view
from intervention.views.share import InterventionShareFormView, InterventionShareByTokenView
app_name = "intervention"
urlpatterns = [
path("", IndexInterventionView.as_view(), name="index"),
path('new/', NewInterventionView.as_view(), name='new'),
path("", InterventionIndexView.as_view(), name="index"),
path('new/', new_view, name='new'),
path('new/id', InterventionIdentifierGeneratorView.as_view(), name='new-id'),
path('<id>', DetailInterventionView.as_view(), name='detail'),
path('<id>', detail_view, name='detail'),
path('<id>/log', InterventionLogView.as_view(), name='log'),
path('<id>/edit', EditInterventionView.as_view(), name='edit'),
path('<id>/remove', RemoveInterventionView.as_view(), name='remove'),
path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'),
path('<id>/share/<token>', InterventionShareByTokenView.as_view(), name='share-token'),
path('<id>/share', InterventionShareFormView.as_view(), name='share-form'),
path('<id>/check', InterventionCheckView.as_view(), name='check'),
path('<id>/check', check_view, name='check'),
path('<id>/record', InterventionRecordView.as_view(), name='record'),
path('<id>/report', InterventionPublicReportView.as_view(), name='report'),
path('<id>/report', report_view, name='report'),
path('<id>/resub', InterventionResubmissionView.as_view(), name='resubmission-create'),
# Compensations
path('<id>/compensation/<comp_id>/remove', RemoveCompensationFromInterventionView.as_view(), name='remove-compensation'),
path('<id>/compensation/<comp_id>/remove', remove_compensation_view, name='remove-compensation'),
# Documents
path('<id>/document/new/', NewInterventionDocumentView.as_view(), name='new-doc'),
@@ -57,10 +55,10 @@ urlpatterns = [
path('<id>/deduction/<deduction_id>/remove', RemoveInterventionDeductionView.as_view(), name='remove-deduction'),
# Revocation routes
path('<id>/revocation/new', NewInterventionRevocationView.as_view(), name='new-revocation'),
path('<id>/revocation/<revocation_id>/edit', EditInterventionRevocationView.as_view(), name='edit-revocation'),
path('<id>/revocation/<revocation_id>/remove', RemoveInterventionRevocationView.as_view(), name='remove-revocation'),
path('revocation/<doc_id>', GetInterventionRevocationView.as_view(), name='get-doc-revocation'),
path('<id>/revocation/new', new_revocation_view, name='new-revocation'),
path('<id>/revocation/<revocation_id>/edit', edit_revocation_view, name='edit-revocation'),
path('<id>/revocation/<revocation_id>/remove', remove_revocation_view, name='remove-revocation'),
path('revocation/<doc_id>', get_revocation_view, name='get-doc-revocation'),
# Autocomplete
path("atcmplt/interventions", InterventionAutocomplete.as_view(), name="autocomplete"),

View File

@@ -5,44 +5,35 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22
"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse
from django.contrib.auth.decorators import login_required
from django.http import HttpRequest
from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.views import View
from intervention.forms.modals.check import CheckModalForm
from intervention.models import Intervention
from konova.decorators import registration_office_group_required, shared_access_required
from konova.utils.message_templates import INTERVENTION_INVALID
class InterventionCheckView(LoginRequiredMixin, View):
def __process_request(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
""" Renders check form for an intervention
@login_required
@registration_office_group_required
@shared_access_required(Intervention, "id")
def check_view(request: HttpRequest, id: str):
""" Renders check form for an intervention
Args:
request (HttpRequest): The incoming request
id (str): Intervention's id
Args:
request (HttpRequest): The incoming request
id (str): Intervention's id
Returns:
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
form = CheckModalForm(request.POST or None, instance=intervention, request=request)
return form.process_request(
request,
msg_success=_("Check performed"),
msg_error=INTERVENTION_INVALID
)
"""
intervention = get_object_or_404(Intervention, id=id)
form = CheckModalForm(request.POST or None, instance=intervention, request=request)
return form.process_request(
request,
msg_success=_("Check performed"),
msg_error=INTERVENTION_INVALID
)
@method_decorator(registration_office_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
return self.__process_request(request, id, *args, **kwargs)
@method_decorator(registration_office_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def post(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
return self.__process_request(request, id, *args, **kwargs)

View File

@@ -5,50 +5,42 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22
"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpRequest, Http404, HttpResponse
from django.http import HttpRequest, Http404
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views import View
from intervention.models import Intervention
from konova.decorators import shared_access_required
from konova.decorators import shared_access_required, login_required_modal
from konova.forms.modals import RemoveModalForm
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE
class RemoveCompensationFromInterventionView(LoginRequiredMixin, View):
@login_required_modal
@login_required
@shared_access_required(Intervention, "id")
def remove_compensation_view(request: HttpRequest, id: str, comp_id: str):
""" Renders a modal view for removing the compensation
def __process_request(self, request: HttpRequest, id: str, comp_id: str, *args, **kwargs) -> HttpResponse:
""" Renders a modal view for removing the compensation
Args:
request (HttpRequest): The incoming request
id (str): The compensation's id
Args:
request (HttpRequest): The incoming request
id (str): The compensation's id
Returns:
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
try:
comp = intervention.compensations.get(
id=comp_id
)
except ObjectDoesNotExist:
raise Http404("Unknown compensation")
form = RemoveModalForm(request.POST or None, instance=comp, request=request)
return form.process_request(
request=request,
msg_success=COMPENSATION_REMOVED_TEMPLATE.format(comp.identifier),
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data",
"""
intervention = get_object_or_404(Intervention, id=id)
try:
comp = intervention.compensations.get(
id=comp_id
)
except ObjectDoesNotExist:
raise Http404("Unknown compensation")
form = RemoveModalForm(request.POST or None, instance=comp, request=request)
return form.process_request(
request=request,
msg_success=COMPENSATION_REMOVED_TEMPLATE.format(comp.identifier),
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data",
)
@method_decorator(shared_access_required(Intervention, "id"))
def get(self, request, id: str, comp_id: str, *args, **kwargs) -> HttpResponse:
return self.__process_request(request, id, comp_id, *args, **kwargs)
@method_decorator(shared_access_required(Intervention, "id"))
def post(self, request, id: str, comp_id: str, *args, **kwargs) -> HttpResponse:
return self.__process_request(request, id, comp_id, *args, **kwargs)

View File

@@ -1,79 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from django.contrib import messages
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render
from intervention.models import Intervention
from konova.contexts import BaseContext
from konova.forms import SimpleGeomForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE, DO_NOT_FORGET_TO_SHARE
from konova.views.detail import AbstractDetailView
class DetailInterventionView(AbstractDetailView):
_TEMPLATE = "intervention/detail/view.html"
def get(self, request, id: str, *args, **kwargs) -> HttpResponse:
# Fetch data, filter out deleted related data
intervention = get_object_or_404(
Intervention.objects.select_related(
"geometry",
"legal",
"responsible",
).prefetch_related(
"legal__revocations",
),
id=id,
deleted=None
)
compensations = intervention.compensations.filter(
deleted=None,
)
_user = request.user
is_data_shared = intervention.is_shared_with(user=_user)
geom_form = SimpleGeomForm(
instance=intervention,
)
last_checked = intervention.get_last_checked_action()
last_checked_tooltip = ""
if last_checked:
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(
last_checked.get_timestamp_str_formatted(),
last_checked.user
)
has_payment_without_document = intervention.payments.exists() and not intervention.get_documents()[1].exists()
requesting_user_is_only_shared_user = intervention.is_only_shared_with(_user)
if requesting_user_is_only_shared_user:
messages.info(
request,
DO_NOT_FORGET_TO_SHARE
)
context = {
"obj": intervention,
"last_checked": last_checked,
"last_checked_tooltip": last_checked_tooltip,
"compensations": compensations,
"is_entry_shared": is_data_shared,
"geom_form": geom_form,
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": intervention.get_LANIS_link(),
"has_payment_without_document": has_payment_without_document,
TAB_TITLE_IDENTIFIER: f"{intervention.identifier} - {intervention.title}",
}
request = intervention.set_status_messages(request)
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)

View File

@@ -8,97 +8,58 @@ Created on: 19.08.22
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse
from django.http import JsonResponse, HttpRequest
from django.shortcuts import get_object_or_404, render, redirect
from django.utils.decorators import method_decorator
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views import View
from intervention.forms.intervention import EditInterventionForm, NewInterventionForm
from intervention.models import Intervention
from intervention.tables import InterventionTable
from konova.contexts import BaseContext
from konova.decorators import default_group_required, shared_access_required
from konova.decorators import default_group_required, shared_access_required, any_group_check, login_required_modal, \
uuid_required
from konova.forms import SimpleGeomForm
from konova.forms.modals import RemoveModalForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, \
CHECK_STATE_RESET, FORM_INVALID, IDENTIFIER_REPLACED, GEOMETRY_SIMPLIFIED, \
from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE, RECORDED_BLOCKS_EDIT, \
CHECK_STATE_RESET, FORM_INVALID, IDENTIFIER_REPLACED, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED, \
GEOMETRIES_IGNORED_TEMPLATE
from konova.views.identifier import AbstractIdentifierGeneratorView
from konova.views.index import AbstractIndexView
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView
class IndexInterventionView(AbstractIndexView):
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""
Renders the index view for Interventions
class InterventionIndexView(LoginRequiredMixin, BaseIndexView):
_INDEX_TABLE_CLS = InterventionTable
_TAB_TITLE = _("Interventions - Overview")
Args:
request (HttpRequest): The incoming request
Returns:
A rendered view
"""
# Filtering by user access is performed in table filter inside InterventionTableFilter class
interventions = Intervention.objects.filter(
deleted=None, # not deleted
def _get_queryset(self):
qs = Intervention.objects.filter(
deleted=None,
).select_related(
"legal"
).order_by(
"-modified__timestamp"
)
table = InterventionTable(
request=request,
queryset=interventions
)
context = {
"table": table,
TAB_TITLE_IDENTIFIER: _("Interventions - Overview"),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
return qs
class NewInterventionView(LoginRequiredMixin, View):
_TEMPLATE = "intervention/form/view.html"
@login_required
@default_group_required
def new_view(request: HttpRequest):
"""
Renders a view for a new intervention creation
@method_decorator(default_group_required)
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
Args:
request (HttpRequest): The incoming request
"""
Renders a view for a new intervention creation
Args:
request (HttpRequest): The incoming request
Returns:
"""
data_form = NewInterventionForm()
geom_form = SimpleGeomForm(read_only=False)
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New intervention"),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@method_decorator(default_group_required)
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""
Renders a view for a new intervention creation
Args:
request (HttpRequest): The incoming request
Returns:
"""
data_form = NewInterventionForm(request.POST or None)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
Returns:
"""
template = "intervention/form/view.html"
data_form = NewInterventionForm(request.POST or None)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
generated_identifier = data_form.cleaned_data.get("identifier", None)
intervention = data_form.save(request.user, geom_form)
@@ -110,7 +71,6 @@ class NewInterventionView(LoginRequiredMixin, View):
intervention.identifier
)
)
messages.success(request, _("Intervention {} added").format(intervention.identifier))
if geom_form.has_geometry_simplified():
messages.info(
@@ -124,86 +84,128 @@ class NewInterventionView(LoginRequiredMixin, View):
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("intervention:detail", id=intervention.id)
else:
messages.error(request, FORM_INVALID, extra_tags="danger", )
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New intervention"),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
messages.error(request, FORM_INVALID, extra_tags="danger",)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New intervention"),
}
context = BaseContext(request, context).context
return render(request, template, context)
class InterventionIdentifierGeneratorView(AbstractIdentifierGeneratorView):
_MODEL = Intervention
class InterventionIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
_MODEL_CLS = Intervention
_REDIRECT_URL_NAME = "intervention:index"
class EditInterventionView(LoginRequiredMixin, View):
_TEMPLATE = "intervention/form/view.html"
@login_required
@any_group_check
@uuid_required
def detail_view(request: HttpRequest, id: str):
""" Renders a detail view for viewing an intervention's data
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
"""
Renders a view for editing interventions
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id
Args:
request (HttpRequest): The incoming request
id (str): The intervention identifier
Returns:
Returns:
HttpResponse: The rendered view
"""
"""
template = "intervention/detail/view.html"
# Get object from db
intervention = get_object_or_404(Intervention, id=id)
if intervention.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("intervention:detail", id=id)
# Fetch data, filter out deleted related data
intervention = get_object_or_404(
Intervention.objects.select_related(
"geometry",
"legal",
"responsible",
).prefetch_related(
"legal__revocations",
),
id=id,
deleted=None
)
compensations = intervention.compensations.filter(
deleted=None,
)
_user = request.user
is_data_shared = intervention.is_shared_with(user=_user)
# Create forms, initialize with values from db/from POST request
data_form = EditInterventionForm(request.POST or None, instance=intervention)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=intervention)
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(intervention.identifier),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
geom_form = SimpleGeomForm(
instance=intervention,
)
last_checked = intervention.get_last_checked_action()
last_checked_tooltip = ""
if last_checked:
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(
last_checked.get_timestamp_str_formatted(),
last_checked.user
)
has_payment_without_document = intervention.payments.exists() and not intervention.get_documents()[1].exists()
requesting_user_is_only_shared_user = intervention.is_only_shared_with(_user)
if requesting_user_is_only_shared_user:
messages.info(
request,
DO_NOT_FORGET_TO_SHARE
)
context = {
"obj": intervention,
"last_checked": last_checked,
"last_checked_tooltip": last_checked_tooltip,
"compensations": compensations,
"is_entry_shared": is_data_shared,
"geom_form": geom_form,
"is_default_member": _user.in_group(DEFAULT_GROUP),
"is_zb_member": _user.in_group(ZB_GROUP),
"is_ets_member": _user.in_group(ETS_GROUP),
"LANIS_LINK": intervention.get_LANIS_link(),
"has_payment_without_document": has_payment_without_document,
TAB_TITLE_IDENTIFIER: f"{intervention.identifier} - {intervention.title}",
}
request = intervention.set_status_messages(request)
context = BaseContext(request, context).context
return render(request, template, context)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def post(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
"""
Process saved form content
@login_required
@default_group_required
@shared_access_required(Intervention, "id")
def edit_view(request: HttpRequest, id: str):
"""
Renders a view for editing interventions
Args:
request (HttpRequest): The incoming request
id (str): The intervention id
Args:
request (HttpRequest): The incoming request
Returns:
HttpResponse:
"""
# Get object from db
intervention = get_object_or_404(Intervention, id=id)
if intervention.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("intervention:detail", id=id)
Returns:
# Create forms, initialize with values from db/from POST request
data_form = EditInterventionForm(request.POST or None, instance=intervention)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=intervention)
"""
template = "intervention/form/view.html"
# Get object from db
intervention = get_object_or_404(Intervention, id=id)
if intervention.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("intervention:detail", id=id)
# Create forms, initialize with values from db/from POST request
data_form = EditInterventionForm(request.POST or None, instance=intervention)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=intervention)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
# The data form takes the geom form for processing, as well as the performing user
# Save the current state of recorded|checked to inform the user in case of a status reset due to editing
@@ -217,17 +219,48 @@ class EditInterventionView(LoginRequiredMixin, View):
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("intervention:detail", id=intervention.id)
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(intervention.identifier),
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
else:
messages.error(request, FORM_INVALID, extra_tags="danger",)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(intervention.identifier),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required_modal
@login_required
@default_group_required
@shared_access_required(Intervention, "id")
def remove_view(request: HttpRequest, id: str):
""" Renders a remove view for this intervention
Args:
request (HttpRequest): The incoming request
id (str): The uuid id as string
Returns:
"""
obj = Intervention.objects.get(id=id)
identifier = obj.identifier
form = RemoveModalForm(request.POST or None, instance=obj, request=request)
return form.process_request(
request,
_("{} removed").format(identifier),
redirect_url=reverse("intervention:index")
)

View File

@@ -1,20 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from django.http import HttpRequest, HttpResponse
from django.utils.decorators import method_decorator
from intervention.models import Intervention
from konova.decorators import shared_access_required
from konova.views.remove import AbstractRemoveView
class RemoveInterventionView(AbstractRemoveView):
_MODEL = Intervention
_REDIRECT_URL = "intervention:index"
@method_decorator(shared_access_required(Intervention, "id"))
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
return super().get(request, *args, **kwargs)

View File

@@ -5,78 +5,72 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 19.08.22
"""
from django.http import HttpRequest, HttpResponse
from django.http import HttpRequest
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from intervention.models import Intervention
from konova.contexts import BaseContext
from konova.decorators import uuid_required
from konova.forms import SimpleGeomForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.qrcode import QrCode
from konova.views.report import AbstractPublicReportView
from konova.utils.generators import generate_qr_code
class InterventionPublicReportView(AbstractPublicReportView):
_TEMPLATE = "intervention/report/report.html"
@uuid_required
def report_view(request: HttpRequest, id: str):
""" Renders the public report view
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
""" Renders the public report view
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
Args:
request (HttpRequest): The incoming request
id (str): The id of the intervention
Returns:
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
tab_title = _("Report {}").format(intervention.identifier)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not intervention.is_ready_for_publish():
template = "report/unavailable.html"
context = {
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=intervention
)
parcels = intervention.get_underlying_parcels()
distinct_deductions = intervention.deductions.all().distinct(
"account"
)
qrcode = QrCode(
content=request.build_absolute_uri(reverse("intervention:report", args=(id,))),
size=10
)
qrcode_lanis = QrCode(
content=intervention.get_LANIS_link(),
size=7
)
"""
template = "intervention/report/report.html"
intervention = get_object_or_404(Intervention, id=id)
tab_title = _("Report {}").format(intervention.identifier)
# If intervention is not recorded (yet or currently) we need to render another template without any data
if not intervention.is_ready_for_publish():
template = "report/unavailable.html"
context = {
"obj": intervention,
"deductions": distinct_deductions,
"qrcode": {
"img": qrcode.get_img(),
"url": qrcode.get_content(),
},
"qrcode_lanis": {
"img": qrcode_lanis.get_img(),
"url": qrcode_lanis.get_content(),
},
"geom_form": geom_form,
"parcels": parcels,
"tables_scrollable": False,
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
return render(request, template, context)
# Prepare data for map viewer
geom_form = SimpleGeomForm(
instance=intervention
)
parcels = intervention.get_underlying_parcels()
distinct_deductions = intervention.deductions.all().distinct(
"account"
)
qrcode_url = request.build_absolute_uri(reverse("intervention:report", args=(id,)))
qrcode_img = generate_qr_code(qrcode_url, 10)
qrcode_lanis_url = intervention.get_LANIS_link()
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
context = {
"obj": intervention,
"deductions": distinct_deductions,
"qrcode": {
"img": qrcode_img,
"url": qrcode_url,
},
"qrcode_lanis": {
"img": qrcode_img_lanis,
"url": qrcode_lanis_url,
},
"geom_form": geom_form,
"parcels": parcels,
"tables_scrollable": False,
TAB_TITLE_IDENTIFIER: tab_title,
}
context = BaseContext(request, context).context
return render(request, template, context)

View File

@@ -6,12 +6,10 @@ Created on: 19.08.22
"""
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse
from django.contrib.auth.decorators import login_required
from django.http import HttpRequest
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views import View
from intervention.forms.modals.revocation import NewRevocationModalForm, EditRevocationModalForm, \
RemoveRevocationModalForm
@@ -21,125 +19,100 @@ from konova.utils.documents import get_document
from konova.utils.message_templates import REVOCATION_ADDED, DATA_UNSHARED, REVOCATION_EDITED, REVOCATION_REMOVED
class NewInterventionRevocationView(LoginRequiredMixin, View):
def __process_request(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
""" Renders sharing form for an intervention
@login_required
@default_group_required
@shared_access_required(Intervention, "id")
def new_revocation_view(request: HttpRequest, id: str):
""" Renders sharing form for an intervention
Args:
request (HttpRequest): The incoming request
id (str): Intervention's id
Args:
request (HttpRequest): The incoming request
id (str): Intervention's id
Returns:
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
form = NewRevocationModalForm(request.POST or None, request.FILES or None, instance=intervention,
request=request)
return form.process_request(
"""
intervention = get_object_or_404(Intervention, id=id)
form = NewRevocationModalForm(request.POST or None, request.FILES or None, instance=intervention, request=request)
return form.process_request(
request,
msg_success=REVOCATION_ADDED,
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data"
)
@login_required
@default_group_required
def get_revocation_view(request: HttpRequest, doc_id: str):
""" Returns the revocation document as downloadable file
Wraps the generic document fetcher function from konova.utils.
Args:
request (HttpRequest): The incoming request
doc_id (str): The document id
Returns:
"""
doc = get_object_or_404(RevocationDocument, id=doc_id)
# File download only possible if related instance is shared with user
if not doc.instance.legal.intervention.users.filter(id=request.user.id):
messages.info(
request,
msg_success=REVOCATION_ADDED,
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data"
DATA_UNSHARED
)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
return self.__process_request(request, id, *args, **kwargs)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def post(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
return self.__process_request(request, id, *args, **kwargs)
return redirect("intervention:detail", id=doc.instance.id)
return get_document(doc)
class GetInterventionRevocationView(LoginRequiredMixin, View):
@method_decorator(default_group_required)
def get(self, request: HttpRequest, doc_id: str, *args, **kwargs) -> HttpResponse:
""" Returns the revocation document as downloadable file
@login_required
@default_group_required
@shared_access_required(Intervention, "id")
def edit_revocation_view(request: HttpRequest, id: str, revocation_id: str):
""" Renders a edit view for a revocation
Wraps the generic document fetcher function from konova.utils.
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id as string
revocation_id (str): The revocation's id as string
Args:
request (HttpRequest): The incoming request
doc_id (str): The document id
Returns:
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
revocation = get_object_or_404(Revocation, id=revocation_id)
"""
doc = get_object_or_404(RevocationDocument, id=doc_id)
# File download only possible if related instance is shared with user
if not doc.instance.legal.intervention.users.filter(id=request.user.id):
messages.info(
request,
DATA_UNSHARED
)
return redirect("intervention:detail", id=doc.instance.id)
return get_document(doc)
form = EditRevocationModalForm(request.POST or None, request.FILES or None, instance=intervention, revocation=revocation, request=request)
return form.process_request(
request,
REVOCATION_EDITED,
redirect_url=reverse("intervention:detail", args=(intervention.id,)) + "#related_data"
)
class EditInterventionRevocationView(LoginRequiredMixin, View):
def __process_request(self, request: HttpRequest, id: str, revocation_id: str, *args, **kwargs) -> HttpResponse:
""" Renders a edit view for a revocation
@login_required_modal
@login_required
@default_group_required
@shared_access_required(Intervention, "id")
def remove_revocation_view(request: HttpRequest, id: str, revocation_id: str):
""" Renders a remove view for a revocation
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id as string
revocation_id (str): The revocation's id as string
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id as string
revocation_id (str): The revocation's id as string
Returns:
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
revocation = get_object_or_404(Revocation, id=revocation_id)
"""
intervention = get_object_or_404(Intervention, id=id)
revocation = get_object_or_404(Revocation, id=revocation_id)
form = EditRevocationModalForm(request.POST or None, request.FILES or None, instance=intervention,
revocation=revocation, request=request)
return form.process_request(
request,
REVOCATION_EDITED,
redirect_url=reverse("intervention:detail", args=(intervention.id,)) + "#related_data"
)
form = RemoveRevocationModalForm(request.POST or None, instance=intervention, revocation=revocation, request=request)
return form.process_request(
request,
REVOCATION_REMOVED,
redirect_url=reverse("intervention:detail", args=(intervention.id,)) + "#related_data"
)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def get(self, request: HttpRequest, id: str, revocation_id: str, *args, **kwargs) -> HttpResponse:
return self.__process_request(request, id, revocation_id, *args, **kwargs)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def post(self, request: HttpRequest, id: str, revocation_id: str, *args, **kwargs) -> HttpResponse:
return self.__process_request(request, id, revocation_id, *args, **kwargs)
class RemoveInterventionRevocationView(LoginRequiredMixin, View):
def __process_request(self, request, id: str, revocation_id: str, *args, **kwargs) -> HttpResponse:
""" Renders a remove view for a revocation
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id as string
revocation_id (str): The revocation's id as string
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
revocation = get_object_or_404(Revocation, id=revocation_id)
form = RemoveRevocationModalForm(request.POST or None, instance=intervention, revocation=revocation,
request=request)
return form.process_request(
request,
REVOCATION_REMOVED,
redirect_url=reverse("intervention:detail", args=(intervention.id,)) + "#related_data"
)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def get(self, request: HttpRequest, id: str, revocation_id: str, *args, **kwargs) -> HttpResponse:
return self.__process_request(request, id, revocation_id, *args, **kwargs)
@method_decorator(default_group_required)
@method_decorator(shared_access_required(Intervention, "id"))
def post(self, request, id: str, revocation_id: str, *args, **kwargs) -> HttpResponse:
return self.__process_request(request, id, revocation_id, *args, **kwargs)

View File

@@ -35,7 +35,6 @@ class SimpleGeomForm(BaseForm):
disabled=False,
)
_num_geometries_ignored: int = 0
empty = False
def __init__(self, *args, **kwargs):
self.read_only = kwargs.pop("read_only", True)
@@ -50,11 +49,11 @@ class SimpleGeomForm(BaseForm):
raise AttributeError
geojson = self.instance.geometry.as_feature_collection(srid=DEFAULT_SRID_RLP)
geojson = self._set_geojson_properties(geojson, title=self.instance.identifier or None)
self._set_geojson_properties(geojson, title=self.instance.identifier or None)
geom = json.dumps(geojson)
except AttributeError:
# If no geometry exists for this form, we simply set the value to None and zoom to the maximum level
geom = json.dumps({})
geom = ""
self.empty = True
self.initialize_form_field("output", geom)
@@ -63,18 +62,18 @@ class SimpleGeomForm(BaseForm):
super().is_valid()
is_valid = True
# Make sure invalid geometry is properly rendered again to the user
# Therefore: write submitted data back into form field
# (does not matter whether we know if it is valid or invalid)
submitted_data = self.data["output"]
submitted_data = json.loads(submitted_data)
submitted_data = self._set_geojson_properties(submitted_data)
self.initialize_form_field("output", json.dumps(submitted_data))
# Get geojson from form for validity checking
geom = self.data.get("output", json.dumps({}))
# Get geojson from form
geom = self.data.get("output", None)
if geom is None or len(geom) == 0:
# empty geometry is a valid geometry
self.cleaned_data["output"] = MultiPolygon(srid=DEFAULT_SRID_RLP).ewkt
return is_valid
geom = json.loads(geom)
# Write submitted data back into form field to make sure invalid geometry
# will be rendered again on failed submit
self.initialize_form_field("output", self.data["output"])
# Initialize features list with empty MultiPolygon, so that an empty input will result in a
# proper empty MultiPolygon object
features = []
@@ -85,23 +84,20 @@ class SimpleGeomForm(BaseForm):
"MultiPolygon",
"MultiPolygon25D",
]
# Check validity for each feature of the geometry
for feature in features_json:
feature_geom = feature.get("geometry", feature)
if feature_geom is None:
# Fallback for rare cases where a feature does not contain any geometry
continue
# Try to create a geometry object from the single feature
feature_geom = json.dumps(feature_geom)
g = gdal.OGRGeometry(feature_geom, srs=DEFAULT_SRID_RLP)
geometry_has_unwanted_dimensions = g.coord_dim > 2
if geometry_has_unwanted_dimensions:
flatten_geometry = g.coord_dim > 2
if flatten_geometry:
g = self.__flatten_geom_to_2D(g)
geometry_type_is_accepted = g.geom_type not in accepted_ogr_types
if geometry_type_is_accepted:
if g.geom_type not in accepted_ogr_types:
self.add_error("output", _("Only surfaces allowed. Points or lines must be buffered."))
is_valid &= False
return is_valid
@@ -113,33 +109,27 @@ class SimpleGeomForm(BaseForm):
self._num_geometries_ignored += 1
continue
# Whatever this geometry object is -> try to create a Polygon from it
# The resulting polygon object automatically detects whether a valid polygon has been created or not
g = Polygon.from_ewkt(g.ewkt)
is_valid &= g.valid
if not g.valid:
self.add_error("output", g.valid_reason)
return is_valid
# If the resulting polygon is just a single polygon, we add it to the list of properly casted features
if isinstance(g, Polygon):
features.append(g)
elif isinstance(g, MultiPolygon):
# The resulting polygon could be of type MultiPolygon (due to multiple surfaces)
# If so, we extract all polygons from the MultiPolygon and extend the casted features list
features.extend(list(g))
# Unionize all polygon features into one new MultiPolygon
# Unionize all geometry features into one new MultiPolygon
if features:
form_geom = MultiPolygon(*features, srid=DEFAULT_SRID_RLP).unary_union
else:
# If no features have been processed, this indicates an empty geometry - so we store an empty geometry
form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP)
# Make sure to convert into a MultiPolygon. Relevant if a single Polygon is provided.
form_geom = Geometry.cast_to_multipolygon(form_geom)
# Write unionized Multipolygon back into cleaned data
# Write unioned Multipolygon into cleaned data
if self.cleaned_data is None:
self.cleaned_data = {}
self.cleaned_data["output"] = form_geom.ewkt
@@ -262,8 +252,6 @@ class SimpleGeomForm(BaseForm):
"""
features = geojson.get("features", [])
for feature in features:
if not feature.get("properties", None):
feature["properties"] = {}
feature["properties"]["editable"] = not self.read_only
if title:
feature["properties"]["title"] = title

View File

@@ -10,7 +10,6 @@ import json
from django.contrib.gis.db.models import MultiPolygonField
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.db import models, transaction
from django.db.models import Q
from django.utils import timezone
from django.contrib.gis.geos import MultiPolygon
@@ -103,41 +102,24 @@ class Geometry(BaseResource):
resolved_conflicts = all_conflicted_by_conflicts.exclude(id__in=still_conflicting_conflicts)
resolved_conflicts.delete()
def get_data_objects(self, limit_to_attrs: list = None):
def get_data_objects(self):
""" Getter for all objects which are related to this geometry
Using the limit_to_attrs we can limit the amount of returned data directly onto the data object attributes
we want to have. Reduces memory consumption and runtime.
Returns:
objs (list): The list of objects
"""
objs = []
# Some related data sets can be processed rather easily
regular_sets = [
sets = [
self.intervention_set,
self.compensation_set,
self.ema_set,
self.ecoaccount_set,
]
for _set in regular_sets:
for _set in sets:
set_objs = _set.filter(
deleted=None
)
if limit_to_attrs:
objs += set_objs.values_list(*limit_to_attrs, flat=True)
else:
objs += set_objs
# ... but we need a special treatment for compensations, since they can be deleted directly OR inherit their
# de-facto-deleted status from their deleted parent intervention
comp_objs = self.compensation_set.filter(
Q(deleted=None) & Q(intervention__deleted=None)
)
if limit_to_attrs:
objs += comp_objs.values_list(*limit_to_attrs, flat=True)
else:
objs += comp_objs
objs += set_objs
return objs
def get_data_object(self):
@@ -415,10 +397,7 @@ class Geometry(BaseResource):
"""
output_geom = input_geom
if not isinstance(input_geom, MultiPolygon):
try:
output_geom = MultiPolygon(input_geom, srid=DEFAULT_SRID_RLP)
except TypeError as e:
raise AssertionError(f"Only (Multi)Polygon allowed! Could not convert {input_geom.geom_type} to MultiPolygon")
output_geom = MultiPolygon(input_geom, srid=DEFAULT_SRID_RLP)
return output_geom
@staticmethod

View File

@@ -677,23 +677,19 @@ class GeoReferencedMixin(models.Model):
return request
instance_objs = []
needed_data_object_attrs = [
"identifier"
]
conflicts = self.geometry.conflicts_geometries.iterator()
conflicts = self.geometry.conflicts_geometries.all()
for conflict in conflicts:
# Only check the affected geometry of this conflict, since we know the conflicting geometry is self.geometry
instance_objs += conflict.affected_geometry.get_data_objects(needed_data_object_attrs)
instance_objs += conflict.affected_geometry.get_data_objects()
conflicts = self.geometry.conflicted_by_geometries.iterator()
conflicts = self.geometry.conflicted_by_geometries.all()
for conflict in conflicts:
# Only check the conflicting geometry of this conflict, since we know the affected geometry is self.geometry
instance_objs += conflict.conflicting_geometry.get_data_objects(needed_data_object_attrs)
instance_objs += conflict.conflicting_geometry.get_data_objects()
add_message = len(instance_objs) > 0
if add_message:
instance_identifiers = ", ".join(instance_objs)
instance_identifiers = [x.identifier for x in instance_objs]
instance_identifiers = ", ".join(instance_identifiers)
message_str = GEOMETRY_CONFLICT_WITH_TEMPLATE.format(instance_identifiers)
messages.info(request, message_str)
return request

View File

@@ -288,8 +288,4 @@ Overwrites netgis.css attributes
Overwrites gradient used on default css of netgis map client
*/
background: var(--rlp-red) !important;
}
.netgis-menu{
z-index: 1 !important;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

View File

@@ -11,4 +11,4 @@ BASE_TITLE = "KSP - Kompensationsverzeichnis Service Portal"
BASE_FRONTEND_TITLE = "Kompensationsverzeichnis Service Portal"
TAB_TITLE_IDENTIFIER = "tab_title"
HELP_LINK = "https://dienste.naturschutz.rlp.de/doku/doku.php?id=ksp2:start"
IMPRESSUM_LINK = "https://naturschutz.rlp.de/ueber-uns/impressum"
IMPRESSUM_LINK = "https://naturschutz.rlp.de/index.php?q=impressum"

View File

@@ -5,9 +5,6 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 11.12.23
"""
import json
from json import JSONDecodeError
from django.views.debug import ExceptionReporter
@@ -33,7 +30,7 @@ class KonovaExceptionReporter(ExceptionReporter):
"""
whitelist = [
"is_email",
"unicode_hint",
"unicdoe_hint",
"frames",
"request",
"user_str",
@@ -42,8 +39,6 @@ class KonovaExceptionReporter(ExceptionReporter):
"raising_view_name",
"exception_type",
"exception_value",
"filtered_GET_items",
"filtered_POST_items",
]
clean_data = dict()
for entry in whitelist:
@@ -61,28 +56,7 @@ class KonovaExceptionReporter(ExceptionReporter):
"""
tb_data = super().get_traceback_data()
return_data = tb_data
if self.is_email:
filtered_data = dict()
filtered_data.update(self._filter_traceback_data(tb_data))
filtered_data.update(self._filter_POST_body(tb_data))
return_data = filtered_data
return return_data
tb_data = self._filter_traceback_data(tb_data)
def _filter_POST_body(self, tb_data: dict):
""" Filters POST body from traceback data
"""
post_data = tb_data.get("request", None)
if post_data:
post_data = post_data.body
try:
post_data = json.loads(post_data)
except JSONDecodeError:
pass
post_data = {
"filtered_POST_items": [
("body", post_data),
]
}
return post_data
return tb_data

View File

@@ -5,6 +5,9 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 17.09.21
"""
from django.contrib import messages
from django.utils.translation import gettext_lazy as _
from django.http import HttpRequest
def format_german_float(num) -> str:
@@ -19,3 +22,19 @@ def format_german_float(num) -> str:
num (str): The number as german Gleitkommazahl
"""
return format(num, "0,.2f").replace(",", "X").replace(".", ",").replace("X", ".")
def check_user_is_in_any_group(request: HttpRequest):
"""
Checks for any group membership. Adds a message in case of having none.
"""
user = request.user
# Inform user about missing group privileges!
groups = user.groups.all()
if not groups:
messages.info(
request,
_("+++ Attention: You are not part of any group. You won't be able to create, edit or do anything. Please contact an administrator. +++")
)
return request

View File

@@ -42,24 +42,23 @@ def generate_random_string(length: int, use_numbers: bool = False, use_letters_l
ret_val = "".join(random.choice(elements) for i in range(length))
return ret_val
class IdentifierGenerator:
_MODEL = None
def __init__(self, model):
from konova.models import BaseObject
if not issubclass(model, BaseObject):
raise AssertionError("Model must be a subclass of BaseObject!")
def generate_qr_code(content: str, size: int = 20) -> str:
""" Generates a qr code from given content
self._MODEL = model
Args:
content (str): The content for the qr code
size (int): The image size
def generate_id(self) -> str:
""" Generates a unique identifier
Returns:
"""
unpersisted_object = self._MODEL()
identifier = unpersisted_object.generate_new_identifier()
while self._MODEL.objects.filter(identifier=identifier).exists():
identifier = unpersisted_object.generate_new_identifier()
return identifier
Returns:
qrcode_svg (str): The qr code as svg
"""
qrcode_factory = qrcode.image.svg.SvgImage
qrcode_img = qrcode.make(
content,
image_factory=qrcode_factory,
box_size=size
)
stream = BytesIO()
qrcode_img.save(stream)
return stream.getvalue().decode()

View File

@@ -5,7 +5,7 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 09.11.20
"""
from django.core.mail import send_mail, EmailMultiAlternatives
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _
@@ -45,19 +45,6 @@ class Mailer:
auth_password=self.auth_password
)
def send_via_bcc(self, recipient_list: list, subject: str, msg: str):
"""
Sends a mail with subject and message where recipients will be masked via bcc
"""
email_obj = EmailMultiAlternatives(
subject,
msg,
self.from_mail,
bcc=recipient_list,
)
email_obj.attach_alternative(msg, "text/html")
return email_obj.send(fail_silently=self.fail_silently)
def send_mail_shared_access_removed(self, obj, user, municipals_names):
""" Send a mail if user has no access to the object anymore
@@ -128,7 +115,7 @@ class Mailer:
}
msg = render_to_string("email/sharing/shared_access_given_team.html", context)
user_mail_address = users_to_notify.values_list("email", flat=True)
self.send_via_bcc(
self.send(
user_mail_address,
_("{} - Shared access given").format(obj.identifier),
msg
@@ -154,7 +141,7 @@ class Mailer:
}
msg = render_to_string("email/sharing/shared_access_removed_team.html", context)
user_mail_address = users_to_notify.values_list("email", flat=True)
self.send_via_bcc(
self.send(
user_mail_address,
_("{} - Shared access removed").format(obj.identifier),
msg
@@ -180,7 +167,7 @@ class Mailer:
}
msg = render_to_string("email/recording/shared_data_unrecorded_team.html", context)
user_mail_address = users_to_notify.values_list("email", flat=True)
self.send_via_bcc(
self.send(
user_mail_address,
_("{} - Shared data unrecorded").format(obj.identifier),
msg
@@ -206,7 +193,7 @@ class Mailer:
}
msg = render_to_string("email/recording/shared_data_recorded_team.html", context)
user_mail_address = users_to_notify.values_list("email", flat=True)
self.send_via_bcc(
self.send(
user_mail_address,
_("{} - Shared data recorded").format(obj.identifier),
msg
@@ -232,7 +219,7 @@ class Mailer:
}
msg = render_to_string("email/checking/shared_data_checked_team.html", context)
user_mail_address = users_to_notify.values_list("email", flat=True)
self.send_via_bcc(
self.send(
user_mail_address,
_("{} - Shared data checked").format(obj.identifier),
msg
@@ -257,7 +244,7 @@ class Mailer:
}
msg = render_to_string("email/other/deduction_changed_team.html", context)
user_mail_address = users_to_notify.values_list("email", flat=True)
self.send_via_bcc(
self.send(
user_mail_address,
_("{} - Deduction changed").format(obj.identifier),
msg
@@ -283,7 +270,7 @@ class Mailer:
}
msg = render_to_string("email/deleting/shared_data_deleted_team.html", context)
user_mail_address = users_to_notify.values_list("email", flat=True)
self.send_via_bcc(
self.send(
user_mail_address,
_("{} - Shared data deleted").format(obj.identifier),
msg

View File

@@ -1,47 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from io import BytesIO
import qrcode
import qrcode.image.svg as svg
class QrCode:
""" A wrapping class for creating a qr code with content
"""
_content = None
_img = None
def __init__(self, content: str, size: int):
self._content = content
self._img = self._generate_qr_code(content, size)
def _generate_qr_code(self, content: str, size: int = 20) -> str:
""" Generates a qr code from given content
Args:
content (str): The content for the qr code
size (int): The image size
Returns:
qrcode_svg (str): The qr code as svg
"""
img_factory = svg.SvgImage
qrcode_img = qrcode.make(
content,
image_factory=img_factory,
box_size=size
)
stream = BytesIO()
qrcode_img.save(stream)
return stream.getvalue().decode()
def get_img(self):
return self._img
def get_content(self):
return self._content

98
konova/views/base.py Normal file
View File

@@ -0,0 +1,98 @@
"""
Author: Michel Peltriaux
Created on: 15.10.25
"""
from abc import abstractmethod
from django.contrib import messages
from django.http import HttpRequest, JsonResponse
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View
from konova.contexts import BaseContext
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.general import check_user_is_in_any_group
from konova.utils.message_templates import MISSING_GROUP_PERMISSION
class BaseView(View):
_TEMPLATE: str = "CHANGE_ME"
_TAB_TITLE: str = "CHANGE_ME"
class Meta:
abstract = True
class BaseModalFormView(BaseView):
_TEMPLATE = "modal/modal_form.html"
_TAB_TITLE = None
class BaseIndexView(BaseView):
""" Base class for index views
"""
_TEMPLATE = "generic_index.html"
_INDEX_TABLE_CLS = None
class Meta:
abstract = True
def dispatch(self, request, *args, **kwargs):
request = check_user_is_in_any_group(request)
return super().dispatch(request, *args, **kwargs)
def get(self, request: HttpRequest):
qs = self._get_queryset()
table = self._INDEX_TABLE_CLS(
request=request,
queryset=qs
)
context = {
"table": table,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@abstractmethod
def _get_queryset(self):
raise NotImplementedError
class BaseIdentifierGeneratorView(View):
_MODEL_CLS = None
_REDIRECT_URL_NAME: str = "home"
class Meta:
abstract = True
def dispatch(self, request, *args, **kwargs):
if not self._user_has_permission(request.user):
messages.info(request, MISSING_GROUP_PERMISSION)
return redirect(reverse(self._REDIRECT_URL_NAME))
return super().dispatch(request, *args, **kwargs)
def get(self, request: HttpRequest):
tmp_obj = self._MODEL_CLS()
identifier = tmp_obj.generate_new_identifier()
while self._MODEL_CLS.objects.filter(identifier=identifier).exists():
identifier = tmp_obj.generate_new_identifier()
return JsonResponse(
data={
"gen_data": identifier
}
)
def _user_has_permission(self, user):
""" Should be overwritten in inheriting classes!
Args:
user ():
Returns:
"""
return user.is_default_user()

View File

@@ -1,25 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from abc import ABC
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse
from django.utils.decorators import method_decorator
from django.views import View
from konova.decorators import uuid_required, any_group_check
class AbstractDetailView(LoginRequiredMixin, View, ABC):
_TEMPLATE = None
@method_decorator(uuid_required)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
@method_decorator(any_group_check)
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
raise NotImplementedError()

View File

@@ -53,7 +53,6 @@ class HomeView(LoginRequiredMixin, View):
# Repeat for other objects
comps = Compensation.objects.filter(
deleted=None,
intervention__deleted=None,
)
user_comps = comps.filter(
Q(intervention__users__in=[user]) | Q(intervention__teams__in=user_teams)

View File

@@ -1,28 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from abc import ABC
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from konova.decorators import default_group_required
from konova.utils.generators import IdentifierGenerator
class AbstractIdentifierGeneratorView(LoginRequiredMixin, View, ABC):
_MODEL = None
@method_decorator(default_group_required)
def get(self, request: HttpRequest, *args, **kwargs):
generator = IdentifierGenerator(model=self._MODEL)
identifier = generator.generate_id()
return JsonResponse(
data={
"gen_data": identifier
}
)

View File

@@ -1,21 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from abc import ABC
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse
from django.utils.decorators import method_decorator
from django.views import View
from konova.decorators import any_group_check
class AbstractIndexView(LoginRequiredMixin, View, ABC):
_TEMPLATE = "generic_index.html"
@method_decorator(any_group_check)
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
raise NotImplementedError()

View File

@@ -1,64 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from abc import ABC
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views import View
from django.utils.translation import gettext_lazy as _
from konova.decorators import default_group_required
from konova.forms.modals import RemoveModalForm
class AbstractRemoveView(LoginRequiredMixin, View, ABC):
_MODEL = None
_REDIRECT_URL = None
_FORM = RemoveModalForm
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
@method_decorator(default_group_required)
def __process_request(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
obj = self._MODEL.objects.get(id=id)
identifier = obj.identifier
form = self._FORM(request.POST or None, instance=obj, request=request)
return form.process_request(
request,
_("{} removed").format(identifier),
redirect_url=reverse(self._REDIRECT_URL)
)
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
""" GET endpoint for removing via modal form
Due to the legacy logic of the form (which processes get and post requests directly), we simply need to pipe
the request from GET and POST endpoints directly into the same method.
Args:
request (HttpRequest): The incoming request
id (str): The uuid id as string
Returns:
"""
return self.__process_request(request, id, *args, **kwargs)
def post(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
""" POST endpoint for removing via modal form
Due to the legacy logic of the form (which processes get and post requests directly), we simply need to pipe
the request from GET and POST endpoints directly into the same method.
Args:
request (HttpRequest): The incoming request
id (str): The uuid id as string
Returns:
"""
return self.__process_request(request, id, *args, **kwargs)

View File

@@ -1,24 +0,0 @@
"""
Author: Michel Peltriaux
Created on: 14.12.25
"""
from abc import abstractmethod, ABC
from django.http import HttpRequest, HttpResponse
from django.utils.decorators import method_decorator
from django.views import View
from konova.decorators import uuid_required
class AbstractPublicReportView(View, ABC):
_TEMPLATE = None
@method_decorator(uuid_required)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
@abstractmethod
def get(self, request: HttpRequest, id: str, *args, **kwargs) -> HttpResponse:
raise NotImplementedError()

Binary file not shown.

View File

@@ -45,7 +45,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-14 17:23+0100\n"
"POT-Creation-Date: 2025-10-15 09:11+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -448,7 +448,7 @@ msgid "Select the intervention for which this compensation compensates"
msgstr "Wählen Sie den Eingriff, für den diese Kompensation bestimmt ist"
#: compensation/forms/compensation.py:114
#: compensation/views/compensation/compensation.py:121
#: compensation/views/compensation/compensation.py:120
msgid "New compensation"
msgstr "Neue Kompensation"
@@ -456,38 +456,38 @@ msgstr "Neue Kompensation"
msgid "Edit compensation"
msgstr "Bearbeite Kompensation"
#: compensation/forms/eco_account.py:32 compensation/utils/quality.py:97
#: compensation/forms/eco_account.py:31 compensation/utils/quality.py:97
msgid "Available Surface"
msgstr "Verfügbare Fläche"
#: compensation/forms/eco_account.py:35
#: compensation/forms/eco_account.py:34
msgid "The amount that can be used for deductions"
msgstr "Die für Abbuchungen zur Verfügung stehende Menge"
#: compensation/forms/eco_account.py:44
#: compensation/forms/eco_account.py:43
#: compensation/templates/compensation/detail/eco_account/view.html:67
#: compensation/utils/quality.py:84
msgid "Agreement date"
msgstr "Vereinbarungsdatum"
#: compensation/forms/eco_account.py:46
#: compensation/forms/eco_account.py:45
msgid "When did the parties agree on this?"
msgstr "Wann wurde dieses Ökokonto offiziell vereinbart?"
#: compensation/forms/eco_account.py:73
#: compensation/views/eco_account/eco_account.py:105
#: compensation/forms/eco_account.py:72
#: compensation/views/eco_account/eco_account.py:101
msgid "New Eco-Account"
msgstr "Neues Ökokonto"
#: compensation/forms/eco_account.py:82
#: compensation/forms/eco_account.py:81
msgid "Eco-Account XY; Location ABC"
msgstr "Ökokonto XY; Flur ABC"
#: compensation/forms/eco_account.py:148
#: compensation/forms/eco_account.py:147
msgid "Edit Eco-Account"
msgstr "Ökokonto bearbeiten"
#: compensation/forms/eco_account.py:184
#: compensation/forms/eco_account.py:183
msgid ""
"{}m² have been deducted from this eco account so far. The given value of {} "
"would be too low."
@@ -495,16 +495,12 @@ msgstr ""
"{}n² wurden bereits von diesem Ökokonto abgebucht. Der eingegebene Wert von "
"{} wäre daher zu klein."
#: compensation/forms/eco_account.py:248
#: compensation/forms/eco_account.py:247
msgid "The account can not be removed, since there are still deductions."
msgstr ""
"Das Ökokonto kann nicht entfernt werden, da hierzu noch Abbuchungen "
"vorliegen."
#: compensation/forms/eco_account.py:257
msgid "Please contact the responsible conservation office to find a solution!"
msgstr "Kontaktieren Sie die zuständige Naturschutzbehörde um eine Lösung zu finden!"
#: compensation/forms/mixins.py:37
#: compensation/templates/compensation/detail/eco_account/view.html:63
#: compensation/templates/compensation/report/eco_account/report.html:20
@@ -1292,40 +1288,44 @@ msgstr ""
msgid "Responsible data"
msgstr "Daten zu den verantwortlichen Stellen"
#: compensation/views/compensation/compensation.py:52
#: compensation/views/compensation/compensation.py:58
msgid "Compensations - Overview"
msgstr "Kompensationen - Übersicht"
#: compensation/views/compensation/compensation.py:167
#: compensation/views/compensation/compensation.py:181
#: konova/utils/message_templates.py:40
msgid "Compensation {} edited"
msgstr "Kompensation {} bearbeitet"
#: compensation/views/compensation/compensation.py:190
#: compensation/views/eco_account/eco_account.py:168 ema/views/ema.py:173
#: intervention/views/intervention.py:175
#: compensation/views/compensation/compensation.py:196
#: compensation/views/eco_account/eco_account.py:173 ema/views/ema.py:238
#: intervention/views/intervention.py:253
msgid "Edit {}"
msgstr "Bearbeite {}"
#: compensation/views/compensation/report.py:35
#: compensation/views/eco_account/report.py:35 ema/views/report.py:35
#: intervention/views/report.py:36
#: compensation/views/eco_account/report.py:36 ema/views/report.py:35
#: intervention/views/report.py:35
msgid "Report {}"
msgstr "Bericht {}"
#: compensation/views/eco_account/eco_account.py:49
#: compensation/views/eco_account/eco_account.py:53
msgid "Eco-account - Overview"
msgstr "Ökokonten - Übersicht"
#: compensation/views/eco_account/eco_account.py:82
#: compensation/views/eco_account/eco_account.py:86
msgid "Eco-Account {} added"
msgstr "Ökokonto {} hinzugefügt"
#: compensation/views/eco_account/eco_account.py:145
#: compensation/views/eco_account/eco_account.py:158
msgid "Eco-Account {} edited"
msgstr "Ökokonto {} bearbeitet"
#: ema/forms.py:42 ema/tests/unit/test_forms.py:27 ema/views/ema.py:107
#: compensation/views/eco_account/eco_account.py:288
msgid "Eco-account removed"
msgstr "Ökokonto entfernt"
#: ema/forms.py:42 ema/tests/unit/test_forms.py:27 ema/views/ema.py:108
msgid "New EMA"
msgstr "Neue EMA hinzufügen"
@@ -1353,18 +1353,22 @@ msgstr ""
msgid "Payment funded compensation"
msgstr "Ersatzzahlungsmaßnahme"
#: ema/views/ema.py:52
#: ema/views/ema.py:53
msgid "EMAs - Overview"
msgstr "EMAs - Übersicht"
#: ema/views/ema.py:85
#: ema/views/ema.py:86
msgid "EMA {} added"
msgstr "EMA {} hinzugefügt"
#: ema/views/ema.py:150
#: ema/views/ema.py:223
msgid "EMA {} edited"
msgstr "EMA {} bearbeitet"
#: ema/views/ema.py:262
msgid "EMA removed"
msgstr "EMA entfernt"
#: intervention/forms/intervention.py:49
msgid "Construction XY; Location ABC"
msgstr "Bauvorhaben XY; Flur ABC"
@@ -1425,7 +1429,7 @@ msgstr "Datum Bestandskraft bzw. Rechtskraft"
#: intervention/forms/intervention.py:216
#: intervention/tests/unit/test_forms.py:36
#: intervention/views/intervention.py:109
#: intervention/views/intervention.py:105
msgid "New intervention"
msgstr "Neuer Eingriff"
@@ -1661,18 +1665,22 @@ msgstr ""
msgid "Check performed"
msgstr "Prüfung durchgeführt"
#: intervention/views/intervention.py:53
#: intervention/views/intervention.py:57
msgid "Interventions - Overview"
msgstr "Eingriffe - Übersicht"
#: intervention/views/intervention.py:86
#: intervention/views/intervention.py:90
msgid "Intervention {} added"
msgstr "Eingriff {} hinzugefügt"
#: intervention/views/intervention.py:150
#: intervention/views/intervention.py:236
msgid "Intervention {} edited"
msgstr "Eingriff {} bearbeitet"
#: intervention/views/intervention.py:278
msgid "{} removed"
msgstr "{} entfernt"
#: konova/decorators.py:32
msgid "You need to be staff to perform this action!"
msgstr "Hierfür müssen Sie Mitarbeiter sein!"
@@ -1802,7 +1810,7 @@ msgstr "Nicht editierbar"
msgid "Geometry"
msgstr "Geometrie"
#: konova/forms/geometry_form.py:105
#: konova/forms/geometry_form.py:100
msgid "Only surfaces allowed. Points or lines must be buffered."
msgstr ""
"Nur Flächen erlaubt. Punkte oder Linien müssen zu Flächen gepuffert werden."
@@ -2260,9 +2268,8 @@ msgid ""
"too small to be valid). These parts have been removed. Please check the "
"stored geometry."
msgstr ""
"Die Geometrie enthielt {} invalide Bestandteile (z.B. unaussagekräftige "
"Kleinstflächen).Diese Bestandteile wurden automatisch entfernt. Bitte "
"überprüfen Sie die angepasste Geometrie."
"Die Geometrie enthielt {} invalide Bestandteile (z.B. unaussagekräftige Kleinstflächen)."
"Diese Bestandteile wurden automatisch entfernt. Bitte überprüfen Sie die angepasste Geometrie."
#: konova/utils/message_templates.py:89
msgid "This intervention has {} revocations"
@@ -2323,10 +2330,6 @@ msgstr "{} verzeichnet"
msgid "Errors found:"
msgstr "Fehler gefunden:"
#: konova/views/remove.py:35
msgid "{} removed"
msgstr "{} entfernt"
#: konova/views/resubmission.py:39
msgid "Resubmission set"
msgstr "Wiedervorlage gesetzt"

View File

@@ -5,7 +5,7 @@
<div class="jumbotron">
<div class="row">
<div class="col-auto">
<img src="{% static 'images/error_imgs/croc_technician_400_sm.png' %}" style="max-width: 150px">
<img src="{% static 'images/error_imgs/croc_technician_400.png' %}" style="max-width: 150px">
</div>
<div class="col-sm-12 col-md-9 col-lg-9 col-xl-10">
<h1 class="display-4">{% fa5_icon 'question-circle' %}400</h1>

View File

@@ -5,7 +5,7 @@
<div class="jumbotron">
<div class="row">
<div class="col-auto">
<img src="{% static 'images/error_imgs/croc_technician_500_sm.png' %}" style="max-width: 150px">
<img src="{% static 'images/error_imgs/croc_technician_500.png' %}" style="max-width: 150px">
</div>
<div class="col-sm-12 col-md-9 col-lg-9 col-xl-10">
<h1 class="display-4">{% fa5_icon 'fire-alt' %} 500</h1>

View File

@@ -58,7 +58,7 @@
[
{ "id": "webatlas_farbe", "folder": "bg", "type": "WMS", "order": -1, "title": "WebatlasRP farbig", "attribution": "LVermGeo", "url": "https://maps.service24.rlp.de/gisserver/services/RP/RP_WebAtlasRP/MapServer/WmsServer?", "name": "RP_WebAtlasRP", "active": true},
{ "id": "webatlas_grau", "folder": "bg", "type": "WMS", "order": -1, "title": "WebatlasRP grau", "attribution": "LVermGeo", "url": "https://maps.service24.rlp.de/gisserver/services/RP/RP_ETRS_Gt/MapServer/WmsServer?", "name": "0", "active": false },
{ "id": "luftbilder", "folder": "bg", "type": "WMS", "order": -1, "title": "Luftbilder", "attribution": "LVermGeo", "url": "https://geo4.service24.rlp.de/wms/rp_dop20.fcgi?", "name": "rp_dop20", "active": false },
{ "id": "luftbilder", "folder": "bg", "type": "WMS", "order": -1, "title": "Luftbilder", "attribution": "LVermGeo", "url": "https://geo4.service24.rlp.de/wms/dop_basis.fcgi?", "name": "rp_dop", "active": false },
{ "id": "basemap_farbe", "folder": "bg", "type": "WMS", "order": -1, "title": "BasemapDE farbig", "attribution": "BKG", "url": "https://sgx.geodatenzentrum.de/wms_basemapde?", "name": "de_basemapde_web_raster_farbe", "active": false },
{ "id": "basemap_grau", "folder": "bg", "type": "WMS", "order": -1, "title": "BasemapDE grau", "attribution": "BKG", "url": "https://sgx.geodatenzentrum.de/wms_basemapde?", "name": "de_basemapde_web_raster_grau", "active": false },
{ "id": "dtk_farbe", "folder": "bg", "type": "WMS", "order": -1, "title": "DTK5 farbig", "attribution": "LVermGeo", "url": "https://geo4.service24.rlp.de/wms/dtk5_rp.fcgi?", "name": "rp_dtk5", "active": false },
@@ -188,7 +188,6 @@
{
"title": "Ebene hinzufügen",
"preview": true,
"editable": true,
"wms_options": [ "https://sgx.geodatenzentrum.de/wms_topplus_open" ],
"wfs_options": [ "http://213.139.159.34:80/geoserver/uesg/wfs" ],
"wfs_proxy": "/client/proxy?",

File diff suppressed because one or more lines are too long

View File

@@ -13,7 +13,7 @@ netgis.Attribution.prototype.add=function(a){for(var b=0;b<this.items.length;b++
netgis.Attribution.prototype.onContextUpdate=function(a){this.initConfig(a.detail.context.config);this.update()};netgis.Attribution.prototype.onContextUpdate_01=function(a){config.attribution&&config.attribution.prefix&&this.items.push(config.attribution.prefix);this.layers=[];for(var b=0;b<a.layers.length;b++){var c=a.layers[b];c.attribution&&0<c.attribution.length&&(this.layers[c.id]=c.attribution)}for(b=0;b<a.layers.length;b++)if(c=a.layers[b],c.active)this.onLayerShow({id:c.id})};
netgis.Attribution.prototype.onLayerShow=function(a){if(a=this.layers[a.id]){for(var b=0;b<this.items.length;b++)if(this.items[b]===a)return;this.items.push(a);this.update()}};netgis.Attribution.prototype.onLayerHide=function(a){if(a=this.layers[a.id]){for(var b=0;b<this.items.length;b++)if(this.items[b]===a){this.items.splice(b,1);break}this.update()}};
netgis.Attribution.prototype.onEditLayerChange=function(a){a=a.detail.geojson.area;for(var b=0;b<this.items.length;b++)if(-1<this.items[b].search("Zeichnungsfl\u00e4che: ")){this.items.splice(b,1);break}this.appendix=a&&0<a?"<b>Zeichnungsfl\u00e4che: "+netgis.util.formatArea(a,!0)+"</b>":null;this.update()};netgis=netgis||{};netgis.Client=function(a,b){b||(b={});a=this.initLegacyConfig(b,a);this.container=this.initContainer(a);this.debug=!1;netgis.util.isString(b)?netgis.util.isJSON(b,!1)?(b=JSON.parse(b),this.init(this.container,b)):(this.showLoader(!0),netgis.util.request(b,this.onConfigResponse.bind(this))):this.init(this.container,b)};netgis.Client.Config={loading_text:"Geoportal Client wird geladen..."};netgis.Client.Output={id:"netgis-storage"};
netgis.Client.prototype.init=function(a,b){this.config=b;this.initParams(b);this.initConfig(b);this.initElements(a);this.initEvents();this.initModules(b);this.initOutput(b)};
netgis.Client.prototype.init=function(a,b){this.config=b;this.initParams(b);this.initConfig(b);this.initElements(a);this.initEvents();this.initModules(b);this.initOutput(b);a=new netgis.ContextMenu;a.attachTo(this.container);this.modules.contextmenu=a;this.popup=new netgis.Popup;this.popup.attachTo(this.container)};
netgis.Client.prototype.initLegacyConfig=function(a,b){var c=netgis.config;if(!c)return b;c.MAP_CONTAINER_ID&&(b=c.MAP_CONTAINER_ID);a.modules||(a.modules={menu:!0,map:!0,controls:!0,attribution:!0,legend:!0,layertree:!0,info:!0,searchplace:!0,geolocation:!0});a.map||(a.map={});!c.INITIAL_CENTER_X&&0!==c.INITIAL_CENTER_X||!c.INITIAL_CENTER_Y&&0!==c.INITIAL_CENTER_Y||(a.map.center=[c.INITIAL_CENTER_X,c.INITIAL_CENTER_Y]);a.map.scalebar=!0;c.INITIAL_SCALE&&(a.map.scale=c.INITIAL_SCALE);c.MAP_SCALES&&
(a.map.scales=c.MAP_SCALES);c.MAP_EXTENT&&(a.map.extent=c.MAP_EXTENT);c.MAX_HISTORY&&(a.map.max_view_history=c.MAX_HISTORY);a.attribution={prefix:"GeoPortal"};c.MAP_PROJECTIONS&&(a.projections=c.MAP_PROJECTIONS);c.MAP_PROJECTION&&(a.map.projection=c.MAP_PROJECTION);a.controls={buttons:[{id:"zoom_in",icon:"<i class='fas fa-plus'></i>",title:"Zoom +"},{id:"zoom_out",icon:"<i class='fas fa-minus'></i>",title:"Zoom -"},{id:"geolocation",icon:"<i class='fas fa-crosshairs'></i>",title:"Ger\u00e4testandort"},
{id:"zoom_home",icon:"<i class='fas fa-home'></i>",title:"Anfangsausdehung"},{id:"legend",icon:"<i class='fas fa-bars'></i>",title:"Legende"}]};a.folders||(a.folders=[{id:"bg",title:"Hintergrund",parent:null,radio:!0}]);a.layers||(a.layers=[]);if(c.URL_BACKGROUND_HYBRID){var d=c.URL_BACKGROUND_HYBRID;-1!==d.indexOf("{x}")||-1!==d.indexOf("{y}")&&-1!==d.indexOf("{-y}")||-1!==d.indexOf("{z}")||(d+="/{z}/{x}/{-y}.jpeg");d={id:"bg_hybrid",active:!0,folder:"bg",order:1,title:"Hybrid",type:"TMS",url:d,
@@ -38,7 +38,10 @@ this.loaderTimeout=null}.bind(this),600)):(this.loader.classList.remove("netgis-
netgis.Client.prototype.isMobile=function(){return netgis.util.isMobile(this.container)};netgis.Client.prototype.onConfigResponse=function(a){a=JSON.parse(a);this.init(this.container,a);this.showLoader(!1)};netgis.Client.prototype.requestContextWMC=function(a,b){if(-1<a.indexOf("{id}"))if(b)a=netgis.util.replace(a,"{id}",b);else{console.warn("No WMC id set in config for url",a);return}(new netgis.WMC(this.config)).requestContext(a,this.onContextResponseWMC.bind(this));this.showLoader(!0)};
netgis.Client.prototype.onContextResponseWMC=function(a){console.info("WMC Response:",a);for(var b=0;b<a.config.layers.length;b++)this.config.layers.push(a.config.layers[b]);a.config.map.bbox&&(this.config.map.bbox=a.config.map.bbox);netgis.util.invoke(this.container,netgis.Events.CLIENT_CONTEXT_RESPONSE,{context:a});this.showLoader(!1)};
netgis.Client.prototype.onContextResponseLayer=function(a){var b=JSON.parse(a);console.info("Layer Response:",b);a=new netgis.WMC;b=b.wms.srv[0];a=a.parseServiceLayer(b.id.toString(),b,null,b.layer[0],null);this.config.layers.push(a);netgis.util.invoke(this.container,netgis.Events.MAP_LAYER_CREATE,a)};netgis.Client.prototype.requestContextOWS=function(a){console.info("Request OWS:",a);netgis.util.request(a,this.onContextResponseOWS.bind(this))};
netgis.Client.prototype.onContextResponseOWS=function(a){a=JSON.parse(a);console.info("OWS Response:",a);a=netgis.OWS.read(a,this);console.info("OWS Config:",a)};netgis.Client.prototype.onMapEditLayerChange=function(a){a=JSON.stringify(a.detail.geojson);this.output.value=a};
netgis.Client.prototype.onContextResponseOWS=function(a){a=JSON.parse(a);console.info("OWS Response:",a);a=netgis.OWS.read(a,this);console.info("OWS Config:",a)};netgis.Client.prototype.onIconbarIconClick=function(a){switch(a.detail.id){case "home":a=this.config.layers;for(var b=0;b<a.length;b++){var c=a[b],d=c.id;!0===c.active?(this.modules.map.addLayer(d,c),this.modules.layertree.tree.setItemChecked(d,!0)):(this.modules.map.removeLayer(d),this.modules.layertree.tree.setItemChecked(d,!1))}}};
netgis.Client.prototype.onIconbarItemClick=function(a){a=a.detail;for(var b=this.config.layers,c=0;c<b.length;c++){var d=b[c],e=d.id;"background"!==d.folder&&(e===a.id?(this.modules.map.addLayer(e,d),this.modules.layertree.tree.setItemChecked(e,!0)):(this.modules.map.removeLayer(e),this.modules.layertree.tree.setItemChecked(e,!1)))}};
netgis.Client.prototype.onSwitcherButtonClick=function(a){a=a.detail;for(var b=this.config.switcher.buttons,c=this.config.layers,d=0;d<b.length;d++){var e=b[d].id;if(e===a.id)for(var f=0;f<c.length;f++){var g=c[f];g.id===e&&(this.modules.map.addLayer(e,g),this.modules.layertree.tree.setItemChecked(e,!0))}else this.modules.map.removeLayer(e),this.modules.layertree.tree.setItemChecked(e,!1)}0===this.modules.switcher.getIndex(a.id)&&this.modules.switcher.shift(1,0)};
netgis.Client.prototype.onGeolocationToggle=function(a){this.modules.map.setGeolocMarkerVisible(a.detail.on)};netgis.Client.prototype.onGeolocationChange=function(a){a=a.detail;this.modules.map.zoomLonLat(a.lon,a.lat,this.config.geolocation.zoom);this.modules.map.setGeolocMarkerLonLat(a.lon,a.lat)};netgis.Client.prototype.onMapEditLayerChange=function(a){a=JSON.stringify(a.detail.geojson);this.output.value=a};
netgis.Client.handleCommand=function(a,b){var c=b.split(":");b=c[0];switch(b.toUpperCase()){case netgis.Commands.PLUGIN:b=c[1];if(!b){console.error("missing second command parameter id",c);break}netgis.util.invoke(a,netgis.Events.PLUGIN_TOGGLE,{id:b});break;case netgis.Commands.LAYERTREE:netgis.util.isMobile()?netgis.util.invoke(a,netgis.Events.LAYERTREE_TOGGLE,{on:!0}):netgis.util.invoke(a,netgis.Events.LAYERTREE_TOGGLE,null);break;case netgis.Commands.SEARCHPLACE:netgis.util.isMobile()?netgis.util.invoke(a,
netgis.Events.SEARCHPLACE_TOGGLE,{on:!0}):netgis.util.invoke(a,netgis.Events.SEARCHPLACE_TOGGLE,null);break;case netgis.Commands.SEARCHPARCEL:netgis.util.isMobile()?netgis.util.invoke(a,netgis.Events.SEARCHPARCEL_TOGGLE,{on:!0}):netgis.util.invoke(a,netgis.Events.SEARCHPARCEL_TOGGLE,null);break;case netgis.Commands.TOOLBOX:netgis.util.isMobile()?netgis.util.invoke(a,netgis.Events.TOOLBOX_TOGGLE,{on:!0}):netgis.util.invoke(a,netgis.Events.TOOLBOX_TOGGLE,null);break;case netgis.Commands.LEGEND:netgis.util.isMobile()?
netgis.util.invoke(a,netgis.Events.LEGEND_TOGGLE,{on:!0}):netgis.util.invoke(a,netgis.Events.LEGEND_TOGGLE,null);break;case netgis.Commands.VIEW_PREV:netgis.util.invoke(a,netgis.Events.MAP_VIEW_PREV,null);break;case netgis.Commands.VIEW_NEXT:netgis.util.invoke(a,netgis.Events.MAP_VIEW_NEXT,null);break;case netgis.Commands.VIEW:netgis.util.invoke(a,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.VIEW});break;case netgis.Commands.ZOOM_BOX:netgis.util.invoke(a,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.ZOOM_BOX});
@@ -69,10 +72,10 @@ netgis.Controls.prototype.onParentPointerDown=function(a){netgis.util.insideElem
netgis.Controls.prototype.onGeolocToggleActive=function(a){a.target!==this.inputGeolocActive&&(this.inputGeolocActive.checked=a.detail.on)};netgis.Controls.prototype.onGeolocToggleCenter=function(a){a.target!==this.inputGeolocCenter&&(this.inputGeolocCenter.checked=a.detail.on)};netgis=netgis||{};
netgis.Events={CLIENT_CONTEXT_RESPONSE:"client-context-response",CLIENT_SET_MODE:"client-set-mode",PLUGIN_TOGGLE:"plugin-toggle",CONTROLS_BUTTON_CLICK:"controls-button-click",MAP_ZOOM:"map-zoom",MAP_ZOOM_HOME:"map-zoom-home",MAP_ZOOM_LONLAT:"map-zoom-lonlat",MAP_ZOOM_SCALE:"map-zoom-scale",MAP_ZOOM_LAYER:"map-zoom-layer",MAP_ZOOM_LEVEL:"map-zoom-level",MAP_LAYER_CREATE:"map-layer-create",MAP_LAYER_TOGGLE:"map-layer-toggle",MAP_LAYER_TRANSPARENCY:"map-layer-transparency",MAP_LAYER_ORDER:"map-layer-order",MAP_LAYER_DELETE:"map-layer-delete",
MAP_VIEW_CHANGE:"map-view-change",MAP_VIEW_NEXT:"map-view-next",MAP_VIEW_PREV:"map-view-prev",MAP_CLICK:"map-click",MAP_FEATURE_ENTER:"map-feature-enter",MAP_FEATURE_CLICK:"map-feature-click",MAP_FEATURE_LEAVE:"map-feature-leave",MAP_SNAP_TOGGLE:"map-snap-toggle",MAP_EDIT_LAYER_CHANGE:"map-edit-layer-change",MAP_EDIT_LAYER_LOADED:"map-edit-layer-loaded",MAP_COPY_FEATURE_TO_EDIT:"map-copy-feature-to-edit",PANEL_TOGGLE:"panel-toggle",PANEL_RESIZE:"panel-resize",WINDOW_TOGGLE:"window-toggle",WINDOW_RESIZE:"window-resize",
TABS_CHANGE:"tabs-change",TREE_ITEM_CHANGE:"tree-item-change",TREE_ITEM_SLIDER_CHANGE:"tree-item-slider-change",TREE_ITEM_ORDER_CHANGE:"tree-item-order-change",TREE_ITEM_REMOVE:"tree-item-remove",TREE_BUTTON_CLICK:"tree-button-click",LAYERTREE_TOGGLE:"layertree-toggle",LEGEND_TOGGLE:"legend-toggle",GEOLOCATION_SHOW_OPTIONS:"geolocation-show-options",GEOLOCATION_TOGGLE_ACTIVE:"geolocation-toggle-active",GEOLOCATION_TOGGLE_CENTER:"geolocation-toggle-center",GEOLOCATION_CHANGE:"geolocation-change",TOOLBOX_TOGGLE:"toolbox-toggle",
TOOLBOX_BUTTON_CLICK:"toolbox-button-click",MENU_BUTTON_CLICK:"menu-button-click",MENU_CHECKBOX_CHANGE:"menu-checkbox-change",MENU_SELECT_CHANGE:"menu-select-change",CONTEXTMENU_SHOW:"contextmenu-show",CONTEXTMENU_BUTTON_CLICK:"contextmenu-button-click",CONTEXTMENU_CHECKBOX_CHANGE:"contextmenu-checkbox-change",CONTEXTMENU_SLIDER_CHANGE:"contextmenu-slider-change",SEARCH_CHANGE:"search-change",SEARCH_SELECT:"search-select",SEARCH_CLEAR:"search-clear",SEARCHPLACE_TOGGLE:"searchplace-toggle",SEARCHPARCEL_TOGGLE:"searchparcel-toggle",
SEARCHPARCEL_RESET:"searchparcel-reset",SEARCHPARCEL_PARCELS_RESPONSE:"searchparcel-parcels-response",SEARCHPARCEL_ITEM_ENTER:"searchparcel-item-enter",SEARCHPARCEL_ITEM_LEAVE:"searchparcel-item-leave",SEARCHPARCEL_ITEM_CLICK:"searchparcel-item-click",SEARCHPARCEL_ITEM_IMPORT:"searchparcel-item-import",MEASURE_CLEAR:"measure-clear",SELECT_MULTI_TOGGLE:"select-multi-toggle",DRAW_BUFFER_TOGGLE:"draw-buffer-toggle",DRAW_BUFFER_CHANGE:"draw-buffer-change",BUFFER_CHANGE:"buffer-change",BUFFER_ACCEPT:"buffer-accept",
IMPORT_LAYER_SHOW:"import-layer-show",IMPORT_LAYER_ACCEPT:"import-layer-accept",IMPORT_LAYER_PREVIEW:"import-layer-preview",IMPORT_LAYER_PREVIEW_FEATURES:"import-layer-preview-features",IMPORT_GEOPORTAL_SUBMIT:"import-geoportal-submit",EXPORT_SHOW:"export-show",EXPORT_BEGIN:"export-begin",EXPORT_END:"export-end",TIMESLIDER_SHOW:"timeslider-show",TIMESLIDER_HIDE:"timeslider-hide",TIMESLIDER_SELECT:"timeslider-select"};netgis=netgis||{};netgis.Export=function(a){this.config=a;this.initElements(a);this.initSections()};netgis.Export.Config={title:"Export",logo:"",gif_worker:"/libs/gifjs/0.2.0/gif.worker.js",default_filename:"Export",default_margin:10};
TREE_ITEM_CHANGE:"tree-item-change",TREE_ITEM_SLIDER_CHANGE:"tree-item-slider-change",TREE_ITEM_ORDER_CHANGE:"tree-item-order-change",TREE_BUTTON_CLICK:"tree-button-click",LAYERTREE_TOGGLE:"layertree-toggle",LEGEND_TOGGLE:"legend-toggle",GEOLOCATION_SHOW_OPTIONS:"geolocation-show-options",GEOLOCATION_TOGGLE_ACTIVE:"geolocation-toggle-active",GEOLOCATION_TOGGLE_CENTER:"geolocation-toggle-center",GEOLOCATION_CHANGE:"geolocation-change",TOOLBOX_TOGGLE:"toolbox-toggle",TOOLBOX_BUTTON_CLICK:"toolbox-button-click",
MENU_BUTTON_CLICK:"menu-button-click",MENU_CHECKBOX_CHANGE:"menu-checkbox-change",MENU_SELECT_CHANGE:"menu-select-change",CONTEXTMENU_SHOW:"contextmenu-show",CONTEXTMENU_BUTTON_CLICK:"contextmenu-button-click",CONTEXTMENU_CHECKBOX_CHANGE:"contextmenu-checkbox-change",CONTEXTMENU_SLIDER_CHANGE:"contextmenu-slider-change",SEARCH_CHANGE:"search-change",SEARCH_SELECT:"search-select",SEARCH_CLEAR:"search-clear",SEARCHPLACE_TOGGLE:"searchplace-toggle",SEARCHPARCEL_TOGGLE:"searchparcel-toggle",SEARCHPARCEL_RESET:"searchparcel-reset",
SEARCHPARCEL_PARCELS_RESPONSE:"searchparcel-parcels-response",SEARCHPARCEL_ITEM_ENTER:"searchparcel-item-enter",SEARCHPARCEL_ITEM_LEAVE:"searchparcel-item-leave",SEARCHPARCEL_ITEM_CLICK:"searchparcel-item-click",SEARCHPARCEL_ITEM_IMPORT:"searchparcel-item-import",MEASURE_CLEAR:"measure-clear",SELECT_MULTI_TOGGLE:"select-multi-toggle",DRAW_BUFFER_TOGGLE:"draw-buffer-toggle",DRAW_BUFFER_CHANGE:"draw-buffer-change",BUFFER_CHANGE:"buffer-change",BUFFER_ACCEPT:"buffer-accept",IMPORT_LAYER_SHOW:"import-layer-show",
IMPORT_LAYER_ACCEPT:"import-layer-accept",IMPORT_LAYER_PREVIEW:"import-layer-preview",IMPORT_LAYER_PREVIEW_FEATURES:"import-layer-preview-features",IMPORT_GEOPORTAL_SUBMIT:"import-geoportal-submit",EXPORT_SHOW:"export-show",EXPORT_BEGIN:"export-begin",EXPORT_END:"export-end",TIMESLIDER_SHOW:"timeslider-show",TIMESLIDER_HIDE:"timeslider-hide",TIMESLIDER_SELECT:"timeslider-select"};netgis=netgis||{};netgis.Export=function(a){this.config=a;this.initElements(a);this.initSections()};netgis.Export.Config={title:"Export",logo:"",gif_worker:"/libs/gifjs/0.2.0/gif.worker.js",default_filename:"Export",default_margin:10};
netgis.Export.prototype.initElements=function(a){a=a["export"];this.modal=new netgis.Modal(a.title?a.title:"Export");this.modal.container.classList.add("netgis-export");this.tabs=new netgis.Tabs(["PDF","JPEG","PNG","GIF","GeoJSON"]);this.tabs.container.style.position="absolute";this.tabs.container.style.left="0mm";this.tabs.container.style.right="0mm";this.tabs.container.style.top="12mm";this.tabs.container.style.bottom="0mm";this.tabs.attachTo(this.modal.content)};
netgis.Export.prototype.initSections=function(){this.sections={};var a=0;this.sections.pdf=this.tabs.getContentSection(a);a+=1;this.addInputNumber(this.sections.pdf,"Breite (Pixel):",1600,0);this.addInputNumber(this.sections.pdf,"H\u00f6he (Pixel):",900,0);this.addInputNumber(this.sections.pdf,"Seitenr\u00e4nder (Millimeter):",10,0);this.addCheckbox(this.sections.pdf,"Querformat",!0);this.addButton(this.sections.pdf,"<i class='netgis-icon fas fa-save'></i><span>Exportieren</span>",this.onExportClickPDF.bind(this));
this.sections.jpeg=this.tabs.getContentSection(a);a+=1;this.addInputNumber(this.sections.jpeg,"Breite (Pixel):",1600,0);this.addInputNumber(this.sections.jpeg,"H\u00f6he (Pixel):",900,0);this.addCheckbox(this.sections.jpeg,"Querformat",!0);this.addButton(this.sections.jpeg,"<i class='netgis-icon fas fa-save'></i><span>Exportieren</span>",this.onExportClickJPEG.bind(this));this.sections.png=this.tabs.getContentSection(a);a+=1;this.addInputNumber(this.sections.png,"Breite (Pixel):",1600,0);this.addInputNumber(this.sections.png,
@@ -91,20 +94,19 @@ netgis.Export.prototype.onExportEnd=function(a){this.modal.hide()};netgis=netgis
netgis.Geolocation.prototype.setActive=function(a,b){var c=this.config.geolocation;a?navigator.geolocation?(this.watch=navigator.geolocation.watchPosition(this.onPositionChange.bind(this),this.onPositionError.bind(this),{timeout:c&&c.timeout?1E3*c.timeout:1E4,maximumAge:0,enableHighAccuracy:!0}),b||netgis.util.invoke(this.container,netgis.Events.GEOLOCATION_TOGGLE_ACTIVE,{on:!0})):this.error("Geolocation not supported by this device!"):(this.watch&&(navigator.geolocation.clearWatch(this.watch),this.watch=
null),b||netgis.util.invoke(this.container,netgis.Events.GEOLOCATION_TOGGLE_ACTIVE,{on:!1}));this.active=a};netgis.Geolocation.prototype.isActive=function(){return this.active};netgis.Geolocation.prototype.error=function(a){console.error(a);this.watch&&(navigator.geolocation.clearWatch(this.watch),this.watch=null);netgis.util.invoke(this.container,netgis.Events.GEOLOCATION_TOGGLE_ACTIVE,{on:!1})};netgis.Geolocation.prototype.onActiveChange=function(a){this.setActive(a.currentTarget.checked)};
netgis.Geolocation.prototype.onCenterChange=function(a){};netgis.Geolocation.prototype.onPositionChange=function(a){netgis.util.invoke(this.container,netgis.Events.GEOLOCATION_CHANGE,{lon:a.coords.longitude,lat:a.coords.latitude,center:this.center})};netgis.Geolocation.prototype.onPositionError=function(a){this.error("Geolocation: "+a.message+" ("+a.code+")")};netgis.Geolocation.prototype.onGeolocToggleActive=function(a){a.target!==this.container&&this.setActive(a.detail.on)};
netgis.Geolocation.prototype.onGeolocToggleCenter=function(a){a.target!==this.container&&(this.center=a.detail.on)};netgis=netgis||{};netgis.Import=function(a){this.config=a;this.initElements(a);this.initSections(a);this.initPreview()};netgis.Import.Config={title:"Import Layer",preview:!0,editable:!0,wms_options:[],wfs_options:[],wfs_proxy:"",geopackage_lib:"/libs/geopackage/4.2.3/",geoportal_tab:!0,geoportal_search_url:"",geoportal_autocomplete:!0};
netgis.Geolocation.prototype.onGeolocToggleCenter=function(a){a.target!==this.container&&(this.center=a.detail.on)};netgis=netgis||{};netgis.Import=function(a){this.config=a;this.initElements(a);this.initSections(a);this.initPreview()};netgis.Import.Config={title:"Import Layer",preview:!0,wms_options:[],wfs_options:[],wfs_proxy:"",geopackage_lib:"/libs/geopackage/4.2.3/",geoportal_tab:!0,geoportal_search_url:"",geoportal_autocomplete:!0};
netgis.Import.prototype.initElements=function(a){a=a["import"];this.modal=new netgis.Modal(a.title?a.title:"Import");this.modal.container.classList.add("netgis-import");var b="WMS WFS GeoJSON GML GeoPackage Spatialite Shapefile".split(" ");a.geoportal_tab&&b.unshift("Geoportal");this.tabs=new netgis.Tabs(b);this.tabs.container.style.position="absolute";this.tabs.container.style.left="0mm";this.tabs.container.style.right="0mm";this.tabs.container.style.top="12mm";this.tabs.container.style.bottom="0mm";
this.tabs.container.addEventListener(netgis.Events.TABS_CHANGE,this.onTabsChange.bind(this));this.tabs.attachTo(this.modal.content)};
netgis.Import.prototype.initSections=function(a){this.sections={};var b=0;a["import"]&&!0===a["import"].geoportal_tab&&(this.sections.geoportal=this.tabs.getContentSection(b),b+=1,this.sections.geoportal.classList.add("netgis-geoportal"),this.geoportalSearch=new netgis.Search("Thema, Schlagwort..."),this.geoportalSearch.autocomplete=a["import"].geoportal_autocomplete,this.geoportalSearch.container.addEventListener(netgis.Events.SEARCH_CHANGE,this.onGeoportalSearchChange.bind(this)),this.geoportalSearch.container.addEventListener(netgis.Events.SEARCH_CLEAR,
this.onGeoportalSearchClear.bind(this)),this.geoportalSearch.attachTo(this.sections.geoportal),a=document.createElement("span"),a.innerHTML="Suche im Datenkatalog:",this.geoportalSearch.label.insertBefore(a,this.geoportalSearch.label.firstChild),a=document.createElement("button"),a.innerHTML="Suchen",a.className="netgis-color-a netgis-hover-c netgis-round netgis-shadow",a.setAttribute("type","button"),a.addEventListener("click",this.onGeoportalSearchButtonClick.bind(this)),this.geoportalSearch.label.appendChild(a),
this.geoportalLoader=document.createElement("div"),this.geoportalLoader.className="netgis-loader netgis-text-a netgis-hide",this.geoportalLoader.innerHTML="<i class='fas fa-cog'></i>",this.sections.geoportal.appendChild(this.geoportalLoader),this.geoportalResults=new netgis.Tree,this.geoportalResults.container.addEventListener(netgis.Events.TREE_ITEM_CHANGE,this.onGeoportalTreeItemChange.bind(this)),this.geoportalResults.attachTo(this.sections.geoportal),a=document.createElement("div"),a.style.position=
"absolute",a.style.left="0mm",a.style.right="0mm",a.style.bottom="0mm",a.style.padding="8mm",a.style.paddingTop="0mm",this.sections.geoportal.appendChild(a),this.geoportalSubmit=this.addButton(a,"<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen<span class='netgis-count'></span></span>",this.onGeoportalSubmit.bind(this)));this.sections.wms=this.tabs.getContentSection(b);b+=1;a=document.createElement("div");a.className="netgis-search";this.sections.wms.appendChild(a);this.addInputText(a,
"WMS-URL:",this.config["import"].wms_options).classList.add("netgis-round","netgis-shadow");this.addButton(this.sections.wms,"<i class='netgis-icon fas fa-cloud-download-alt'></i><span>Dienst laden</span>",this.onWMSLoadClick.bind(this));a=document.createElement("input");a.setAttribute("type","hidden");this.sections.wms.appendChild(a);console.info("WMS INPUT:",a);this.addInputText(this.sections.wms,"Bezeichnung:");this.addInputSelect(this.sections.wms,"Ebene:");this.addInputSelect(this.sections.wms,
"Format:");this.addButton(this.sections.wms,"<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen</span>",this.onWMSAcceptClick.bind(this));this.showDetailsWMS(!1);this.sections.wfs=this.tabs.getContentSection(b);b+=1;a=document.createElement("div");a.className="netgis-search";this.sections.wfs.appendChild(a);this.addInputText(a,"WFS-URL:",this.config["import"].wfs_options).classList.add("netgis-round","netgis-shadow");this.addButton(this.sections.wfs,"<i class='netgis-icon fas fa-cloud-download-alt'></i><span>Dienst laden</span>",
this.onWFSLoadClick.bind(this));this.addInputText(this.sections.wfs,"Bezeichnung:");this.addInputSelect(this.sections.wfs,"Ebene:");this.addInputSelect(this.sections.wfs,"Format:");this.addButton(this.sections.wfs,"<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen</span>",this.onWFSAcceptClick.bind(this));this.showDetailsWFS(!1);this.sections.geojson=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.geojson,"GeoJSON-Datei:",".geojson,.json");this.addText(this.sections.geojson,
"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.geojson,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onGeoJSONAcceptClick.bind(this));this.sections.gml=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.gml,"GML-Datei:",".gml,.xml");this.addText(this.sections.gml,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");
this.addButton(this.sections.gml,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onGMLAcceptClick.bind(this));this.sections.geopackage=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.geopackage,"GeoPackage-Datei:",".gpkg");this.addText(this.sections.geopackage,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.geopackage,
"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onGeoPackageAcceptClick.bind(this));this.sections.spatialite=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.spatialite,"Spatialite-Datei:",".sqlite");this.addText(this.sections.spatialite,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.spatialite,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",
this.onSpatialiteAcceptClick.bind(this));this.sections.shapefile=this.tabs.getContentSection(b);this.addInputFile(this.sections.shapefile,"Shapefile-Zip-Datei:",".zip");this.addText(this.sections.shapefile,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.shapefile,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onShapefileAcceptClick.bind(this))};
this.tabs.attachTo(this.modal.content)};
netgis.Import.prototype.initSections=function(a){this.sections={};var b=0;a["import"]&&!0===a["import"].geoportal_tab&&(this.sections.geoportal=this.tabs.getContentSection(b),b+=1,this.sections.geoportal.classList.add("netgis-geoportal"),this.geoportalSearch=new netgis.Search("Thema, Schlagwort..."),this.geoportalSearch.container.addEventListener(netgis.Events.SEARCH_CHANGE,this.onGeoportalSearchChange.bind(this)),this.geoportalSearch.container.addEventListener(netgis.Events.SEARCH_CLEAR,this.onGeoportalSearchClear.bind(this)),
this.geoportalSearch.attachTo(this.sections.geoportal),a=document.createElement("span"),a.innerHTML="Suche im Datenkatalog:",this.geoportalSearch.label.insertBefore(a,this.geoportalSearch.label.firstChild),this.geoportalLoader=document.createElement("div"),this.geoportalLoader.className="netgis-loader netgis-text-a netgis-hide",this.geoportalLoader.innerHTML="<i class='fas fa-cog'></i>",this.sections.geoportal.appendChild(this.geoportalLoader),this.geoportalResults=new netgis.Tree,this.geoportalResults.container.addEventListener(netgis.Events.TREE_ITEM_CHANGE,
this.onGeoportalTreeItemChange.bind(this)),this.geoportalResults.attachTo(this.sections.geoportal),this.geoportalSubmit=this.addButton(this.sections.geoportal,"<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen<span class='netgis-count'></span></span>",this.onGeoportalSubmit.bind(this)));this.sections.wms=this.tabs.getContentSection(b);b+=1;this.addInputText(this.sections.wms,"WMS-URL:",this.config["import"].wms_options);this.addButton(this.sections.wms,"<i class='netgis-icon fas fa-cloud-download-alt'></i><span>Dienst laden</span>",
this.onWMSLoadClick.bind(this));this.addInputText(this.sections.wms,"Bezeichnung:");this.addInputSelect(this.sections.wms,"Ebene:");this.addInputSelect(this.sections.wms,"Format:");this.addButton(this.sections.wms,"<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen</span>",this.onWMSAcceptClick.bind(this));this.showDetailsWMS(!1);this.sections.wfs=this.tabs.getContentSection(b);b+=1;this.addInputText(this.sections.wfs,"WFS-URL:",this.config["import"].wfs_options);this.addButton(this.sections.wfs,
"<i class='netgis-icon fas fa-cloud-download-alt'></i><span>Dienst laden</span>",this.onWFSLoadClick.bind(this));this.addInputText(this.sections.wfs,"Bezeichnung:");this.addInputSelect(this.sections.wfs,"Ebene:");this.addInputSelect(this.sections.wfs,"Format:");this.addButton(this.sections.wfs,"<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen</span>",this.onWFSAcceptClick.bind(this));this.showDetailsWFS(!1);this.sections.geojson=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.geojson,
"GeoJSON-Datei:",".geojson,.json");this.addText(this.sections.geojson,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.geojson,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onGeoJSONAcceptClick.bind(this));this.sections.gml=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.gml,"GML-Datei:",".gml,.xml");this.addText(this.sections.gml,
"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.gml,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onGMLAcceptClick.bind(this));this.sections.geopackage=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.geopackage,"GeoPackage-Datei:",".gpkg");this.addText(this.sections.geopackage,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");
this.addButton(this.sections.geopackage,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onGeoPackageAcceptClick.bind(this));this.sections.spatialite=this.tabs.getContentSection(b);b+=1;this.addInputFile(this.sections.spatialite,"Spatialite-Datei:",".sqlite");this.addText(this.sections.spatialite,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.spatialite,
"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",this.onSpatialiteAcceptClick.bind(this));this.sections.shapefile=this.tabs.getContentSection(b);this.addInputFile(this.sections.shapefile,"Shapefile-Zip-Datei:",".zip");this.addText(this.sections.shapefile,"<h3>Unterst\u00fctzte Koordinatensysteme:</h3><ul><li>Web Mercator (EPSG:3857)</li><li>WGS84 / Lon-Lat (EPSG:4326)</li><li>ETRS89 / UTM Zone 32N (EPSG:25832)</li></ul>");this.addButton(this.sections.shapefile,"<i class='netgis-icon fas fa-check'></i><span>Datei laden</span>",
this.onShapefileAcceptClick.bind(this))};
netgis.Import.prototype.initPreview=function(){this.preview=new netgis.Modal("Vorschau");this.preview.attachTo(this.modal.content);this.previewMapContainer=document.createElement("div");this.previewMapContainer.className="netgis-preview-map";this.preview.content.appendChild(this.previewMapContainer);if(ol){var a=this.config.map;a={projection:a.projection,center:a.centerLonLat?ol.proj.fromLonLat(a.centerLonLat):a.center,zoom:a.zoom};this.previewMap=new ol.Map({target:this.previewMapContainer,view:new ol.View(a),
pixelRatio:1,moveTolerance:3,controls:[]});this.previewMap.getView().padding=[10,10,10,10];this.previewMap.addLayer(new ol.layer.Tile({source:new ol.source.OSM}))}this.previewTree=new netgis.Tree;this.previewTree.container.classList.add("netgis-preview-tree");this.previewTree.attachTo(this.preview.content);this.previewTree.container.addEventListener(netgis.Events.TREE_ITEM_CHANGE,this.onPreviewTreeItemChange.bind(this));this.previewSubmit=document.createElement("button");this.previewSubmit.setAttribute("type",
"button");this.previewSubmit.className="netgis-import-submit netgis-button netgis-center netgis-color-a netgis-hover-c netgis-shadow";this.previewSubmit.innerHTML="<i class='netgis-icon fas fa-check'></i><span>Hinzuf\u00fcgen</span>";this.previewSubmit.addEventListener("click",this.onPreviewSubmitClick.bind(this));this.preview.content.appendChild(this.previewSubmit)};
@@ -114,13 +116,13 @@ netgis.Import.prototype.addInputText=function(a,b,c){var d=document.createElemen
netgis.Import.prototype.addInputSelect=function(a,b,c){c=document.createElement("label");c.innerHTML=b;b=document.createElement("select");c.appendChild(b);a.appendChild(c);return b};netgis.Import.prototype.addInputFile=function(a,b,c){var d=document.createElement("label");d.innerHTML=b;b=document.createElement("input");b.setAttribute("type","file");b.setAttribute("accept",c);d.appendChild(b);a.appendChild(d);return b};netgis.Import.prototype.getLayerOrder=function(){return 1E4};
netgis.Import.prototype.showDetailsWMS=function(a){var b=this.sections.wms,c=b.getElementsByTagName("label");b=b.getElementsByTagName("button");a?(c[1].classList.remove("netgis-hide"),c[2].classList.remove("netgis-hide"),c[3].classList.remove("netgis-hide"),b[1].classList.remove("netgis-hide")):(c[1].classList.add("netgis-hide"),c[2].classList.add("netgis-hide"),c[3].classList.add("netgis-hide"),b[1].classList.add("netgis-hide"))};
netgis.Import.prototype.showDetailsWFS=function(a){var b=this.sections.wfs,c=b.getElementsByTagName("label");b=b.getElementsByTagName("button");a?(c[1].classList.remove("netgis-hide"),c[2].classList.remove("netgis-hide"),c[3].classList.remove("netgis-hide"),b[1].classList.remove("netgis-hide")):(c[1].classList.add("netgis-hide"),c[2].classList.add("netgis-hide"),c[3].classList.add("netgis-hide"),b[1].classList.add("netgis-hide"))};
netgis.Import.prototype.submitImportLayer=function(a){!0===this.config["import"].preview?netgis.util.invoke(this.modal.container,netgis.Events.IMPORT_LAYER_PREVIEW,a):(this.config.layers.push(a),netgis.util.invoke(this.modal.container,netgis.Events.IMPORT_LAYER_ACCEPT,a),this.modal.hide())};netgis.Import.prototype.onImportShow=function(a){this.modal.show();this.tabs.updateHeaderScroll();this.onTabsChange({detail:{section:this.tabs.activeSection}})};
netgis.Import.prototype.onTabsChange=function(a){switch(a.detail.section){case this.sections.geoportal:this.geoportalSearch.focus();break;case this.sections.wms:this.sections.wms.getElementsByTagName("input")[0].focus();break;case this.sections.wfs:this.sections.wfs.getElementsByTagName("input")[0].focus()}};
netgis.Import.prototype.onWMSLoadClick=function(a){this.showDetailsWMS(!1);a=this.sections.wms.getElementsByTagName("input")[0].value;a=a.trim();1>a.length||(a=netgis.WMS.buildRequestURL(a,"GetCapabilities"),netgis.util.request(a,this.onWMSCapsResponse.bind(this)))};
netgis.Import.prototype.onWMSCapsResponse=function(a){var b=(new DOMParser).parseFromString(a,"text/xml"),c=b.documentElement;b=b.getElementsByTagName("parsererror");for(var d=0;d<b.length;d++)console.error("WMS caps parser error:",b[d].textContent);0<b.length&&alert(0<a.length?a:b[0].textContent);b=this.sections.wms;var e=b.getElementsByTagName("input");b=b.getElementsByTagName("select");var f=b[0];b=b[1];for(d=f.options.length-1;0<=d;d--)f.options.remove(d);for(d=b.options.length-1;0<=d;d--)b.options.remove(d);
switch(c.nodeName){default:case "HTML":console.warn("could not detect WMS service",c);break;case "WMS_Capabilities":case "WMT_MS_Capabilities":a=netgis.WMS.parseCapabilities(a);console.info("WMS PARSED:",a);c.getAttribute("version");a=c.getElementsByTagName("Service")[0].getElementsByTagName("Title")[0].textContent;e[2].value=a;d=c.getElementsByTagName("Layer");var g=[];console.info("WMS LAYERS:",d.length,"->",d);for(var h=0;h<d.length;h++){a=d[h];var k=a.getElementsByTagName("Name")[0].textContent,
l=a.getElementsByTagName("Title")[0].textContent;g.push({name:k,title:l});a=document.createElement("option");a.text=l;a.value=k;f.options.add(a)}c=c.getElementsByTagName("GetMap")[0];e[1].value=c.getElementsByTagName("Get")[0].getElementsByTagName("OnlineResource")[0].getAttribute("xlink:href");c=c.getElementsByTagName("Format");e=[];for(f=0;f<c.length;f++)a=c[f],d=a.textContent,-1!==d.search("image")&&(e.push(d),a=document.createElement("option"),a.text=d,a.value=d,b.options.add(a))}this.showDetailsWMS(!0)};
netgis.Import.prototype.onWMSAcceptClick=function(a){a=this.sections.wms;var b=a.getElementsByTagName("input"),c=a.getElementsByTagName("select"),d="import_"+netgis.util.getTimeStamp(!0);b=b[1].value;var e=c[0].selectedOptions[0].innerText,f=c[0].value;c=c[1].value;d={id:d,folder:null,active:!0,query:!0,order:this.getLayerOrder(),type:netgis.LayerTypes.WMS,url:b,title:e,name:f,format:c,tiled:!0};this.config.layers.push(d);netgis.util.invoke(a,netgis.Events.IMPORT_LAYER_ACCEPT,d);this.modal.hide()};
netgis.Import.prototype.submitImportLayer=function(a){!0===this.config["import"].preview?netgis.util.invoke(this.modal.container,netgis.Events.IMPORT_LAYER_PREVIEW,a):(this.config.layers.push(a),netgis.util.invoke(this.modal.container,netgis.Events.IMPORT_LAYER_ACCEPT,a),this.modal.hide())};netgis.Import.prototype.onImportShow=function(a){this.modal.show();this.tabs.updateHeaderScroll()};
netgis.Import.prototype.onWMSLoadClick=function(a){this.showDetailsWMS(!1);a=this.sections.wms.getElementsByTagName("input")[0].value;a=a.trim();if(!(1>a.length)){var b=netgis.util.parseURL(a);a=b.base;b=b.parameters;b.push("request=GetCapabilities");b=b.join("&");-1===b.search("service=")&&(b+="&service=WMS");netgis.util.request(a+"?"+b,this.onWMSCapsResponse.bind(this))}};
netgis.Import.prototype.onWMSCapsResponse=function(a){var b=(new DOMParser).parseFromString(a,"text/xml"),c=b.documentElement;b=b.getElementsByTagName("parsererror");for(var d=0;d<b.length;d++)console.error("WMS caps parser error:",b[d].textContent);0<b.length&&alert(0<a.length?a:b[0].textContent);a=this.sections.wms;var e=a.getElementsByTagName("input");a=a.getElementsByTagName("select");b=a[0];a=a[1];for(d=b.options.length-1;0<=d;d--)b.options.remove(d);for(d=a.options.length-1;0<=d;d--)a.options.remove(d);
switch(c.nodeName){default:case "HTML":console.warn("could not detect WMS service",c);break;case "WMS_Capabilities":case "WMT_MS_Capabilities":c.getAttribute("version");d=c.getElementsByTagName("Service")[0].getElementsByTagName("Title")[0].textContent;e[1].value=d;d=c.getElementsByTagName("Layer");for(var f=[],g=0;g<d.length;g++){e=d[g];var h=e.getElementsByTagName("Name")[0].textContent,k=e.getElementsByTagName("Title")[0].textContent;f.push({name:h,title:k});e=document.createElement("option");
e.text=k;e.value=h;b.options.add(e)}c=c.getElementsByTagName("GetMap")[0].getElementsByTagName("Format");b=[];for(d=0;d<c.length;d++)e=c[d],f=e.textContent,-1!==f.search("image")&&(b.push(f),e=document.createElement("option"),e.text=f,e.value=f,a.options.add(e))}this.showDetailsWMS(!0)};
netgis.Import.prototype.onWMSAcceptClick=function(a){a=this.sections.wms;var b=a.getElementsByTagName("input"),c=a.getElementsByTagName("select"),d="import_"+netgis.util.getTimeStamp(!0),e=b[0].value;b=c[0].selectedOptions[0].innerText;var f=c[0].value;c=c[1].value;e=netgis.util.replace(e,"request=","oldrequest=");e=netgis.util.replace(e,"Request=","oldrequest=");var g=netgis.util.parseURL(e);e=g.base;g=g.parameters;g=g.join("&");-1===g.search("service=")&&(g+="&service=WMS");e=e+"?"+g;g={id:d,folder:null,
active:!0,query:!0,order:this.getLayerOrder(),type:netgis.LayerTypes.WMS,url:e,title:b,name:f,format:c,tiled:!0};this.config.layers.push(g);netgis.util.invoke(a,netgis.Events.IMPORT_LAYER_ACCEPT,g);this.modal.hide()};
netgis.Import.prototype.onWFSLoadClick=function(a){this.showDetailsWFS(!1);var b=this.sections.wfs.getElementsByTagName("input")[0].value;b=b.trim();if(!(1>b.length)){var c=b.indexOf("?");a=-1<c?b.substr(0,c):b;var d=["request=GetCapabilities"];if(-1<c)for(b=b.substr(c+1),b=b.split("&"),c=0;c<b.length;c++){var e=b[c];e=e.toLowerCase();-1<e.search("service")?d.push(e):-1<e.search("version")?d.push(e):-1<e.search("request")||d.push(e)}d=d.join("&");-1===d.search("service=")&&(d+="&service=WFS");a=a+
"?"+d;this.config["import"].wfs_proxy&&(a=this.config["import"].wfs_proxy+a);netgis.util.request(a,this.onWFSCapsResponse.bind(this))}};
netgis.Import.prototype.onWFSCapsResponse=function(a){var b=(new DOMParser).parseFromString(a,"text/xml"),c=b.documentElement;b=b.getElementsByTagName("parsererror");for(var d=0;d<b.length;d++)console.error("WFS caps parser error:",b[d].textContent);0<b.length&&alert(0<a.length?a:b[0].textContent);a=this.sections.wfs;var e=a.getElementsByTagName("input");a=a.getElementsByTagName("select");b=a[0];a=a[1];for(d=b.options.length-1;0<=d;d--)b.options.remove(d);for(d=a.options.length-1;0<=d;d--)a.options.remove(d);
@@ -128,64 +130,54 @@ switch(c.nodeName){default:case "HTML":console.error("could not detect WFS servi
e.text=k;e.value=h;b.options.add(e)}c=c.getElementsByTagName("ows:Operation");b=null;for(e=0;e<c.length;e++)if("GetFeature"===c[e].getAttribute("name")){b=c[e];break}c=null;if(b)for(b=b.getElementsByTagName("ows:Parameter"),e=0;e<b.length;e++)if(d=b[e],"outputFormat"===d.getAttribute("name")){b=d.getElementsByTagName("ows:Value");for(d=0;d<b.length;d++)e=b[d],f=e.textContent,e=document.createElement("option"),e.text=f,e.value=f,a.options.add(e),-1<f.search("json")&&(c=f);break}c&&(a.value=c)}this.showDetailsWFS(!0)};
netgis.Import.prototype.onWFSAcceptClick=function(a){a=this.sections.wfs;var b=a.getElementsByTagName("input"),c=a.getElementsByTagName("select"),d="import_"+netgis.util.getTimeStamp(!0);b=b[0].value;var e=c[0].selectedOptions[0].innerText,f=c[0].value;c=c[1].value;b=netgis.util.replace(b,"request=","oldrequest=");b=netgis.util.replace(b,"Request=","oldrequest=");this.config["import"].wfs_proxy&&(b=this.config["import"].wfs_proxy+b);d={id:d,folder:null,active:!0,order:this.getLayerOrder(),style:this.config.styles["import"],
title:e,type:netgis.LayerTypes.WFS,url:b,name:f,format:c};this.config.layers.push(d);netgis.util.invoke(a,netgis.Events.IMPORT_LAYER_ACCEPT,d);this.modal.hide()};netgis.Import.prototype.onGeoJSONAcceptClick=function(a){if(a=this.sections.geojson.getElementsByTagName("input")[0].files[0]){var b=new FileReader;b.title=a.name;b.onload=this.onGeoJSONLoad.bind(this);b.readAsText(a)}else alert("No file selected!")};
netgis.Import.prototype.onGeoJSONLoad=function(a){var b=a.target;a=b.title;b=b.result;a={id:"import_"+netgis.util.getTimeStamp(!0),folder:null,active:!0,order:this.getLayerOrder(),style:this.config.styles&&this.config.styles["import"]?this.config.styles["import"]:null,title:a,type:netgis.LayerTypes.GEOJSON,data:b};this.submitImportLayer(a)};
netgis.Import.prototype.onGMLAcceptClick=function(a){if(a=this.sections.gml.getElementsByTagName("input")[0].files[0]){var b=new FileReader;b.title=a.name;b.onload=this.onGMLLoad.bind(this);b.readAsText(a)}else alert("No file selected!")};
netgis.Import.prototype.onGeoJSONLoad=function(a){var b=a.target;a=b.title;b=b.result;a={id:"import_"+netgis.util.getTimeStamp(!0),folder:null,active:!0,order:this.getLayerOrder(),style:this.config.styles["import"],title:a,type:netgis.LayerTypes.GEOJSON,data:b};this.submitImportLayer(a)};netgis.Import.prototype.onGMLAcceptClick=function(a){if(a=this.sections.gml.getElementsByTagName("input")[0].files[0]){var b=new FileReader;b.title=a.name;b.onload=this.onGMLLoad.bind(this);b.readAsText(a)}else alert("No file selected!")};
netgis.Import.prototype.onGMLLoad=function(a){var b=a.target;a=b.result;var c="import_"+netgis.util.getTimeStamp(!0);b=b.title;a={id:c,folder:null,active:!0,order:this.getLayerOrder(),style:this.config.styles["import"],title:b,type:netgis.LayerTypes.GML,data:a};this.submitImportLayer(a)};netgis.Import.prototype.onGeoPackageAcceptClick=function(a){if(a=this.sections.geopackage.getElementsByTagName("input")[0].files[0]){var b=new FileReader;b.title=a.name;b.onload=this.onGeoPackageLoad.bind(this);b.readAsArrayBuffer(a)}else alert("No file selected!")};
netgis.Import.prototype.onGeoPackageLoad=function(a){var b=a.target;a=b.result;var c="import_"+netgis.util.getTimeStamp(!0);b=b.title;a={id:c,folder:null,active:!0,order:this.getLayerOrder(),style:this.config.styles["import"],title:b,type:netgis.LayerTypes.GEOPACKAGE,data:a};this.submitImportLayer(a)};
netgis.Import.prototype.onSpatialiteAcceptClick=function(a){if(a=this.sections.spatialite.getElementsByTagName("input")[0].files[0]){var b=new FileReader;b.title=a.name;b.onload=this.onSpatialiteLoad.bind(this);b.readAsArrayBuffer(a)}else alert("No file selected!")};
netgis.Import.prototype.onSpatialiteLoad=function(a){var b=a.target;a=b.result;var c="import_"+netgis.util.getTimeStamp(!0);b=b.title;a={id:c,folder:null,active:!0,order:this.getLayerOrder(),style:this.config.styles["import"],title:b,type:netgis.LayerTypes.SPATIALITE,data:a};this.submitImportLayer(a)};
netgis.Import.prototype.onShapefileAcceptClick=function(a){if(a=this.sections.shapefile.getElementsByTagName("input")[0].files[0]){var b=new FileReader;b.title=a.name;b.onload=this.onShapefileLoad.bind(this);b.readAsArrayBuffer(a)}else alert("No file selected!")};
netgis.Import.prototype.onShapefileLoad=function(a){var b=a.target;a=b.result;var c="import_"+netgis.util.getTimeStamp(!0);b=b.title;a={id:c,folder:null,active:!0,order:this.getLayerOrder(),style:this.config.styles["import"],title:b,type:netgis.LayerTypes.SHAPEFILE,data:a};this.submitImportLayer(a)};
netgis.Import.prototype.onImportPreviewFeatures=function(a){var b=a.detail,c=b.layer;this.previewTree.clear();a=this.previewMap.getLayers().getArray();for(var d=1;d<a.length;d++)this.previewMap.removeLayer(a[d]);if(ol){this.config.styles&&this.config.styles["import"]&&(a=this.config.styles["import"],a=new ol.style.Style({fill:new ol.style.Fill({color:a.fill}),stroke:new ol.style.Stroke({color:a.stroke,width:a.width})}),c.setStyle(a));this.previewMap.addLayer(c);var e=this.previewTree.addFolder(null,
b.id,b.title);a=c.getSource().getFeatures();if(0===a.length){var f=this;c.getSource().on("addfeature",function(a){f.featureLoadTimeout&&window.clearTimeout(f.featureLoadTimeout);f.featureLoadTimeout=window.setTimeout(function(){var a=c.getSource().getFeatures();f.updatePreviewFeatures(a,e,c,b);f.featureLoadTimeout=null},100)})}else this.updatePreviewFeatures(a,e,c,b)}else console.error("import preview only supported with OL map renderer",c)};
netgis.Import.prototype.onImportPreviewFeatures=function(a){var b=a.detail,c=b.layer;this.previewTree.clear();a=this.previewMap.getLayers().getArray();for(var d=1;d<a.length;d++)this.previewMap.removeLayer(a[d]);if(ol){a=config.styles["import"];a=new ol.style.Style({fill:new ol.style.Fill({color:a.fill}),stroke:new ol.style.Stroke({color:a.stroke,width:a.width})});c.setStyle(a);this.previewMap.addLayer(c);var e=this.previewTree.addFolder(null,b.id,b.title);a=c.getSource().getFeatures();if(0===a.length){var f=
this;c.getSource().on("addfeature",function(a){f.featureLoadTimeout&&window.clearTimeout(f.featureLoadTimeout);f.featureLoadTimeout=window.setTimeout(function(){var a=c.getSource().getFeatures();f.updatePreviewFeatures(a,e,c,b);f.featureLoadTimeout=null},100)})}else this.updatePreviewFeatures(a,e,c,b)}else console.error("import preview only supported with OL map renderer",c)};
netgis.Import.prototype.updatePreviewFeatures=function(a,b,c,d){for(var e=0;e<a.length;e++){var f=a[e],g=f.getId(),h=f.getProperties();g||(g=e+1,f.setId(g));var k=null,l;for(l in h)switch(l.toLowerCase()){case "name":k=h[l];break;case "title":k=h[l];break;case "id":k=h[l];break;case "gid":k=h[l];break;case "oid":k=h[l];break;case "objectid":k=h[l]}k||(k=g);h=f.getGeometry();if(h instanceof ol.geom.Polygon||h instanceof ol.geom.MultiPolygon)k+=" ("+netgis.util.formatArea(h.getArea())+")";else if(h instanceof
ol.geom.LineString)k+=" ("+netgis.util.formatArea(h.getLength())+")";else if(h instanceof ol.geom.MultiLineString){f=0;h=h.getLineStrings();for(var n=0;n<h.length;n++)f+=h[n].getLength();k+=" ("+netgis.util.formatArea(f)+")"}this.previewTree.addCheckbox(b,g,"Feature "+k,!0)}this.previewTree.setFolderOpen(d.id,!0);this.previewTree.updateFolderChecks();this.preview.show();this.previewMap.updateSize();this.previewMap.getView().fit(c.getSource().getExtent())};
ol.geom.LineString)k+=" ("+netgis.util.formatArea(h.getLength())+")";else if(h instanceof ol.geom.MultiLineString){f=0;h=h.getLineStrings();for(var m=0;m<h.length;m++)f+=h[m].getLength();k+=" ("+netgis.util.formatArea(f)+")"}this.previewTree.addCheckbox(b,g,"Feature "+k,!0)}this.previewTree.setFolderOpen(d.id,!0);this.previewTree.updateFolderChecks();this.preview.show();this.previewMap.updateSize();this.previewMap.getView().fit(c.getSource().getExtent())};
netgis.Import.prototype.onPreviewSubmitClick=function(a){var b=this.previewTree.container.getElementsByClassName("netgis-folder")[0];a=b.getAttribute("data-id");b=b.getElementsByTagName("span")[0].innerText;for(var c=this.previewTree.container.getElementsByTagName("input"),d=this.previewMap.getLayers().getArray()[1].getSource(),e=[],f=1;f<c.length;f++){var g=c[f];g.checked&&(g=g.getAttribute("data-id"),g=d.getFeatureById(g),e.push(g))}c=(new ol.format.GeoJSON).writeFeaturesObject(e);d=this.previewMap.getView().getProjection().getCode();
c.crs={type:"name",properties:{name:"urn:ogc:def:crs:"+d.replace(":","::")}};a={id:a,folder:null,active:!0,editable:this.config["import"]&&!0===this.config["import"].editable,order:this.getLayerOrder(),style:this.config.styles&&this.config.styles["import"]?this.config.styles["import"]:null,title:b,type:netgis.LayerTypes.GEOJSON,data:c};this.config.layers.push(a);netgis.util.invoke(this.preview.container,netgis.Events.IMPORT_LAYER_ACCEPT,a);this.preview.hide();this.modal.hide()};
c.crs={type:"name",properties:{name:"urn:ogc:def:crs:"+d.replace(":","::")}};a={id:a,folder:null,active:!0,order:this.getLayerOrder(),style:this.config.styles["import"],title:b,type:netgis.LayerTypes.GEOJSON,data:c};this.config.layers.push(a);netgis.util.invoke(this.preview.container,netgis.Events.IMPORT_LAYER_ACCEPT,a);this.preview.hide();this.modal.hide()};
netgis.Import.prototype.onPreviewTreeItemChange=function(a){a=a.detail;var b=this.previewMap.getLayers().getArray()[1].getSource().getFeatureById(a.id);a.checked?b.setStyle(null):b.setStyle(new ol.style.Style({}))};netgis.Import.prototype.onGeoportalSearchKeyUp=function(a){switch(a.keyCode){case 13:break;case 27:break;default:this.onGeoportalSearchChange()}};
netgis.Import.prototype.onGeoportalSearchChange=function(a){a=a.detail.query;if(0<a.length){this.geoportalLoader.classList.remove("netgis-hide");this.geoportalResults.clear();a=netgis.util.replace(a," ",",");var b=this.config["import"].geoportal_search_url;b=netgis.util.replace(b,"{query}",window.encodeURIComponent(a));netgis.util.request(b,this.onGeoportalSearchResponse.bind(this));this.geoportalSearch.showClearButton(!0)}};netgis.Import.prototype.onGeoportalSearchClear=function(a){this.geoportalResults.clear()};
netgis.Import.prototype.onGeoportalSearchResponse=function(a){function b(a,c){if(a&&a.layer)for(var d=0;d<a.layer.length;d++)b(a.layer[d],c);else a&&c.push(a);return c.length}a=JSON.parse(a);if(this.config["import"].geoportal_search_dynamic)this.updateGeoportalResults(a,!0);else{this.geoportalDataRaw=a=a.wms.srv;this.geoportalData=[];for(var c={},d=0;d<a.length;d++){var e=a[d];var f=[];var g=b(e,f),h=e.title;e.wmsRootLayerId&&(h=e.wmsRootLayerId);if(c[h]){var k=c[h];l=k.getElementsByTagName("summary")[0];
h=l.getElementsByClassName("netgis-count")[0];h.innerHTML=Number.parseInt(h.innerText)+g}else{var l=e.title+" (<span class='netgis-count'>"+g+"</span>)";k=this.geoportalResults.addFolder(null,d,l);k.setAttribute("title",e["abstract"]);c[h]=k}for(g=0;g<f.length;g++)this.geoportalResults.addCheckbox(k,g,f[g].title);e.children=f;this.geoportalData.push(e)}this.geoportalLoader.classList.add("netgis-hide")}};
netgis.Import.prototype.updateGeoportalResults=function(a,b){this.geoportalData={data:a,folders:{}};a=a.wms.srv;if(b){b={};for(var c=0;c<a.length;c++){var d=a[c],e=d.title;d.wmsRootLayerId&&(e=d.wmsRootLayerId);if(b[e]){var f=b[e];f.getElementsByTagName("summary")}else f=this.geoportalResults.addFolder(null,e,d.title,!1,!1),f.setAttribute("title",d["abstract"]),f.setAttribute("data-url",d.wmsGetCapabilitiesUrl),f.setAttribute("data-title",d.title),f.getElementsByTagName("details")[0].addEventListener("toggle",
this.onGeoportalFolderToggle.bind(this)),b[e]=f}this.geoportalData.folders=b}this.geoportalResults.container.scrollTop=0;this.geoportalLoader.classList.add("netgis-hide")};
netgis.Import.prototype.onGeoportalFolderToggle=function(a){a=a.currentTarget;var b=a.parentNode,c=a.getElementsByTagName("netgis-item");!a.open||0<c||(a=b.getAttribute("data-url"),netgis.util.request(a,function(a){this.onGeoportalFolderResponse(b,a)}.bind(this)),b.getElementsByTagName("ul")[0].innerHTML="",0===b.getElementsByClassName("netgis-loader").length&&this.geoportalResults.addButton(b,0,"<span class='netgis-loader netgis-color-e netgis-text-a'><i class='fas fa-spinner'></i></span>",null))};
netgis.Import.prototype.onGeoportalFolderResponse=function(a,b){a.getElementsByTagName("ul")[0].innerHTML="";b=netgis.WMS.parseCapabilities(b);this.createGeoportalLayers(a,b,b.layers,!0)};
netgis.Import.prototype.createGeoportalLayers=function(a,b,c,d){var e=a.getAttribute("data-id"),f=b.requests.map.url,g=b.requests.info.url,h=null;-1<b.requests.map.format.indexOf("image/jpeg")&&(h="image/jpeg");-1<b.requests.map.format.indexOf("image/png")&&(h="image/png");var k=null;-1<b.requests.info.format.indexOf("text/plain")&&(k="text/plain");-1<b.requests.info.format.indexOf("text/html")&&(k="text/html");for(var l=0;l<c.length;l++){var n=c[l],m=e+"_"+n.name;n.id=m;0===n.children.length?(m=
this.geoportalResults.addCheckbox(a,m,n.title),m.setAttribute("title",n.abstract),m=m.getElementsByTagName("input")[0],m.setAttribute("data-map-url",f),m.setAttribute("data-info-url",g),m.setAttribute("data-map-format",h),m.setAttribute("data-info-format",k),m.setAttribute("data-name",n.name),m.setAttribute("data-title",n.title),m.setAttribute("data-queryable",n.queryable)):(m=this.geoportalResults.addFolder(a,m,n.title),m.setAttribute("title",n.abstract),m.setAttribute("data-title",n.title),d&&this.createGeoportalLayers(m,
b,n.children,!0))}};netgis.Import.prototype.onGeoportalSearchButtonClick=function(a){this.geoportalSearch.onInputTimeout()};netgis.Import.prototype.onGeoportalTreeItemChange=function(a){a=this.geoportalResults.container.getElementsByClassName("netgis-item");for(var b=0,c=0;c<a.length;c++)a[c].getElementsByTagName("input")[0].checked&&(b+=1);this.geoportalSubmit.getElementsByClassName("netgis-count")[0].innerHTML=0===b?"":" ("+b+")"};
netgis.Import.prototype.onGeoportalSubmitDynamic=function(a){var b=this.geoportalResults.container.getElementsByClassName("netgis-item"),c=0;for(a=0;a<b.length;a++){var d=b[a],e=d.getElementsByTagName("input")[0];if(e.checked){c+=1;var f=d.parentNode.parentNode.parentNode;d=e.getAttribute("data-id");var g=f.getAttribute("data-id");d="geoportal_"+d;g="geoportal_"+g;f=f.getAttribute("data-title");var h=e.getAttribute("data-title"),k=e.getAttribute("data-map-url");e=e.getAttribute("data-name");netgis.util.invoke(this.sections.geoportal,
netgis.Events.IMPORT_GEOPORTAL_SUBMIT,{folder:{id:g,title:f},layer:{id:d,url:k,name:e,title:h}})}}if(0<c){this.modal.hide();b=this.geoportalResults.container.getElementsByTagName("input");for(a=0;a<b.length;a++)b[a].checked=!1;this.geoportalResults.updateFolderChecks();this.onGeoportalTreeItemChange()}};
netgis.Import.prototype.onGeoportalSubmit=function(a){if(this.config["import"].geoportal_search_dynamic)this.onGeoportalSubmitDynamic(a);else{a=this.geoportalResults.container.getElementsByClassName("netgis-item");for(var b=0,c=0;c<a.length;c++){var d=a[c],e=d.getElementsByTagName("input")[0];if(e.checked){b+=1;e=e.getAttribute("data-id");d=d.parentNode.parentNode.parentNode.getAttribute("data-id");var f=this.geoportalData[d],g=f.children[e],h=f.title;f=f.getMapUrl;var k=g.name;g=g.title;d="geoportal_"+
d;e=d+"_"+e;netgis.util.invoke(this.sections.geoportal,netgis.Events.IMPORT_GEOPORTAL_SUBMIT,{folder:{id:d,title:h},layer:{id:e,url:f,name:k,title:g}})}}0<b&&this.modal.hide()}};netgis=netgis||{};netgis.Info=function(a){this.config=a;this.queryLayers={};this.popup=new netgis.Popup;this.popup.setHeader("Abfrage");this.initConfig(a)};netgis.Info.Config={default_format:"text/plain",proxy:""};netgis.Info.prototype.initConfig=function(a){a=a.layers;for(var b=a.length-1;0<=b;b--){var c=a[b];!0===c.active&&this.isLayerQueryable(c)?this.queryLayers[c.id]=c:this.queryLayers[c.id]&&delete this.queryLayers[c.id]}};
netgis.Import.prototype.onGeoportalSearchChange=function(a){a=a.detail.query;if(0<a.length){this.geoportalLoader.classList.remove("netgis-hide");a=netgis.util.replace(a," ",",");var b=this.config["import"].geoportal_search_url;b=netgis.util.replace(b,"{query}",window.encodeURIComponent(a));netgis.util.request(b,this.onGeoportalSearchResponse.bind(this));this.geoportalSearch.showClearButton(!0)}};netgis.Import.prototype.onGeoportalSearchClear=function(a){this.geoportalResults.clear()};
netgis.Import.prototype.onGeoportalSearchResponse=function(a){function b(a){if(a&&a.layer)for(var c=0;c<a.layer.length;c++)b(a.layer[c]);else a&&e.push(a);return e.length}a=JSON.parse(a);this.geoportalResults.clear();this.geoportalDataRaw=a=a.wms.srv;this.geoportalData=[];for(var c=0;c<a.length;c++){var d=a[c];var e=[];var f=b(d);f=this.geoportalResults.addFolder(null,c,d.title+" ("+f+")");f.setAttribute("title",d["abstract"]);for(var g=0;g<e.length;g++)this.geoportalResults.addCheckbox(f,g,e[g].title);
d.children=e;this.geoportalData.push(d)}this.geoportalLoader.classList.add("netgis-hide")};netgis.Import.prototype.onGeoportalTreeItemChange=function(a){a=this.geoportalResults.container.getElementsByClassName("netgis-item");for(var b=0,c=0;c<a.length;c++)a[c].getElementsByTagName("input")[0].checked&&(b+=1);this.geoportalSubmit.getElementsByClassName("netgis-count")[0].innerHTML=0===b?"":" ("+b+")"};
netgis.Import.prototype.onGeoportalSubmit=function(a){a=this.geoportalResults.container.getElementsByClassName("netgis-item");for(var b=0,c=0;c<a.length;c++){var d=a[c],e=d.getElementsByTagName("input")[0];if(e.checked){b+=1;e=e.getAttribute("data-id");d=d.parentNode.parentNode.parentNode.getAttribute("data-id");var f=this.geoportalData[d],g=f.children[e],h=f.title;f=f.getMapUrl;var k=g.name;g=g.title;d="geoportal_"+d;e=d+"_"+e;netgis.util.invoke(this.sections.geoportal,netgis.Events.IMPORT_GEOPORTAL_SUBMIT,
{folder:{id:d,title:h},layer:{id:e,url:f,name:k,title:g}})}}0<b&&this.modal.hide()};netgis=netgis||{};netgis.Info=function(a){this.config=a;this.queryLayers={};this.popup=new netgis.Popup;this.popup.setHeader("Abfrage");this.initConfig(a)};netgis.Info.Config={default_format:"text/plain",proxy:""};netgis.Info.prototype.initConfig=function(a){a=a.layers;for(var b=a.length-1;0<=b;b--){var c=a[b];!0===c.active&&this.isLayerQueryable(c)?this.queryLayers[c.id]=c:this.queryLayers[c.id]&&delete this.queryLayers[c.id]}};
netgis.Info.prototype.attachTo=function(a){this.popup.attachTo(a);a.addEventListener(netgis.Events.CLIENT_CONTEXT_RESPONSE,this.onClientContextResponse.bind(this));a.addEventListener(netgis.Events.CLIENT_SET_MODE,this.onClientSetMode.bind(this));a.addEventListener(netgis.Events.MAP_LAYER_TOGGLE,this.onMapLayerToggle.bind(this));a.addEventListener(netgis.Events.MAP_LAYER_CREATE,this.onMapLayerCreate.bind(this));a.addEventListener(netgis.Events.MAP_LAYER_DELETE,this.onMapLayerDelete.bind(this));a.addEventListener(netgis.Events.IMPORT_LAYER_ACCEPT,
this.onImportLayerAccept.bind(this));a.addEventListener(netgis.Events.IMPORT_GEOPORTAL_SUBMIT,this.onImportGeoportalSubmit.bind(this));a.addEventListener(netgis.Events.MAP_CLICK,this.onMapClick.bind(this));a.addEventListener(netgis.Events.MAP_FEATURE_CLICK,this.onMapFeatureClick.bind(this))};netgis.Info.prototype.isLayerQueryable=function(a){var b=!1;if(!0===a.query)b=!0;else if(!1!==a.query)switch(a.type){case netgis.LayerTypes.WMS:case netgis.LayerTypes.WMST:b=!0}return b};
netgis.Info.prototype.addSection=function(a,b,c){this.popup.addContent([!0===c?"<details open='open'>":"<details>","<summary class='netgis-button netgis-noselect netgis-clip-text netgis-color-d netgis-hover-text-a netgis-hover-d'>",a,"</summary><div class='netgis-border-d'>",b,"</div></details>"].join(""))};netgis.Info.prototype.onClientContextResponse=function(a){this.initConfig(a.detail.context.config)};netgis.Info.prototype.onClientSetMode=function(a){this.popup.hide()};
netgis.Info.prototype.onMapLayerToggle=function(a){var b=a.detail;a=b.id;if(b.on){b=this.config.layers;for(var c=null,d=0;d<b.length;d++)if(b[d].id===a){c=b[d];break}c&&this.isLayerQueryable(c)&&(this.queryLayers[a]=c)}else delete this.queryLayers[a]};netgis.Info.prototype.onMapLayerCreate=function(a){a=a.detail;this.isLayerQueryable(a)&&(this.queryLayers[a.id]=a)};netgis.Info.prototype.onMapLayerDelete=function(a){delete this.queryLayers[a.detail.id]};
netgis.Info.prototype.onImportLayerAccept=function(a){a=a.detail;this.isLayerQueryable(a)&&(this.queryLayers[a.id]=a)};netgis.Info.prototype.onImportGeoportalSubmit=function(a){};
netgis.Info.prototype.onMapClick=function(a){a=a.detail;if(a.mode===netgis.Modes.SEARCH_PARCEL)this.popup.clearContent(),this.popup.hide();else if(a.mode===netgis.Modes.VIEW){var b=this.config.info;this.popup.container!==a.overlay&&this.popup.attachTo(a.overlay);this.popup.clearContent();var c=0,d;for(d in this.queryLayers){var e=this.queryLayers[d];if(a.info&&a.info[d]){var f=a.info[d];b&&b.proxy&&0<b.proxy.length&&(f=b.proxy+f);netgis.util.request(f,this.onLayerResponseWMS.bind(this),{title:e.title});
netgis.Info.prototype.onMapClick=function(a){a=a.detail;if(a.mode===netgis.Modes.SEARCH_PARCEL)this.popup.clearContent(),this.popup.hide();else if(a.mode===netgis.Modes.VIEW){var b=this.config.info;this.popup.container!==a.overlay&&this.popup.attachTo(a.overlay);this.popup.clearContent();var c=0,d;for(d in this.queryLayers){var e=this.queryLayers[d];if(a.info&&a.info[d]){var f=a.info[d];b.proxy&&0<b.proxy.length&&(f=b.proxy+f);netgis.util.request(f,this.onLayerResponseWMS.bind(this),{title:e.title});
c+=1}else{if(!e.query_url||""===e.query_url)switch(e.type){case netgis.LayerTypes.WMS:case netgis.LayerTypes.WMST:f=e.url;var g=["SERVICE=WMS","VERSION=1.1.1","REQUEST=GetFeatureInfo","STYLES=","LAYERS="+window.encodeURIComponent(e.name),"QUERY_LAYERS="+window.encodeURIComponent(e.name),"BBOX="+a.view.bbox.join(","),"SRS="+a.view.projection,"WIDTH="+a.view.width,"HEIGHT="+a.view.height,"X="+Math.round(a.pixel[0]),"Y="+Math.round(a.pixel[1]),"INFO_FORMAT="+(b&&b.default_format?b.default_format:"text/plain")];
f=f+(-1===f.indexOf("?")?"?":"")+g.join("&");b&&b.proxy&&0<b.proxy.length&&(f=b.proxy+f);netgis.util.request(f,this.onLayerResponseWMS.bind(this),{title:e.title});c+=1}(f=e.query_url)&&""!==f&&(f=netgis.util.replace(f,"{bbox}",a.view.bbox.join(",")),f=netgis.util.replace(f,"{proj}",a.view.projection),f=netgis.util.replace(f,"{width}",a.view.width),f=netgis.util.replace(f,"{height}",a.view.height),f=netgis.util.replace(f,"{x}",a.coords[0]),f=netgis.util.replace(f,"{y}",a.coords[1]),f=netgis.util.replace(f,
"{px}",a.pixel[0]),f=netgis.util.replace(f,"{py}",a.pixel[1]),f=netgis.util.replace(f,"{lon}",a.lon),f=netgis.util.replace(f,"{lat}",a.lat),b&&b.proxy&&0<b.proxy.length&&(f=b.proxy+f),netgis.util.request(f,this.onLayerResponseWMS.bind(this),{title:e.title}),c+=1)}}0<c?(this.popup.showLoader(),this.popup.show()):this.popup.hide()}};
f=f+(-1===f.indexOf("?")?"?":"")+g.join("&");b.proxy&&0<b.proxy.length&&(f=b.proxy+f);netgis.util.request(f,this.onLayerResponseWMS.bind(this),{title:e.title});c+=1}(f=e.query_url)&&""!==f&&(f=netgis.util.replace(f,"{bbox}",a.view.bbox.join(",")),f=netgis.util.replace(f,"{proj}",a.view.projection),f=netgis.util.replace(f,"{width}",a.view.width),f=netgis.util.replace(f,"{height}",a.view.height),f=netgis.util.replace(f,"{x}",a.coords[0]),f=netgis.util.replace(f,"{y}",a.coords[1]),f=netgis.util.replace(f,
"{px}",a.pixel[0]),f=netgis.util.replace(f,"{py}",a.pixel[1]),f=netgis.util.replace(f,"{lon}",a.lon),f=netgis.util.replace(f,"{lat}",a.lat),b.proxy&&0<b.proxy.length&&(f=b.proxy+f),netgis.util.request(f,this.onLayerResponseWMS.bind(this),{title:e.title}),c+=1)}}0<c?(this.popup.showLoader(),this.popup.show()):this.popup.hide()}};
netgis.Info.prototype.onMapFeatureClick=function(a){var b=a.detail,c=b.properties;if(b.mode===netgis.Modes.SEARCH_PARCEL)this.popup.clearContent(),this.popup.hide();else{a=null;var d=[],e="geometry fill fill-opacity stroke stroke-opacity stroke-width styleUrl".split(" ");for(g in c)if(!(-1<e.indexOf(g))){var f=c[g];d.push([g,f]);a||("name"===g&&""!==f?a=f:"title"===g&&""!==f?a=f:"id"===g&&f&&(a=f))}!a&&b.id&&(a=b.id);a=a?'Feature "'+a+'"':"Feature";"geolocation"===b.id&&((a=this.config.geolocation.marker_title)&&
""!==a||(a="Geolocation"),d.push(["L\u00e4ngengrad (Lon.)",b.lon]),d.push(["Breitengrad (Lat.)",b.lat]));b=[];if(0<d.length){b.push("<table>");for(c=0;c<d.length;c++){f=d[c];var g=f[0];f=f[1];b.push("<tr class='netgis-hover-d'>");b.push("<th>"+g+"</th>");b.push("<td>"+f+"</td>");b.push("</tr>")}b.push("</table>")}else b.push("<i>Keine Eigenschaften vorhanden...</i>");b=b.join("");this.addSection(a,b,!1);!this.popup.isVisible()&&this.popup.show()}};
netgis.Info.prototype.onLayerResponseWMS=function(a,b,c){b=b.title;if(c=c.getResponseHeader("Content-Type"))switch(c.split(";")[0]){case "text/plain":a="<pre>"+a+"</pre>"}this.popup.hideLoader();this.addSection(b,a,!1)};netgis=netgis||{};netgis.LayerID={EDITABLE:"editable-layer",NON_EDITABLE:"non-editable-layer"};netgis=netgis||{};netgis.LayerTree=function(a){this.config=a;this.importFolder=null;this.initElements(a);this.initFolders();this.initConfig(a)};netgis.LayerTree.Config={open:!1,title:"Layers",draggable:!1,buttons:[]};netgis.LayerTree.Folders=[];
netgis.Info.prototype.onLayerResponseWMS=function(a,b,c){b=b.title;if(c=c.getResponseHeader("Content-Type"))switch(c.split(";")[0]){case "text/plain":a="<pre>"+a+"</pre>"}this.popup.hideLoader();this.addSection(b,a,!1)};netgis=netgis||{};netgis.LayerID={EDITABLE:"editable-layer",NON_EDITABLE:"non-editable-layer"};netgis=netgis||{};netgis.LayerTree=function(a){this.config=a;this.importFolder=null;this.initElements(a);this.initFolders();this.initConfig(a)};netgis.LayerTree.Config={open:!1,title:"Layers",buttons:[]};netgis.LayerTree.Folders=[];
netgis.LayerTree.prototype.initElements=function(a){a=a.layertree;this.panel=new netgis.Panel("Layers");this.tree=new netgis.Tree(a.draggable);this.tree.attachTo(this.panel.content);this.tree.container.addEventListener(netgis.Events.TREE_ITEM_CHANGE,this.onTreeItemChange.bind(this));this.tree.container.addEventListener(netgis.Events.TREE_ITEM_SLIDER_CHANGE,this.onTreeItemSliderChange.bind(this));this.tree.container.addEventListener(netgis.Events.TREE_ITEM_ORDER_CHANGE,this.onTreeItemOrderChange.bind(this));
this.tree.container.addEventListener(netgis.Events.TREE_ITEM_REMOVE,this.onTreeItemRemove.bind(this));!0===a.draggable&&(this.panel.content.addEventListener("dragover",this.onDragOver.bind(this)),this.panel.content.addEventListener("drop",this.onDragDrop.bind(this)))};
!0===a.draggable&&(this.panel.content.addEventListener("dragover",this.onDragOver.bind(this)),this.panel.content.addEventListener("drop",this.onDragDrop.bind(this)))};
netgis.LayerTree.prototype.initFolders=function(){this.editFolder=this.tree.addFolder(null,"edit-folder","Zeichnung",!0,!0);this.tree.addCheckbox(this.editFolder,netgis.LayerID.EDITABLE,"Editierbar");this.tree.setItemChecked(netgis.LayerID.EDITABLE,!0);this.tree.addCheckbox(this.editFolder,netgis.LayerID.NON_EDITABLE,"Nicht-Editierbar");this.tree.setItemChecked(netgis.LayerID.NON_EDITABLE,!0);this.editFolder.classList.add("netgis-hide")};
netgis.LayerTree.prototype.initConfig=function(a,b){var c=a.layertree;c&&c.title&&this.panel.setTitle(c.title);var d=a.folders,e={};if(d){for(var f=0;f<d.length;f++){var g=d[f],h=this.tree.addFolder(null,g.id,g.title,b,!1,g.draggable,g.removable);e[g.id]=h;!0===g.open&&this.tree.setFolderOpen(g.id,!0)}for(f=0;f<d.length;f++)g=d[f],h=g.id,g=g.parent,-1===g&&(g=null),""===g&&(g=null),g&&this.tree.setFolderParent(e[h],e[g])}if(b=a.layers)for(f=0;f<b.length;f++){var k=b[f];g=e[k.folder]?e[k.folder]:null;
h=k.id?k.id:f.toString();var l=!1;if(g)for(var n=0;n<d.length;n++){var m=d[n];m.id===k.folder&&(l=m.radio)}n=k.title;m='<i class="fas fa-mouse-pointer" title="Ebene ist abfragbar"></i>';a.layertree&&a.layertree.query_icon&&(m=a.layertree.query_icon);(!0===k.query||k.query_url&&""!==k.query_url)&&m&&""!==m&&(n+='<span class="netgis-right">'+m+"</span>");m=k.removable;h=!0===l?this.tree.addRadioButton(g,h,n,k.active,this.createDefaultDetails(k,!0,m)):this.tree.addCheckbox(g,h,n,k.active,!1,this.createDefaultDetails(k,
!0,m));h.addEventListener("contextmenu",this.onTreeItemMenu.bind(this));!0===k.hidden&&h.classList.add("netgis-hide")}this.tree.updateFolderChecks();if(c&&c.buttons)for(a=a.layertree.buttons,f=0;f<a.length;f++)d=a[f],this.tree.addButton(null,d.id,d.title,this.onTreeButtonClick.bind(this));c&&!0===c.open&&this.panel.show()};
netgis.LayerTree.prototype.initConfig=function(a,b){var c=a.layertree;c&&c.title&&this.panel.setTitle(c.title);var d=a.folders,e={};if(d){for(var f=0;f<d.length;f++){var g=d[f],h=this.tree.addFolder(null,g.id,g.title,b,!1,g.draggable);e[g.id]=h;!0===g.open&&this.tree.setFolderOpen(g.id,!0)}for(f=0;f<d.length;f++)g=d[f],h=g.id,g=g.parent,-1===g&&(g=null),""===g&&(g=null),g&&this.tree.setFolderParent(e[h],e[g])}if(b=a.layers)for(f=0;f<b.length;f++){var k=b[f];g=e[k.folder]?e[k.folder]:null;h=k.id?k.id:
f.toString();var l=!1;if(g)for(var m=0;m<d.length;m++){var n=d[m];n.id===k.folder&&(l=n.radio)}m=k.title;n='<i class="fas fa-mouse-pointer" title="Ebene ist abfragbar"></i>';a.layertree&&a.layertree.query_icon&&(n=a.layertree.query_icon);(!0===k.query||k.query_url&&""!==k.query_url)&&n&&""!==n&&(m+='<span class="netgis-right">'+n+"</span>");h=!0===l?this.tree.addRadioButton(g,h,m,k.active,this.createDefaultDetails(k,!0,!1)):this.tree.addCheckbox(g,h,m,k.active,!1,this.createDefaultDetails(k,!0,!0));
h.addEventListener("contextmenu",this.onTreeItemMenu.bind(this));!0===k.hidden&&h.classList.add("netgis-hide")}this.tree.updateFolderChecks();if(c&&c.buttons)for(a=a.layertree.buttons,f=0;f<a.length;f++)d=a[f],this.tree.addButton(null,d.id,d.title,this.onTreeButtonClick.bind(this));c&&!0===c.open&&this.panel.show()};
netgis.LayerTree.prototype.attachTo=function(a){this.panel.attachTo(a);a.addEventListener(netgis.Events.CLIENT_CONTEXT_RESPONSE,this.onClientContextResponse.bind(this));a.addEventListener(netgis.Events.LAYERTREE_TOGGLE,this.onLayerTreeToggle.bind(this));a.addEventListener(netgis.Events.MAP_LAYER_CREATE,this.onMapLayerCreate.bind(this));a.addEventListener(netgis.Events.MAP_LAYER_TOGGLE,this.onMapLayerToggle.bind(this));a.addEventListener(netgis.Events.IMPORT_LAYER_ACCEPT,this.onImportLayerAccept.bind(this));
a.addEventListener(netgis.Events.IMPORT_GEOPORTAL_SUBMIT,this.onImportGeoportalSubmit.bind(this));a.addEventListener(netgis.Events.CONTEXTMENU_SLIDER_CHANGE,this.onContextMenuSliderChange.bind(this));a.addEventListener(netgis.Events.MAP_EDIT_LAYER_LOADED,this.onMapEditLayerChange.bind(this));a.addEventListener(netgis.Events.MAP_EDIT_LAYER_CHANGE,this.onMapEditLayerChange.bind(this))};
netgis.LayerTree.prototype.createDefaultDetails=function(a,b,c){var d=[];!0===b&&d.push({title:"<i class='netgis-icon fas fa-eye-slash'></i> Transparenz:",type:"slider",val:a.transparency?Math.round(100*a.transparency):0});!0===c&&d.push({title:"<i class='netgis-icon fas fa-times'></i> Entfernen",type:"button",callback:this.onTreeItemDeleteClick.bind(this)});return d};
netgis.LayerTree.prototype.onTreeItemChange=function(a){var b=a.detail;netgis.util.invoke(a.target,netgis.Events.MAP_LAYER_TOGGLE,{id:b.id,on:b.checked})};netgis.LayerTree.prototype.onClientContextResponse=function(a){this.initConfig(a.detail.context.config,!0)};netgis.LayerTree.prototype.onLayerTreeToggle=function(a){this.panel.toggle()};netgis.LayerTree.prototype.onTreeButtonClick=function(a){a=a.currentTarget;var b=a.getAttribute("data-id");netgis.Client.handleCommand(a,b)};
netgis.LayerTree.prototype.onMapLayerCreate=function(a){this.addLayerItem(a.detail,null)};netgis.LayerTree.prototype.onMapLayerToggle=function(a){a=a.detail;this.tree.setItemChecked(a.id,a.on,!0);this.tree.updateFolderChecks()};netgis.LayerTree.prototype.onImportLayerAccept=function(a){a=a.detail;!0!==a.editable&&(this.importFolder||(this.importFolder=this.tree.addFolder(null,"_import","Import",!0,!1)),this.addLayerItem(a,this.importFolder),this.tree.updateFolderChecks())};
netgis.LayerTree.prototype.onMapLayerCreate=function(a){this.addLayerItem(a.detail,null)};netgis.LayerTree.prototype.onMapLayerToggle=function(a){a=a.detail;this.tree.setItemChecked(a.id,a.on,!0);this.tree.updateFolderChecks()};netgis.LayerTree.prototype.onImportLayerAccept=function(a){a=a.detail;this.importFolder||(this.importFolder=this.tree.addFolder(null,"_import","Import",!0,!1));this.addLayerItem(a,this.importFolder);this.tree.updateFolderChecks()};
netgis.LayerTree.prototype.addLayerItem=function(a,b){var c=a.title,d='<i class="fas fa-mouse-pointer" title="Ebene ist abfragbar"></i>';config.layertree&&config.layertree.query_icon&&(d=config.layertree.query_icon);(!0===a.query||a.query_url&&""!==a.query_url)&&d&&""!==d&&(c+='<span class="netgis-right">'+d+"</span>");this.tree.addCheckbox(b,a.id,c,!0,!0,this.createDefaultDetails(a,!0,!0)).addEventListener("contextmenu",this.onTreeItemMenu.bind(this))};
netgis.LayerTree.prototype.onImportGeoportalSubmit=function(a){a=a.detail;var b=a.folder.id,c=this.tree.getFolder(b);c||(c=this.tree.addFolder(null,b,a.folder.title,!0,!1,!0,!0));var d=a.layer.id;b={id:d,folder:b,title:a.layer.title,active:!0,type:netgis.LayerTypes.WMS,url:a.layer.url,name:a.layer.name,order:1E4,transparency:0};this.config.layers.push(b);this.tree.addCheckbox(c,d,a.layer.title,!1,!1,this.createDefaultDetails(b,!0,!0));this.tree.setItemChecked(d,!0,!1)};
netgis.LayerTree.prototype.onImportGeoportalSubmit=function(a){a=a.detail;var b=a.folder.id,c=this.tree.getFolder(b);c||(c=this.tree.addFolder(null,b,a.folder.title,!0,!1));var d=a.layer.id;b={id:d,folder:b,title:a.layer.title,active:!0,type:netgis.LayerTypes.WMS,url:a.layer.url,name:a.layer.name,order:1E4,transparency:0};this.config.layers.push(b);this.tree.addCheckbox(c,d,a.layer.title,!1,!1,this.createDefaultDetails(b,!0,!0));this.tree.setItemChecked(d,!0,!1)};
netgis.LayerTree.prototype.onTreeItemMenu=function(a){a.preventDefault();return!1};netgis.LayerTree.prototype.onContextMenuSliderChange=function(a){var b=a.detail;a=.01*b.val;var c=null;0===b.id.indexOf("layer_trans_")&&(c=b.id.split("layer_trans_")[1]);b=this.config.layers;for(var d=0;d<b.length;d++){var e=b[d];if(e.id===c){e.transparency=a;break}}netgis.util.invoke(this.tree.container,netgis.Events.MAP_LAYER_TRANSPARENCY,{id:c,transparency:a})};
netgis.LayerTree.prototype.onTreeItemSliderChange=function(a){var b=a.detail;a=b.id;b=.01*b.val;for(var c=this.config.layers,d=0;d<c.length;d++){var e=c[d];if(e.id===a){e.transparency=b;break}}netgis.util.invoke(this.tree.container,netgis.Events.MAP_LAYER_TRANSPARENCY,{id:a,transparency:b})};
netgis.LayerTree.prototype.onTreeItemOrderChange=function(a){a=a.detail.items;for(var b=this.config.layers,c=a.length,d=0;d<a.length;d++){for(var e=a[d].getElementsByTagName("input")[0].getAttribute("data-id"),f=0;f<b.length;f++){var g=b[f];g.id===e&&(g.order=c,netgis.util.invoke(this.tree.container,netgis.Events.MAP_LAYER_ORDER,{id:e,order:c}))}c--}};
netgis.LayerTree.prototype.onTreeItemDeleteClick=function(a){a=a.currentTarget.parentNode.parentNode.parentNode.parentNode.getElementsByTagName("input")[0].getAttribute("data-id");this.tree.removeItem(a)};netgis.LayerTree.prototype.onTreeItemRemove=function(a){netgis.util.invoke(this.tree.container,netgis.Events.MAP_LAYER_DELETE,{id:a.detail.id})};netgis.LayerTree.prototype.onMapEditLayerChange=function(a){this.editFolder.classList.remove("netgis-hide")};netgis.LayerTree.prototype.onDragOver=function(a){a.preventDefault()};
netgis.LayerTree.prototype.onTreeItemDeleteClick=function(a){a=a.currentTarget.parentNode.parentNode.parentNode.parentNode.getElementsByTagName("input")[0].getAttribute("data-id");netgis.util.invoke(this.tree.container,netgis.Events.MAP_LAYER_DELETE,{id:a});this.tree.removeItem(a)};netgis.LayerTree.prototype.onMapEditLayerChange=function(a){this.editFolder.classList.remove("netgis-hide")};netgis.LayerTree.prototype.onDragOver=function(a){a.preventDefault()};
netgis.LayerTree.prototype.onDragDrop=function(a){a=this.tree.dragElement;"summary"===a.nodeName.toLowerCase()&&(a=a.parentNode.parentNode);a.parentNode.removeChild(a);0<this.tree.container.childNodes.length?this.tree.container.insertBefore(a,this.tree.container.childNodes[0]):this.tree.container.appendChild(a);netgis.util.invoke(this.tree.container,netgis.Events.TREE_ITEM_ORDER_CHANGE,{items:this.tree.container.getElementsByClassName("netgis-item")})};netgis=netgis||{};netgis.LayerTypes={TMS:"TMS",XYZ:"XYZ",OSM:"OSM",WMTS:"WMTS",WMS:"WMS",WMST:"WMST",GEOJSON:"GEOJSON",VTILES:"VTILES",WFS:"WFS",GML:"GML",KML:"KML",GEOPACKAGE:"GEOPACKAGE",SPATIALITE:"SPATIALITE",SHAPEFILE:"SHAPEFILE",WKT:"WKT",HIDDEN:"HIDDEN"};netgis=netgis||{};netgis.Legend=function(a){this.config=a;this.initElements();this.initConfig(a)};netgis.Legend.Config={open:!1};netgis.Legend.prototype.initElements=function(){this.panel=new netgis.Panel("<i class='netgis-icon fas fa-bars'></i><span>Legende</span>");this.panel.content.classList.add("netgis-legend")};netgis.Legend.prototype.initConfig=function(a){var b=a.legend;b&&!0===b.open&&this.show();a=a.layers;for(b=0;b<a.length;b++){var c=a[b];!0===c.active?this.addLayerLegend(c.id):this.removeLayerLegend(c.id)}};
netgis.Legend.prototype.attachTo=function(a){this.panel.attachTo(a);a.addEventListener(netgis.Events.CLIENT_CONTEXT_RESPONSE,this.onClientContextResponse.bind(this));a.addEventListener(netgis.Events.MAP_LAYER_TOGGLE,this.onMapLayerToggle.bind(this));a.addEventListener(netgis.Events.MAP_LAYER_CREATE,this.onMapLayerCreate.bind(this));a.addEventListener(netgis.Events.MAP_LAYER_DELETE,this.onMapLayerDelete.bind(this));a.addEventListener(netgis.Events.LEGEND_TOGGLE,this.onLegendToggle.bind(this))};
netgis.Legend.prototype.show=function(){this.panel.show()};netgis.Legend.prototype.hide=function(){this.panel.hide()};netgis.Legend.prototype.addSection=function(a,b,c,d){this.panel.content.innerHTML=[!0===d?"<details data-id='"+a+"' open='open'>":"<details data-id='"+a+"'>","<summary class='netgis-button netgis-noselect netgis-clip-text netgis-color-d netgis-hover-text-a netgis-hover-d'>",b,"</summary><div class='netgis-border-d'>",c,"</div></details>"].join("")+this.panel.content.innerHTML};
@@ -256,9 +248,8 @@ netgis.Map.prototype.setSnapping=function(a){var b=this.config.tools.snapping;a?
netgis.Map.prototype.removeSnapLayer=function(a){a=a.getSource().getFeatures();for(var b=0;b<a.length;b++)this.snapFeatures.remove(a[b])};netgis.Map.prototype.setDrawBuffer=function(a,b,c){if(a){var d=this.createBufferFeature(new ol.geom.Point(this.view.getCenter()),b,c);this.previewLayer.getSource().addFeature(d);this.drawBufferRadius=b;this.drawBufferSegments=c}else this.previewLayer.getSource().clear();this.drawBufferOn=a};
netgis.Map.prototype.createLayerTMS=function(a,b,c,d,e){"map"===d&&(this.config.map&&this.config.map.scales?d=this.config.map.scales:(console.error("TMS layer references map scales, but none given in config,",a),d=void 0));"map"===c&&(this.config.map&&this.config.map.extent?c=this.config.map.extent:(console.error("TMS layer references map 'extent', but none given in config 'map',",a),c=void 0));if(b&&c&&(d||e)){if(d)for(e=[],b=0;b<d.length;b++)e.unshift(this.getResolutionFromScale(d[b]));c=new ol.source.TileImage({crossOrigin:null,
projection:this.view.getProjection(),tileGrid:new ol.tilegrid.TileGrid({extent:c,origin:c?[c[0],c[1]]:[0,0],resolutions:e}),tileUrlFunction:function(b){if(null!==b){var c=a;c=netgis.util.replace(c,"{z}",b[0]);c=netgis.util.replace(c,"{x}",b[1]);c=netgis.util.replace(c,"{y}",b[2]);return c=netgis.util.replace(c,"{-y}",-b[2]-1)}}});c=new ol.layer.Tile({source:c})}else c=new ol.layer.Tile({source:new ol.source.XYZ({url:a,crossOrigin:"anonymous"})});return c};
netgis.Map.prototype.createLayerWMS=function(a,b,c,d,e,f){"image/tiff"===c&&(console.error("WMS layer format 'image/tiff' detected but not supported in OL, reverting to 'image/png' for '"+a+"'"),c="image/png");var g=netgis.util.parseURL(a,!0);console.info("MAP WMS URL:",a,g);a=g.base;var h=[],k;for(k in g.parameters)"service"!==k.toLowerCase()&&"version"!==k.toLowerCase()&&"request"!==k.toLowerCase()&&h.push(k+"="+g.parameters[k]);a=-1<a.search("\\?")?a+("&"+h.join("&")):a+("?"+h.join("&"));a={url:a,
params:{LAYERS:b,FORMAT:c?c:"image/png",TRANSPARENT:"true"},hidpi:!1};for(k in g.parameters)"service"===k.toLowerCase()&&(a.params.SERVICE=g.parameters[k]),"version"===k.toLowerCase()&&(a.params.VERSION=g.parameters[k]);console.info("MAP WMS PARAMS:",a);e&&f&&(a.imageLoadFunction=function(a,b){var c=new XMLHttpRequest;c.open("GET",b);c.setRequestHeader("Authorization","Basic "+window.btoa(e+":"+f));c.onload=function(){a.getImage().src=b};c.send()});d?(d=new ol.source.TileWMS(a),d=new ol.layer.Tile({source:d})):
(d=new ol.source.ImageWMS(a),d=new ol.layer.Image({source:d}));return d};
netgis.Map.prototype.createLayerWMS=function(a,b,c,d,e,f){"image/tiff"===c&&(console.error("WMS layer format 'image/tiff' detected but not supported in OL, reverting to 'image/png' for '"+a+"'"),c="image/png");a={url:a,params:{LAYERS:b,FORMAT:c?c:"image/png",TRANSPARENT:"true"},hidpi:!1};e&&f&&(a.imageLoadFunction=function(a,b){var c=new XMLHttpRequest;c.open("GET",b);c.setRequestHeader("Authorization","Basic "+window.btoa(e+":"+f));c.onload=function(){a.getImage().src=b};c.send()});d?(d=new ol.source.TileWMS(a),
d=new ol.layer.Tile({source:d})):(d=new ol.source.ImageWMS(a),d=new ol.layer.Image({source:d}));return d};
netgis.Map.prototype.createLayerWMST=function(a,b,c,d,e,f){"image/tiff"===c&&(console.error("WMST layer format 'image/tiff' detected but not supported in OL, reverting to 'image/png' for '"+a+"'"),c="image/png");a={url:a,params:{LAYERS:b,FORMAT:c?c:"image/png",TRANSPARENT:"true",VERSION:"1.1.1"},hidpi:!1};e&&f&&(a.imageLoadFunction=function(a,b){var c=new XMLHttpRequest;c.open("GET",b);c.setRequestHeader("Authorization","Basic "+window.btoa(e+":"+f));c.onload=function(){a.getImage().src=b};c.send()});
d?(d=new ol.source.TileWMS(a),d=new ol.layer.Tile({source:d})):(d=new ol.source.ImageWMS(a),d=new ol.layer.Image({source:d}));return d};
netgis.Map.prototype.createLayerWMTS=function(a){for(var b=[],c=this.client.config.map.scales,d=this.client.config.map.extent,e=0;e<c.length;e++)b.unshift(this.getResolutionFromScale(c[e]));b=new ol.source.TileImage({crossOrigin:null,projection:this.view.getProjection(),tileGrid:new ol.tilegrid.TileGrid({extent:d,origin:[d[0],d[1]],resolutions:b}),tileUrlFunction:function(b){if(null!==b){var c=a;c=netgis.util.replace(c,"{z}",b[0]);c=netgis.util.replace(c,"{x}",b[1]);c=netgis.util.replace(c,"{y}",
@@ -270,14 +261,14 @@ netgis.Map.prototype.createLayerGML=function(a){console.warn("GML support is exp
(f=e.getAttribute("srsName"))&&"EPSG:4326"!==f&&f!==this.projection&&console.warn("unsupported import projection:",f);switch(e.nodeName){case "gml:Polygon":d.geometry=this.gmlParsePolygon(e,f);break;case "gml:MultiPolygon":d.geometry=this.gmlParseMultiPolygon(e,f)}d=new ol.Feature(d);b.push(d)}return new ol.layer.Vector({source:new ol.source.Vector({features:b})})};
netgis.Map.prototype.gmlParsePolygon=function(a,b){var c=[];a=a.getElementsByTagName("gml:LinearRing");for(var d=0;d<a.length;d++){var e=a[d].getElementsByTagName("gml:coordinates")[0].innerHTML;c.push(this.gmlParseCoordinates(e,b))}return new ol.geom.Polygon(c)};netgis.Map.prototype.gmlParseMultiPolygon=function(a,b){var c=[];a=a.getElementsByTagName("gml:polygonMember");for(var d=0;d<a.length;d++){var e=a[d].getElementsByTagName("gml:Polygon")[0];c.push(this.gmlParsePolygon(e,b))}return new ol.geom.MultiPolygon(c)};
netgis.Map.prototype.gmlParseCoordinates=function(a,b){a=a.split(" ");for(var c=0;c<a.length;c++){a[c]=a[c].split(",");for(var d=0;d<a[c].length;d++)a[c][d]=Number.parseFloat(a[c][d]);b&&(a[c]=ol.proj.transform(a[c],b,this.view.getProjection()))}return a};
netgis.Map.prototype.createLayerGeoPackage=function(a){var b=new ol.layer.Vector({source:new ol.source.Vector({features:[]})}),c=this;a=new Uint8Array(a);window.GeoPackage.setSqljsWasmLocateFile(function(a){return c.config["import"].geopackage_lib+a});window.GeoPackage.GeoPackageAPI.open(a).then(function(a){for(var d=[],f=new ol.format.GeoJSON,g=a.getFeatureTables(),h=0;h<g.length;h++)for(var k=a.queryForGeoJSONFeaturesInTable(g[h]),l=0;l<k.length;l++){var n=f.readGeometry(k[l].geometry,{featureProjection:c.view.getProjection()});
n=new ol.Feature({geometry:n});d.push(n)}b.getSource().addFeatures(d)});return b};
netgis.Map.prototype.createLayerGeoPackage=function(a){var b=new ol.layer.Vector({source:new ol.source.Vector({features:[]})}),c=this;a=new Uint8Array(a);window.GeoPackage.setSqljsWasmLocateFile(function(a){return c.config["import"].geopackage_lib+a});window.GeoPackage.GeoPackageAPI.open(a).then(function(a){for(var d=[],f=new ol.format.GeoJSON,g=a.getFeatureTables(),h=0;h<g.length;h++)for(var k=a.queryForGeoJSONFeaturesInTable(g[h]),l=0;l<k.length;l++){var m=f.readGeometry(k[l].geometry,{featureProjection:c.view.getProjection()});
m=new ol.Feature({geometry:m});d.push(m)}b.getSource().addFeatures(d)});return b};
netgis.Map.prototype.createLayerSpatialite=function(a){var b=new ol.layer.Vector({source:new ol.source.Vector({features:[]})}),c=this;window.initSqlJs().then(function(d){var e=[],f=new Uint8Array(a);d=new d.Database(f);var g=d.exec("SELECT name FROM sqlite_schema WHERE type = 'table' \n\t\t\t\t\tAND name NOT LIKE 'sqlite_%' \n\t\t\t\t\tAND name NOT LIKE 'sql_%' \n\t\t\t\t\tAND name NOT LIKE 'idx_%' \n\t\t\t\t\tAND name NOT LIKE 'spatial_ref_sys%' \n\t\t\t\t\tAND name NOT LIKE 'spatialite_%' \n\t\t\t\t\tAND name NOT LIKE 'geometry_columns%' \n\t\t\t\t\tAND name NOT LIKE 'views_%' \n\t\t\t\t\tAND name NOT LIKE 'virts_%' \n\t\t\t\t\tAND name NOT LIKE 'SpatialIndex' \n\t\t\t\t\tAND name NOT LIKE 'KNN%' \n\t\t\t\t\tAND name NOT LIKE 'ElementaryGeometries' \n\t\t\t\t;");f=
g[0].values;for(var h=0;h<f.length;h++){g=d.exec("SELECT * FROM "+f[h][0]);var k=g[0];g=null;for(var l=0;l<k.columns.length;l++){if("geometry"===k.columns[l].toLowerCase()){g=l;break}if("geom"===k.columns[l].toLowerCase()){g=l;break}}if(null!==g)for(k=k.values,l=0;l<k.length;l++){var n=k[l][g],m=new Uint8Array(n.length-43-1+5);m[0]=n[1];m[1]=n[39];m[2]=n[40];m[3]=n[41];m[4]=n[42];for(var q=n.length-43-1,p=0;p<q;p++)m[5+p]=n[43+p];n=(new ol.format.WKB).readGeometry(m,{featureProjection:c.view.getProjection()});
e.push(new ol.Feature({geometry:n}))}}b.getSource().addFeatures(e)});return b};netgis.Map.prototype.createLayerShapefile=function(a){var b=new ol.layer.Vector({source:new ol.source.Vector({features:[]})}),c=this;shp(a).then(function(a){var d=new ol.format.GeoJSON;d.readProjection(a);a=d.readFeatures(a,{featureProjection:c.view.getProjection()});b.getSource().addFeatures(a)});return b};
g[0].values;for(var h=0;h<f.length;h++){g=d.exec("SELECT * FROM "+f[h][0]);var k=g[0];g=null;for(var l=0;l<k.columns.length;l++){if("geometry"===k.columns[l].toLowerCase()){g=l;break}if("geom"===k.columns[l].toLowerCase()){g=l;break}}if(null!==g)for(k=k.values,l=0;l<k.length;l++){var m=k[l][g],n=new Uint8Array(m.length-43-1+5);n[0]=m[1];n[1]=m[39];n[2]=m[40];n[3]=m[41];n[4]=m[42];for(var p=m.length-43-1,q=0;q<p;q++)n[5+q]=m[43+q];m=(new ol.format.WKB).readGeometry(n,{featureProjection:c.view.getProjection()});
e.push(new ol.Feature({geometry:m}))}}b.getSource().addFeatures(e)});return b};netgis.Map.prototype.createLayerShapefile=function(a){var b=new ol.layer.Vector({source:new ol.source.Vector({features:[]})}),c=this;shp(a).then(function(a){var d=new ol.format.GeoJSON;d.readProjection(a);a=d.readFeatures(a,{featureProjection:c.view.getProjection()});b.getSource().addFeatures(a)});return b};
netgis.Map.prototype.createLayerWKT=function(a){for(var b=new ol.format.WKT,c=[],d=0;d<a.length;d++){var e=a[d],f=b.readGeometry(e.geometry),g=e.properties;g.geometry=f;g.wkt=e.geometry;f=new ol.Feature(g);f.setId(e.id);c.push(f)}return new ol.layer.Vector({source:new ol.source.Vector({features:c})})};
netgis.Map.prototype.createLayerWFS=function(a,b,c,d,e,f){"?"!==a[a.length-1]&&(a+="?");a+="service=WFS&version=1.1.0&request=GetFeature";c||(c=this.view.getProjection().getCode());d=d?netgis.util.replace(d," ","+"):"application/json";var g=new ol.source.Vector({format:new ol.format.GeoJSON,strategy:ol.loadingstrategy.bbox,loader:function(h,k,m,q,p){h=a+"&typename="+b+"&srsname="+c+"&bbox="+h.join(",")+","+c+"&outputFormat="+d;var l=new XMLHttpRequest;l.open("GET",h);e&&f&&l.setRequestHeader("Authorization",
"Basic "+window.btoa(e+":"+f));l.onerror=function(){console.error("WFS request error");p()};l.onload=function(){if(200===l.status){g.clear();var a=g.getFormat().readFeatures(l.responseText);g.addFeatures(a);q(a)}else console.error("WFS request status",l.status),p()};l.send()}}),h=new ol.layer.Vector({source:g}),k=this;g.on("featuresloadstart",function(a){k.removeSnapLayer(h)});g.on("featuresloadend",function(a){window.setTimeout(function(){k.addSnapLayer(h)},10)});return h};
netgis.Map.prototype.createLayerWFS=function(a,b,c,d,e,f){"?"!==a[a.length-1]&&(a+="?");a+="service=WFS&version=1.1.0&request=GetFeature";c||(c=this.view.getProjection().getCode());d=d?netgis.util.replace(d," ","+"):"application/json";var g=new ol.source.Vector({format:new ol.format.GeoJSON,strategy:ol.loadingstrategy.bbox,loader:function(h,k,n,p,q){h=a+"&typename="+b+"&srsname="+c+"&bbox="+h.join(",")+","+c+"&outputFormat="+d;var l=new XMLHttpRequest;l.open("GET",h);e&&f&&l.setRequestHeader("Authorization",
"Basic "+window.btoa(e+":"+f));l.onerror=function(){console.error("WFS request error");q()};l.onload=function(){if(200===l.status){g.clear();var a=g.getFormat().readFeatures(l.responseText);g.addFeatures(a);p(a)}else console.error("WFS request status",l.status),q()};l.send()}}),h=new ol.layer.Vector({source:g}),k=this;g.on("featuresloadstart",function(a){k.removeSnapLayer(h)});g.on("featuresloadend",function(a){window.setTimeout(function(){k.addSnapLayer(h)},10)});return h};
netgis.Map.prototype.createLayerVectorTiles=function(a,b,c,d){return new ol.layer.VectorTile({extent:b,source:new ol.source.VectorTile({format:new ol.format.MVT,overlaps:!0,url:a,minZoom:c,maxZoom:d})})};
netgis.Map.prototype.createLayerKML=function(a){var b=new ol.layer.Vector({source:new ol.source.Vector({features:[]})}),c=this;netgis.util.request(a,function(a){a=(new ol.format.KML).readFeatures(a,{featureProjection:c.view.getProjection()});for(var d=0;d<a.length;d++){var f=a[d],g=f.getProperties(),h={fill:"rgba( 127, 127, 127, 0.5 )",stroke:"rgba( 127, 127, 127, 1.0 )",radius:5,width:3},k;for(k in g){var l=g[k];switch(k){case "fill":h.fill=l;break;case "fill-opacity":h["fill-opacity"]=l;break;case "stroke":h.stroke=
l;break;case "stroke-opacity":h["stroke-opacity"]=l;break;case "stroke-width":h.width=l}}h["fill-opacity"]&&(g=netgis.util.hexToRGB(h.fill),g="rgba("+g.join(",")+","+h["fill-opacity"]+")",h.fill=g);h["stroke-opacity"]&&(g=netgis.util.hexToRGB(h.stroke),g="rgba("+g.join(",")+","+h["stroke-opacity"]+")",h.stroke=g);h=new ol.style.Style({image:new ol.style.Circle({radius:h.radius,fill:new ol.style.Fill({color:h.stroke})}),fill:new ol.style.Fill({color:h.fill}),stroke:new ol.style.Stroke({color:h.stroke,
@@ -295,11 +286,11 @@ netgis.Map.prototype.zoomExtentLonLat=function(a,b,c,d){a=ol.proj.fromLonLat([a,
netgis.Map.prototype.zoomFeature=function(a,b){a=this.layers[a].getSource().getFeatureById(b);this.view.fit(a.getGeometry().getExtent(),{duration:500})};netgis.Map.prototype.zoomFeatures=function(a){if(a&&!(1>a.length)){for(var b=a[0].getGeometry().getExtent(),c=1;c<a.length;c++)b=ol.extent.extend(b,a[c].getGeometry().getExtent());this.view.fit(b,{duration:0,padding:this.view.padding})}};
netgis.Map.prototype.addViewHistory=function(a,b){if(0<this.viewHistory.length){var c=this.viewHistory[this.viewHistory.length-1],d=!0;10<Math.abs(a[0]-c.center[0])&&(d=!1);10<Math.abs(a[1]-c.center[1])&&(d=!1);.1<Math.abs(b-c.zoom)&&(d=!1);if(!0===d)return}this.viewHistory.push({center:a,zoom:b});this.viewHistory.length>this.viewHistoryMax&&this.viewHistory.shift();this.viewIndex=this.viewHistory.length-1};
netgis.Map.prototype.gotoViewHistory=function(a){if(!(1>this.viewHistory.length)){var b=this.viewHistory.length-1;0>a&&(a=b);a>b&&(a=0);a!==this.viewIndex&&(b=this.viewHistory[a],this.viewIndex=a,this.viewFromHistory=!0,this.view.setCenter(b.center),this.view.setZoom(b.zoom))}};netgis.Map.prototype.setPadding=function(a,b,c,d){var e=this.paddingBuffer;this.view.padding=[a+e,b+e,c+e,d+e]};
netgis.Map.prototype.exportImage=function(a,b,c,d,e){var f=this,g=this.container,h=this.map,k=this.config["export"],l=new Image;l.onload=function(){var n=document.createElement("div");n.style.position="fixed";n.style.top="0px";n.style.left="0px";n.style.width=b+"px";n.style.height=c+"px";n.style.background="white";n.style.zIndex=-1;n.style.opacity=0;n.style.pointerEvents="none";g.appendChild(n);h.setTarget(n);h.once("rendercomplete",function(){var m=document.createElement("canvas");m.width=b;m.height=
c;var q=m.getContext("2d");q.webkitImageSmoothingEnabled=!1;q.mozImageSmoothingEnabled=!1;q.imageSmoothingEnabled=!1;Array.prototype.forEach.call(document.querySelectorAll(".ol-layer canvas"),function(a){if(0<a.width){var b=a.parentNode.style.opacity;q.globalAlpha=""===b?1:Number(b);b=a.style.transform.match(/^matrix\(([^\(]*)\)$/)[1].split(",").map(Number);CanvasRenderingContext2D.prototype.setTransform.apply(q,b);q.drawImage(a,0,0)}});q.drawImage(l,0,0);q.fillStyle="#fff";q.fillRect(0,m.height-
30,140,30);q.fillStyle="#000";q.font="4mm sans-serif";q.fillText(netgis.util.getTimeStamp(),10,m.height-10);var p=document.createElement("a");switch(a){case "pdf":e=e?e:0;var r=297-e-e,v=210-e-e,t=m.width/m.height;if(!d){var w=r;r=v;v=w}if(m.height>m.width){var u=v;w=u*t;w>r&&(w=r,u=w/t)}else w=r,u=w/t,u>v&&(u=v,w=u*t);t=new jsPDF(d?"l":"p");var x=e;x+=(r-w)/2;r=e;r+=(v-u)/2;t.addImage(m.toDataURL("image/png,1.0",1),"PNG",x,r,w,u);t.setFillColor(255,255,255);t.rect(x,r+u-11,80,11,"F");t.setFontSize(8);
t.text("Datum: "+netgis.util.getTimeStamp(),x+2,r+u-2-4);t.text("Quelle: "+window.location.href,x+2,r+u-2);m=t.output("bloburl",{filename:k.default_filename+".pdf"});window.open(m,"_blank");break;case "jpeg":window.navigator.msSaveBlob?window.navigator.msSaveBlob(m.msToBlob(),k.default_filename+".jpg"):(p.setAttribute("download",k.default_filename+".jpg"),p.setAttribute("href",m.toDataURL("image/jpeg",1)),p.click());break;case "png":window.navigator.msSaveBlob?window.navigator.msSaveBlob(m.msToBlob(),
k.default_filename+".png"):(p.setAttribute("download",k.default_filename+".png"),p.setAttribute("href",m.toDataURL("image/png",1)),p.click());break;case "gif":p.setAttribute("download",k.default_filename+".gif"),v=new GIF({workerScript:k.gif_worker,quality:1}),v.addFrame(m),v.on("finished",function(a){p.setAttribute("href",window.URL.createObjectURL(a));p.click()}),v.render()}h.setTarget(g);g.removeChild(n);netgis.util.invoke(f.container,netgis.Events.EXPORT_END,null)});h.renderSync()};l.src=k.logo};
netgis.Map.prototype.exportImage=function(a,b,c,d,e){var f=this,g=this.container,h=this.map,k=this.config["export"],l=new Image;l.onload=function(){var m=document.createElement("div");m.style.position="fixed";m.style.top="0px";m.style.left="0px";m.style.width=b+"px";m.style.height=c+"px";m.style.background="white";m.style.zIndex=-1;m.style.opacity=0;m.style.pointerEvents="none";g.appendChild(m);h.setTarget(m);h.once("rendercomplete",function(){var n=document.createElement("canvas");n.width=b;n.height=
c;var p=n.getContext("2d");p.webkitImageSmoothingEnabled=!1;p.mozImageSmoothingEnabled=!1;p.imageSmoothingEnabled=!1;Array.prototype.forEach.call(document.querySelectorAll(".ol-layer canvas"),function(a){if(0<a.width){var b=a.parentNode.style.opacity;p.globalAlpha=""===b?1:Number(b);b=a.style.transform.match(/^matrix\(([^\(]*)\)$/)[1].split(",").map(Number);CanvasRenderingContext2D.prototype.setTransform.apply(p,b);p.drawImage(a,0,0)}});p.drawImage(l,0,0);p.fillStyle="#fff";p.fillRect(0,n.height-
30,140,30);p.fillStyle="#000";p.font="4mm sans-serif";p.fillText(netgis.util.getTimeStamp(),10,n.height-10);var q=document.createElement("a");switch(a){case "pdf":e=e?e:0;var r=297-e-e,t=210-e-e,u=n.width/n.height;if(!d){var v=r;r=t;t=v}if(n.height>n.width){var w=t;v=w*u;v>r&&(v=r,w=v/u)}else v=r,w=v/u,w>t&&(w=t,v=w*u);u=new jsPDF(d?"l":"p");var x=e;x+=(r-v)/2;r=e;r+=(t-w)/2;u.addImage(n.toDataURL("image/png,1.0",1),"PNG",x,r,v,w);u.setFillColor(255,255,255);u.rect(x,r+w-11,80,11,"F");u.setFontSize(8);
u.text("Datum: "+netgis.util.getTimeStamp(),x+2,r+w-2-4);u.text("Quelle: "+window.location.href,x+2,r+w-2);n=u.output("bloburl",{filename:k.default_filename+".pdf"});window.open(n,"_blank");break;case "jpeg":window.navigator.msSaveBlob?window.navigator.msSaveBlob(n.msToBlob(),k.default_filename+".jpg"):(q.setAttribute("download",k.default_filename+".jpg"),q.setAttribute("href",n.toDataURL("image/jpeg",1)),q.click());break;case "png":window.navigator.msSaveBlob?window.navigator.msSaveBlob(n.msToBlob(),
k.default_filename+".png"):(q.setAttribute("download",k.default_filename+".png"),q.setAttribute("href",n.toDataURL("image/png",1)),q.click());break;case "gif":q.setAttribute("download",k.default_filename+".gif"),t=new GIF({workerScript:k.gif_worker,quality:1}),t.addFrame(n),t.on("finished",function(a){q.setAttribute("href",window.URL.createObjectURL(a));q.click()}),t.render()}h.setTarget(g);g.removeChild(m);netgis.util.invoke(f.container,netgis.Events.EXPORT_END,null)});h.renderSync()};l.src=k.logo};
netgis.Map.prototype.exportFeatures=function(a){var b=this.editLayer.getSource().getFeatures();!0===a&&(a=this.nonEditLayer.getSource().getFeatures(),b=b.concat(a));b=(new ol.format.GeoJSON).writeFeaturesObject(b,{featureProjection:this.view.getProjection(),dataProjection:"EPSG:4326"});a=this.config["export"].default_filename+".geojson";b.name=a;netgis.util.downloadJSON(b,a);netgis.util.invoke(this.container,netgis.Events.EXPORT_END,null)};netgis.Map.prototype.onClientContextResponse=function(a){this.initConfig(a.detail.context.config)};
netgis.Map.prototype.onEditLayerLoaded=function(a){a=a.detail.geojson;var b=new ol.format.GeoJSON;b.readProjection(a);a=b.readFeatures(a,{featureProjection:this.view.getProjection().getCode()});var c=this,d=a.slice();window.setTimeout(function(){c.zoomFeatures(d)},10);b=[];for(var e=0;e<a.length;e++){var f=a[e];!0===f.getProperties().editable&&b.push(f)}for(e=0;e<b.length;e++)a.splice(a.indexOf(b[e]),1);this.editEventsSilent=!0;this.editLayer.getSource().addFeatures(b);this.nonEditLayer.getSource().addFeatures(a);
this.editEventsSilent=!1};netgis.Map.prototype.onClientSetMode=function(a){this.setMode(a.detail.mode)};netgis.Map.prototype.onPanelResize=function(a){this.setPadding(0,0,0,a.detail.width);this.redrawVectorLayers()};
@@ -316,8 +307,7 @@ d,b,a)};netgis.Map.prototype.onPointerLeave=function(a){this.hoverFeature&&(this
netgis.Map.prototype.onPointerClick=function(a){var b=a.pixel;a=a.coordinate;this.popupOverlay.setPosition(a);var c={resolution:this.view.getResolution(),projection:this.view.getProjection().getCode(),bbox:this.view.calculateExtent(this.map.getSize()),width:this.map.getSize()[0],height:this.map.getSize()[1]},d=ol.proj.toLonLat(a,this.view.getProjection()),e={};for(g in this.layers){var f=this.layers[g].getSource();f.getFeatureInfoUrl&&(e[g]=f.getFeatureInfoUrl(a,c.resolution,c.projection,{INFO_FORMAT:"text/html"}))}var g=
{mode:this.mode,pixel:b,coords:a,lon:d[0],lat:d[1],overlay:this.popupOverlay.getElement(),view:c,info:e};this.mode===netgis.Modes.VIEW&&netgis.util.invoke(this.container,netgis.Events.MAP_CLICK,g);var h=[],k=this;this.map.forEachFeatureAtPixel(b,function(a,b){b&&b!==k.nonEditLayer&&b!==k.boundsLayer&&b!==k.measureLayer&&b!==k.previewLayer&&(-1<k.sketchFeatures.indexOf(a)||h.push({feature:a,layer:b}))});g=!0;this.mode===netgis.Modes.VIEW&&(g=!1);this.mode===netgis.Modes.MEASURE_LINE&&(g=!1);this.mode===
netgis.Modes.MEASURE_AREA&&(g=!1);this.mode===netgis.Modes.DRAW_POINTS&&(g=!1);this.mode===netgis.Modes.DRAW_LINES&&(g=!1);this.mode===netgis.Modes.DRAW_POLYGONS&&(g=!1);this.mode===netgis.Modes.CUT_FEATURES_DRAW&&(g=!1);g&&(0<h.length&&!1===this.selectMultiple&&(this.selectedFeatures=[]),0===h.length&&!1===this.selectMultiple&&(this.selectedFeatures=[]),!0===this.selectReset&&(this.selectedFeatures=[],this.selectReset=!1),this.mode===netgis.Modes.BUFFER_FEATURES_DYNAMIC&&this.updateBufferFeaturesSketch(this.bufferFeaturesRadius,
this.bufferFeaturesSegments));for(c=0;c<h.length;c++)if(d=h[c],g&&(e=this.selectedFeatures.indexOf(d.feature),-1<e?this.selectedFeatures.splice(e,1):this.selectedFeatures.push(d.feature)),e=!1,this.mode===netgis.Modes.VIEW&&(e=!0),this.mode===netgis.Modes.DELETE_FEATURES&&(e=!0),this.mode===netgis.Modes.BUFFER_FEATURES_DYNAMIC&&(e=!0),this.mode===netgis.Modes.CUT_FEATURES&&(e=!0),this.mode===netgis.Modes.SEARCH_PARCEL&&(e=!0),e)this.onFeatureClick(d.feature,d.layer,b,a);this.redrawVectorLayers()};
netgis.Map.prototype.onContainerClick=function(a){if(2===a.detail)this.onDoubleClick(a)};
this.bufferFeaturesSegments));for(c=0;c<h.length;c++)d=h[c],g&&(e=this.selectedFeatures.indexOf(d.feature),-1<e?this.selectedFeatures.splice(e,1):this.selectedFeatures.push(d.feature)),this.onFeatureClick(d.feature,d.layer,b,a);this.redrawVectorLayers()};netgis.Map.prototype.onContainerClick=function(a){if(2===a.detail)this.onDoubleClick(a)};
netgis.Map.prototype.onDoubleClick=function(a){switch(this.mode){case netgis.Modes.MEASURE_LINE:this.interactions[netgis.Modes.MEASURE_LINE]&&this.interactions[netgis.Modes.MEASURE_LINE][2].finishDrawing();break;case netgis.Modes.MEASURE_AREA:this.interactions[netgis.Modes.MEASURE_AREA]&&this.interactions[netgis.Modes.MEASURE_AREA][2].finishDrawing();break;case netgis.Modes.DRAW_LINES:this.interactions[netgis.Modes.DRAW_LINES]&&this.interactions[netgis.Modes.DRAW_LINES][0].finishDrawing();break;case netgis.Modes.DRAW_POLYGONS:this.interactions[netgis.Modes.DRAW_POLYGONS]&&
this.interactions[netgis.Modes.DRAW_POLYGONS][0].finishDrawing();break;case netgis.Modes.CUT_FEATURES_DRAW:this.interactions[netgis.Modes.CUT_FEATURES_DRAW]&&this.interactions[netgis.Modes.CUT_FEATURES_DRAW][0].finishDrawing()}};
netgis.Map.prototype.onRightClick=function(a){switch(this.mode){case netgis.Modes.MEASURE_LINE:this.interactions[netgis.Modes.MEASURE_LINE]&&this.interactions[netgis.Modes.MEASURE_LINE][2].finishDrawing();break;case netgis.Modes.MEASURE_AREA:this.interactions[netgis.Modes.MEASURE_AREA]&&this.interactions[netgis.Modes.MEASURE_AREA][2].finishDrawing();break;case netgis.Modes.DRAW_LINES:this.interactions[netgis.Modes.DRAW_LINES]&&this.interactions[netgis.Modes.DRAW_LINES][0].finishDrawing();break;case netgis.Modes.DRAW_POLYGONS:this.interactions[netgis.Modes.DRAW_POLYGONS]&&
@@ -329,9 +319,9 @@ netgis.Map.prototype.onKeyUp=function(a){a=a.keyCode||a.which;switch(this.mode){
!1,!0===this.config.tools.select_multi_reset&&(this.selectReset=!0))};
netgis.Map.prototype.onFeatureEnter=function(a,b,c,d){if(b){switch(this.mode){case netgis.Modes.VIEW:this.container.classList.add("netgis-clickable");break;case netgis.Modes.DELETE_FEATURES:case netgis.Modes.BUFFER_FEATURES:case netgis.Modes.BUFFER_FEATURES_DYNAMIC:case netgis.Modes.CUT_FEATURES:this.container.classList.add("netgis-clickable");a.setStyle(this.styleHover.bind(this));break;case netgis.Modes.SEARCH_PARCEL:this.container.classList.add("netgis-clickable"),a.setStyle(this.styleHover.bind(this))}netgis.util.invoke(this.container,
netgis.Events.MAP_FEATURE_ENTER,{pixel:c,coords:d,layer:b.get("id"),properties:a.getProperties()})}};netgis.Map.prototype.onFeatureHover=function(a,b,c,d){};
netgis.Map.prototype.onFeatureClick=function(a,b,c,d){if(this.mode!==netgis.Modes.SEARCH_PARCEL||"searchparcel_districts"===b.get("id")||"searchparcel_fields"===b.get("id")||"searchparcel_parcels"===b.get("id")){var e=ol.proj.toLonLat(d,this.view.getProjection());c={pixel:c,coords:d,lon:e[0],lat:e[1],overlay:this.popupOverlay.getElement(),layer:b.get("id"),id:a.getId(),properties:a.getProperties(),mode:this.mode};netgis.util.invoke(this.container,netgis.Events.MAP_FEATURE_CLICK,c);switch(this.mode){case netgis.Modes.DELETE_FEATURES:b.getSource().removeFeature(a);
this.onFeatureLeave(a,b);netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.VIEW});break;case netgis.Modes.BUFFER_FEATURES:case netgis.Modes.BUFFER_FEATURES_EDIT:this.onFeatureLeave(a,b);this.selectMultiple?netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.BUFFER_FEATURES}):netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.BUFFER_FEATURES_EDIT});break;case netgis.Modes.BUFFER_FEATURES_DYNAMIC:this.updateBufferFeaturesSketch(this.bufferFeaturesRadius,
this.bufferFeaturesSegments);break;case netgis.Modes.CUT_FEATURES:if(a.getGeometry()instanceof ol.geom.Point)this.onFeatureLeave(a,b);else this.selectMultiple||netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.CUT_FEATURES_DRAW})}}};
netgis.Map.prototype.onFeatureClick=function(a,b,c,d){var e=ol.proj.toLonLat(d,this.view.getProjection());c={pixel:c,coords:d,lon:e[0],lat:e[1],overlay:this.popupOverlay.getElement(),layer:b.get("id"),id:a.getId(),properties:a.getProperties(),mode:this.mode};netgis.util.invoke(this.container,netgis.Events.MAP_FEATURE_CLICK,c);switch(this.mode){case netgis.Modes.DELETE_FEATURES:b.getSource().removeFeature(a);this.onFeatureLeave(a,b);netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.VIEW});
break;case netgis.Modes.BUFFER_FEATURES:case netgis.Modes.BUFFER_FEATURES_EDIT:this.onFeatureLeave(a,b);this.selectMultiple?netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.BUFFER_FEATURES}):netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.BUFFER_FEATURES_EDIT});break;case netgis.Modes.BUFFER_FEATURES_DYNAMIC:this.updateBufferFeaturesSketch(this.bufferFeaturesRadius,this.bufferFeaturesSegments);break;case netgis.Modes.CUT_FEATURES:if(a.getGeometry()instanceof
ol.geom.Point)this.onFeatureLeave(a,b);else this.selectMultiple||netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.CUT_FEATURES_DRAW})}};
netgis.Map.prototype.onFeatureLeave=function(a,b,c,d){netgis.util.invoke(this.container,netgis.Events.MAP_FEATURE_LEAVE,{pixel:c,coords:d,layer:b?b.get("id"):null,properties:a.getProperties()});switch(this.mode){case netgis.Modes.DELETE_FEATURES:case netgis.Modes.BUFFER_FEATURES:case netgis.Modes.BUFFER_FEATURES_DYNAMIC:case netgis.Modes.CUT_FEATURES:case netgis.Modes.CUT_FEATURES_DRAW:this.container.classList.remove("netgis-clickable");a.setStyle(null);break;case netgis.Modes.SEARCH_PARCEL:this.container.classList.remove("netgis-clickable"),
a.setStyle(null)}};netgis.Map.prototype.onEditLayerAdd=function(a){this.editEventsSilent||this.updateEditOutput();this.snapFeatures.push(a.feature)};netgis.Map.prototype.onEditLayerRemove=function(a){this.editEventsSilent||this.updateEditOutput();this.snapFeatures.remove(a.feature)};netgis.Map.prototype.onEditLayerChange=function(a){this.editEventsSilent||this.updateEditOutput()};
netgis.Map.prototype.onCopyFeatureToEdit=function(a){a=a.detail;var b=this.layers[a.source].getSource().getFeatureById(a.id);b?this.editLayer.getSource().getFeatureById(a.id)||(b.setStyle(void 0),this.selectedFeatures=[],this.editLayer.getSource().addFeature(b)):console.error("feature to copy not found",a)};netgis.Map.prototype.onGeolocToggleActive=function(a){a.detail.on?this.geolocLayer.setVisible(!0):this.geolocLayer.setVisible(!1)};
@@ -341,15 +331,14 @@ d.intersects(g)&&(d=d.intersection(g))}c=c.write(d);a.setGeometry(c)}var h=this.
netgis.Map.prototype.onDrawBufferChange=function(a){a=a.detail;this.drawBufferRadius=a.radius;this.drawBufferSegments=a.segments;this.updateDrawBufferPreview()};netgis.Map.prototype.onBufferChange=function(a){a=a.detail;this.updateBufferFeaturesSketch(a.radius,a.segments);this.bufferFeaturesRadius=a.radius;this.bufferFeaturesSegments=a.segments};
netgis.Map.prototype.updateBufferFeaturesSketch=function(a,b){var c=this.selectedFeatures,d=this.editLayer.getSource();this.clearSketchFeatures();for(var e=0;e<c.length;e++){var f=this.createBufferFeature(this.selectedFeatures[e].getGeometry(),a,b);d.addFeature(f);this.sketchFeatures.push(f)}};netgis.Map.prototype.onBufferAccept=function(a){a=this.selectedFeatures;for(var b=this.editLayer.getSource(),c=0;c<a.length;c++)b.removeFeature(a[c]);this.sketchFeatures=[];this.selectedFeatures=[]};
netgis.Map.prototype.onCutFeaturesDrawEnd=function(a){a=a.feature;for(var b=0;b<this.selectedFeatures.length;b++){var c=this.selectedFeatures[b];if(c)if(this.onFeatureLeave(c,null),c.getGeometry()instanceof ol.geom.Point)console.error("trying to cut a point feature",c);else{var d=new jsts.io.OL3Parser,e=d.read(c.getGeometry()),f=d.read(a.getGeometry());e=e.difference(f);d=d.write(e);d=new ol.Feature({geometry:d});e=this.editLayer.getSource();e.removeFeature(c);e.addFeature(d)}}this.selectedFeatures=
[];this.editEventsSilent=!0;this.splitMultiPolygons(this.editLayer);this.editEventsSilent=!1;this.updateEditOutput();netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.VIEW})};
netgis.Map.prototype.onImportLayerAccept=function(a){a=a.detail;a.editable?(a=this.createLayerGeoJSON(a.data),a=a.getSource(),this.editLayer.getSource().addFeatures(a.getFeatures())):(a=this.addLayer(a.id,a),a=a.getSource());a instanceof ol.source.Vector&&0<a.getFeatures().length&&this.view.fit(a.getExtent(),{duration:600})};netgis.Map.prototype.onImportGeoportalSubmit=function(a){};
[];this.editEventsSilent=!0;this.splitMultiPolygons(this.editLayer);this.editEventsSilent=!1;this.updateEditOutput();netgis.util.invoke(this.container,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.VIEW})};netgis.Map.prototype.onImportLayerAccept=function(a){a=a.detail;a=this.addLayer(a.id,a);var b=a.getSource();b instanceof ol.source.Vector&&0<b.getFeatures().length&&this.view.fit(a.getSource().getExtent(),{duration:600})};netgis.Map.prototype.onImportGeoportalSubmit=function(a){};
netgis.Map.prototype.onImportLayerPreview=function(a){a=a.detail;var b=this.createLayer(a),c=this.view.getProjection().getCode();netgis.util.invoke(this.container,netgis.Events.IMPORT_LAYER_PREVIEW_FEATURES,{id:a.id,title:a.title,layer:b,proj:c})};netgis.Map.prototype.onSearchParcelReset=function(a){(a=this.config.searchparcel.districts_service.min_zoom)&&this.view.setZoom(a)};netgis.Map.prototype.onSearchParcelItemEnter=function(a){a=a.detail.id;this.layers.searchparcel_parcels.getSource().getFeatureById(a).setStyle(this.styleHover.bind(this))};
netgis.Map.prototype.onSearchParcelItemLeave=function(a){a=a.detail.id;this.layers.searchparcel_parcels.getSource().getFeatureById(a).setStyle(null)};netgis.Map.prototype.onSearchParcelItemClick=function(a){this.zoomFeature("searchparcel_parcels",a.detail.id)};netgis.Map.prototype.onSearchParcelItemImport=function(a){};netgis.Map.prototype.onDrawPointsUpdateGeom=function(a,b,c){b?b.setCoordinates(a):b=new ol.geom.Point(a);return b};
netgis.Map.prototype.onDrawLinesUpdateGeom=function(a,b,c){b?b.setCoordinates(a):b=new ol.geom.LineString(a);this.drawError=!this.isGeomInsideLayer(this.boundsLayer,b);return b};
netgis.Map.prototype.onDrawPolygonsUpdateGeom=function(a,b,c){b?(a=[a[0].concat([a[0][0]])],b.setCoordinates(a)):b=new ol.geom.Polygon(a);c=!0;if(4>a[0].length)for(var d=0;d<a[0].length;d++){if(!this.isPointInsideLayer(this.boundsLayer,a[0][d])){c=!1;break}}else c=this.isGeomInsideLayer(this.boundsLayer,b);this.drawError=!c;return b};
netgis.Map.prototype.onDrawPointsEnd=function(a){if(this.boundsLayer){var b=a.feature,c=this.editLayer;this.isPointInsideLayer(this.boundsLayer,b.getGeometry().getCoordinates())||window.setTimeout(function(){c.getSource().removeFeature(b)},10)}};netgis.Map.prototype.onDrawLinesEnd=function(a){if(this.boundsLayer){var b=a.feature,c=this.editLayer;this.isGeomInsideLayer(this.boundsLayer,b.getGeometry())||window.setTimeout(function(){c.getSource().removeFeature(b)},10)}};
netgis.Map.prototype.onDrawPolygonsEnd=function(a){if(this.boundsLayer){var b=a.feature,c=this.editLayer;this.isGeomInsideLayer(this.boundsLayer,b.getGeometry())||window.setTimeout(function(){c.getSource().removeFeature(b)},10)}};netgis.Map.prototype.onExportBegin=function(a){a=a.detail;switch(a.format){case "geojson":this.exportFeatures(a.nonEdits);break;default:this.exportImage(a.format,a.width,a.height,a.landscape,a.padding)}};
netgis.Map.prototype.onScalebarSelectChange=function(a){netgis.util.invoke(this.scalebarSelect,netgis.Events.MAP_ZOOM_SCALE,{scale:this.scalebarSelect.value,anim:!0})};netgis.Map.prototype.onTimeSliderShow=function(a){};netgis.Map.prototype.onTimeSliderHide=function(a){};netgis.Map.prototype.onTimeSliderSelect=function(a){a=a.detail;this.layers[a.layer].getSource().updateParams({TIME:a.time})};netgis=netgis||{};netgis.Menu=function(a){this.config=a;this.initElements();this.initConfig(a)};netgis.Menu.Config={header:"<a href='.' target='_self'>NetGIS Client</a>",items:[],compact:!0};
netgis.Map.prototype.onScalebarSelectChange=function(a){netgis.util.invoke(this.scalebarSelect,netgis.Events.MAP_ZOOM_SCALE,{scale:this.scalebarSelect.value,anim:!0})};netgis.Map.prototype.onTimeSliderShow=function(a){};netgis.Map.prototype.onTimeSliderHide=function(a){};netgis.Map.prototype.onTimeSliderSelect=function(a){a=a.detail;console.info("Time Slider Select:",a);this.layers[a.layer].getSource().updateParams({TIME:a.time})};netgis=netgis||{};netgis.Menu=function(a){this.config=a;this.initElements();this.initConfig(a)};netgis.Menu.Config={header:"<a href='.' target='_self'>NetGIS Client</a>",items:[],compact:!0};
netgis.Menu.prototype.initElements=function(){this.container=document.createElement("nav");this.container.className="netgis-menu netgis-noselect netgis-color-a netgis-gradient-a netgis-shadow-large";this.toggle=document.createElement("button");this.toggle.setAttribute("type","button");this.toggle.addEventListener("click",this.onToggleClick.bind(this));this.toggle.className="netgis-menu-toggle netgis-hover-c";this.toggle.innerHTML="<i class='fas fa-bars'></i>";this.container.appendChild(this.toggle)};
netgis.Menu.prototype.initConfig=function(a){var b=a.menu;if(b&&(b.header&&this.addHeader(b.header),!0===b.compact&&this.container.classList.add("netgis-compact"),b.items)){b=b.items;for(var c=0;c<b.length;c++){var d=b[c];if(d.items){var e=d.items;if("scales"===d.id)for(var f=this.getScaleItems(),g=0;g<f.length;g++)e.push(f[g]);this.addDropdown(d.title,e)}else if(d.url&&0<d.url.length)this.addLink(d.url,d.title);else if(d.options)if("scales"===d.options){e={0:"1:X"};for(g=0;g<a.map.scales.length;g++)e[a.map.scales[g]]=
"1:"+a.map.scales[g];var h=a.map.default_scale;this.addSelect(d.id,d.title,e,h).options[0].classList.add("netgis-hide")}else{e=d.options;if(d.value)h=d.value;else for(var k in e){h=k;break}this.addSelect(d.id,d.title,e,h)}else this.addButton(d.id,d.title)}}};netgis.Menu.prototype.attachTo=function(a){a.appendChild(this.container);a.addEventListener(netgis.Events.MAP_VIEW_CHANGE,this.onMapViewChange.bind(this))};
@@ -370,7 +359,10 @@ netgis.Modal.prototype.initEvents=function(){};netgis.Modal.prototype.attachTo=f
netgis.Modal.prototype.addHeader=function(a,b,c){var d=document.createElement("button");d.className="netgis-button netgis-clip-text netgis-color-c netgis-gradient-a";d.innerHTML="<span>"+b+"</span><i class='netgis-icon fas fa-times'></i>";d.setAttribute("type","button");c&&(d.onclick=c);a&&a.appendChild(d);return d};netgis.Modal.prototype.onHeaderClick=function(a){this.hide()};netgis.Modal.prototype.onContainerClick=function(a){a.target===this.container&&this.hide()};netgis=netgis||{};
netgis.Modes=Object.freeze({VIEW:"view",ZOOM_BOX:"zoom-box",MEASURE_LINE:"measure-line",MEASURE_AREA:"measure-area",DRAW_POINTS:"draw-points",DRAW_LINES:"draw-lines",DRAW_POLYGONS:"draw-polygons",MODIFY_FEATURES:"modify-features",DELETE_FEATURES:"delete-features",BUFFER_FEATURES:"buffer-features",BUFFER_FEATURES_EDIT:"buffer-features-edit",BUFFER_FEATURES_DYNAMIC:"buffer-features-dynamic",CUT_FEATURES:"cut-features",CUT_FEATURES_DRAW:"cut-features-draw",CUT_FEATURES_DYNAMIC:"cut-features-dynamic",SEARCH_PARCEL:"search-parcel"});netgis=netgis||{};netgis.Modules={menu:!0,layertree:!0,map:!0,controls:!0,attribution:!0,legend:!0,geolocation:!0,info:!0,searchplace:!0,searchparcel:!0,toolbox:!0,"import":!0,"export":!0,timeslider:!0};netgis=netgis||{};
netgis.OWS=function(){return{read:function(a,b){console.info("OWS READ:",a);console.info("OWS PROPS:",a.properties);a=a.features;for(b=0;b<a.length;b++){var c=a[b];console.info("OWS FEATURE:",b,c);var d=c;c=d.properties;switch(d.type){case "Feature":d=c.minScaleDenominator;var e=c.maxScaleDenominator;console.info("TITLE:",c.title,"FOLDER:",c.folder);console.info("MIN/MAX SCALE:",d,e);c=c.offerings;for(d=0;d<c.length;d++){e=c[d];console.info("OFFERING:",d,e);var f=e,g=f.code;e=f.content;switch(g){case "http://www.opengis.net/spec/owc-atom/1.0/req/kml":for(f=
0;f<e.length;f++)switch(g=e[f],g.type){case "application/vnd.google-earth.kml+xml":break;default:console.error("OWS: unknown offering content type '"+g.type+"'",g)}break;default:console.error("OWS: unknown offering code '"+g+"'",f)}}break;default:console.error("OWS: unknown feature type '"+d.type+"'",d)}}}}}();netgis=netgis||{};netgis.Panel=function(a){this.initElements(a);this.initEvents()};netgis.Panel.prototype.initElements=function(a){var b=document.createElement("section");b.className="netgis-panel netgis-resize-right netgis-color-e netgis-shadow";this.content=document.createElement("div");b.appendChild(this.content);this.header=this.addHeader(b,a,this.onHeaderClick.bind(this));this.container=b};netgis.Panel.prototype.initEvents=function(){this.resizeObserver=(new ResizeObserver(this.onResize.bind(this))).observe(this.container)};
0;f<e.length;f++)switch(g=e[f],g.type){case "application/vnd.google-earth.kml+xml":break;default:console.error("OWS: unknown offering content type '"+g.type+"'",g)}break;default:console.error("OWS: unknown offering code '"+g+"'",f)}}break;default:console.error("OWS: unknown feature type '"+d.type+"'",d)}}}}}();netgis=netgis||{};
netgis.OWS=function(){return{read:function(a,b){b={layers:[],folders:[]};netgis.util.isDefined(a.properties)&&(b.bbox=a.properties.bbox);a=a.features;for(var c=0;c<a.length;c++){var d=a[c];if("Feature"===d.type){var e=d.properties;e=e.folder;var f=!1;for(d=0;d<b.folders.length;d++)if(b.folders[d].id===e){f=!0;break}if(!f){d=e.split("/");e=[];for(f=0;f<d.length;f++){var g=d[f];0<g.length&&e.push(g)}var h=-1;for(f=0;f<e.length;f++){g=e[f];var k="/"+e.slice(0,f+1).join("/"),l=!1;for(d=0;d<b.folders.length;d++)if(b.folders[d].path===
k){h=d;l=!0;break}l||(d=b.folders.length,b.folders.push({title:g,parent:h,path:k}),h=d)}}}}for(c=0;c<a.length;c++)if(d=a[c],"Feature"===d.type){e=d.properties;f=-1;for(d=0;d<b.folders.length;d++)if(b.folders[d].path===e.folder){f=d;break}d=e.offerings;for(g=0;g<d.length;g++)switch(h=d[g],k=h.operations,h.code){case "http://www.opengis.net/spec/owc-geojson/1.0/req/wms":b.layers.push({folder:f,type:netgis.LayerTypes.WMS,url:k[0].href,title:e.title,attribution:e.rights,active:e.active});break;case "http://www.opengis.net/spec/owc-geojson/1.0/req/xyz":h=
k[0];b.layers.push({folder:f,type:netgis.LayerTypes.XYZ,url:h.href,title:e.title,attribution:e.rights,active:e.active});break;case "http://www.opengis.net/spec/owc-geojson/1.0/req/osm":h=k[0],b.layers.push({folder:f,type:netgis.LayerTypes.XYZ,url:h.href,title:e.title,attribution:e.rights,active:e.active})}}return b}}}();netgis.OWS.Config={url:""};netgis=netgis||{};netgis.Panel=function(a){this.initElements(a);this.initEvents()};netgis.Panel.prototype.initElements=function(a){var b=document.createElement("section");b.className="netgis-panel netgis-resize-right netgis-color-e netgis-shadow";this.content=document.createElement("div");b.appendChild(this.content);this.header=this.addHeader(b,a,this.onHeaderClick.bind(this));this.container=b};netgis.Panel.prototype.initEvents=function(){this.resizeObserver=(new ResizeObserver(this.onResize.bind(this))).observe(this.container)};
netgis.Panel.prototype.attachTo=function(a){a.appendChild(this.container);a.addEventListener(netgis.Events.PANEL_TOGGLE,this.onPanelToggle.bind(this))};netgis.Panel.prototype.addHeader=function(a,b,c){var d=document.createElement("button");d.className="netgis-button netgis-clip-text netgis-color-c netgis-gradient-a netgis-shadow";d.innerHTML="<span>"+b+"</span><i class='netgis-icon fas fa-times'></i>";d.setAttribute("type","button");c&&(d.onclick=c);a&&a.appendChild(d);return d};
netgis.Panel.prototype.show=function(){this.container.classList.contains("netgis-show")||(this.container.classList.add("netgis-show"),netgis.util.invoke(this.container,netgis.Events.PANEL_TOGGLE,{container:this.container,visible:!0,width:this.container.getBoundingClientRect().width}))};
netgis.Panel.prototype.hide=function(){this.container.classList.contains("netgis-show")&&(this.container.classList.remove("netgis-show"),netgis.util.invoke(this.container,netgis.Events.PANEL_TOGGLE,{container:this.container,visible:!1}))};
@@ -389,16 +381,16 @@ netgis.Popup.prototype.show=function(){this.container.classList.add("netgis-show
netgis.Popup.prototype.hideLoader=function(){this.loader.parentNode===this.content&&this.content.removeChild(this.loader)};
netgis.Popup.prototype.setPosition=function(a,b){var c=this.container.parentNode.getBoundingClientRect(),d=this.arrow.getBoundingClientRect();a>c.width-d.width&&(a=c.width-d.width);a<d.width&&(a=d.width);switch(this.options.direction){default:case "down":this.container.style.left=a+"px";this.container.style.top=b+"px";break;case "right":this.container.style.right=c.width-a+"px",this.container.style.top=b+"px"}this.content.style.left="";a=this.content.getBoundingClientRect();0>a.x?this.content.style.left=
-a.x+"px":a.x+a.width>c.width&&(this.content.style.left=-(a.x+a.width-c.width)+"px")};netgis.Popup.prototype.setHeader=function(a){this.closer.getElementsByTagName("span")[0].innerHTML=a};netgis.Popup.prototype.setContent=function(a){this.wrapper.innerHTML=a};netgis.Popup.prototype.clearContent=function(){this.wrapper.innerHTML=""};netgis.Popup.prototype.addContent=function(a){this.wrapper.innerHTML+=a};netgis.Popup.prototype.onDocumentPointerDown=function(a){};
netgis.Popup.prototype.onPointerDown=function(a){a.stopPropagation()};netgis.Popup.prototype.onCloserClick=function(a){this.hide()};netgis=netgis||{};netgis.Search=function(a){this.debounce=400;this.autocomplete=!0;this.initElements(a);this.initEvents()};
netgis.Search.prototype.initElements=function(a){var b=document.createElement("div");b.className="netgis-search";this.container=b;var c=document.createElement("label");b.appendChild(c);this.label=c;var d=document.createElement("input");d.className="netgis-round netgis-shadow";d.setAttribute("type","text");d.setAttribute("placeholder",a);c.appendChild(d);this.input=d;a=document.createElement("button");a.setAttribute("type","button");a.innerHTML="<i class='fas fa-search'></i>";a.className="netgis-no-background";
c.appendChild(a);this.button=a;a=document.createElement("button");a.setAttribute("type","button");a.className="netgis-hide netgis-no-background";a.innerHTML="<i class='fas fa-times'></i>";c.appendChild(a);this.closer=a;c=document.createElement("ul");b.appendChild(c);this.results=c};
netgis.Popup.prototype.onPointerDown=function(a){a.stopPropagation()};netgis.Popup.prototype.onCloserClick=function(a){this.hide()};netgis=netgis||{};netgis.Search=function(a){this.debounce=400;this.initElements(a);this.initEvents()};
netgis.Search.prototype.initElements=function(a){var b=document.createElement("div");b.className="netgis-search";this.container=b;var c=document.createElement("label");b.appendChild(c);this.label=c;var d=document.createElement("input");d.className="netgis-round netgis-shadow";d.setAttribute("type","text");d.setAttribute("placeholder",a);c.appendChild(d);this.input=d;a=document.createElement("button");a.setAttribute("type","button");a.innerHTML="<i class='fas fa-search'></i>";c.appendChild(a);this.button=
a;a=document.createElement("button");a.setAttribute("type","button");a.className="netgis-hide";a.innerHTML="<i class='fas fa-times'></i>";c.appendChild(a);this.closer=a;c=document.createElement("ul");b.appendChild(c);this.results=c};
netgis.Search.prototype.initEvents=function(){this.input.addEventListener("change",this.onInputChange.bind(this));this.input.addEventListener("keydown",this.onInputKeyDown.bind(this));this.input.addEventListener("keyup",this.onInputKeyUp.bind(this));this.button.addEventListener("click",this.onButtonClick.bind(this));this.closer.addEventListener("click",this.onCloserClick.bind(this))};netgis.Search.prototype.attachTo=function(a){a.appendChild(this.container)};netgis.Search.prototype.show=function(){this.container.classList.remove("netgis-hide")};
netgis.Search.prototype.hide=function(){this.container.classList.add("netgis-hide")};netgis.Search.prototype.toggle=function(){this.container.classList.toggle("netgis-hide")};netgis.Search.prototype.isVisible=function(){return!this.container.classList.contains("netgis-hide")};netgis.Search.prototype.minimize=function(){this.container.classList.remove("netgis-color-e","netgis-shadow");this.input.classList.add("netgis-shadow")};
netgis.Search.prototype.maximize=function(){this.container.classList.add("netgis-color-e","netgis-shadow");this.input.classList.remove("netgis-shadow")};netgis.Search.prototype.focus=function(){this.input.focus()};netgis.Search.prototype.setTitle=function(a){this.input.setAttribute("placeholder",a)};
netgis.Search.prototype.addResult=function(a,b){var c=document.createElement("li"),d=document.createElement("button");d.className="netgis-button netgis-clip-text netgis-color-e netgis-hover-a";d.innerHTML=a;d.setAttribute("type","button");d.setAttribute("title",d.innerText);d.setAttribute("data-result",b);d.addEventListener("click",this.onResultClick.bind(this));c.appendChild(d);0===this.results.childNodes.length&&this.showClearButton(!0);this.results.appendChild(c);this.maximize();return c};
netgis.Search.prototype.clearResults=function(){this.results.innerHTML="";this.minimize();netgis.util.invoke(this.container,netgis.Events.SEARCH_CLEAR,null)};netgis.Search.prototype.clearAll=function(){this.clearResults();this.lastQuery=null;this.input.value="";this.showClearButton(!1)};netgis.Search.prototype.requestSearch=function(a){this.lastQuery&&this.lastQuery===a||(this.lastQuery=a,netgis.util.invoke(this.container,netgis.Events.SEARCH_CHANGE,{query:a}))};
netgis.Search.prototype.showClearButton=function(a){!1===a?(this.button.classList.remove("netgis-hide"),this.closer.classList.add("netgis-hide")):(this.button.classList.add("netgis-hide"),this.closer.classList.remove("netgis-hide"))};netgis.Search.prototype.onInputKeyDown=function(a){if(13===a.keyCode)return a.preventDefault(),!1};
netgis.Search.prototype.onInputKeyUp=function(a){switch(a.keyCode){case 13:if(this.autocomplete)a=this.results.getElementsByTagName("button"),0<a.length&&a[0].click();else this.onInputTimeout();break;case 27:this.clearAll();break;default:if(this.autocomplete)this.onInputChange()}};netgis.Search.prototype.onInputChange=function(a){this.timeout&&window.clearTimeout(this.timeout);this.timeout=window.setTimeout(this.onInputTimeout.bind(this),this.debounce)};
netgis.Search.prototype.onInputKeyUp=function(a){switch(a.keyCode){case 13:a=this.results.getElementsByTagName("button");0<a.length&&a[0].click();break;case 27:this.clearAll();break;default:this.onInputChange()}};netgis.Search.prototype.onInputChange=function(a){this.timeout&&window.clearTimeout(this.timeout);this.timeout=window.setTimeout(this.onInputTimeout.bind(this),this.debounce)};
netgis.Search.prototype.onInputTimeout=function(){var a=this.input.value;a=a.trim();0<a.length?this.requestSearch(a):this.clearAll()};netgis.Search.prototype.onButtonClick=function(a){this.input.focus()};netgis.Search.prototype.onCloserClick=function(a){this.clearAll();this.input.focus()};netgis.Search.prototype.onResultClick=function(a){a=a.currentTarget;var b=a.getAttribute("data-result");netgis.util.invoke(a,netgis.Events.SEARCH_SELECT,{item:a,data:b})};netgis=netgis||{};netgis.SearchParcel=function(a){this.config=a;this.districtsLayerID="searchparcel_districts";this.fieldsLayerID="searchparcel_fields";this.parcelsLayerID="searchparcel_parcels";this.selected={};this.initElements();this.initEvents();this.initConfig(a)};
netgis.SearchParcel.Config={open:!1,name_url:"./proxy.php?https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_alkis/gem_search.php?placename={q}",parcel_url:"./proxy.php?https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_alkis/flur_search.php?gmk_gmn={district}&fln={field}&fsn_zae={parcelA}&fsn_nen={parcelB}&export=json",districts_service:{type:"WFS",url:"http://geo5.service24.rlp.de/wfs/verwaltungsgrenzen_rp.fcgi?",name:"vermkv:gemarkungen_rlp",format:"application/json; subtype=geojson",
min_zoom:12},fields_service:{url:"http://geo5.service24.rlp.de/wfs/verwaltungsgrenzen_rp.fcgi?",name:"vermkv:fluren_rlp",filter_property:"gmkgnr"}};
@@ -454,25 +446,25 @@ netgis.SearchPlace.prototype.onSearchResponse=function(a){a=JSON.parse(a);this.s
this.search.addResult(d,JSON.stringify(c))};netgis.SearchPlace.prototype.onSearchSelect=function(a){a=JSON.parse(a.detail.data);netgis.util.invoke(this.container,netgis.Events.MAP_ZOOM_LONLAT,{lon:a.lon,lat:a.lat,zoom:this.config.searchplace.zoom});if("street"===a.type){var b=this.config.searchplace.url_detail;b&&(b=netgis.util.replace(b,"{id}",a.id),netgis.util.request(b,this.onSearchDetailResponse.bind(this)))}};
netgis.SearchPlace.prototype.onSearchDetailResponse=function(a){a=JSON.parse(a);var b=a.hsnrarr;if(0!==b.length){this.search.clearResults();for(var c=0;c<b.length;c++){var d=b[c],e=a.strname+" "+d.hsnr;d={type:"address",lon:Number.parseFloat(d.wgs_x),lat:Number.parseFloat(d.wgs_y)};this.search.addResult(e,JSON.stringify(d))}}};netgis.SearchPlace.prototype.onSearchClear=function(a){};netgis=netgis||{};
netgis.SLD=function(){return{read:function(a,b){var c={};a=(new DOMParser).parseFromString(a,"text/xml").getElementsByTagName("NamedLayer");for(var d=0;d<a.length;d++){var e=a[d],f=e.getElementsByTagName("se:Name")[0].innerHTML;console.info("Layer:",f);e=e.getElementsByTagName("se:FeatureTypeStyle");for(f=0;f<e.length;f++)for(var g=e[f].getElementsByTagName("se:Rule"),h=0;h<g.length;h++){var k=g[h],l=k.getElementsByTagName("se:Name")[0].innerHTML;console.info("Rule:",l);l=k.getElementsByTagName("se:PolygonSymbolizer")[0];k=
l.getElementsByTagName("se:Fill")[0];l=l.getElementsByTagName("se:Stroke")[0];c.polygon={fill:k.querySelector("[name='fill']").innerHTML,stroke:l.querySelector("[name='stroke']").innerHTML,strokeWidth:Number.parseFloat(l.querySelector("[name='stroke-width']").innerHTML)}}}console.info("SLD:",c);b.invoke(netgis.Events.MAP_UPDATE_STYLE,c);return c}}}();netgis=netgis||{};netgis.Tabs=function(a){this.activeSection=null;this.initElements(a);this.setActiveTab(0)};
l.getElementsByTagName("se:Fill")[0];l=l.getElementsByTagName("se:Stroke")[0];c.polygon={fill:k.querySelector("[name='fill']").innerHTML,stroke:l.querySelector("[name='stroke']").innerHTML,strokeWidth:Number.parseFloat(l.querySelector("[name='stroke-width']").innerHTML)}}}console.info("SLD:",c);b.invoke(netgis.Events.MAP_UPDATE_STYLE,c);return c}}}();netgis=netgis||{};netgis.Tabs=function(a){this.initElements(a);this.setActiveTab(0)};
netgis.Tabs.prototype.initElements=function(a){this.container=document.createElement("div");this.container.className="netgis-tabs";this.header=document.createElement("div");this.header.className="netgis-header netgis-color-d";this.container.appendChild(this.header);this.content=document.createElement("div");this.content.className="netgis-content netgis-color-e";this.container.appendChild(this.content);for(var b=0;b<a.length;b++){var c=document.createElement("button");c.setAttribute("type","button");
c.addEventListener("click",this.onHeaderButtonClick.bind(this));c.className="netgis-button netgis-color-d";c.innerHTML=a[b];this.header.appendChild(c);c=document.createElement("section");c.className="netgis-color-e netgis-form";this.content.appendChild(c)}};netgis.Tabs.prototype.attachTo=function(a){a.appendChild(this.container)};
netgis.Tabs.prototype.setActiveTab=function(a){var b=this.header.getElementsByClassName("netgis-button"),c=this.content.getElementsByTagName("section");this.activeSection=null;for(var d=0;d<b.length;d++){var e=b[d],f=c[d];d===a?(e.classList.add("netgis-color-e"),e.classList.add("netgis-text-a"),e.classList.add("netgis-bar-a"),e.classList.add("netgis-active"),e.scrollIntoView({behavior:"smooth"}),f.classList.remove("netgis-hide"),f.scrollTop=0,this.activeSection=f):(e.classList.remove("netgis-color-e"),
e.classList.remove("netgis-text-a"),e.classList.remove("netgis-bar-a"),e.classList.remove("netgis-active"),f.classList.add("netgis-hide"))}netgis.util.invoke(this.container,netgis.Events.TABS_CHANGE,{index:a,section:this.activeSection})};netgis.Tabs.prototype.getContentSection=function(a){return this.content.getElementsByTagName("section")[a]};netgis.Tabs.prototype.appendContent=function(a,b){this.content.getElementsByTagName("section")[a].appendChild(b)};
netgis.Tabs.prototype.setActiveTab=function(a){for(var b=this.header.getElementsByClassName("netgis-button"),c=this.content.getElementsByTagName("section"),d=0;d<b.length;d++){var e=b[d],f=c[d];d===a?(e.classList.add("netgis-color-e"),e.classList.add("netgis-text-a"),e.classList.add("netgis-bar-a"),e.classList.add("netgis-active"),e.scrollIntoView({behavior:"smooth"}),f.classList.remove("netgis-hide"),f.scrollTop=0):(e.classList.remove("netgis-color-e"),e.classList.remove("netgis-text-a"),e.classList.remove("netgis-bar-a"),
e.classList.remove("netgis-active"),f.classList.add("netgis-hide"))}};netgis.Tabs.prototype.getContentSection=function(a){return this.content.getElementsByTagName("section")[a]};netgis.Tabs.prototype.appendContent=function(a,b){this.content.getElementsByTagName("section")[a].appendChild(b)};
netgis.Tabs.prototype.updateHeaderScroll=function(){for(var a=0,b=this.header.getElementsByTagName("button"),c=0;c<b.length;c++){var d=b[c];a+=d.getBoundingClientRect().width;d.classList.contains("netgis-active")&&d.scrollIntoView()}b=this.header.getBoundingClientRect().width;a>b?this.container.classList.add("netgis-scroll"):this.container.classList.remove("netgis-scroll")};
netgis.Tabs.prototype.onHeaderButtonClick=function(a){a=a.currentTarget;for(var b=this.header.getElementsByClassName("netgis-button"),c=0,d=0;d<b.length;d++)if(b[d]===a){c=d;break}this.setActiveTab(c)};netgis=netgis||{};netgis.TimeSlider=function(a){this.config=a;this.insertTop=!0;this.initElements();var b=this;window.setTimeout(function(){b.container.scrollLeft=0},1)};netgis.TimeSlider.Config={};
netgis.Tabs.prototype.onHeaderButtonClick=function(a){a=a.currentTarget;for(var b=this.header.getElementsByClassName("netgis-button"),c=0,d=0;d<b.length;d++)if(b[d]===a){c=d;break}this.setActiveTab(c)};netgis=netgis||{};netgis.TimeSlider=function(a){this.config=a;this.insertTop=!0;this.initElements();for(a=1900;2E3>=a;a++);var b=this;window.setTimeout(function(){b.container.scrollLeft=0},1)};netgis.TimeSlider.Config={};
netgis.TimeSlider.prototype.initElements=function(){this.container=document.createElement("section");this.container.className="netgis-timeslider netgis-footer netgis-noselect netgis-color-e netgis-hide";document.addEventListener("pointermove",this.onPointerMove.bind(this));document.addEventListener("pointerup",this.onPointerUp.bind(this));this.header=document.createElement("button");this.header.className="netgis-header netgis-button netgis-clip-text netgis-color-a netgis-hover-c netgis-shadow";this.header.innerHTML=
"<i class='netgis-icon fas fa-clock'></i><span>TimeSlider</span><i class='netgis-icon fas fa-times'></i>";this.header.setAttribute("type","button");this.header.addEventListener("click",this.onHeaderClick.bind(this));this.container.appendChild(this.header);this.wrapper=document.createElement("div");this.wrapper.className="netgis-wrapper";this.wrapper.addEventListener("pointerdown",this.onPointerDown.bind(this));this.container.appendChild(this.wrapper);this.table=document.createElement("table");this.wrapper.appendChild(this.table);
this.top=document.createElement("tr");this.table.appendChild(this.top);this.bottom=document.createElement("tr")};netgis.TimeSlider.prototype.attachTo=function(a){a.appendChild(this.container);a.addEventListener(netgis.Events.TIMESLIDER_SHOW,this.onTimeSliderShow.bind(this));a.addEventListener(netgis.Events.TIMESLIDER_HIDE,this.onTimeSliderHide.bind(this))};
netgis.TimeSlider.prototype.setVisible=function(a){a?(this.container.classList.remove("netgis-hide"),this.container.parentNode.classList.add("netgis-footer")):(this.container.classList.add("netgis-hide"),this.container.parentNode.classList.remove("netgis-footer"))};netgis.TimeSlider.prototype.setTitle=function(a){this.header.getElementsByTagName("span")[0].innerHTML=a};netgis.TimeSlider.prototype.clearTimeSteps=function(){this.top.innerHTML=""};
netgis.TimeSlider.prototype.addTimeStep=function(a,b){var c=document.createElement("td");c.setAttribute("data-id",a);a=document.createElement("button");a.className="netgis-button netgis-color-e netgis-hover-d";a.innerHTML="<i class='netgis-icon netgis-text-a far fa-calendar'></i><span>"+b+"</span>";a.setAttribute("type","button");a.addEventListener("click",this.onTimeStepClick.bind(this));c.appendChild(a);this.top.appendChild(c)};
netgis.TimeSlider.prototype.addTimeStep_01=function(a,b){a=document.createElement("td");var c=document.createElement("td");this.insertTop?a.innerHTML=b:c.innerHTML=b;this.top.appendChild(a);this.bottom.appendChild(c);this.insertTop=!this.insertTop};
netgis.TimeSlider.prototype.setActiveTimeStep=function(a){for(var b=this.top.getElementsByTagName("td"),c=b[a],d=c.getAttribute("data-id"),e=0;e<b.length;e++){var f=b[e],g=f.getElementsByClassName("netgis-icon")[0];e===a?(f.classList.add("netgis-active"),g.classList.remove("fa-calendar"),g.classList.add("fa-calendar-check")):(f.classList.remove("netgis-active"),g.classList.add("fa-calendar"),g.classList.remove("fa-calendar-check"))}c.scrollIntoView();netgis.util.invoke(c,netgis.Events.TIMESLIDER_SELECT,
{layer:this.layerID,time:d})};netgis.TimeSlider.prototype.requestServiceWMST=function(a,b){a=a.trim();1>a.length||(-1===a.indexOf("GetCapabilities")&&(a+="&REQUEST=GetCapabilities"),netgis.util.request(a,this.onServiceResponseWMST.bind(this),{id:b}))};
netgis.TimeSlider.prototype.setActiveTimeStep=function(a){var b=this.top.getElementsByTagName("td"),c=b[a],d=c.getAttribute("data-id");console.info("Set Active Step:",a,d);for(var e=0;e<b.length;e++){var f=b[e],g=f.getElementsByClassName("netgis-icon")[0];e===a?(f.classList.add("netgis-active"),g.classList.remove("fa-calendar"),g.classList.add("fa-calendar-check")):(f.classList.remove("netgis-active"),g.classList.add("fa-calendar"),g.classList.remove("fa-calendar-check"))}c.scrollIntoView();netgis.util.invoke(c,
netgis.Events.TIMESLIDER_SELECT,{layer:this.layerID,time:d})};netgis.TimeSlider.prototype.requestServiceWMST=function(a,b){a=a.trim();1>a.length||(-1===a.indexOf("GetCapabilities")&&(a+="&REQUEST=GetCapabilities"),netgis.util.request(a,this.onServiceResponseWMST.bind(this),{id:b}))};
netgis.TimeSlider.prototype.onServiceResponseWMST=function(a,b){for(var c=(new DOMParser).parseFromString(a,"text/xml").documentElement.getElementsByTagName("Layer"),d,e=0;e<c.length;e++){a=c[e];var f=a.getElementsByTagName("Name")[0].textContent;if(f===b.id){d={name:f,title:a.getElementsByTagName("Title")[0].textContent,abstract:a.getElementsByTagName("Abstract")[0].textContent,queryable:"1"===a.getAttribute("queryable"),opaque:"1"===a.getAttribute("opaque")};e=a.getElementsByTagName("SRS");0<e.length&&
(d.projection=e[0].textContent);e=a.getElementsByTagName("CRS");0<e.length&&(d.projection=e[0].textContent);e=a.getElementsByTagName("BoundingBox")[0];d.bbox=[Number.parseFloat(e.getAttribute("minx")),Number.parseFloat(e.getAttribute("miny")),Number.parseFloat(e.getAttribute("maxx")),Number.parseFloat(e.getAttribute("maxy"))];b=a.getElementsByTagName("Dimension");for(e=0;e<b.length;e++)if(c=b[e],"time"===c.getAttribute("name")){var g={defaultTime:c.getAttribute("default"),extent:c.textContent.split("/")};
break}a=a.getElementsByTagName("Extent");for(e=0;e<a.length;e++)if(b=a[e],"time"===b.getAttribute("name")){g.defaultTime=b.getAttribute("default");g.extent=b.textContent.split("/");break}d.time=g;break}}this.setTitle(d.title);if(d.time.extent){g=new Date(d.time.extent[0]);a=new Date(d.time.extent[1]);b=d.time.extent[2];d=[];switch(b){case "P1D":for(;g<=a;)d.push(new Date(g)),g.setDate(g.getDate()+1);break;default:console.error("unsupported WMST date range",b,g,a)}for(e=0;e<d.length;e++)b=d[e],a=g=
b.toISOString(),a=a.replace("T",", "),a=a.replace("Z",""),this.addTimeStep(g,a);this.setActiveTimeStep(d.length-1)}};netgis.TimeSlider.prototype.onTimeSliderShow=function(a){a=a.detail;this.layerID=a.layer;this.setTitle(a.title);this.clearTimeSteps();this.requestServiceWMST(a.url,a.name);this.setVisible(!0)};netgis.TimeSlider.prototype.onTimeSliderHide=function(a){this.setVisible(!1)};netgis.TimeSlider.prototype.onHeaderClick=function(a){this.setVisible(!1)};
break}a=a.getElementsByTagName("Extent");for(e=0;e<a.length;e++)if(b=a[e],"time"===b.getAttribute("name")){g.defaultTime=b.getAttribute("default");g.extent=b.textContent.split("/");break}d.time=g;break}}console.info("WMST Layer:",d);this.setTitle(d.title);if(d.time.extent){g=new Date(d.time.extent[0]);a=new Date(d.time.extent[1]);b=d.time.extent[2];d=[];switch(b){case "P1D":for(;g<=a;)d.push(new Date(g)),g.setDate(g.getDate()+1);break;default:console.error("unsupported WMST date range",b,g,a)}for(e=
0;e<d.length;e++)b=d[e],a=g=b.toISOString(),a=a.replace("T",", "),a=a.replace("Z",""),this.addTimeStep(g,a);this.setActiveTimeStep(d.length-1)}};netgis.TimeSlider.prototype.onTimeSliderShow=function(a){a=a.detail;console.info("TimeSlider Show:",a);this.layerID=a.layer;this.setTitle(a.title);this.clearTimeSteps();this.requestServiceWMST(a.url,a.name);this.setVisible(!0)};netgis.TimeSlider.prototype.onTimeSliderHide=function(a){this.setVisible(!1)};netgis.TimeSlider.prototype.onHeaderClick=function(a){this.setVisible(!1)};
netgis.TimeSlider.prototype.onPointerDown=function(a){a=a.pageX-this.wrapper.offsetLeft;this.down=!0;this.downX=a;this.downScroll=this.wrapper.scrollLeft;this.container.classList.add("netgis-active")};netgis.TimeSlider.prototype.onPointerMove=function(a){this.down&&(a.preventDefault(),this.wrapper.scrollLeft=this.downScroll-(a.pageX-this.wrapper.offsetLeft-this.downX))};netgis.TimeSlider.prototype.onPointerUp=function(a){this.down&&a.preventDefault();this.down=!1;this.container.classList.remove("netgis-active")};
netgis.TimeSlider.prototype.onTimeStepClick=function(a){a=a.currentTarget.parentNode;a.getAttribute("data-id");if(!(5<Math.abs(this.wrapper.scrollLeft-this.downScroll))){for(var b=this.top.getElementsByTagName("td"),c=-1,d=0;d<b.length;d++)if(b[d]===a){c=d;break}this.setActiveTimeStep(c)}};netgis=netgis||{};netgis.Toolbox=function(a){this.config=a;this.bottomPanels={};this.initElements(a);this.initOptions(a);this.initEvents();this.setActiveButton(netgis.Commands.VIEW)};netgis.Toolbox.Config={open:!1,items:[],options:{buffer_features:{title:"Buffer",items:[{id:"buffer_radius",type:"integer",title:"Radius (Meter)"},{id:"buffer_segments",type:"integer",title:"Segments"},{id:"buffer_submit",type:"button",title:"<i class='netgis-icon netgis-text-a fas fa-arrow-circle-right'></i><span>Akzeptieren</span>"}]}}};
netgis.Toolbox.prototype.initElements=function(a){this.panel=new netgis.Panel("Toolbox");this.panel.content.classList.add("netgis-toolbox");this.top=document.createElement("section");this.panel.content.appendChild(this.top);for(var b=a.toolbox.items,c=0;c<b.length;c++){var d=b[c];if(this.config.tools&&!1===this.config.tools.editable)switch(d.id){case "draw_points":continue;case "draw_lines":continue;case "draw_polygons":continue;case "modify_features":continue;case "delete_features":continue;case "buffer_features":continue;
@@ -507,9 +499,8 @@ netgis.Toolbox.prototype.onDrawBufferChange=function(a){a=a.currentTarget.parent
c,b[2].value=d,b[3].value=e)};netgis.Toolbox.prototype.onBufferFeaturesChange=function(a){var b=this.bottomPanels.bufferFeatures.getElementsByTagName("input");a=Number.parseFloat(b[0].value);b=Number.parseInt(b[1].value);netgis.util.invoke(this.bottomPanels.bufferFeatures,netgis.Events.BUFFER_CHANGE,{radius:a,segments:b})};netgis.Toolbox.prototype.onSelectMultipleChange=function(a){netgis.util.invoke(this.bottomPanels.bufferFeatures,netgis.Events.SELECT_MULTI_TOGGLE,{on:a.currentTarget.checked})};
netgis.Toolbox.prototype.onSelectMultiToggle=function(a){a=a.detail;this.bottomPanels.bufferFeatures.getElementsByTagName("input")[2].checked=a.on};
netgis.Toolbox.prototype.onBufferFeaturesAccept=function(a){var b=this.bottomPanels.bufferFeatures.getElementsByTagName("input");a=Number.parseFloat(b[0].value);b=Number.parseInt(b[1].value);netgis.util.invoke(this.bottomPanels.bufferFeatures,netgis.Events.BUFFER_ACCEPT,{radius:a,segments:b});netgis.util.invoke(this.bottomPanels.bufferFeatures,netgis.Events.CLIENT_SET_MODE,{mode:netgis.Modes.VIEW})};netgis=netgis||{};netgis.Tree=function(a){this.draggable=a?a:!1;this.dragElement=null;this.initElements()};netgis.Tree.prototype.initElements=function(){var a=document.createElement("ul");a.className="netgis-tree";this.container=a};netgis.Tree.prototype.attachTo=function(a){a.appendChild(this.container)};netgis.Tree.prototype.clear=function(){this.container.innerHTML=""};
netgis.Tree.prototype.addFolder=function(a,b,c,d,e,f,g){var h=document.createElement("li");h.className="netgis-folder";h.setAttribute("data-id",b);h.setAttribute("data-removable",g?!0:!1);b=document.createElement("details");h.appendChild(b);var k=document.createElement("summary");k.className="netgis-button netgis-noselect netgis-clip-text netgis-color-e netgis-hover-d";k.innerHTML="<i class='netgis-icon netgis-hide-open fas fa-folder'></i><i class='netgis-icon netgis-show-open fas fa-folder-open'></i><span>"+
c+"</span>";b.appendChild(k);!0===this.draggable&&(netgis.util.isDefined(f)&&!0!==f||this.setItemDraggable(k));!0===g&&(c=document.createElement("button"),c.className="netgis-closer netgis-clickable netgis-color-d netgis-hover-d",c.innerHTML="<i class='fas fa-times'></i>",c.addEventListener("click",this.onFolderDeleteClick.bind(this)),k.appendChild(c));c=document.createElement("label");k.appendChild(c);k=document.createElement("input");k.setAttribute("type","checkbox");k.onchange=this.onFolderChange.bind(this);
c.appendChild(k);!0===e&&h.classList.add("netgis-nocheck");e=document.createElement("ul");b.appendChild(e);a=a?a.getElementsByTagName("ul")[0]:this.container;d?a.insertBefore(h,a.firstChild):a.appendChild(h);return h};
netgis.Tree.prototype.addFolder=function(a,b,c,d,e,f){var g=document.createElement("li");g.className="netgis-folder";g.setAttribute("data-id",b);b=document.createElement("details");g.appendChild(b);var h=document.createElement("summary");h.className="netgis-button netgis-noselect netgis-clip-text netgis-color-e netgis-hover-d";h.innerHTML="<i class='netgis-icon netgis-hide-open fas fa-folder'></i><i class='netgis-icon netgis-show-open fas fa-folder-open'></i><span>"+c+"</span>";b.appendChild(h);!0===
this.draggable&&(netgis.util.isDefined(f)&&!0!==f||this.setItemDraggable(h));c=document.createElement("label");h.appendChild(c);f=document.createElement("input");f.setAttribute("type","checkbox");f.onchange=this.onFolderChange.bind(this);c.appendChild(f);!0===e&&g.classList.add("netgis-nocheck");e=document.createElement("ul");b.appendChild(e);a=a?a.getElementsByTagName("ul")[0]:this.container;d?a.insertBefore(g,a.firstChild):a.appendChild(g);return g};
netgis.Tree.prototype.addCheckbox=function(a,b,c,d,e,f){var g=document.createElement("li");g.className="netgis-item";!0===this.draggable&&this.setItemDraggable(g);var h=document.createElement("label");h.className="netgis-button netgis-noselect netgis-clip-text netgis-color-e netgis-hover-d";h.innerHTML="<span>"+c+"</span>";g.appendChild(h);c=document.createElement("span");c.className="netgis-wrapper";h.insertBefore(c,h.firstChild);h=document.createElement("input");h.setAttribute("type","checkbox");
h.setAttribute("data-id",b);d&&(h.checked=d);h.onchange=this.onItemChange.bind(this);c.appendChild(h);f&&this.addItemDetails(g,b,f);a=a?a.getElementsByTagName("ul")[0]:this.container;e?a.insertBefore(g,a.firstChild):a.appendChild(g);return g};
netgis.Tree.prototype.addRadioButton=function(a,b,c,d,e){var f=document.createElement("li");f.className="netgis-item";var g=document.createElement("label");g.className="netgis-button netgis-noselect netgis-clip-text netgis-color-e netgis-hover-d";g.innerHTML="<span>"+c+"</span>";f.appendChild(g);c=document.createElement("span");c.className="netgis-wrapper";g.insertBefore(c,g.firstChild);g=a?"radio-"+a.getAttribute("data-id"):"radio-noname";var h=document.createElement("input");h.setAttribute("type",
@@ -521,16 +512,14 @@ netgis.Tree.prototype.addItemDetails=function(a,b,c){var d=document.createElemen
b);l.addEventListener("change",this.onItemSliderChange.bind(this));l.addEventListener("input",this.onItemSliderChange.bind(this));this.draggable&&(l.setAttribute("draggable","true"),l.addEventListener("dragstart",function(a){a.preventDefault();a.stopImmediatePropagation()}));k.appendChild(l)}e.appendChild(h)}a.appendChild(d)};
netgis.Tree.prototype.setItemDraggable=function(a){a.setAttribute("draggable","true");a.addEventListener("dragstart",this.onDragStart.bind(this));a.addEventListener("dragenter",this.onDragEnter.bind(this));a.addEventListener("dragover",this.onDragOver.bind(this));a.addEventListener("dragleave",this.onDragLeave.bind(this));a.addEventListener("drop",this.onDragDrop.bind(this));a.addEventListener("dragend",this.onDragEnd.bind(this))};
netgis.Tree.prototype.getFolder=function(a){for(var b=this.container.getElementsByClassName("netgis-folder"),c=0;c<b.length;c++){var d=b[c];if(d.getAttribute("data-id")===a)return d}return null};netgis.Tree.prototype.getItem=function(a){for(var b=this.container.getElementsByTagName("input"),c=0;c<b.length;c++){var d=b[c];if(d.getAttribute("data-id")===a)return d}return null};
netgis.Tree.prototype.removeItem=function(a){var b=this.getItem(a);b&&(b=b.parentNode.parentNode.parentNode,netgis.util.invoke(b,netgis.Events.TREE_ITEM_REMOVE,{id:a}),b.parentNode.removeChild(b),this.removeEmptyFolders(),this.updateFolderChecks())};netgis.Tree.prototype.removeFolder=function(a){"true"===a.getAttribute("data-removable")&&(a.parentNode.removeChild(a),this.removeEmptyFolders())};
netgis.Tree.prototype.removeEmptyFolders=function(){for(var a=this.container.getElementsByClassName("netgis-folder"),b=0;b<a.length;b++){var c=a[b];0===c.getElementsByTagName("ul")[0].getElementsByTagName("li").length&&this.removeFolder(c)}};
netgis.Tree.prototype.removeItem=function(a){if(a=this.getItem(a))a=a.parentNode.parentNode.parentNode,a.parentNode.removeChild(a),this.removeEmptyFolders(),this.updateFolderChecks()};netgis.Tree.prototype.removeEmptyFolders=function(){for(var a=this.container.getElementsByClassName("netgis-folder"),b=0;b<a.length;b++)a[b].getElementsByTagName("ul")[0].getElementsByTagName("li")};
netgis.Tree.prototype.setFolderOpen=function(a,b){var c=this.container.getElementsByClassName("netgis-folder");a=a.toString();for(var d=0;d<c.length;d++){var e=c[d],f=e.getElementsByTagName("details")[0];e.getAttribute("data-id")===a&&(!0===b?f.setAttribute("open",""):f.removeAttribute("open"))}};
netgis.Tree.prototype.setItemChecked=function(a,b,c){var d=this.container.getElementsByClassName("netgis-item");a=a.toString();for(var e=0;e<d.length;e++){var f=d[e],g=f.getElementsByTagName("input")[0];if(g.getAttribute("data-id")===a){if("radio"===g.getAttribute("type")){f=f.parentNode.getElementsByTagName("input");for(var h=0;h<f.length;h++){var k=f[h];k!==g&&"radio"===k.getAttribute("type")&&!1!==k.checked&&(k.checked=!1,netgis.util.invoke(g,netgis.Events.TREE_ITEM_CHANGE,{id:k.getAttribute("data-id"),
checked:!1}))}}c?g.checked=b:g.checked!==b&&g.click()}}};netgis.Tree.prototype.setFolderParent=function(a,b){var c=a.parentNode;c&&c.removeChild(a);null!==b?b.getElementsByTagName("ul")[0].appendChild(a):this.container.appendChild(a)};netgis.Tree.prototype.updateFolderChecks=function(){for(var a=this.container.getElementsByClassName("netgis-folder"),b=0;b<a.length;b++)this.updateFolderCheck(a[b])};
netgis.Tree.prototype.updateFolderCheck=function(a){a||(a=this.container);for(var b=a.getElementsByClassName("netgis-item"),c=0,d=0;d<b.length;d++){var e=b[d].getElementsByTagName("input")[0];e.checked&&c++}e=a.getElementsByTagName("input")[0];d=0;0<c&&(d=1);0<c&&c===b.length&&(d=2);switch(d){case 0:e.checked=!1;e.classList.remove("netgis-partial");break;case 1:e.checked=!0;e.classList.add("netgis-partial");break;case 2:e.checked=!0,e.classList.remove("netgis-partial")}(a=a.parentElement)&&a!==this.container&&
(a=a.parentElement.parentElement)&&-1!==a.className.search("netgis-folder")&&this.updateFolderCheck(a)};netgis.Tree.prototype.onFolderChange=function(a){var b=a.currentTarget;a=b.checked;b=b.parentElement.parentElement.parentElement.parentElement.getElementsByTagName("input");for(var c=1;c<b.length;c++){var d=b[c];d.checked!==a&&d.click()}this.updateFolderChecks()};
netgis.Tree.prototype.onFolderDeleteClick=function(a){a=a.currentTarget.parentNode.parentNode.parentNode.getElementsByClassName("netgis-item");for(var b=[],c=0;c<a.length;c++){var d=a[c].getElementsByTagName("input")[0].getAttribute("data-id");b.push(d)}for(c=0;c<b.length;c++)this.removeItem(b[c])};
netgis.Tree.prototype.onItemChange=function(a){a=a.currentTarget;var b=a.checked,c=a.getAttribute("data-id");if("radio"===a.getAttribute("type"))for(var d=a.parentNode.parentNode.parentNode.parentNode.getElementsByTagName("input"),e=0;e<d.length;e++){var f=d[e].getAttribute("data-id");f&&f!==c&&netgis.util.invoke(a,netgis.Events.TREE_ITEM_CHANGE,{id:f,checked:!1})}netgis.util.invoke(a,netgis.Events.TREE_ITEM_CHANGE,{id:c,checked:b});this.updateFolderChecks()};
netgis.Tree.prototype.onRadioChange=function(a){a=a.currentTarget;var b=a.checked,c=a.getAttribute("data-id"),d=Number.parseInt(c);Number.isNaN(d)||(c=d);d=a.parentElement.parentElement.parentElement.parentElement.parentElement;netgis.util.invoke(a,netgis.Events.TREE_ITEM_CHANGE,{id:c,checked:b});-1!==d.className.search("netgis-folder")&&this.updateFolderCheck(d)};
(a=a.parentElement.parentElement)&&-1!==a.className.search("netgis-folder")&&this.updateFolderCheck(a)};netgis.Tree.prototype.onFolderChange=function(a){var b=a.currentTarget;a=b.checked;b=b.parentElement.parentElement.parentElement.parentElement;for(var c=b.getElementsByTagName("input"),d=1;d<c.length;d++){var e=c[d];e.checked!==a&&e.click()}this.updateFolderCheck(b);a=b.parentElement.parentElement.parentElement;-1!==a.className.search("netgis-folder")&&this.updateFolderCheck(a)};
netgis.Tree.prototype.onItemChange=function(a){a=a.currentTarget;var b=a.checked,c=a.getAttribute("data-id"),d=a.parentElement.parentElement.parentElement.parentElement.parentElement;if("radio"===a.getAttribute("type"))for(var e=a.parentNode.parentNode.parentNode.parentNode.getElementsByTagName("input"),f=0;f<e.length;f++){var g=e[f].getAttribute("data-id");g&&g!==c&&netgis.util.invoke(a,netgis.Events.TREE_ITEM_CHANGE,{id:g,checked:!1})}netgis.util.invoke(a,netgis.Events.TREE_ITEM_CHANGE,{id:c,checked:b});
-1!==d.className.search("netgis-folder")&&this.updateFolderCheck(d)};netgis.Tree.prototype.onRadioChange=function(a){a=a.currentTarget;var b=a.checked,c=a.getAttribute("data-id"),d=Number.parseInt(c);Number.isNaN(d)||(c=d);d=a.parentElement.parentElement.parentElement.parentElement.parentElement;netgis.util.invoke(a,netgis.Events.TREE_ITEM_CHANGE,{id:c,checked:b});-1!==d.className.search("netgis-folder")&&this.updateFolderCheck(d)};
netgis.Tree.prototype.onButtonClick=function(a){a=a.currentTarget;var b=a.getAttribute("data-id");netgis.util.invoke(a,netgis.Events.TREE_BUTTON_CLICK,{id:b})};netgis.Tree.prototype.onItemSliderChange=function(a){a=a.currentTarget;var b=a.getAttribute("data-id");netgis.util.invoke(a,netgis.Events.TREE_ITEM_SLIDER_CHANGE,{id:b,val:Number.parseFloat(a.value)})};
netgis.Tree.prototype.onDragStart=function(a){a.stopPropagation();var b=a.currentTarget,c=b.getElementsByTagName("input")[0].getAttribute("data-id");b.classList.add("netgis-dragging");a.dataTransfer.setData("text/plain",c);a.dataTransfer.dropEffect="move";this.dragElement=b};netgis.Tree.prototype.onDragEnter=function(a){a=a.currentTarget;a!==this.dragElement&&a.classList.add("netgis-dragover")};
netgis.Tree.prototype.onDragOver=function(a){a.preventDefault();a=a.currentTarget;a!==this.dragElement&&a.classList.add("netgis-dragover")};netgis.Tree.prototype.onDragLeave=function(a){a.currentTarget.classList.remove("netgis-dragover")};
@@ -544,9 +533,9 @@ this.replace(a,":",b);a=this.replace(a,";",b);a=this.replace(a,'"',b);a=this.rep
b){a=a.replace(new RegExp("^"+b+"+"),"");return a=a.replace(new RegExp(b+"+$"),"")},foreach:a,template:function(b,d){a(d,function(a,c){b=b.replace(new RegExp("{"+a+"}","g"),c)});return b},newlines:function(a){return a.replace(/\n/g,"<br />")},create:function(a){var b=document.createElement("tbody");b.innerHTML=a;return b.children[0]},insideElement:function(a,b,e){a=a.getBoundingClientRect();return b<a.left||e<a.top||b>a.right||e>a.bottom?!1:!0},size:function(a){a=(new TextEncoder).encode(JSON.stringify(a)).length;
var b=a/1024;return{bytes:a,kilobytes:b,megabytes:b/1024}},request:function(a,b,e,f){f=new XMLHttpRequest;e&&(f._requestData=e);f.onload=function(){b(this.responseText,this._requestData,this)};f.withCredentials=!1;f.open("GET",a,!0);f.send();return f},downloadJSON:function(a,b){a="data:text/json;charset=utf-8,"+encodeURIComponent(JSON.stringify(a));var c=document.createElement("a");c.setAttribute("href",a);c.setAttribute("download",b);document.body.appendChild(c);c.click();c.remove()},padstr:function(a,
b){for(a=a.toString();a.length<b;)a="0"+a;return a},merge:function(a,b){return Object.assign(a,b)},getTimeStamp:function(a){var b=new Date;if(!0===a){a=b.getFullYear();var c=b.getMonth()+1,f=b.getDate(),g=b.getHours(),h=b.getMinutes();b=b.getSeconds();10>c&&(c="0"+c);10>f&&(f="0"+f);10>g&&(g="0"+g);10>h&&(h="0"+h);10>b&&(b="0"+b);a=[a,c,f,"_",g,h,b].join("")}else a=b.getDate()+"."+(b.getMonth()+1)+"."+b.getFullYear(),a+=" "+b.getHours()+":"+b.getMinutes();return a},getUserLanguage:b,getFileExtension:function(a){a=
a.split(".");return 1>=a.length?"":a[a.length-1]},parseURL:function(a,b){var c=a.indexOf("?"),d=-1<c?a.substr(0,c):a,g=[];if(-1<c)if(a=a.substr(c+1),a=a.split("&"),b)for(g={},b=0;b<a.length;b++)c=a[b],c=c.split("="),0<c[0].length&&(g[c[0]]=1<c.length?c[1]:!0);else for(b=0;b<a.length;b++)c=a[b],g.push(c);return{base:d,parameters:g}},formatDistance:function(a){return 100<a?Math.round(a/1E3*100)/100+" km":Math.round(100*a)/100+" m"},formatLength:function(a,b){var c=1E3<a;a=c?b?Math.round(a/1E3*1E3)/
1E3:Math.round(a/1E3):b?Math.round(100*a)/100:Math.round(a);0===a&&(c=!1);return a+(c?" km":" m")},formatArea:function(a,d,e,f){a=(e=a>(e||1E5))?d?Math.round(a/1E6*1E3)/1E3:Math.round(a/1E6):d?Math.round(100*a)/100:Math.round(a);0===a&&(e=!1);a=a.toLocaleString(b());return a+(e?" km\u00b2":" m\u00b2")},hexToRGB:function(a){"#"===a.charAt(0)&&(a=a.substr(1));a=Number.parseInt(a,16);return[a>>16&255,a>>8&255,a&255]},invoke:function(a,b,e){a.dispatchEvent(new CustomEvent(b,{bubbles:!0,detail:e}))},handler:function(a,
b){return function(c){b||(b=c);netgis.util.invoke(this,a,b)}}}}();netgis=netgis||{};netgis.Window=function(a){this.resizing=this.dragging=!1;this.py=this.px=0;this.initElements(a);this.initEvents();var b=this;window.setTimeout(function(){var a=b.container.getBoundingClientRect();b.container.style.right="auto";b.container.style.bottom="auto";b.container.style.left=a.left+"px";b.container.style.top=a.top+"px"},100)};
a.split(".");return 1>=a.length?"":a[a.length-1]},parseURL:function(a){var b=a.indexOf("?"),c=-1<b?a.substr(0,b):a,f=[];if(-1<b)for(a=a.substr(b+1),a=a.split("&"),b=0;b<a.length;b++){var g=a[b];g=g.toLowerCase();-1<g.search("request")||f.push(g)}return{base:c,parameters:f}},formatDistance:function(a){return 100<a?Math.round(a/1E3*100)/100+" km":Math.round(100*a)/100+" m"},formatLength:function(a,b){var c=1E3<a;a=c?b?Math.round(a/1E3*1E3)/1E3:Math.round(a/1E3):b?Math.round(100*a)/100:Math.round(a);
0===a&&(c=!1);return a+(c?" km":" m")},formatArea:function(a,d,e,f){a=(e=a>(e||1E5))?d?Math.round(a/1E6*1E3)/1E3:Math.round(a/1E6):d?Math.round(100*a)/100:Math.round(a);0===a&&(e=!1);a=a.toLocaleString(b());return a+(e?" km\u00b2":" m\u00b2")},hexToRGB:function(a){"#"===a.charAt(0)&&(a=a.substr(1));a=Number.parseInt(a,16);return[a>>16&255,a>>8&255,a&255]},invoke:function(a,b,e){a.dispatchEvent(new CustomEvent(b,{bubbles:!0,detail:e}))},handler:function(a,b){return function(c){b||(b=c);netgis.util.invoke(this,
a,b)}}}}();netgis=netgis||{};netgis.Window=function(a){this.resizing=this.dragging=!1;this.py=this.px=0;this.initElements(a);this.initEvents();var b=this;window.setTimeout(function(){var a=b.container.getBoundingClientRect();b.container.style.right="auto";b.container.style.bottom="auto";b.container.style.left=a.left+"px";b.container.style.top=a.top+"px"},100)};
netgis.Window.prototype.initElements=function(a){this.container=document.createElement("section");this.container.className="netgis-window netgis-shadow";this.header=document.createElement("button");this.header.setAttribute("type","button");this.header.className="netgis-mover netgis-button netgis-clip-text netgis-color-c netgis-gradient-a netgis-shadow";this.header.innerHTML=a;this.container.appendChild(this.header);this.closer=document.createElement("button");this.closer.className="netgis-closer netgis-button netgis-text-e";
this.closer.innerHTML='<i class="netgis-icon fas fa-times"></i>';this.closer.setAttribute("type","button");this.container.appendChild(this.closer);this.content=document.createElement("div");this.content.className="netgis-content";this.container.appendChild(this.content)};
netgis.Window.prototype.initEvents=function(){this.closer.addEventListener("click",this.onCloserClick.bind(this));this.header.addEventListener("pointerdown",this.onPointerDown.bind(this));document.addEventListener("pointermove",this.onDocPointerMove.bind(this));document.addEventListener("pointerup",this.onDocPointerUp.bind(this));this.observer=new ResizeObserver(this.onResizeObserve.bind(this));this.observer.observe(this.container,{attributes:!0})};netgis.Window.prototype.attachTo=function(a){a.appendChild(this.container)};
@@ -557,13 +546,14 @@ this.container.style.right="auto";this.container.style.bottom="auto"}};netgis.Wi
netgis.WMC.prototype.requestLayers=function(a){var b=this.config.wmc;b?(b=b.layers_url,b=netgis.util.replace(b,"{ids}",a.join(",")),netgis.util.request(b,this.onLayersResponse.bind(this))):console.error("no config[ 'wmc' ] found, skipping WMC layer loading")};netgis.WMC.prototype.onLayersResponse=function(a){this.layers=a=JSON.parse(a);console.info("WMC Layers Response:",a);this.callback&&this.callback({config:this.toConfig()})};
netgis.WMC.prototype.toConfig=function(){var a=this.data.wmc,b=this.data.layerList,c=this.layers.wms.srv,d={},e=a.bbox;e=e.split(",");for(var f=0;f<e.length;f++)e[f]=Number.parseFloat(e[f]);d.map={projection:a.crs,bbox:e};d.attribution||(d.attribution={});d.attribution.prefix=d.attribution.prefix?a.title+", "+d.attribution.prefix:a.title;a=d.folders=[];e=d.layers=[];this.parseServiceLayers(c,b,a,e);b=1E3;for(c=0;c<a.length;c++){f=a[c];for(var g=e.length-1;0<=g;g--){var h=e[g];h.folder===f.id&&(h.order=
b,b+=1)}}return d};
netgis.WMC.prototype.parseServiceLayers=function(a,b,c,d){c||(c=[]);d||(d=[]);for(var e=0;e<a.length;e++)for(var f=a[e],g=0;g<f.layer.length;g++){var h=f.layer[g];h.isRoot&&c.push({id:h.id,title:h.title,open:"1"===f.isopen});var k=h.layer;if(k){b&&k.sort(function(a,c){a=a.id;var d=c.id,e=null;c=null;for(var f=0;f<b.length;f++){var g=b[f];g.layerId.toString()===a&&(e=g);g.layerId.toString()===d&&(c=g)}a=e.layerPos;c=c.layerPos;return a<c?-1:a>c?1:0});for(var l=k.length-1;0<=l;l--){var n=k[l],m=n.id,
q=null;if(b)for(var p=0;p<b.length;p++)if(b[p].layerId.toString()===m){q=b[p];break}n=this.parseServiceLayer(m,f,h.id,n,q);d.push(n)}}}return{folders:c,layers:d}};netgis.WMC.prototype.parseServiceLayer=function(a,b,c,d,e){return{id:a,folder:c,title:d.title,active:e?e.active:!0,query:1===d.queryable,transparency:e?1-.01*e.opacity:0,order:1E3,type:netgis.LayerTypes.WMS,url:b.getMapUrl,name:d.name,format:e?e.currentFormat:"image/png"}};
netgis.WMC.prototype.parseServiceLayers=function(a,b,c,d){c||(c=[]);d||(d=[]);for(var e=0;e<a.length;e++)for(var f=a[e],g=0;g<f.layer.length;g++){var h=f.layer[g];h.isRoot&&c.push({id:h.id,title:h.title,open:"1"===f.isopen});var k=h.layer;if(k){b&&k.sort(function(a,c){a=a.id;var d=c.id,e=null;c=null;for(var f=0;f<b.length;f++){var g=b[f];g.layerId.toString()===a&&(e=g);g.layerId.toString()===d&&(c=g)}a=e.layerPos;c=c.layerPos;return a<c?-1:a>c?1:0});for(var l=k.length-1;0<=l;l--){var m=k[l],n=m.id,
p=null;if(b)for(var q=0;q<b.length;q++)if(b[q].layerId.toString()===n){p=b[q];break}m=this.parseServiceLayer(n,f,h.id,m,p);d.push(m)}}}return{folders:c,layers:d}};netgis.WMC.prototype.parseServiceLayer=function(a,b,c,d,e){return{id:a,folder:c,title:d.title,active:e?e.active:!0,query:1===d.queryable,transparency:e?1-.01*e.opacity:0,order:1E3,type:netgis.LayerTypes.WMS,url:b.getMapUrl,name:d.name,format:e?e.currentFormat:"image/png"}};
netgis.WMC.prototype.toConfig_02=function(){var a=this.data.wmc,b=this.layers.wms.srv,c=this.data.layerList,d={},e=a.bbox;e=e.split(",");for(var f=0;f<e.length;f++)e[f]=Number.parseFloat(e[f]);d.map={projection:a.crs,bbox:e};d.attribution||(d.attribution={});d.attribution.prefix=d.attribution.prefix?a.title+", "+d.attribution.prefix:a.title;a=d.folders=[];e=d.layers=[];var g=[];for(f=0;f<c.length;f++){var h=c[f],k={id:Number.parseInt(h.layerId),type:"layer",order:h.layerPos,active:h.active,opacity:h.opacity};
g.push(k)}for(f=0;f<b.length;f++){c=b[f];k={id:Number.parseInt(c.id),type:"service",title:c.title,url:c.getMapUrl,open:"1"===c.isopen};g.push(k);for(var l=0;l<c.layer.length;l++){h=c.layer[l];var m=this.parseLayer(h,k.id,g);if(h.layer)for(var n=0;n<h.layer.length;n++){var p=h.layer[n],q=this.parseLayer(p,m.id,g);if(p.layer)for(var r=0;r<p.layer.length;r++){var t=p.layer[r],u=this.parseLayer(t,q.id,g);if(t.layer)for(var v=0;v<t.layer.length;v++)this.parseLayer(t.layer[v],u.id,g)}}}}console.info("WMC ITEMS:",
g);for(f=0;f<g.length;f++)switch(k=g[f],k.type){case "service":a.push({id:k.id,title:k.title,open:k.open});break;case "layer":h={},e.push(h)}return d};
netgis.WMC.prototype.parseLayer=function(a,b,c){for(var d=null,e=0;e<c.length;e++)if(c[e].id===Number.parseInt(a.id)){d=c[e];break}d||(d={id:Number.parseInt(a.id),type:"layer"},c.push(d));d.title=a.title;d.name=a.name;d.parent=b;a.getLegendGraphicUrl&&a.getLegendGraphicUrlFormat&&(d.legendURL=a.getLegendGraphicUrl,d.legendFormat=a.getLegendGraphicUrlFormat);a.legendUrl&&(d.legendURL=window.decodeURIComponent(a.legendUrl),d.legendFormat=a.getLegendGraphicUrlFormat);d.queryable=1===a.layerQueryable||
1===a.queryable?!0:!1;if(a.bbox){a=a.bbox.split(",");for(b=0;b<a.length;b++)a[b]=parseFloat(a[b]);d.bbox=[a[0],a[1],a[2],a[3]]}return d};netgis=netgis||{};
netgis.WMS={parseCapabilities:function(a){var b={layers:[],requests:{}};a=(new DOMParser).parseFromString(a,"text/xml").getElementsByTagName("Capability")[0];var c=a.getElementsByTagName("Request");if(0<c.length){c=c[0];var d=c.getElementsByTagName("GetMap");if(0<d.length){d=d[0];var e=null,f=d.getElementsByTagName("Get");0<f.length&&(e=f[0].getElementsByTagName("OnlineResource")[0].getAttribute("xlink:href"));f=[];d=d.getElementsByTagName("Format");for(var g=0;g<d.length;g++)f.push(d[g].innerHTML);b.requests.map=
{url:e,format:f}}c=c.getElementsByTagName("GetFeatureInfo");if(0<c.length){c=c[0];e=null;f=c.getElementsByTagName("Get");0<f.length&&(e=f[0].getElementsByTagName("OnlineResource")[0].getAttribute("xlink:href"));f=[];d=c.getElementsByTagName("Format");for(g=0;g<d.length;g++)f.push(d[g].innerHTML);b.requests.info={url:e,format:f}}}e=a.getElementsByTagName("Layer");for(c=0;c<e.length;c++)f=e[c],f.parentNode===a&&b.layers.push(this.parseLayer(f,!0));return b},parseLayer:function(a,b){var c=this.getChildString(a,
"Name"),d=this.getChildString(a,"Title"),e=this.getChildString(a,"Abstract"),f="1"===a.getAttribute("queryable"),g=this.getChild(a,"LatLonBoundingBox");g&&(g=[Number.parseFloat(g.getAttribute("minx")),Number.parseFloat(g.getAttribute("miny")),Number.parseFloat(g.getAttribute("maxx")),Number.parseFloat(g.getAttribute("maxy"))]);var h=a.getElementsByTagName("LegendURL");0<h.length?(h=h[0].getElementsByTagName("OnlineResource"),h=0<h.length?h[0].getAttribute("xlink:href"):null):h=null;c={name:c,title:d,
abstract:e,queryable:f,bounds:g,legend:h,children:[]};if(b)for(b=a.getElementsByTagName("Layer"),d=0;d<b.length;d++)e=b[d],e.parentNode===a&&c.children.push(this.parseLayer(e,!0));return c},getChild:function(a,b){b=a.getElementsByTagName(b);if(0===b.length)return null;b=b[0];return b.parentNode!==a?null:b},getChildString:function(a,b){return(a=this.getChild(a,b))?a.innerHTML:null},buildRequestURL:function(a,b){var c=netgis.util.parseURL(a,!0);a=c.base;c=c.parameters;for(var d in c)"service"===d.toLowerCase()&&
delete c[d],"request"===d.toLowerCase()&&delete c[d];console.info("CLEAN PARAMS:",c);c.service="WMS";c.request=b;b=[];for(d in c)b.push(d+"="+c[d]);return a+"?"+b.join("&")}};
netgis.build="20251223";
1===a.queryable?!0:!1;if(a.bbox){a=a.bbox.split(",");for(b=0;b<a.length;b++)a[b]=parseFloat(a[b]);d.bbox=[a[0],a[1],a[2],a[3]]}return d};
netgis.WMC.prototype.toConfig_01=function(){var a=this.data.wmc,b=this.layers.wms.srv,c=this.data.layerList,d={},e=a.bbox;e=e.split(",");for(var f=0;f<e.length;f++)e[f]=Number.parseFloat(e[f]);d.map={attribution:a.title+", GeoPortal RLP",projection:a.crs,bbox:e};e=[];var g={};for(f=0;f<b.length;f++){var h=b[f],k={id:h.id,title:h.title,open:"1"===h.isopen};e.push(k);for(k=0;k<h.layer.length;k++){var l=h.layer[k];l.getMapUrl||(l.getMapUrl=h.getMapUrl);g[l.id.toString()]=l;if(l.layer)for(var m=0;m<l.layer.length;m++){var n=
l.layer[m];n.getMapUrl||(n.getMapUrl=l.getMapUrl);g[n.id.toString()]=n}}}console.info("WMC FOLDERS:",b,"---\x3e",e);b=[];h=9999;(this.kmloverlay=a.kmloverlay)&&""!==this.kmloverlay&&(k={id:"kmloverlay",title:"Meine Geodaten",parent:null},e.push(k),l={id:"kmloverlay",folder:"kmloverlay",title:"KML Overlay",active:!0,order:h,type:netgis.LayerTypes.KML,url:this.kmloverlay,query:!0},b.push(l),--h);for(f=c.length-1;0<=f;f--)a=c[f],m=a.layerId.toString(),(l=g[m])&&!l.isRoot&&(k=null!==a.layerParent?e[a.layerParent].id:
null,l={id:m,folder:k,title:l.title,active:a.active,query:1===a.layerQueryable,transparency:1-.01*a.opacity,order:h,type:netgis.LayerTypes.WMS,url:l.getMapUrl,name:l.name,format:a.currentFormat},"image/tiff"===l.format&&(l.format="image/png"),b.push(l),--h);d.folders=e;d.layers=b;return d};
netgis.build="20251010";

View File

@@ -13,9 +13,9 @@
</div>
{% endif %}
{% if geom_form.output.errors %}
{% if geom_form.geom.errors %}
<div class="alert-danger p-2">
{% for error in geom_form.output.errors %}
{% for error in geom_form.geom.errors %}
<strong class="invalid">{{ error }}</strong>
<br>
{% endfor %}

View File

@@ -56,7 +56,7 @@
{% if user.is_staff or user.is_superuser %}
<a class="dropdown-item" target="_blank" href="{% url 'admin:index' %}">{% fa5_icon 'tools' %} {% trans 'Admin' %}</a>
{% endif %}
<a class="dropdown-item" href="{% url 'user:index' %}">{% fa5_icon 'cogs' %} {% trans 'Settings' %}</a>
<a class="dropdown-item" href="{% url 'user:detail' %}">{% fa5_icon 'cogs' %} {% trans 'Settings' %}</a>
<a class="dropdown-item" href="{% url 'logout' %}">{% fa5_icon 'sign-out-alt' %} {% trans 'Logout' %}</a>
</div>
</li>

View File

@@ -38,7 +38,7 @@ class UserNotificationForm(BaseForm):
self.form_title = _("Edit notifications")
self.form_caption = _("")
self.action_url = reverse("user:notifications")
self.cancel_redirect = reverse("user:index")
self.cancel_redirect = reverse("user:detail")
# Insert all notifications into form field by creating choices as tuples
notifications = UserNotification.objects.filter(

View File

@@ -26,7 +26,7 @@ class UserViewTestCase(BaseViewTestCase):
self.team.users.add(self.superuser)
self.team.admins.add(self.superuser)
# Prepare urls
self.index_url = reverse("user:index", args=())
self.index_url = reverse("user:detail", args=())
self.notification_url = reverse("user:notifications", args=())
self.api_token_url = reverse("user:api-token", args=())
self.contact_url = reverse("user:contact", args=(self.superuser.id,))

View File

@@ -233,7 +233,7 @@ class UserNotificationFormTestCase(BaseTestCase):
self.assertEqual(form.form_title, str(_("Edit notifications")))
self.assertEqual(form.form_caption, "")
self.assertEqual(form.action_url, reverse("user:notifications"))
self.assertEqual(form.cancel_redirect, reverse("user:index"))
self.assertEqual(form.cancel_redirect, reverse("user:detail"))
def test_save(self):
selected_notification = UserNotification.objects.first()

View File

@@ -15,15 +15,15 @@ from user.views.views import *
app_name = "user"
urlpatterns = [
path("", index_view, name="index"),
path("", UserDetailView.as_view(), name="detail"),
path("propagate/", PropagateUserView.as_view(), name="propagate"),
path("notifications/", notifications_view, name="notifications"),
path("notifications/", NotificationsView.as_view(), name="notifications"),
path("token/api", APITokenView.as_view(), name="api-token"),
path("token/api/new", new_api_token_view, name="api-token-new"),
path("contact/<id>", contact_view, name="contact"),
path("team/", index_team_view, name="team-index"),
path("contact/<id>", ContactView.as_view(), name="contact"),
path("team/", TeamIndexView.as_view(), name="team-index"),
path("team/new", new_team_view, name="team-new"),
path("team/<id>", data_team_view, name="team-data"),
path("team/<id>", TeamDetailModalView.as_view(), name="team-data"),
path("team/<id>/edit", edit_team_view, name="team-edit"),
path("team/<id>/remove", remove_team_view, name="team-remove"),
path("team/<id>/leave", leave_team_view, name="team-leave"),

View File

@@ -1,8 +1,10 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.views.base import BaseView, BaseModalFormView
from user.forms.modals.team import NewTeamModalForm, EditTeamModalForm, RemoveTeamModalForm, LeaveTeamModalForm
from user.forms.modals.user import UserContactForm
from user.forms.team import TeamDataForm
@@ -13,129 +15,108 @@ from django.shortcuts import render, redirect, get_object_or_404
from django.utils.translation import gettext_lazy as _
from konova.contexts import BaseContext
from konova.decorators import any_group_check, login_required_modal
from konova.decorators import login_required_modal
@login_required
@any_group_check
def index_view(request: HttpRequest):
""" Renders the user's data index view
class UserDetailView(LoginRequiredMixin, BaseView):
_TAB_TITLE = _("User settings")
_TEMPLATE = "user/index.html"
Args:
request ():
Returns:
"""
template = "user/index.html"
context = {
"user": request.user,
TAB_TITLE_IDENTIFIER: _("User settings"),
}
context = BaseContext(request, context).context
return render(request, template, context)
def get(self, request: HttpRequest):
context = {
"user": request.user,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@login_required
@any_group_check
def notifications_view(request: HttpRequest):
""" Renders the notifications settings view
class NotificationsView(LoginRequiredMixin, BaseView):
_TEMPLATE = "user/notifications.html"
_TAB_TITLE = _("User notifications")
Args:
request ():
def get(self, request: HttpRequest):
user = request.user
form = UserNotificationForm(user=user, data=None)
context = {
"user": user,
"form": form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
Returns:
"""
template = "user/notifications.html"
user = request.user
form = UserNotificationForm(user=user, data=request.POST or None)
if request.method == "POST":
def post(self, request: HttpRequest):
user = request.user
form = UserNotificationForm(user=user, data=request.POST)
if form.is_valid():
form.save()
messages.success(
request,
_("Notifications edited")
)
return redirect("user:index")
elif request.method == "GET":
# Implicit
pass
else:
raise NotImplementedError
context = {
"user": user,
"form": form,
TAB_TITLE_IDENTIFIER: _("User notifications"),
}
context = BaseContext(request, context).context
return render(request, template, context)
return redirect("user:detail")
context = {
"user": user,
"form": form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@login_required_modal
@login_required
def contact_view(request: HttpRequest, id: str):
""" Renders contact modal view of a users contact data
class ContactView(LoginRequiredMixin, BaseModalFormView):
def get(self, request: HttpRequest, id: str):
""" Renders contact modal view of a users contact data
Args:
request (HttpRequest): The incoming request
id (str): The user's id
Args:
request (HttpRequest): The incoming request
id (str): The user's id
Returns:
Returns:
"""
user = get_object_or_404(User, id=id)
form = UserContactForm(request.POST or None, instance=user, request=request)
template = "modal/modal_form.html"
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(
request,
template,
context
)
"""
user = get_object_or_404(User, id=id)
form = UserContactForm(request.POST or None, instance=user, request=request)
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@login_required_modal
@login_required
def data_team_view(request: HttpRequest, id: str):
""" Renders team data
class TeamDetailModalView(LoginRequiredMixin, BaseModalFormView):
def get(self, request: HttpRequest, id: str):
""" Renders team data
Args:
request (HttpRequest): The incoming request
id (str): The team's id
Args:
request (HttpRequest): The incoming request
id (str): The team's id
Returns:
Returns:
"""
team = get_object_or_404(Team, id=id)
form = TeamDataForm(request.POST or None, instance=team, request=request)
template = "modal/modal_form.html"
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(
request,
template,
context
)
"""
team = get_object_or_404(Team, id=id)
form = TeamDataForm(request.POST or None, instance=team, request=request)
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@login_required
def index_team_view(request: HttpRequest):
template = "user/team/index.html"
user = request.user
context = {
"teams": user.shared_teams,
"tab_title": _("Teams"),
}
context = BaseContext(request, context).context
return render(request, template, context)
class TeamIndexView(LoginRequiredMixin, BaseView):
_TEMPLATE = "user/team/index.html"
_TAB_TITLE = _("Teams")
def get(self, request: HttpRequest):
user = request.user
context = {
"teams": user.shared_teams,
"tab_title": self._TAB_TITLE,
}
context = BaseContext(request, context).context
return render(request, self._TEMPLATE, context)
@login_required_modal