156 lines
5.7 KiB
Python
156 lines
5.7 KiB
Python
# combo - content management system
|
|
# Copyright (C) 2017 Entr'ouvert
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
# under the terms of the GNU Affero General Public License as published
|
|
# by the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
from django.db import models
|
|
from django.utils.text import slugify
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from django.core.urlresolvers import reverse_lazy
|
|
from django import forms
|
|
from django import template
|
|
from django.conf import settings
|
|
from django.core.exceptions import PermissionDenied
|
|
|
|
from combo.data.models import CellBase
|
|
from combo.data.library import register_cell_class
|
|
from combo.utils import requests
|
|
|
|
|
|
ICONS = [
|
|
('home', _('Home')),
|
|
('building', _('Building')),
|
|
('hospital', _('Hospital')),
|
|
('ambulance', _('Ambulance')),
|
|
('taxi', _('Taxi')),
|
|
('subway', _('Subway')),
|
|
('wheelchair', _('Wheelchair')),
|
|
('bicycle', _('Bicycle')),
|
|
('car', _('Car')),
|
|
('train', _('Train')),
|
|
('bus', _('Bus')),
|
|
('motorcycle', _('Motorcycle')),
|
|
('truck', _('Truck')),
|
|
]
|
|
|
|
ZOOM_LEVELS = [ ('0', _('Whole world')),
|
|
('9', _('Wide area')),
|
|
('11', _('Area')),
|
|
('13', _('Town')),
|
|
('16', _('Small road')),
|
|
('19', _('Ant')),]
|
|
|
|
|
|
class MapLayer(models.Model):
|
|
label = models.CharField(_('Label'), max_length=128)
|
|
slug = models.SlugField(_('Slug'))
|
|
geojson_url = models.URLField(_('Geojson URL'), max_length=1024)
|
|
marker_colour = models.CharField(_('Marker colour'), max_length=7, default='#0000FF')
|
|
icon = models.CharField(_('Marker icon'), max_length=32, blank=True, null=True,
|
|
choices=ICONS)
|
|
icon_colour = models.CharField(_('Icon colour'), max_length=7, default='#FFFFFF')
|
|
|
|
def save(self, *args, **kwargs):
|
|
if not self.slug:
|
|
base_slug = slugify(self.label)
|
|
slug = base_slug
|
|
i = 1
|
|
while True:
|
|
try:
|
|
MapLayer.objects.get(slug=slug)
|
|
except self.DoesNotExist:
|
|
break
|
|
slug = '%s-%s' % (base_slug, i)
|
|
i += 1
|
|
self.slug = slug
|
|
super(MapLayer, self).save(*args, **kwargs)
|
|
|
|
def __unicode__(self):
|
|
return self.label
|
|
|
|
def get_geojson(self, request):
|
|
response = requests.get(self.geojson_url,
|
|
remote_service='auto',
|
|
user=request.user,
|
|
headers={'accept': 'application/json'})
|
|
if not response.ok:
|
|
return []
|
|
data = response.json()
|
|
if 'features' in data:
|
|
features = data['features']
|
|
else:
|
|
features = data
|
|
for feature in features:
|
|
feature['properties']['colour'] = self.marker_colour
|
|
feature['properties']['icon_colour'] = self.icon_colour
|
|
feature['properties']['label'] = self.label
|
|
feature['properties']['icon'] = self.icon
|
|
return features
|
|
|
|
|
|
@register_cell_class
|
|
class Map(CellBase):
|
|
title = models.CharField(_('Title'), max_length=150, blank=True)
|
|
initial_zoom = models.CharField(_('Initial zoom level'), max_length=2,
|
|
choices=ZOOM_LEVELS, default='13')
|
|
min_zoom = models.CharField(_('Minimal zoom level'), max_length=2,
|
|
choices=ZOOM_LEVELS, default='0')
|
|
max_zoom = models.CharField(_('Maximal zoom level'), max_length=2,
|
|
choices=ZOOM_LEVELS, default=19)
|
|
layers = models.ManyToManyField(MapLayer, verbose_name=_('Layers'), blank=True)
|
|
|
|
template_name = 'maps/map_cell.html'
|
|
|
|
class Meta:
|
|
verbose_name = _('Map')
|
|
|
|
class Media:
|
|
js = ('xstatic/leaflet.js', 'js/combo.map.js')
|
|
css = {'all': ('xstatic/leaflet.css', 'css/combo.map.css')}
|
|
|
|
def get_default_position(self):
|
|
return settings.COMBO_MAP_DEFAULT_POSITION
|
|
|
|
def get_default_form_class(self):
|
|
fields = ('title', 'initial_zoom', 'min_zoom',
|
|
'max_zoom', 'layers')
|
|
widgets = {'layers': forms.widgets.CheckboxSelectMultiple}
|
|
return forms.models.modelform_factory(self.__class__, fields=fields,
|
|
widgets=widgets)
|
|
|
|
def get_geojson(self, request):
|
|
geojson = {'type': 'FeatureCollection', 'features': []}
|
|
for layer in self.layers.all():
|
|
geojson['features'] += layer.get_geojson(request)
|
|
return geojson
|
|
|
|
@classmethod
|
|
def is_enabled(cls):
|
|
return MapLayer.objects.count() > 0
|
|
|
|
def get_cell_extra_context(self, context):
|
|
ctx = super(Map, self).get_cell_extra_context(context)
|
|
ctx['title'] = self.title
|
|
default_position = self.get_default_position()
|
|
ctx['init_lat'] = default_position['lat']
|
|
ctx['init_lng'] = default_position['lng']
|
|
ctx['initial_zoom'] = self.initial_zoom
|
|
ctx['min_zoom'] = self.min_zoom
|
|
ctx['max_zoom'] = self.max_zoom
|
|
ctx['geojson_url'] = reverse_lazy('mapcell-geojson', kwargs={'cell_id': self.pk})
|
|
ctx['tile_urltemplate'] = settings.COMBO_MAP_TILE_URLTEMPLATE
|
|
ctx['map_attribution'] = settings.COMBO_MAP_ATTRIBUTION
|
|
return ctx
|