maps: add option to clusterize markers (#21048)

This commit is contained in:
Serghei Mihai 2018-02-12 19:20:18 +01:00
parent 4ae2ac1ad5
commit ce7386ffd6
9 changed files with 107 additions and 4 deletions

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('maps', '0004_map_initial_state'),
]
operations = [
migrations.AddField(
model_name='map',
name='group_markers',
field=models.BooleanField(default=False, verbose_name='Group markers in clusters'),
),
]

View File

@ -200,6 +200,7 @@ class Map(CellBase):
choices=ZOOM_LEVELS, default='0')
max_zoom = models.CharField(_('Maximal zoom level'), max_length=2,
choices=ZOOM_LEVELS, default=19)
group_markers = models.BooleanField(_('Group markers in clusters'), default=False)
layers = models.ManyToManyField(MapLayer, verbose_name=_('Layers'), blank=True)
template_name = 'maps/map_cell.html'
@ -208,7 +209,7 @@ class Map(CellBase):
verbose_name = _('Map')
class Media:
js = ('xstatic/leaflet.js', 'js/combo.map.js')
js = ('xstatic/leaflet.js', 'js/combo.map.js', 'xstatic/leaflet.markercluster.js')
css = {'all': ('xstatic/leaflet.css', 'css/combo.map.css')}
def get_default_position(self):
@ -216,7 +217,7 @@ class Map(CellBase):
def get_default_form_class(self):
fields = ('title', 'initial_state', 'initial_zoom', 'min_zoom',
'max_zoom', 'layers')
'max_zoom', 'group_markers', 'layers')
widgets = {'layers': forms.widgets.CheckboxSelectMultiple}
return forms.models.modelform_factory(self.__class__, fields=fields,
widgets=widgets)
@ -245,4 +246,5 @@ class Map(CellBase):
ctx['tile_urltemplate'] = settings.COMBO_MAP_TILE_URLTEMPLATE
ctx['map_attribution'] = settings.COMBO_MAP_ATTRIBUTION
ctx['max_bounds'] = settings.COMBO_MAP_MAX_BOUNDS
ctx['group_markers'] = self.group_markers
return ctx

View File

@ -44,7 +44,7 @@ div.combo-cell-map.leaflet-container {
/* leaflet styles */
div.leaflet-marker-icon {
div.leaflet-marker-icon.leaflet-div-icon {
border: none;
background: transparent;
}
@ -135,3 +135,52 @@ ul#id_icon {
}
}
}
/* leaflet markercluster styles */
.leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow {
transition: transform 0.3s ease-out, opacity 0.3s ease-in;
}
.leaflet-cluster-spider-leg {
/* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */
transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in;
}
.marker-cluster {
background-clip: padding-box;
border-radius: 30px;
div {
display: block;
background: #fff;
border-radius: 30px;
font-weight: bold;
height: 50px;
width: 50px;
text-align: center;
margin-left: 5px;
margin-top: 5px;
}
span {
line-height: 50px;
}
&-small {
background: #b5e28c;
div {
font-size: 200%;
}
}
&-medium {
background: #f1d357;
div {
font-size: 200%;
}
}
&-large {
background: #fd9c73;
div {
font-size: 150%;
}
}
}

View File

@ -29,7 +29,30 @@ $(function() {
});
if (map.geo_json) map.geo_json.remove();
map.geo_json = geo_json;
geo_json.addTo(map);
if ($map_widget.data('group-markers')) {
var markers = L.markerClusterGroup({showCoverageOnHover: false,
zoomToBoundsOnClick: true,
removeOutsideVisibleBounds: true,
iconCreateFunction: function (cluster) {
var icon_size = 60;
var childCount = cluster.getChildCount();
var icon_html = '<div><span>' + childCount + '</span></div>';
var c = ' marker-cluster-';
if (childCount < 10) {
c += 'small';
} else if (childCount < 100) {
c += 'medium';
} else {
c += 'large';
}
return new L.DivIcon({html: icon_html, className: 'marker-cluster' + c, iconSize: new L.Point(icon_size, icon_size)});
}});
markers.addLayer(geo_json);
map.addLayer(markers);
map.clustered_markers = markers;
} else {
geo_json.addTo(map);
}
if (callback) {
callback(geo_json);
}

View File

@ -6,6 +6,7 @@
data-max-zoom="{{ max_zoom }}" data-init-lat="{{ init_lat }}"
data-init-lng="{{ init_lng }}" data-geojson-url="{{ geojson_url }}"
data-tile-urltemplate="{{ tile_urltemplate}}" data-map-attribution="{{ map_attribution}}"
{% if group_markers %}data-group-markers="1"{% endif %}
{% if max_bounds.corner1.lat %}
data-max-bounds-lat1="{{ max_bounds.corner1.lat }}"
data-max-bounds-lng1="{{ max_bounds.corner1.lng }}"

View File

@ -82,6 +82,7 @@ INSTALLED_APPS = (
'xstatic.pkg.leaflet',
'xstatic.pkg.opensans',
'xstatic.pkg.roboto_fontface',
'xstatic.pkg.leaflet_markercluster',
)
INSTALLED_APPS = plugins.register_plugins_apps(INSTALLED_APPS)

1
debian/control vendored
View File

@ -17,6 +17,7 @@ Depends: ${misc:Depends}, ${python:Depends},
python-xstatic-chartnew-js,
python-xstatic-josefinsans,
python-xstatic-leaflet,
python-xstatic-leaflet-markercluster,
python-xstatic-opensans,
python-xstatic-roboto-fontface,
python-eopayment (>= 1.9),

View File

@ -142,6 +142,7 @@ setup(
'requests',
'XStatic-ChartNew.js',
'XStatic-Leaflet',
'XStatic-Leaflet-MarkerCluster',
'XStatic_JosefinSans',
'XStatic_OpenSans',
'XStatic_roboto-fontface',

View File

@ -94,12 +94,18 @@ def test_cell_rendering(layer):
assert 'data-init-lat="48.83369263315934"' in rendered
assert 'data-init-lng="2.3233688436448574"' in rendered
assert 'data-geojson-url="/ajax/mapcell/geojson/1/"' in rendered
assert 'data-group-markers="1"' not in rendered
resp = client.get('/test_map_cell/')
assert 'xstatic/leaflet.js' in resp.content
assert 'js/combo.map.js' in resp.content
assert 'xstatic/leaflet.css' in resp.content
assert 'css/combo.map.css' in resp.content
cell.group_markers = True
cell.save()
rendered = cell.render(context)
assert 'data-group-markers="1"' in rendered
def test_get_geojson_on_non_public_page(layer):
page = Page(title='xxx', slug='new', template_name='standard',