On introduit une API pour avoir un chargement plus fluide de la carte d'édition
Fix #378
This commit is contained in:
parent
0cf9a69525
commit
9a0c7016a5
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user