Skip to content

Commit

Permalink
Merge pull request #590 from 12rambau/ee_layer_style
Browse files Browse the repository at this point in the history
Set default style to vector layers in GEE
  • Loading branch information
dfguerrerom authored Sep 12, 2022
2 parents cb536f4 + 648f352 commit 4359d45
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 41 deletions.
53 changes: 49 additions & 4 deletions docs/source/tutorials/create_asset.rst
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
Create a GEE asset compatible with SEPAL
========================================
Map visualization
*****************

In this section, you will find information related to map visualization. In the :ref:`first section <geeraster>` you will learn how to set the styles to a GEE image fully compatible with both SEPAL and sepal-ui, and in the :ref:`last section <geevector>`, you will find the info about the default and custom styles that can be applied to :code:`sepal_ui.mapping.SepalMap`.

.. _geeraster:

Set EE Image visualization
==========================


In the current release of SEPAL, Custom assets can be displayed in the main interface to overlay different information in the same place.
As the main goal of this library is to create assets in the the SEPAL framework and as :code:`sepal_ui` is bound to GEE for many modules, we will present you here the different way of creating an :code:`Image` asset that can be automatically displayed in SEPAL and remain consistent with what you display in your app.

create a visualization
----------------------
Create a visualization
``````````````````````

To create an :code:`ee.Image` compatible with the visualization tool of SEPAL, add extra-properties to the :code:`ee.Image` using the :code:`set` method:

Expand Down Expand Up @@ -43,6 +51,9 @@ Each key of this dictionary should start with :code:`visualisation_<number>_<key
.. figure:: ../_image/tutorials/create_asset/all_viz_example.png
:alt: all viz example
:align: center

Visualization types
```````````````````

RGB false colors
----------------
Expand Down Expand Up @@ -164,6 +175,40 @@ and the corresponding display:
.. figure:: ../_image/tutorials/create_asset/viz_class.png
:alt: rgb display
:align: center


.. _geevector:

Set EE vector visualization
===========================

By default, when adding a vector (ee.Geometry, ee.Feature, ee.FeatureCollection) layer to a :code:`SepalMap` map, it will use pre-defined styles (features without fill color and primary color in the borders), which means, that if no visualization parameters (:code:`vis_params`) are passed, it will use these.

The visualization parameters are added as a second argument in the :code:`SepalMap().add_layer(layer, vis_params)` method, and it is receiving a dictionary. To check the available style parameters, please refer to the `ee.FeatureCollection style <https://developers.google.com/earth-engine/apidocs/ee-featurecollection-style>`_ documentation.


.. note::

By default, the :code:`fillColor` property is set as transparent. If you'd like to use the same color as the border but with 50% of opacity, just set the value of the property as :code:`None`, just as in the example below.

.. code-block:: python
geometry = ee.FeatureCollection(
ee.Geometry.Polygon(
[
[
[-103.198046875, 36.866172202843465],
[-103.198046875, 34.655531078083534],
[-100.385546875, 34.655531078083534],
[-100.385546875, 36.866172202843465],
]
],
)
)
map_ = sm.SepalMap()
map_.addLayer(geometry, {"color": "red", "fillColor": None})
.. spelling:word-list::
Expand Down
19 changes: 14 additions & 5 deletions sepal_ui/frontend/json/layer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
{
"stroke": true,
"weight": 2,
"opacity": 1,
"fill": true,
"fillOpacity": 0
"layer" : {
"stroke": true,
"weight": 2,
"opacity": 1,
"fill": true,
"fillOpacity": 0
},
"ee_layer" : {
"pointSize": 3,
"pointShape": "circle",
"width": 2,
"lineType": "solid",
"fillColor": "00000000"
}
}
74 changes: 45 additions & 29 deletions sepal_ui/mapping/sepal_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from matplotlib import colors as mpc
from rasterio.crs import CRS

from sepal_ui import color
from sepal_ui import color as scolors
from sepal_ui.frontend import styles as ss
from sepal_ui.mapping.basemaps import basemap_tiles
from sepal_ui.mapping.draw_control import DrawControl
Expand Down Expand Up @@ -493,16 +493,32 @@ def add_ee_layer(
viz_name (str, optional): the name of the vizaulization you want ot use. default to the first one if existing
"""

# check the type of the ee object and raise an error if it's not recognized
if not isinstance(
ee_object,
(
ee.Image,
ee.ImageCollection,
ee.FeatureCollection,
ee.Feature,
ee.Geometry,
),
):
raise AttributeError(
"\n\nThe image argument in 'addLayer' function must be an instance of "
"one of ee.Image, ee.Geometry, ee.Feature or ee.FeatureCollection."
)

# get the list of viz params
viz = self.get_viz_params(ee_object)

# get the requested vizparameters name
# if non is set use the first one
if not viz == {}:
if viz:
viz_name = viz_name or viz[next(iter(viz))]["name"]

# apply it to vis_params
if vis_params == {} and viz != {}:
if not vis_params and viz:

# find the viz params in the list
try:
Expand Down Expand Up @@ -585,22 +601,6 @@ def add_ee_layer(
layer_count = len(self.layers)
name = "Layer " + str(layer_count + 1)

# check the type of the ee object and raise an error if it's not recognized
if not isinstance(
ee_object,
(
ee.Image,
ee.ImageCollection,
ee.FeatureCollection,
ee.Feature,
ee.Geometry,
),
):
raise AttributeError(
"\n\nThe image argument in 'addLayer' function must be an instance of "
"one of ee.Image, ee.Geometry, ee.Feature or ee.FeatureCollection."
)

# force cast to featureCollection if needed
if isinstance(
ee_object,
Expand All @@ -610,17 +610,31 @@ def add_ee_layer(
ee.featurecollection.FeatureCollection,
),
):
default_vis = json.loads((ss.JSON_DIR / "layer.json").read_text())[
"ee_layer"
]
default_vis.update(color=scolors.primary)

features = ee.FeatureCollection(ee_object)
# We want to get all the default styles and only change those whose are
# in the provided visualization.
default_vis.update(vis_params)

width = vis_params.pop("width", 2)
color = vis_params.pop("color", "000000")
vis_params = default_vis

features = ee.FeatureCollection(ee_object)
const_image = ee.Image.constant(0.5)
image_fill = features.style(fillColor=color).updateMask(const_image)
image_outline = features.style(
color=color, fillColor="00000000", width=width
)

try:
image_fill = features.style(**vis_params).updateMask(const_image)
image_outline = features.style(**vis_params)

except AttributeError:
# Raise a more understandable error
raise AttributeError(
"You can only use the following styles: 'color', 'pointSize', "
"'pointShape', 'width', 'fillColor', 'styleProperty', "
"'neighborhood', 'lineType'"
)

image = image_fill.blend(image_outline)
obj = features
Expand Down Expand Up @@ -786,12 +800,14 @@ def add_layer(self, layer, hover=False):
if isinstance(layer, ipl.GeoJSON):

# define the default values
default_style = json.loads((ss.JSON_DIR / "layer.json").read_text())
default_style.update(color=color.primary)
default_style = json.loads((ss.JSON_DIR / "layer.json").read_text())[
"layer"
]
default_style.update(color=scolors.primary)
default_hover_style = json.loads(
(ss.JSON_DIR / "layer_hover.json").read_text()
)
default_hover_style.update(color=color.primary)
default_hover_style.update(color=scolors.primary)

# apply the style depending on the parameters
layer.style = layer.style or default_style
Expand Down
59 changes: 56 additions & 3 deletions tests/test_SepalMap.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,31 @@ def test_add_colorbar(self):

return

def test_add_ee_layer_exceptions(self):

map_ = sm.SepalMap()

# Test add a non ee map element
with pytest.raises(AttributeError):
map_.addLayer({})

# Test add a feature collection with invalid style
geometry = ee.FeatureCollection(
ee.Geometry.Polygon(
[
[
[-103.198046875, 36.866172202843465],
[-103.198046875, 34.655531078083534],
[-100.385546875, 34.655531078083534],
[-100.385546875, 36.866172202843465],
]
],
)
)

with pytest.raises(AttributeError):
map_.addLayer(geometry, {"invalid_propery": "red", "fillColor": None})

def test_add_ee_layer(self, asset_image_viz):

# create map and image
Expand All @@ -165,14 +190,42 @@ def test_add_ee_layer(self, asset_image_viz):

assert len(m.layers) == 2

# Test with vector

geometry = ee.FeatureCollection(
ee.Geometry.Polygon(
[
[
[-103.198046875, 36.866172202843465],
[-103.198046875, 34.655531078083534],
[-100.385546875, 34.655531078083534],
[-100.385546875, 36.866172202843465],
]
],
)
)

map_ = sm.SepalMap()
map_.addLayer(geometry, {"color": "red", "fillColor": None})

assert len(m.layers) == 2

return

def test_get_basemap_list(self):

# Retrieve 5 random maps
random_basemaps = [
"Esri.OceanBasemap",
"OpenStreetMap",
"HikeBike.HikeBike",
"HikeBike.HillShading",
"BasemapAT.orthofoto",
]

res = sm.SepalMap.get_basemap_list()

# last time I checked there were 128
assert len(res) == 131
assert all([bm in res for bm in random_basemaps])

return

Expand Down Expand Up @@ -290,7 +343,7 @@ def test_add_layer(self):
# Assert
new_layer = m.layers[-1]

layer_style = json.loads((ss.JSON_DIR / "layer.json").read_text())
layer_style = json.loads((ss.JSON_DIR / "layer.json").read_text())["layer"]
hover_style = json.loads((ss.JSON_DIR / "layer_hover.json").read_text())

assert all([new_layer.style[k] == v for k, v in layer_style.items()])
Expand Down

0 comments on commit 4359d45

Please sign in to comment.