combo/combo/apps/maps/models.py

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