feat: Feed map with real data

This commit is contained in:
2025-06-15 17:25:55 +02:00
parent 16998b85d5
commit 0b483ce630
6 changed files with 88 additions and 6 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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/<int:id>/", AdoptionNoticeApiView.as_view(), name="api-adoption-notice-detail"),
path("animals/", AnimalApiView.as_view(), name="api-animal-list"),
path("animals/<int:id>/", AnimalApiView.as_view(), name="api-animal-detail"),

View File

@@ -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]

View File

@@ -1,7 +1,7 @@
{% load custom_tags %}
{% load i18n %}
{% load static %}
{% get_current_language as LANGUAGE_CODE%}
{% get_current_language as LANGUAGE_CODE %}
<!DOCTYPE html>
<html lang="{{ LANGUAGE_CODE }}">
<head>

View File

@@ -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, {