diff --git a/combo/apps/maps/forms.py b/combo/apps/maps/forms.py index d8d8c260..3220ef63 100644 --- a/combo/apps/maps/forms.py +++ b/combo/apps/maps/forms.py @@ -73,12 +73,25 @@ class MapLayerForm(forms.ModelForm): class MapLayerOptionsForm(forms.ModelForm): class Meta: model = MapLayerOptions - fields = ['map_layer'] + fields = ['map_layer', 'opacity'] + widgets = { + 'opacity': forms.NumberInput(attrs={'step': 0.1, 'min': 0, 'max': 1}) + } def __init__(self, *args, **kwargs): self.kind = kwargs.pop('kind') super(MapLayerOptionsForm, self).__init__(*args, **kwargs) - if self.kind == 'geojson': - self.fields['map_layer'].queryset = self.instance.map_cell.get_free_geojson_layers() + # if edition, no possibility to change the layer + if self.instance.pk: + del self.fields['map_layer'] else: - self.fields['map_layer'].queryset = self.instance.map_cell.get_free_tiles_layers() + if self.kind == 'geojson': + self.fields['map_layer'].queryset = self.instance.map_cell.get_free_geojson_layers() + else: + self.fields['map_layer'].queryset = self.instance.map_cell.get_free_tiles_layers() + # init opacity field only for tiles layers + if self.kind == 'geojson': + del self.fields['opacity'] + else: + self.fields['opacity'].required = True + self.fields['opacity'].initial = 1 diff --git a/combo/apps/maps/manager_views.py b/combo/apps/maps/manager_views.py index dbeb520f..b7e1f36b 100644 --- a/combo/apps/maps/manager_views.py +++ b/combo/apps/maps/manager_views.py @@ -94,6 +94,45 @@ class MapCellAddLayer(CreateView): map_cell_add_layer = MapCellAddLayer.as_view() +class MapCellEditLayer(UpdateView): + form_class = MapLayerOptionsForm + template_name = 'maps/layer_options_form.html' + + def dispatch(self, request, *args, **kwargs): + try: + self.cell = CellBase.get_cell(kwargs['cell_reference'], page=kwargs['page_pk']) + except Map.DoesNotExist: + raise Http404 + self.object = get_object_or_404( + MapLayerOptions, + pk=kwargs['layeroptions_pk'], + map_cell=self.cell) + return super(MapCellEditLayer, self).dispatch(request, *args, **kwargs) + + def get_object(self, *args, **kwargs): + return self.object + + def get_form_kwargs(self): + kwargs = super(MapCellEditLayer, self).get_form_kwargs() + kwargs['kind'] = self.object.map_layer.kind + return kwargs + + def form_valid(self, form): + PageSnapshot.take( + self.cell.page, + request=self.request, + comment=_('changed options of layer "%s" in cell "%s"') % (form.instance.map_layer, self.cell)) + return super(MapCellEditLayer, self).form_valid(form) + + def get_success_url(self): + return '%s#cell-%s' % ( + reverse('combo-manager-page-view', kwargs={'pk': self.kwargs.get('page_pk')}), + self.kwargs['cell_reference']) + + +map_cell_edit_layer = MapCellEditLayer.as_view() + + class MapCellDeleteLayer(DeleteView): template_name = 'combo/generic_confirm_delete.html' diff --git a/combo/apps/maps/migrations/0010_map_layer_opacity.py b/combo/apps/maps/migrations/0010_map_layer_opacity.py new file mode 100644 index 00000000..db64b06d --- /dev/null +++ b/combo/apps/maps/migrations/0010_map_layer_opacity.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('maps', '0009_map_layer_kind'), + ] + + operations = [ + migrations.AddField( + model_name='maplayeroptions', + name='opacity', + field=models.FloatField( + help_text='Float value between 0 and 1', null=True, + validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1)], verbose_name='Opacity'), + ), + ] diff --git a/combo/apps/maps/models.py b/combo/apps/maps/models.py index 1aeb2824..718a9d9b 100644 --- a/combo/apps/maps/models.py +++ b/combo/apps/maps/models.py @@ -17,6 +17,7 @@ import json from django.core import serializers +from django.core import validators from django.db import models from django.utils import six from django.utils.encoding import python_2_unicode_compatible @@ -333,6 +334,38 @@ class Map(CellBase): def is_enabled(cls): return MapLayer.objects.exists() + def get_tiles_layers(self): + tiles_layers = [] + options_qs = ( + self.maplayeroptions_set + .filter(map_layer__kind='tiles') + .select_related('map_layer') + .order_by('-opacity')) + for options in options_qs: + tiles_layers.append({ + 'tile_urltemplate': options.map_layer.tiles_template_url, + 'map_attribution': options.map_layer.tiles_attribution, + 'opacity': options.opacity or 0, + }) + # check if at least one layer with opacity set to 1 exists + if any([l['opacity'] == 1 for l in tiles_layers]): + return tiles_layers + # add the default tiles layer + default_tiles_layer = MapLayer.get_default_tiles_layer() + if default_tiles_layer is not None: + tiles_layers.insert(0, { + 'tile_urltemplate': default_tiles_layer.tiles_template_url, + 'map_attribution': default_tiles_layer.tiles_attribution, + 'opacity': 1, + }) + else: + tiles_layers.insert(0, { + 'tile_urltemplate': settings.COMBO_MAP_TILE_URLTEMPLATE, + 'map_attribution': settings.COMBO_MAP_ATTRIBUTION, + 'opacity': 1, + }) + return tiles_layers + def get_cell_extra_context(self, context): ctx = super(Map, self).get_cell_extra_context(context) ctx['title'] = self.title @@ -344,13 +377,7 @@ class Map(CellBase): ctx['min_zoom'] = self.min_zoom ctx['max_zoom'] = self.max_zoom ctx['geojson_url'] = reverse_lazy('mapcell-geojson', kwargs={'cell_id': self.pk}) - default_tiles_layer = MapLayer.get_default_tiles_layer() - if default_tiles_layer is not None: - ctx['tile_urltemplate'] = default_tiles_layer.tiles_template_url - ctx['map_attribution'] = default_tiles_layer.tiles_attribution - else: - ctx['tile_urltemplate'] = settings.COMBO_MAP_TILE_URLTEMPLATE - ctx['map_attribution'] = settings.COMBO_MAP_ATTRIBUTION + ctx['tiles_layers'] = self.get_tiles_layers() ctx['max_bounds'] = settings.COMBO_MAP_MAX_BOUNDS ctx['group_markers'] = self.group_markers ctx['marker_behaviour_onclick'] = self.marker_behaviour_onclick @@ -400,6 +427,14 @@ class Map(CellBase): class MapLayerOptions(models.Model): map_cell = models.ForeignKey(Map, on_delete=models.CASCADE, db_column='map_id') map_layer = models.ForeignKey(MapLayer, verbose_name=_('Layer'), on_delete=models.CASCADE, db_column='maplayer_id') + opacity = models.FloatField( + verbose_name=_('Opacity'), + validators=[ + validators.MinValueValidator(0), + validators.MaxValueValidator(1)], + null=True, + help_text=_('Float value between 0 and 1'), + ) class Meta: db_table = 'maps_map_layers' diff --git a/combo/apps/maps/static/js/combo.map.js b/combo/apps/maps/static/js/combo.map.js index e9cd0bba..1283e8d9 100644 --- a/combo/apps/maps/static/js/combo.map.js +++ b/combo/apps/maps/static/js/combo.map.js @@ -124,8 +124,6 @@ $(function() { map_options.zoomControl = false; var latlng = [$map_widget.data('init-lat'), $map_widget.data('init-lng')]; var geojson_url = $map_widget.data('geojson-url'); - var map_tile_url = $map_widget.data('tile-urltemplate'); - var map_attribution = $map_widget.data('map-attribution'); if ($map_widget.data('max-bounds-lat1')) { map_options.maxBounds = L.latLngBounds( L.latLng($map_widget.data('max-bounds-lat1'), $map_widget.data('max-bounds-lng1')), @@ -175,11 +173,18 @@ $(function() { }); } - L.tileLayer(map_tile_url, - { - attribution: map_attribution, - maxZoom: map_options.maxZoom - }).addTo(map); + var map_id = $map_widget.data('cell-id'); + var tiles_layers = window['tiles_'+map_id]; + $.each(tiles_layers, function(idx, layer) { + L.tileLayer( + layer.tile_urltemplate, + { + attribution: layer.map_attribution, + opacity: layer.opacity, + maxZoom: map_options.maxZoom + } + ).addTo(map); + }); if (geojson_url) { map.add_geojson_layer(function(geo_json) { var bounds = geo_json.getBounds(); diff --git a/combo/apps/maps/templates/maps/map_cell.html b/combo/apps/maps/templates/maps/map_cell.html index 880906e8..d16af414 100644 --- a/combo/apps/maps/templates/maps/map_cell.html +++ b/combo/apps/maps/templates/maps/map_cell.html @@ -6,7 +6,6 @@ data-init-zoom="{{ initial_zoom }}" data-min-zoom="{{ min_zoom }}" 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}}" data-include-geoloc-button="true" {% if group_markers %}data-group-markers="1"{% endif %} data-marker-behaviour-onclick="{{ cell.marker_behaviour_onclick }}" @@ -16,7 +15,18 @@ data-max-bounds-lat2="{{ max_bounds.corner2.lat }}" data-max-bounds-lng2="{{ max_bounds.corner2.lng }}" {% endif %} + data-cell-id="{{ cell.pk }}" > {% endlocalize %} + {% endblock %} diff --git a/combo/apps/maps/templates/maps/map_cell_form.html b/combo/apps/maps/templates/maps/map_cell_form.html index 983da251..ab5e2a15 100644 --- a/combo/apps/maps/templates/maps/map_cell_form.html +++ b/combo/apps/maps/templates/maps/map_cell_form.html @@ -11,6 +11,9 @@ {% for option in options %}