Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new option base_layer #49

Merged
merged 5 commits into from
Nov 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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