On introduit une API pour avoir un chargement plus fluide de la carte d'édition

Fix #378
This commit is contained in:
Jean-Marie Favreau 2025-05-04 23:19:12 +02:00
parent 0cf9a69525
commit 9a0c7016a5
8 changed files with 107 additions and 35 deletions

View File

@ -0,0 +1 @@
from .place_serializer import PlaceSerializer

View 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()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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