Skip to content
Snippets Groups Projects
blocks.py 7.03 KiB
import json
from uuid import uuid4

from django.forms.utils import ErrorList
from django.utils.text import slugify
from wagtail import blocks
from wagtail.blocks.struct_block import StructBlockValidationError
from wagtail.images.blocks import ImageChooserBlock

from .const import (
    DEFAULT_MAP_STYLE,
    MAP_STYLES,
    SUPPORTED_FEATURE_TYPES,
    TILE_SERVER_CONFIG,
)
from .validation import validators


class MapPointBlock(blocks.StructBlock):
    lat = blocks.DecimalBlock(label="Zeměpisná šířka", help_text="Např. 50.04075")
    lon = blocks.DecimalBlock(label="Zeměpisná délka", help_text="Např. 15.77659")
    hex_color = blocks.CharBlock(
        label="Barva špendlíku (HEX)",
        help_text="Zadejte barvu pomocí HEX notace (bez # na začátku).",
        default="000000",
    )
    zoom = blocks.IntegerBlock(
        label="Výchozí zoom", min_value=1, max_value=18, default=15
    )
    style = blocks.ChoiceBlock(
        choices=MAP_STYLES, label="Styl", default=DEFAULT_MAP_STYLE
    )
    height = blocks.IntegerBlock(label="Výška v px", min_value=100, max_value=1000)

    class Meta:
        label = "Mapa se špendlíkem"
        template = "maps_utils/blocks/map_point.html"
        icon = "thumbtack"
        group = "2. Obrázky"

    def get_context(self, value, parent_context=None):
        context = super().get_context(value, parent_context)
        feature_id = str(uuid4())
        context["js_map"] = {
            "tile_server_config": json.dumps(TILE_SERVER_CONFIG),
            "geojson": json.dumps(
                [
                    {
                        "type": "FeatureCollection",
                        "properties": {
                            "slug": feature_id,
                            "title": None,
                            "collectionTitle": None,
                            "collectionDescription": None,
                        },
                        "features": [
                            {
                                "type": "Feature",
                                "geometry": {
                                    "type": "Point",
                                    "coordinates": [
                                        float(value["lon"]),
                                        float(value["lat"]),
                                    ],
                                },
                                "properties": {
                                    "id": feature_id,
                                    "slug": feature_id,
                                    "title": None,
                                    "description": None,
                                    "collectionTitle": None,
                                    "collectionDescription": None,
                                    "image": None,
                                    "color": value["hex_color"],
                                },
                            },
                        ],
                    }
                ]
            ),
        }

        return context


class MapFeatureBlock(blocks.StructBlock):
    title = blocks.CharBlock(label="Titulek", required=True)
    description = blocks.TextBlock(label="Popisek", required=False)
    geojson = blocks.TextBlock(
        label="Geodata",
        help_text="Vložte surový GeoJSON objekt typu 'Feature'. Vyrobit jej můžete např. pomocí online služby geojson.io. Pokud u objektu poskytnete properties 'title' a 'description', zobrazí se jak na mapě, tak i v detailu.",
        required=True,
    )
    image = ImageChooserBlock(label="Obrázek", required=False)
    link = blocks.URLBlock(label="Odkaz", required=False)
    hex_color = blocks.CharBlock(
        label="Barva (HEX)",
        help_text="Zadejte barvu pomocí HEX notace (bez # na začátku).",
        default="000000",
    )

    class Meta:
        label = "Položka mapové kolekce"
        icon = "list-ul"

    def clean(self, value):
        errors = {}

        if value["geojson"]:
            try:
                value["geojson"] = validators.normalize_geojson_feature(
                    value["geojson"], allowed_types=SUPPORTED_FEATURE_TYPES
                )
            except ValueError as exc:
                print(exc)

                errors["geojson"] = ErrorList(str(exc))

        if errors:
            raise StructBlockValidationError(errors)

        return super().clean(value)


class MapFeatureCollectionBlock(blocks.StructBlock):
    features = blocks.ListBlock(MapFeatureBlock(required=True), label="Součásti")

    zoom = blocks.IntegerBlock(
        label="Výchozí zoom", min_value=1, max_value=18, default=15
    )
    style = blocks.ChoiceBlock(
        choices=MAP_STYLES, label="Styl", default=DEFAULT_MAP_STYLE
    )
    height = blocks.IntegerBlock(label="Výška v px", min_value=100, max_value=1000)

    class Meta:
        label = "Mapová kolekce"
        template = "maps_utils/blocks/map_feature_collection.html"
        icon = "site"
        group = "2. Obrázky"

    def get_context(self, value, parent_context=None):
        def _geojson_feature_with_props(feature_id, feature):
            fwp = json.loads(feature["geojson"])
            fwp["properties"].update(
                {
                    "id": feature_id,
                    "slug": f"0-{feature_id}-{slugify(feature['title'])}",
                    # Individual features are suppressed to emulate collection-like style.
                    "title": None,
                    "description": None,
                    "collectionTitle": feature["title"],
                    "collectionDescription": feature["description"],
                    "image": feature["image"]
                    .get_rendition("fill-1200x675|jpegquality-80")
                    .url
                    if feature["image"]
                    else None,
                    "link": feature["link"],
                    "color": feature["hex_color"],
                }
            )
            return fwp

        features = [
            _geojson_feature_with_props(i, f) for i, f in enumerate(value["features"])
        ]

        context = super().get_context(value, parent_context)
        context["js_map"] = {
            "tile_server_config": json.dumps(TILE_SERVER_CONFIG),
            "geojson": json.dumps(
                [
                    # Each feature dumped as individual collection to make things consistent.
                    {
                        "type": "FeatureCollection",
                        "properties": {
                            "slug": f"{f['properties']['id']}-{slugify(f['properties']['collectionTitle'])}",
                            "collectionTitle": f["properties"]["collectionTitle"],
                            "collectionDescription": f["properties"][
                                "collectionDescription"
                            ],
                            "image": f["properties"]["image"],
                        },
                        "features": [f],
                    }
                    for f in features
                ]
            ),
        }

        return context