Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new leaflet map background configuration #4881

Merged
merged 8 commits into from
Dec 17, 2024
Merged
13 changes: 10 additions & 3 deletions INSTALL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -432,11 +432,18 @@ After configuring the application groups in the admin through point-and-click, y
call this script to dump the configuration into a fixture which will be loaded on
all other installations.

``bin/generate_default_groups_fixtures.sh``
-------------------------------------------
``bin/generate_default_groups_fixture.sh``
------------------------------------------

After configuring the user groups with the appropriate permissions in the admin,
you can this script to dump the configuration into a fixture which will be loaded on
you call this script to dump the configuration into a fixture which will be loaded on
all other installations.

``bin/generate_default_map_tile_layers_fixture.sh``
-----------------------------------------------------------

After configuring the map tile layers in the admin,
you call this script to dump the configuration into a fixture which will be loaded on
all other installations.

``bin/generate_oas.sh``
Expand Down
11 changes: 11 additions & 0 deletions bin/generate_default_map_tile_layers_fixture.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
#
# Dump the current (local database) config MapTileLayer to a JSON fixture.
# This overwrites the existing one.
#
# You can load this fixture with:
# $ src/manage.py loaddata default_map_tile_layers
#
# Run this script from the root of the repository

src/manage.py dumpdata --indent=4 --natural-foreign --natural-primary config.MapTileLayer > src/openforms/fixtures/default_map_tile_layers.json
15 changes: 8 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"dependencies": {
"@fortawesome/fontawesome-free": "^6.1.1",
"@open-formulieren/design-tokens": "^0.53.0",
"@open-formulieren/formio-builder": "^0.33.0",
"@open-formulieren/formio-builder": "^0.34.0",
"@open-formulieren/leaflet-tools": "^1.0.0",
"@open-formulieren/monaco-json-editor": "^0.2.0",
"@tinymce/tinymce-react": "^4.3.2",
Expand Down
16 changes: 15 additions & 1 deletion src/openforms/config/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from .admin_views import ThemePreviewView
from .forms import GlobalConfigurationAdminForm, ThemeAdminForm
from .models import CSPSetting, GlobalConfiguration, RichTextColor, Theme
from .models import CSPSetting, GlobalConfiguration, MapTileLayer, RichTextColor, Theme


@admin.register(GlobalConfiguration)
Expand Down Expand Up @@ -221,6 +221,20 @@ class RichTextColorAdmin(admin.ModelAdmin):
]


@admin.register(MapTileLayer)
class MapTileLayerAdmin(admin.ModelAdmin):
fields = (
"label",
"identifier",
"url",
)
list_display = (
"label",
"identifier",
"url",
)


@admin.register(CSPSetting)
class CSPSettingAdmin(admin.ModelAdmin):
readonly_fields = ("content_type_link",)
Expand Down
56 changes: 56 additions & 0 deletions src/openforms/config/migrations/0069_maptilelayer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Generated by Django 4.2.17 on 2024-12-17 12:42

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("config", "0068_update_summary_tags"),
]

operations = [
migrations.CreateModel(
name="MapTileLayer",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"identifier",
models.SlugField(
help_text="A unique identifier for the tile layer.",
unique=True,
verbose_name="identifier",
),
),
(
"url",
models.URLField(
help_text="URL to the tile layer image, used to define the map component background. To ensure correct functionality of the map, EPSG 28992 projection should be used. Example value: https://service.pdok.nl/brt/achtergrondkaart/wmts/v2_0/standaard/EPSG:28992/{z}/{x}/{y}.png",
max_length=255,
verbose_name="tile layer url",
),
),
(
"label",
models.CharField(
help_text="An easily recognizable name for the tile layer, used to identify it.",
max_length=100,
verbose_name="label",
),
),
],
options={
"verbose_name": "map tile layer",
"verbose_name_plural": "map tile layers",
"ordering": ("label",),
},
),
]
2 changes: 2 additions & 0 deletions src/openforms/config/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from .color import RichTextColor
from .config import GlobalConfiguration
from .csp import CSPSetting
from .map import MapTileLayer
from .theme import Theme

__all__ = [
"CSPSetting",
"GlobalConfiguration",
"RichTextColor",
"MapTileLayer",
"Theme",
]
36 changes: 36 additions & 0 deletions src/openforms/config/models/map.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from django.db import models
from django.utils.translation import gettext_lazy as _


class MapTileLayer(models.Model):
identifier = models.SlugField(
_("identifier"),
unique=True,
max_length=50,
help_text=_("A unique identifier for the tile layer."),
)
url = models.URLField(
_("tile layer url"),
max_length=255,
help_text=_(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same here (max length).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need a disclaimer that the tiler layers must use EPSG:28992 (Rijksdriehoek coordinate system), using other systems will probably lead to weird results.

"URL to the tile layer image, used to define the map component "
"background. To ensure correct functionality of the map, "
"EPSG 28992 projection should be used. "
"Example value: https://service.pdok.nl/brt/achtergrondkaart/wmts/v2_0/standaard/EPSG:28992/{z}/{x}/{y}.png"
),
)
label = models.CharField(
_("label"),
max_length=100,
help_text=_(
"An easily recognizable name for the tile layer, used to identify it."
),
)

class Meta:
verbose_name = _("map tile layer")
verbose_name_plural = _("map tile layers")
ordering = ("label",)

def __str__(self):
return self.label
9 changes: 9 additions & 0 deletions src/openforms/config/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,12 @@ class ThemeFactory(factory.django.DjangoModelFactory):

class Meta:
model = "config.Theme"


class MapTileLayerFactory(factory.django.DjangoModelFactory):
identifier = factory.Faker("word")
url = factory.Sequence(lambda n: f"http://example-{n}.com")
label = factory.Faker("word")

class Meta:
model = "config.MapTileLayer"
23 changes: 22 additions & 1 deletion src/openforms/config/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from openforms.accounts.tests.factories import SuperUserFactory

from .factories import RichTextColorFactory
from .factories import MapTileLayerFactory, RichTextColorFactory


@disable_admin_mfa()
Expand All @@ -27,3 +27,24 @@ def test_color_detail(self):
response = self.app.get(url, user=user)

self.assertEqual(response.status_code, 200)


@disable_admin_mfa()
class MapTileLayerTests(WebTest):
def test_map_tile_layer_changelist(self):
MapTileLayerFactory.create_batch(9)
url = reverse("admin:config_maptilelayer_changelist")
user = SuperUserFactory.create()

response = self.app.get(url, user=user)

self.assertEqual(response.status_code, 200)

def test_map_tile_layer_detail(self):
map = MapTileLayerFactory.create()
url = reverse("admin:config_maptilelayer_change", args=(map.pk,))
user = SuperUserFactory.create()

response = self.app.get(url, user=user)

self.assertEqual(response.status_code, 200)
4 changes: 4 additions & 0 deletions src/openforms/fixtures/default_admin_index.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@
"config",
"globalconfiguration"
],
[
"config",
"maptilelayer"
],
[
"config",
"theme"
Expand Down
20 changes: 20 additions & 0 deletions src/openforms/fixtures/default_map_tile_layers.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"model": "config.maptilelayer",
"pk": 1,
"fields": {
"identifier": "brt",
"url": "https://service.pdok.nl/brt/achtergrondkaart/wmts/v2_0/standaard/EPSG:28992/{z}/{x}/{y}.png",
"label": "BRT"
}
},
{
"model": "config.maptilelayer",
"pk": 2,
"fields": {
"identifier": "luchtfoto",
"url": "https://service.pdok.nl/hwh/luchtfotorgb/wmts/v1_0/Actueel_orthoHR/EPSG:28992/{z}/{x}/{y}.png",
"label": "Luchtfoto"
}
}
]
25 changes: 20 additions & 5 deletions src/openforms/formio/components/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from rest_framework.request import Request

from openforms.authentication.service import AuthAttribute
from openforms.config.models import GlobalConfiguration
from openforms.config.models import GlobalConfiguration, MapTileLayer
from openforms.submissions.models import Submission
from openforms.typing import DataMapping
from openforms.utils.date import TIMEZONE_AMS, datetime_in_amsterdam, format_date_value
Expand All @@ -31,7 +31,13 @@
)
from ..formatters.formio import DefaultFormatter, TextFieldFormatter
from ..registry import BasePlugin, register
from ..typing import AddressNLComponent, Component, DateComponent, DatetimeComponent
from ..typing import (
AddressNLComponent,
Component,
DateComponent,
DatetimeComponent,
MapComponent,
)
from ..utils import conform_to_mask
from .np_family_members.constants import FamilyMembersDataAPIChoices
from .np_family_members.haal_centraal import get_np_family_members_haal_centraal
Expand Down Expand Up @@ -186,19 +192,28 @@ def build_serializer_field(


@register("map")
class Map(BasePlugin[Component]):
class Map(BasePlugin[MapComponent]):
formatter = MapFormatter

def mutate_config_dynamically(
self, component: MapComponent, submission: Submission, data: DataMapping
) -> None:
if (identifier := component.get("tileLayerIdentifier")) is not None:
tile_layer = MapTileLayer.objects.filter(identifier=identifier).first()
if tile_layer is not None:
# Add the tile layer url information
component["tileLayerUrl"] = tile_layer.url

@staticmethod
def rewrite_for_request(component, request: Request):
def rewrite_for_request(component: MapComponent, request: Request):
if component.get("useConfigDefaultMapSettings", False):
config = GlobalConfiguration.get_solo()
component["defaultZoom"] = config.form_map_default_zoom_level
component.setdefault("initialCenter", {})
component["initialCenter"]["lat"] = config.form_map_default_latitude
component["initialCenter"]["lng"] = config.form_map_default_longitude

def build_serializer_field(self, component: Component) -> serializers.ListField:
def build_serializer_field(self, component: MapComponent) -> serializers.ListField:
validate = component.get("validate", {})
required = validate.get("required", False)
base = serializers.FloatField(
Expand Down
4 changes: 2 additions & 2 deletions src/openforms/formio/formatters/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.utils.html import format_html
from django.utils.safestring import mark_safe

from ..typing import AddressNLComponent, Component
from ..typing import AddressNLComponent, Component, MapComponent
from .base import FormatterBase


Expand All @@ -22,7 +22,7 @@ def format(self, component: Component, value: str) -> str:


class MapFormatter(FormatterBase):
def format(self, component: Component, value: list[float]) -> str:
def format(self, component: MapComponent, value: list[float]) -> str:
# use a comma here since its a single data element
return ", ".join((str(x) for x in value))

Expand Down
Loading
Loading