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_cleanup.apps.CleanupConfig",
|
||||||
"django_unused_media",
|
"django_unused_media",
|
||||||
"solo.apps.SoloAppConfig",
|
"solo.apps.SoloAppConfig",
|
||||||
|
"rest_framework",
|
||||||
]
|
]
|
||||||
|
|
||||||
SOLO_CACHE_TIMEOUT = 60 * 15 # 15 minutes
|
SOLO_CACHE_TIMEOUT = 60 * 15 # 15 minutes
|
||||||
|
@ -375,6 +375,10 @@ var SequentialLoader = function() {
|
|||||||
_getMap: function(mapOptions) {
|
_getMap: function(mapOptions) {
|
||||||
var map = new L.Map(this.options.id, mapOptions), layer;
|
var map = new L.Map(this.options.id, mapOptions), layer;
|
||||||
|
|
||||||
|
if (window.loadTiles !== undefined) {
|
||||||
|
window.loadTiles(map);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.options.provider == 'google') {
|
if (this.options.provider == 'google') {
|
||||||
layer = new L.gridLayer.googleMutant({
|
layer = new L.gridLayer.googleMutant({
|
||||||
type: this.options.providerOptions.google.mapType.toLowerCase(),
|
type: this.options.providerOptions.google.mapType.toLowerCase(),
|
||||||
@ -397,25 +401,6 @@ var SequentialLoader = function() {
|
|||||||
|
|
||||||
map.addLayer(layer);
|
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;
|
return map;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -434,7 +419,8 @@ var SequentialLoader = function() {
|
|||||||
var self = this,
|
var self = this,
|
||||||
markerOptions = {
|
markerOptions = {
|
||||||
draggable: true,
|
draggable: true,
|
||||||
icon: window.pinIcon
|
icon: window.pinIcon,
|
||||||
|
zIndexOffset: 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
var marker = L.marker(center, markerOptions).addTo(map);
|
var marker = L.marker(center, markerOptions).addTo(map);
|
||||||
|
@ -37,18 +37,51 @@
|
|||||||
shadowSize: [19, 19]
|
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 = [];
|
const bounds = map.getBounds();
|
||||||
{% with cache_timeout=user.is_authenticated|yesno:"300,6000" %}
|
const tileSize = 0.1;
|
||||||
{% cache cache_timeout place_lists_js user.is_authenticated %}
|
|
||||||
|
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>
|
</script>
|
||||||
<link href="{% static 'css/django_better_admin_arrayfield.min.css' %}"
|
<link href="{% static 'css/django_better_admin_arrayfield.min.css' %}"
|
||||||
type="text/css"
|
type="text/css"
|
||||||
|
@ -116,6 +116,7 @@ from .views import (
|
|||||||
UnknownPlaceAddView,
|
UnknownPlaceAddView,
|
||||||
UnknownPlacesListView,
|
UnknownPlacesListView,
|
||||||
fix_unknown_places,
|
fix_unknown_places,
|
||||||
|
PlaceTileView,
|
||||||
# Search
|
# Search
|
||||||
event_search,
|
event_search,
|
||||||
event_search_full,
|
event_search_full,
|
||||||
@ -550,6 +551,8 @@ urlpatterns = [
|
|||||||
load_specialperiods_from_ical,
|
load_specialperiods_from_ical,
|
||||||
name="load_specialperiods_from_ical",
|
name="load_specialperiods_from_ical",
|
||||||
),
|
),
|
||||||
|
# API
|
||||||
|
path("api/places/tile/", PlaceTileView.as_view(), name="place_list"),
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
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.views.generic import ListView, UpdateView, CreateView, DeleteView
|
||||||
from django.contrib.gis.measure import D
|
from django.contrib.gis.measure import D
|
||||||
from django.utils.safestring import mark_safe
|
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 .utils import get_event_qs
|
||||||
from ..forms import PlaceForm, EventAddPlaceForm
|
from ..forms import PlaceForm, EventAddPlaceForm
|
||||||
from ..models import Place, Event
|
from ..models import Place, Event
|
||||||
from ..utils import PlaceGuesser
|
from ..utils import PlaceGuesser
|
||||||
|
from ..serializers import PlaceSerializer
|
||||||
|
|
||||||
|
|
||||||
class PlaceListView(ListView):
|
class PlaceListView(ListView):
|
||||||
@ -123,11 +127,6 @@ class PlaceCreateView(
|
|||||||
success_message = _("The place has been successfully created.")
|
success_message = _("The place has been successfully created.")
|
||||||
form_class = PlaceForm
|
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):
|
class PlaceDeleteView(PermissionRequiredMixin, DeleteView):
|
||||||
model = Place
|
model = Place
|
||||||
@ -256,3 +255,21 @@ class PlaceFromEventCreateView(PlaceCreateView):
|
|||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return self.event.get_absolute_url()
|
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-resized==1.0.3
|
||||||
django-solo==2.4.0
|
django-solo==2.4.0
|
||||||
chronostring==0.1.2
|
chronostring==0.1.2
|
||||||
|
djangorestframework==3.16.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user