Skip to content

Commit

Permalink
Merge pull request #49 from Terralego/add_template_tag_option_base_layer
Browse files Browse the repository at this point in the history
Add new option base_layer
  • Loading branch information
submarcos committed Nov 27, 2019
2 parents ccd8465 + 44c53c1 commit e06eebb
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 70 deletions.
4 changes: 1 addition & 3 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ CHANGELOG
----------------------------

* fix settings with only MAX_ZOOM for map style
* Create map_tags allowing to send which feature will be used
* Automatically use

* Add map_image_url_loader tag allowing to add map with style, extra_features

0.3.23 (2019-11-18)
----------------------------
Expand Down
5 changes: 3 additions & 2 deletions doc/use.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Settings provide default config values, and formatted list of crud group and vie
::

{% load map_tags %}
{% map_image_url_loader feature_included=True extra_features="Extra_feature_slug,Extra_feature_2_slug" %}
{% map_image_url_loader feature_included=True extra_features="Extra_feature_slug,Extra_feature_2_slug"
base_layer="mapbox_baselayer_slug" %}

You can use like the other tags : width, heigth, anchor.
You can use the other tags : width, height, anchor.
18 changes: 1 addition & 17 deletions terra_geocrud/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from copy import deepcopy

import requests
from django.contrib.gis.db.models import Extent
from django.contrib.postgres.fields import JSONField, ArrayField
from django.core.exceptions import ValidationError
Expand All @@ -9,13 +8,12 @@
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
from geostore.mixins import BaseUpdatableModel
from mapbox_baselayer.models import MapBaseLayer
from sorl.thumbnail import ImageField, get_thumbnail

from . import settings as app_settings
from .properties.files import get_storage
from .properties.widgets import get_widgets_choices
from .utils import DEFAULT_MBGL_RENDERER_STYLE, get_default_style
from .utils import get_default_style


class CrudModelMixin(models.Model):
Expand Down Expand Up @@ -73,20 +71,6 @@ def clean(self):
if self.feature_title_property and self.feature_title_property not in self.properties:
raise ValidationError(f'Property should exists for feature title : {self.feature_title_property}')

@cached_property
def mblg_renderer_style(self):
if MapBaseLayer.objects.exists():
map_base_layer = MapBaseLayer.objects.first()
if map_base_layer.base_layer_type == 'mapbox':
response = requests.get(map_base_layer.map_box_url.replace("mapbox://styles",
"https://api.mapbox.com/styles/v1"),
params={"access_token": app_settings.TERRA_GEOCRUD.get('map', {}).get('mapbox_access_token')})
if response.status_code == 200:
return response.json()
else:
return map_base_layer.tilejson
return deepcopy(DEFAULT_MBGL_RENDERER_STYLE)

@cached_property
def map_style_with_default(self):
response = get_default_style(self.layer)
Expand Down
45 changes: 35 additions & 10 deletions terra_geocrud/templatetags/map_tags.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
from copy import deepcopy
from json import dumps, loads
import requests
import secrets

from django import template
from django.contrib.gis.geos import GeometryCollection, Point

from mapbox_baselayer.models import MapBaseLayer
from template_engines.templatetags.odt_tags import ImageLoaderNodeURL
from template_engines.templatetags.utils import parse_tag
from terra_geocrud import settings as app_settings
from terra_geocrud.utils import get_default_style
from terra_geocrud.utils import DEFAULT_MBGL_RENDERER_STYLE, get_default_style


register = template.Library()
Expand All @@ -22,8 +25,10 @@ def get_data(self, context):
feature_included = True if not final_data['feature_included'] else final_data['feature_included'].resolve(context)
extras_included = [] if not final_data['extra_features'] else final_data['extra_features'].resolve(
context).split(',')
base_layer = None if not final_data['extra_features'] else final_data['extra_features'].resolve(context)

feature = context['object']
style = self.get_style(feature, feature_included, extras_included)
style = self.get_style(feature, feature_included, extras_included, base_layer)
token = app_settings.TERRA_GEOCRUD.get('map', {}).get('mapbox_access_token')
final_style = {
'style': dumps(style),
Expand All @@ -35,8 +40,8 @@ def get_data(self, context):
if feature_included:
geoms.append(feature.geom)

for l in feature.layer.extra_geometries.filter(slug__in=extras_included):
geoms.append(l.features.first().geom)
for l in feature.extra_geometries.filter(layer_extra_geom__slug__in=extras_included):
geoms.append(l.geom)
collections = GeometryCollection(*geoms)
if not collections:
return final_style
Expand All @@ -55,8 +60,26 @@ def get_value_context(self, context):
final_data = self.get_data(context)
return final_url, final_request, None, None, final_anchor, final_data

def get_style(self, feature, feature_included, extras_included):
style_map = feature.layer.crud_view.mblg_renderer_style
def get_style_base_layer(self, base_layer):
try:
map_base_layer = MapBaseLayer.objects.get(slug=base_layer)
except MapBaseLayer.DoesNotExist:
map_base_layer = MapBaseLayer.objects.first()

if map_base_layer:
if map_base_layer.base_layer_type == 'mapbox':
response = requests.get(map_base_layer.map_box_url.replace("mapbox://styles",
"https://api.mapbox.com/styles/v1"),
params={"access_token": app_settings.TERRA_GEOCRUD.get('map', {}).get(
'mapbox_access_token')})
if response.status_code == 200:
return response.json()
else:
return map_base_layer.tilejson
return deepcopy(DEFAULT_MBGL_RENDERER_STYLE)

def get_style(self, feature, feature_included, extras_included, base_layer):
style_map = self.get_style_base_layer(base_layer)
view = feature.layer.crud_view
primary_layer = {}
if feature_included:
Expand Down Expand Up @@ -100,13 +123,15 @@ def map_image_url_loader(parser, token):
- anchor : Type of anchor, paragraph, as-char, char, frame, page
"""
tag_name, args, kwargs = parse_tag(token, parser)
usage = '{{% {tag_name} width="5000" height="5000" feature_included=False extra_features="feature_1"' \
'anchor="as-char" %}}'.format(tag_name=tag_name)
if not all(key in ['width', 'height', 'feature_included', 'extra_features', 'anchor'] for key in kwargs.keys()):
usage = '{{% {tag_name} width="5000" height="5000" feature_included=False extra_features="feature_1" ' \
'base_layer="mapbaselayer_1" anchor="as-char" %}}'.format(tag_name=tag_name)
if not all(key in ['width', 'height', 'feature_included',
'extra_features', 'anchor', 'base_layer'] for key in kwargs.keys()):
raise template.TemplateSyntaxError("Usage: %s" % usage)
kwargs['request'] = 'POST'
kwargs['data'] = {'feature_included': kwargs.pop('feature_included', None),
'extra_features': kwargs.pop('extra_features', None),
'width': kwargs.pop('width', None),
'height': kwargs.pop('height', None)}
'height': kwargs.pop('height', None),
'base_layer': kwargs.pop('base_layer', None)}
return MapImageLoaderNodeURL(f"{app_settings.TERRA_GEOCRUD['MBGLRENDERER_URL']}/render", **kwargs)
108 changes: 70 additions & 38 deletions terra_geocrud/tests/test_template_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from terra_geocrud import settings as app_settings


@mock.patch('secrets.token_hex', side_effect=['primary', 'test'])
class MapImageUrlLoaderTestCase(TestCase):
def setUp(self):
self.crud_view_line = factories.CrudViewFactory(name="Line", order=0,
Expand All @@ -42,6 +41,8 @@ def setUp(self):
)

self.extra_layer = LayerExtraGeom.objects.create(layer=self.crud_view_line.layer, title='test')
FeatureExtraGeom.objects.create(feature=self.point, layer_extra_geom=self.extra_layer,
geom=Point((-0.5, 45.2)))
FeatureExtraGeom.objects.create(feature=self.line, layer_extra_geom=self.extra_layer,
geom=Point((-0.1, 44.2)))

Expand All @@ -56,6 +57,9 @@ def setUp(self):

self.token_mapbox = app_settings.TERRA_GEOCRUD.get('map', {}).get('mapbox_access_token')


@mock.patch('secrets.token_hex', side_effect=['primary', 'test'])
class StyleMapImageUrlLoaderTestCase(MapImageUrlLoaderTestCase):
def test_get_style_default(self, token):
self.maxDiff = None
dict_style = {
Expand All @@ -73,7 +77,7 @@ def test_get_style_default(self, token):
{"type": "line", "paint": {"line-color": "#000", "line-width": 3},
"id": "primary", "source": "primary"}]
}
self.assertDictEqual(dict_style, self.node.get_style(self.line, True, ['']))
self.assertDictEqual(dict_style, self.node.get_style(self.line, True, [''], None))

def test_get_style_no_feature(self, token):
self.maxDiff = None
Expand All @@ -88,7 +92,7 @@ def test_get_style_no_feature(self, token):
{"id": "DEFAULT_MBGL_RENDERER_STYLE", "type": "raster", "source": "DEFAULT_MBGL_RENDERER_STYLE"}]
}

self.assertDictEqual(dict_style, self.node.get_style(self.line, False, ['']))
self.assertDictEqual(dict_style, self.node.get_style(self.line, False, [''], None))

def test_get_style_no_feature_extra_feature(self, token):
self.maxDiff = None
Expand All @@ -106,7 +110,7 @@ def test_get_style_no_feature_extra_feature(self, token):
{"type": "circle", "paint": {"circle-color": "#000", "circle-radius": 8},
"id": "primary", "source": "primary"}]
}
self.assertDictEqual(dict_style, self.node.get_style(self.line, False, ['test']))
self.assertDictEqual(dict_style, self.node.get_style(self.line, False, ['test'], None))

def test_get_style_no_feature_extra_feature_custom_style(self, token):
self.maxDiff = None
Expand All @@ -127,14 +131,66 @@ def test_get_style_no_feature_extra_feature_custom_style(self, token):
{"type": "circle", "paint": {"circle-color": "#fff", "circle-radius": 8},
"id": "primary", "source": "primary"}]
}
self.assertDictEqual(dict_style, self.node.get_style(self.line, False, ['test']))
self.assertDictEqual(dict_style, self.node.get_style(self.line, False, ['test'], None))

def test_get_style_no_feature_from_baselayer(self, token):
self.maxDiff = None
layer = MapBaseLayer.objects.create(name="BaseLayerCustom", order=0, base_layer_type="raster")
BaseLayerTile.objects.create(url="test.test", base_layer=layer)
MapBaseLayer.objects.create(name="OtherLayerCustom", order=1, base_layer_type="raster")

dict_style = {
"version": 8,
"sources":
{"baselayercustom": {"type": "raster",
"tiles": ["test.test"],
"minzoom": 0,
"maxzoom": 22}},
"layers": [
{"id": "baselayercustom-background", "type": "raster", "source": "baselayercustom"}]
}

self.assertDictEqual(dict_style, self.node.get_style(self.line, False, [''], None))

def test_get_style_chosen_baselayer(self, token):
self.maxDiff = None
MapBaseLayer.objects.create(name="BaseLayerCustom", order=0, base_layer_type="raster")
layer = MapBaseLayer.objects.create(name="OtherLayerCustom", order=1, base_layer_type="raster")
BaseLayerTile.objects.create(url="test.test", base_layer=layer)

dict_style = {
"version": 8,
"sources":
{"otherlayercustom": {"type": "raster",
"tiles": ["test.test"],
"minzoom": 0,
"maxzoom": 22}},
"layers": [
{"id": "otherlayercustom-background", "type": "raster", "source": "otherlayercustom"}]
}

self.assertDictEqual(dict_style, self.node.get_style(self.line, False, [''], 'otherlayercustom'))

@mock.patch('requests.get')
def test_get_style_no_feature_from_mapbox_baselayer(self, mocked_get, token):
mocked_get.return_value.status_code = 200
mocked_get.return_value.json.return_value = {"custom": "style"}
self.maxDiff = None
MapBaseLayer.objects.create(name="BaseLayerCustom", order=0, base_layer_type="mapbox", map_box_url="test.com")
MapBaseLayer.objects.create(name="OtherLayerCustom", order=1, base_layer_type="raster")

self.assertDictEqual({"custom": "style"}, self.node.get_style(self.line, False, [''], None))


@mock.patch('secrets.token_hex', side_effect=['primary', 'test'])
class ContextMapImageUrlLoaderTestCase(MapImageUrlLoaderTestCase):
def test_get_value_context_line(self, token):
self.maxDiff = None
self.node = MapImageLoaderNodeURL('http://mbglrenderer/render', data={'width': None,
'height': None,
'feature_included': None,
'extra_features': None})
'extra_features': None,
'base_layer': None})
style = self.node.get_data(Context({'object': self.line}))
dict_style = {
"version": 8,
Expand Down Expand Up @@ -165,7 +221,8 @@ def test_get_value_context_point(self, token):
self.node = MapImageLoaderNodeURL('http://mbglrenderer/render', data={'width': None,
'height': None,
'feature_included': None,
'extra_features': None})
'extra_features': None,
'base_layer': None})

with override_settings(TERRA_GEOCRUD=settings_terra):
style = self.node.get_data(Context({'object': self.point}))
Expand Down Expand Up @@ -196,6 +253,7 @@ def test_get_value_context_line_with_extra_features(self, token):
self.node = MapImageLoaderNodeURL('http://mbglrenderer/render', data={'width': None,
'height': None,
'feature_included': None,
'base_layer': None,
'extra_features': FilterExpression("'test'", Parser(''))})
style = self.node.get_data(Context({'object': self.line}))

Expand Down Expand Up @@ -226,35 +284,6 @@ def test_get_value_context_line_with_extra_features(self, token):

self.assertDictEqual(dict_style_post, style)

def test_get_style_no_feature_from_baselayer(self, token):
self.maxDiff = None
layer = MapBaseLayer.objects.create(name="BaseLayerCustom", order=0, base_layer_type="raster")
BaseLayerTile.objects.create(url="test.test", base_layer=layer)
MapBaseLayer.objects.create(name="OtherLayerCustom", order=1, base_layer_type="raster")

dict_style = {
"version": 8,
"sources":
{"baselayercustom": {"type": "raster",
"tiles": ["test.test"],
"minzoom": 0,
"maxzoom": 22}},
"layers": [
{"id": "baselayercustom-background", "type": "raster", "source": "baselayercustom"}]
}

self.assertDictEqual(dict_style, self.node.get_style(self.line, False, ['']))

@mock.patch('requests.get')
def test_get_style_no_feature_from_mapbox_baselayer(self, mocked_get, token):
mocked_get.return_value.status_code = 200
mocked_get.return_value.json.return_value = {"custom": "style"}
self.maxDiff = None
MapBaseLayer.objects.create(name="BaseLayerCustom", order=0, base_layer_type="mapbox", map_box_url="test.com")
MapBaseLayer.objects.create(name="OtherLayerCustom", order=1, base_layer_type="raster")

self.assertDictEqual({"custom": "style"}, self.node.get_style(self.line, False, ['']))

@mock.patch('requests.get')
def test_get_style_extra_layer_no_extra_feature(self, mocked_get, token):
mocked_get.return_value.status_code = 200
Expand All @@ -264,8 +293,11 @@ def test_get_style_extra_layer_no_extra_feature(self, mocked_get, token):
MapBaseLayer.objects.create(name="BaseLayerCustom", order=0, base_layer_type="mapbox", map_box_url="test.com")
MapBaseLayer.objects.create(name="OtherLayerCustom", order=1, base_layer_type="raster")

self.assertDictEqual({"custom": "style"}, self.node.get_style(self.line, False, ['test']))
self.assertDictEqual({"custom": "style"}, self.node.get_style(self.line, False, ['test'], None))


@mock.patch('secrets.token_hex', side_effect=['primary', 'test'])
class RenderMapImageUrlLoaderTestCase(MapImageUrlLoaderTestCase):
@mock.patch('requests.post')
def test_image_url_loader_object(self, mocked_post, token):
mocked_post.return_value.status_code = 200
Expand All @@ -284,7 +316,7 @@ def test_map_image_url_loader_usage(self, mocked_post, token):
with self.assertRaises(TemplateSyntaxError) as cm:
Template('{% load map_tags %}{% map_image_url_loader wrong_key="test" %}')
self.assertEqual('Usage: {% map_image_url_loader width="5000" height="5000" feature_included=False '
'extra_features="feature_1"anchor="as-char" %}', str(cm.exception))
'extra_features="feature_1" base_layer="mapbaselayer_1" anchor="as-char" %}', str(cm.exception))

def test_image_url_loader_no_object(self, token):
context = Context({'object': self.line})
Expand Down

0 comments on commit e06eebb

Please sign in to comment.