On introduit une API pour avoir un chargement plus fluide de la carte d'édition
Fix #378
This commit is contained in:
		
							
								
								
									
										1
									
								
								src/agenda_culturel/serializers/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/agenda_culturel/serializers/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
from .place_serializer import PlaceSerializer
 | 
			
		||||
							
								
								
									
										30
									
								
								src/agenda_culturel/serializers/place_serializer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/agenda_culturel/serializers/place_serializer.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
from rest_framework import serializers
 | 
			
		||||
from ..models import Place
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PlaceSerializer(serializers.ModelSerializer):
 | 
			
		||||
    lat = serializers.SerializerMethodField()
 | 
			
		||||
    lng = serializers.SerializerMethodField()
 | 
			
		||||
    url = serializers.SerializerMethodField()
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Place
 | 
			
		||||
        fields = [
 | 
			
		||||
            "name",
 | 
			
		||||
            "address",
 | 
			
		||||
            "postcode",
 | 
			
		||||
            "city",
 | 
			
		||||
            "description",
 | 
			
		||||
            "lat",
 | 
			
		||||
            "lng",
 | 
			
		||||
            "url",
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def get_lat(self, obj):
 | 
			
		||||
        return obj.location[1] if obj.location else None
 | 
			
		||||
 | 
			
		||||
    def get_lng(self, obj):
 | 
			
		||||
        return obj.location[0] if obj.location else None
 | 
			
		||||
 | 
			
		||||
    def get_url(self, obj):
 | 
			
		||||
        return obj.get_absolute_url()
 | 
			
		||||
@@ -72,6 +72,7 @@ INSTALLED_APPS = [
 | 
			
		||||
    "django_cleanup.apps.CleanupConfig",
 | 
			
		||||
    "django_unused_media",
 | 
			
		||||
    "solo.apps.SoloAppConfig",
 | 
			
		||||
    "rest_framework",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
SOLO_CACHE_TIMEOUT = 60 * 15  # 15 minutes
 | 
			
		||||
 
 | 
			
		||||
@@ -375,6 +375,10 @@ var SequentialLoader = function() {
 | 
			
		||||
            _getMap: function(mapOptions) {
 | 
			
		||||
                var map = new L.Map(this.options.id, mapOptions), layer;
 | 
			
		||||
 | 
			
		||||
                if (window.loadTiles !== undefined) {
 | 
			
		||||
                    window.loadTiles(map);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (this.options.provider == 'google') {
 | 
			
		||||
                    layer = new L.gridLayer.googleMutant({
 | 
			
		||||
                        type: this.options.providerOptions.google.mapType.toLowerCase(),
 | 
			
		||||
@@ -397,25 +401,6 @@ var SequentialLoader = function() {
 | 
			
		||||
 | 
			
		||||
                map.addLayer(layer);
 | 
			
		||||
 | 
			
		||||
                if ((window.other_markers !== null) && (window.other_markers.length > 0)) {
 | 
			
		||||
                    var layerGroup = L.layerGroup().addTo(map);
 | 
			
		||||
                    window.other_markers.forEach(x => layerGroup.addLayer(x));
 | 
			
		||||
                    map.removeLayer(layerGroup);
 | 
			
		||||
                    map.on('zoomend', function () {
 | 
			
		||||
                        var currentZoom = map.getZoom();
 | 
			
		||||
 | 
			
		||||
                        if (currentZoom > 12) {
 | 
			
		||||
                        if (!map.hasLayer(layerGroup)) {
 | 
			
		||||
                            map.addLayer(layerGroup);
 | 
			
		||||
                        }
 | 
			
		||||
                        } else {
 | 
			
		||||
                        if (map.hasLayer(layerGroup)) {
 | 
			
		||||
                            map.removeLayer(layerGroup);
 | 
			
		||||
                        }
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return map;
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
@@ -434,7 +419,8 @@ var SequentialLoader = function() {
 | 
			
		||||
                var self = this,
 | 
			
		||||
                    markerOptions = {
 | 
			
		||||
                        draggable: true,
 | 
			
		||||
                        icon: window.pinIcon
 | 
			
		||||
                        icon: window.pinIcon,
 | 
			
		||||
                        zIndexOffset: 1000,
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                var marker = L.marker(center, markerOptions).addTo(map);
 | 
			
		||||
 
 | 
			
		||||
@@ -37,18 +37,51 @@
 | 
			
		||||
                  shadowSize: [19, 19]
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    window.loadedTiles = new Set();
 | 
			
		||||
    window.loadTiles = (map) => {
 | 
			
		||||
      var layerGroup = L.layerGroup().addTo(map);
 | 
			
		||||
      var dl_places = () => {
 | 
			
		||||
        const zoom = map.getZoom();
 | 
			
		||||
        if (zoom < 12) {
 | 
			
		||||
          if (map.hasLayer(layerGroup))
 | 
			
		||||
            map.removeLayer(layerGroup);
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        if (!map.hasLayer(layerGroup)) {
 | 
			
		||||
            map.addLayer(layerGroup);
 | 
			
		||||
            layerGroup.bringToBack();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    window.other_markers = [];
 | 
			
		||||
    {% with cache_timeout=user.is_authenticated|yesno:"300,6000" %}
 | 
			
		||||
          {% cache cache_timeout place_lists_js user.is_authenticated %}
 | 
			
		||||
        const bounds = map.getBounds();
 | 
			
		||||
        const tileSize = 0.1;
 | 
			
		||||
 | 
			
		||||
        const latStart = Math.floor(bounds.getSouth() / tileSize);
 | 
			
		||||
        const latEnd = Math.ceil(bounds.getNorth() / tileSize);
 | 
			
		||||
        const lngStart = Math.floor(bounds.getWest() / tileSize);
 | 
			
		||||
        const lngEnd = Math.ceil(bounds.getEast() / tileSize);
 | 
			
		||||
 | 
			
		||||
        for (let x = lngStart; x <= lngEnd; x++) {
 | 
			
		||||
            for (let y = latStart; y <= latEnd; y++) {
 | 
			
		||||
                const tileKey = `${x}_${y}`;
 | 
			
		||||
                if (window.loadedTiles.has(tileKey)) continue;
 | 
			
		||||
 | 
			
		||||
                fetch(`/api/places/tile/?tile_x=${x}&tile_y=${y}&tile_size=${tileSize}`)
 | 
			
		||||
                    .then((res) => res.json())
 | 
			
		||||
                    .then((data) => {
 | 
			
		||||
                        data.forEach((place) => {
 | 
			
		||||
                          layerGroup.addLayer(L.marker([place.lat, place.lng], {'icon': circleIcon})
 | 
			
		||||
                                .bindPopup(`<a href="${place.url}">${place.name}</a>`));
 | 
			
		||||
                        });
 | 
			
		||||
                        window.loadedTiles.add(tileKey);
 | 
			
		||||
                    });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
      map.on("moveend", dl_places);
 | 
			
		||||
      dl_places();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
          {% if place_list %}
 | 
			
		||||
            {% for place in place_list %}
 | 
			
		||||
            window.other_markers.push(L.marker([{{ place.location|tocoords }}], {'icon': circleIcon}).bindPopup('<a href="{{ place.get_absolute_url }}">{{ place.name }}</a><br />{% if place.address %}{{ place.address }}, {% endif %}{{ place.city }}'));
 | 
			
		||||
            {% endfor %}
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    {% endcache %}
 | 
			
		||||
      {% endwith %}
 | 
			
		||||
  </script>
 | 
			
		||||
  <link href="{% static 'css/django_better_admin_arrayfield.min.css' %}"
 | 
			
		||||
        type="text/css"
 | 
			
		||||
 
 | 
			
		||||
@@ -116,6 +116,7 @@ from .views import (
 | 
			
		||||
    UnknownPlaceAddView,
 | 
			
		||||
    UnknownPlacesListView,
 | 
			
		||||
    fix_unknown_places,
 | 
			
		||||
    PlaceTileView,
 | 
			
		||||
    # Search
 | 
			
		||||
    event_search,
 | 
			
		||||
    event_search_full,
 | 
			
		||||
@@ -550,6 +551,8 @@ urlpatterns = [
 | 
			
		||||
        load_specialperiods_from_ical,
 | 
			
		||||
        name="load_specialperiods_from_ical",
 | 
			
		||||
    ),
 | 
			
		||||
    # API
 | 
			
		||||
    path("api/places/tile/", PlaceTileView.as_view(), name="place_list"),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
if settings.DEBUG:
 | 
			
		||||
 
 | 
			
		||||
@@ -11,11 +11,15 @@ from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from django.views.generic import ListView, UpdateView, CreateView, DeleteView
 | 
			
		||||
from django.contrib.gis.measure import D
 | 
			
		||||
from django.utils.safestring import mark_safe
 | 
			
		||||
from rest_framework.views import APIView
 | 
			
		||||
from rest_framework.response import Response
 | 
			
		||||
from django.contrib.gis.geos import Polygon
 | 
			
		||||
 | 
			
		||||
from .utils import get_event_qs
 | 
			
		||||
from ..forms import PlaceForm, EventAddPlaceForm
 | 
			
		||||
from ..models import Place, Event
 | 
			
		||||
from ..utils import PlaceGuesser
 | 
			
		||||
from ..serializers import PlaceSerializer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PlaceListView(ListView):
 | 
			
		||||
@@ -123,11 +127,6 @@ class PlaceCreateView(
 | 
			
		||||
    success_message = _("The place has been successfully created.")
 | 
			
		||||
    form_class = PlaceForm
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        context = super().get_context_data(**kwargs)
 | 
			
		||||
        context["place_list"] = Place.objects.all().only("location", "name", "pk")
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PlaceDeleteView(PermissionRequiredMixin, DeleteView):
 | 
			
		||||
    model = Place
 | 
			
		||||
@@ -256,3 +255,21 @@ class PlaceFromEventCreateView(PlaceCreateView):
 | 
			
		||||
 | 
			
		||||
    def get_success_url(self):
 | 
			
		||||
        return self.event.get_absolute_url()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PlaceTileView(APIView):
 | 
			
		||||
 | 
			
		||||
    def get(self, request):
 | 
			
		||||
        tile_x = int(request.query_params.get("tile_x"))
 | 
			
		||||
        tile_y = int(request.query_params.get("tile_y"))
 | 
			
		||||
        tile_size = float(request.query_params.get("tile_size", 0.005))
 | 
			
		||||
 | 
			
		||||
        min_lng = tile_x * tile_size
 | 
			
		||||
        min_lat = tile_y * tile_size
 | 
			
		||||
        max_lng = min_lng + tile_size
 | 
			
		||||
        max_lat = min_lat + tile_size
 | 
			
		||||
 | 
			
		||||
        bbox = Polygon.from_bbox((min_lng, min_lat, max_lng, max_lat))
 | 
			
		||||
        places = Place.objects.filter(location__within=bbox)
 | 
			
		||||
        serializer = PlaceSerializer(places, many=True)
 | 
			
		||||
        return Response(serializer.data)
 | 
			
		||||
 
 | 
			
		||||
@@ -52,3 +52,4 @@ django-unused-media==0.2.2
 | 
			
		||||
django-resized==1.0.3
 | 
			
		||||
django-solo==2.4.0
 | 
			
		||||
chronostring==0.1.2
 | 
			
		||||
djangorestframework==3.16.0
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user