From 0b483ce6302a492993c4584d10ec27041dce3bea Mon Sep 17 00:00:00 2001 From: moanos Date: Sun, 15 Jun 2025 17:25:55 +0200 Subject: [PATCH] feat: Feed map with real data --- src/fellchensammlung/api/renderers.py | 33 ++++++++++++++++ src/fellchensammlung/api/serializers.py | 38 ++++++++++++++++++- src/fellchensammlung/api/urls.py | 4 +- src/fellchensammlung/api/views.py | 14 ++++++- .../fellchensammlung/base_bulma.html | 2 +- .../partials/bulma-partial-map.html | 3 +- 6 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 src/fellchensammlung/api/renderers.py diff --git a/src/fellchensammlung/api/renderers.py b/src/fellchensammlung/api/renderers.py new file mode 100644 index 0000000..255dbc0 --- /dev/null +++ b/src/fellchensammlung/api/renderers.py @@ -0,0 +1,33 @@ +from rest_framework.renderers import BaseRenderer +import json + + +class GeoJSONRenderer(BaseRenderer): + media_type = 'application/json' + format = 'geojson' + charset = 'utf-8' + + def render(self, data, accepted_media_type=None, renderer_context=None): + features = [] + for item in data: + coords = item["coordinates"] + if coords: + feature = { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": coords + }, + "properties": { + k: v for k, v in item.items() + }, + "id": f"adoptionnotice/{item['id']}" + } + features.append(feature) + + geojson = { + "type": "FeatureCollection", + "generator": "notfellchen", + "features": features + } + return json.dumps(geojson) diff --git a/src/fellchensammlung/api/serializers.py b/src/fellchensammlung/api/serializers.py index bb4d322..974474c 100644 --- a/src/fellchensammlung/api/serializers.py +++ b/src/fellchensammlung/api/serializers.py @@ -1,6 +1,6 @@ from ..models import Animal, RescueOrganization, AdoptionNotice, Species, Image, Location from rest_framework import serializers - +import math class AdoptionNoticeSerializer(serializers.HyperlinkedModelSerializer): location = serializers.PrimaryKeyRelatedField( @@ -32,6 +32,42 @@ class AdoptionNoticeSerializer(serializers.HyperlinkedModelSerializer): "group_only", "location", "location_details", "organization", "photos"] +class AdoptionNoticeGeoJSONSerializer(serializers.ModelSerializer): + species = serializers.SerializerMethodField() + title = serializers.CharField(source='name') + url = serializers.SerializerMethodField() + location_hr = serializers.SerializerMethodField() + coordinates = serializers.SerializerMethodField() + + class Meta: + model = AdoptionNotice + fields = ('id', 'species', 'title', 'description', 'url', 'location_hr', 'coordinates') + + def get_species(self, obj): + return None + + def get_url(self, obj): + return obj.get_absolute_url() + + def get_coordinates(self, obj): + """ + Coordinates are randomly moved around real location, roughly in a circle. The object id is used as angle so that + points are always displayed at the same location (as if they were a seed for a random function). + + It's not exactly a circle, because the earth is round. + """ + if obj.location: + longitude_addition = math.sin(obj.id)/2000 + latitude_addition = math.cos(obj.id)/2000 + return [obj.location.longitude+longitude_addition, obj.location.latitude+latitude_addition] + return None + + def get_location_hr(self, obj): + if obj.location: + return f"{obj.location.city}" + return None + + class AnimalCreateSerializer(serializers.ModelSerializer): class Meta: model = Animal diff --git a/src/fellchensammlung/api/urls.py b/src/fellchensammlung/api/urls.py index ae82b08..953dadc 100644 --- a/src/fellchensammlung/api/urls.py +++ b/src/fellchensammlung/api/urls.py @@ -1,11 +1,13 @@ from django.urls import path from .views import ( AdoptionNoticeApiView, - AnimalApiView, RescueOrganizationApiView, AddImageApiView, SpeciesApiView, LocationApiView + AnimalApiView, RescueOrganizationApiView, AddImageApiView, SpeciesApiView, LocationApiView, + AdoptionNoticeGeoJSONView ) urlpatterns = [ path("adoption_notice", AdoptionNoticeApiView.as_view(), name="api-adoption-notice-list"), + path("adoption_notice.geojson", AdoptionNoticeGeoJSONView.as_view(), name="api-adoption-notice-list-geojson"), path("adoption_notice//", AdoptionNoticeApiView.as_view(), name="api-adoption-notice-detail"), path("animals/", AnimalApiView.as_view(), name="api-animal-list"), path("animals//", AnimalApiView.as_view(), name="api-animal-detail"), diff --git a/src/fellchensammlung/api/views.py b/src/fellchensammlung/api/views.py index 767778e..4d442a4 100644 --- a/src/fellchensammlung/api/views.py +++ b/src/fellchensammlung/api/views.py @@ -1,13 +1,16 @@ from django.db.models import Q +from rest_framework.generics import ListAPIView -from fellchensammlung.api.serializers import LocationSerializer +from fellchensammlung.api.serializers import LocationSerializer, AdoptionNoticeGeoJSONSerializer from rest_framework.views import APIView from rest_framework.response import Response from django.db import transaction -from fellchensammlung.models import AdoptionNotice, Animal, Log, TrustLevel, Location +from fellchensammlung.models import AdoptionNotice, Animal, Log, TrustLevel, Location, AdoptionNoticeStatus from fellchensammlung.tasks import post_adoption_notice_save, post_rescue_org_save from rest_framework import status from rest_framework.permissions import IsAuthenticated + +from .renderers import GeoJSONRenderer from .serializers import ( AnimalGetSerializer, AnimalCreateSerializer, @@ -354,3 +357,10 @@ class LocationApiView(APIView): {"message": "Location created successfully!", "id": location.pk}, status=status.HTTP_201_CREATED, ) + + +class AdoptionNoticeGeoJSONView(ListAPIView): + queryset = AdoptionNotice.objects.select_related('location').filter(location__isnull=False).filter( + adoptionnoticestatus__major_status=AdoptionNoticeStatus.ACTIVE) + serializer_class = AdoptionNoticeGeoJSONSerializer + renderer_classes = [GeoJSONRenderer] diff --git a/src/fellchensammlung/templates/fellchensammlung/base_bulma.html b/src/fellchensammlung/templates/fellchensammlung/base_bulma.html index 2cea58f..afc3683 100644 --- a/src/fellchensammlung/templates/fellchensammlung/base_bulma.html +++ b/src/fellchensammlung/templates/fellchensammlung/base_bulma.html @@ -1,7 +1,7 @@ {% load custom_tags %} {% load i18n %} {% load static %} -{% get_current_language as LANGUAGE_CODE%} +{% get_current_language as LANGUAGE_CODE %} diff --git a/src/fellchensammlung/templates/fellchensammlung/partials/bulma-partial-map.html b/src/fellchensammlung/templates/fellchensammlung/partials/bulma-partial-map.html index 14544cf..a7bfd84 100644 --- a/src/fellchensammlung/templates/fellchensammlung/partials/bulma-partial-map.html +++ b/src/fellchensammlung/templates/fellchensammlung/partials/bulma-partial-map.html @@ -63,7 +63,7 @@ // Load adoption notices as geojson map.addSource('adoption-notices', { type: 'geojson', - data: '{% static "fellchensammlung/ans.geojson" %}', + data: '{% url "api-adoption-notice-list-geojson" %}', cluster: true, clusterMaxZoom: 14, clusterRadius: 50 @@ -121,6 +121,7 @@ } }); + // inspect a cluster on click map.on('click', 'clusters', async (e) => { const features = map.queryRenderedFeatures(e.point, {