Skip to content

Commit

Permalink
Merge pull request #4881 from open-formulieren/feature/2173-leaflet-m…
Browse files Browse the repository at this point in the history
…ap-background

Add new leaflet map background configuration
  • Loading branch information
sergei-maertens authored Dec 17, 2024
2 parents 4f57eb8 + b58cddf commit 77b340a
Show file tree
Hide file tree
Showing 22 changed files with 558 additions and 24 deletions.
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=_(
"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

0 comments on commit 77b340a

Please sign in to comment.