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