feat: Feed map with real data
This commit is contained in:
33
src/fellchensammlung/api/renderers.py
Normal file
33
src/fellchensammlung/api/renderers.py
Normal 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)
|
@@ -1,6 +1,6 @@
|
|||||||
from ..models import Animal, RescueOrganization, AdoptionNotice, Species, Image, Location
|
from ..models import Animal, RescueOrganization, AdoptionNotice, Species, Image, Location
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
import math
|
||||||
|
|
||||||
class AdoptionNoticeSerializer(serializers.HyperlinkedModelSerializer):
|
class AdoptionNoticeSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
location = serializers.PrimaryKeyRelatedField(
|
location = serializers.PrimaryKeyRelatedField(
|
||||||
@@ -32,6 +32,42 @@ class AdoptionNoticeSerializer(serializers.HyperlinkedModelSerializer):
|
|||||||
"group_only", "location", "location_details", "organization", "photos"]
|
"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 AnimalCreateSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Animal
|
model = Animal
|
||||||
|
@@ -1,11 +1,13 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
from .views import (
|
from .views import (
|
||||||
AdoptionNoticeApiView,
|
AdoptionNoticeApiView,
|
||||||
AnimalApiView, RescueOrganizationApiView, AddImageApiView, SpeciesApiView, LocationApiView
|
AnimalApiView, RescueOrganizationApiView, AddImageApiView, SpeciesApiView, LocationApiView,
|
||||||
|
AdoptionNoticeGeoJSONView
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("adoption_notice", AdoptionNoticeApiView.as_view(), name="api-adoption-notice-list"),
|
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("adoption_notice/<int:id>/", 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-list"),
|
||||||
path("animals/<int:id>/", AnimalApiView.as_view(), name="api-animal-detail"),
|
path("animals/<int:id>/", AnimalApiView.as_view(), name="api-animal-detail"),
|
||||||
|
@@ -1,13 +1,16 @@
|
|||||||
from django.db.models import Q
|
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.views import APIView
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from django.db import transaction
|
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 fellchensammlung.tasks import post_adoption_notice_save, post_rescue_org_save
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
|
||||||
|
from .renderers import GeoJSONRenderer
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
AnimalGetSerializer,
|
AnimalGetSerializer,
|
||||||
AnimalCreateSerializer,
|
AnimalCreateSerializer,
|
||||||
@@ -354,3 +357,10 @@ class LocationApiView(APIView):
|
|||||||
{"message": "Location created successfully!", "id": location.pk},
|
{"message": "Location created successfully!", "id": location.pk},
|
||||||
status=status.HTTP_201_CREATED,
|
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]
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{% load custom_tags %}
|
{% load custom_tags %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% get_current_language as LANGUAGE_CODE%}
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{ LANGUAGE_CODE }}">
|
<html lang="{{ LANGUAGE_CODE }}">
|
||||||
<head>
|
<head>
|
||||||
|
@@ -63,7 +63,7 @@
|
|||||||
// Load adoption notices as geojson
|
// Load adoption notices as geojson
|
||||||
map.addSource('adoption-notices', {
|
map.addSource('adoption-notices', {
|
||||||
type: 'geojson',
|
type: 'geojson',
|
||||||
data: '{% static "fellchensammlung/ans.geojson" %}',
|
data: '{% url "api-adoption-notice-list-geojson" %}',
|
||||||
cluster: true,
|
cluster: true,
|
||||||
clusterMaxZoom: 14,
|
clusterMaxZoom: 14,
|
||||||
clusterRadius: 50
|
clusterRadius: 50
|
||||||
@@ -121,6 +121,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// inspect a cluster on click
|
// inspect a cluster on click
|
||||||
map.on('click', 'clusters', async (e) => {
|
map.on('click', 'clusters', async (e) => {
|
||||||
const features = map.queryRenderedFeatures(e.point, {
|
const features = map.queryRenderedFeatures(e.point, {
|
||||||
|
Reference in New Issue
Block a user