Skip to content

Commit

Permalink
Adding title and abstract to raster styles
Browse files Browse the repository at this point in the history
Showing configured raster styles in WMS capabilities
Adding command to link RasterStyle to BrowseTypes
  • Loading branch information
constantinius committed Sep 15, 2023
1 parent b6752d6 commit 0e87b7c
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 50 deletions.
15 changes: 13 additions & 2 deletions eoxserver/render/browse/defaultstyles.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
from eoxserver.render.colors import COLOR_SCALES
from eoxserver.render.browse.objects import RasterStyle, RasterStyleColorEntry
from eoxserver.render.colors import COLOR_SCALES, BASE_COLORS
from eoxserver.render.browse.objects import (
GeometryStyle,
RasterStyle,
RasterStyleColorEntry,
)

DEFAULT_RASTER_STYLES = {}
DEFAULT_GEOMETRY_STYLES = {}

for name, entries in COLOR_SCALES.items():
DEFAULT_RASTER_STYLES[name] = RasterStyle(
name,
name,
name,
"ramp",
[
RasterStyleColorEntry(i, color)
for i, color in entries
]
)


for name in BASE_COLORS.keys():
DEFAULT_GEOMETRY_STYLES[name] = GeometryStyle(name, name, name)
33 changes: 27 additions & 6 deletions eoxserver/render/browse/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,16 +329,35 @@ def from_models(cls, product_model, browse_model, mask_model,
)


class RasterStyle(object):
def __init__(self, name, type, entries):
class BaseStyle(object):
def __init__(self, name, title, abstract):
self._name = name
self._type = type
self._entries = entries
self._title = title or ''
self._abstract = abstract or ''

@property
def name(self):
return self._name

@property
def title(self):
return self._title

@property
def abstract(self):
return self._abstract


class GeometryStyle(BaseStyle):
pass


class RasterStyle(BaseStyle):
def __init__(self, name, type, title, abstract, entries):
super().__init__(name, title, abstract)
self._type = type
self._entries = entries

@property
def type(self):
return self._type
Expand All @@ -348,10 +367,12 @@ def entries(self):
return self._entries

@classmethod
def from_model(cls, raster_style_model):
def from_model(cls, raster_style_model, name=None):
return cls(
raster_style_model.name,
name or raster_style_model.name,
raster_style_model.type,
raster_style_model.title,
raster_style_model.abstract,
[
RasterStyleColorEntry.from_model(entry_model)
for entry_model in raster_style_model.color_entries.all()
Expand Down
12 changes: 8 additions & 4 deletions eoxserver/render/mapserver/map_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@

from eoxserver.contrib import mapserver as ms
from eoxserver.contrib import vsi
from eoxserver.render.colors import BASE_COLORS, COLOR_SCALES
from eoxserver.render.mapserver.factories import BaseMapServerLayerFactory, get_layer_factories
from eoxserver.render.browse.defaultstyles import (
DEFAULT_RASTER_STYLES, DEFAULT_GEOMETRY_STYLES
)
from eoxserver.render.mapserver.factories import (
BaseMapServerLayerFactory, get_layer_factories
)
from eoxserver.render.map.objects import Map, Layer
from eoxserver.resources.coverages.formats import getFormatRegistry

Expand All @@ -56,10 +60,10 @@ class MapserverMapRenderer(object):
]

def get_geometry_styles(self):
return BASE_COLORS.keys()
return list(DEFAULT_GEOMETRY_STYLES.values())

def get_raster_styles(self):
return COLOR_SCALES.keys()
return list(DEFAULT_RASTER_STYLES.values())

def get_supported_layer_types(self):
layer_types = []
Expand Down
7 changes: 6 additions & 1 deletion eoxserver/resources/coverages/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,13 @@ class RasterStyleColorEntryInline(admin.TabularInline):
extra = 0


class RasterStyleToBrowseTypeThroughInline(admin.TabularInline):
model = models.RasterStyleToBrowseTypeThrough
extra = 0


class RasterStyleAdmin(admin.ModelAdmin):
inlines = [RasterStyleColorEntryInline]
inlines = [RasterStyleToBrowseTypeThroughInline, RasterStyleColorEntryInline]


admin.site.register(models.RasterStyle, RasterStyleAdmin)
28 changes: 25 additions & 3 deletions eoxserver/resources/coverages/management/commands/rasterstyle.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ def add_arguments(self, parser):
help="Rename a style from a name to another name"
)

link_parser.add_argument(
'product_type_name', nargs=1, help=''
)
link_parser.add_argument(
'browse_type_name', nargs=1, help=''
)
link_parser.add_argument(
'style_name', nargs=1, help=''
)

@transaction.atomic
def handle(self, subcommand, *args, **kwargs):
""" Dispatch sub-commands: create, delete, list, link.
Expand All @@ -89,7 +99,14 @@ def handle(self, subcommand, *args, **kwargs):
elif subcommand == "list":
self.handle_list(*args, **kwargs)
elif subcommand == "link":
self.handle_link(kwargs.pop('name')[0], *args, **kwargs)
style_name = kwargs.pop('style_name')
self.handle_link(
kwargs.pop('name')[0],
kwargs.pop('product_type_name')[0],
kwargs.pop('browse_type_name')[0],
style_name[0] if style_name else None,
*args, **kwargs
)

def handle_create(self, name, discrete, color_entries, from_sld,
*args, **kwargs):
Expand Down Expand Up @@ -181,15 +198,20 @@ def handle_list(self, *args, **kwargs):
entry.value, entry.color, entry.opacity, entry.label
))

def handle_link(self, name, product_type_name, browse_type_name, *args, **kwargs):
def handle_link(self, name, product_type_name, browse_type_name, stylename,
*args, **kwargs):
""" Handle the linking of raster styles to browse types
"""
raster_style = models.RasterStyle.objects.get(name=name)
browse_type = models.BrowseType.objects.get(
product_type__name=product_type_name,
name=browse_type_name
)
raster_style.browse_types.add(browse_type)
models.RasterStyleToBrowseTypeThrough.objects.create(
raster_style=raster_style,
browse_type=browse_type,
style_name=stylename,
)
print('Successfully linked raster style %r to browse type %r/%r' % (
name, product_type_name, browse_type_name
))
24 changes: 21 additions & 3 deletions eoxserver/resources/coverages/migrations/0013_raster_style.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 3.2.18 on 2023-09-08 08:48
# Generated by Django 3.2.18 on 2023-09-08 17:26

import django.core.validators
from django.db import migrations, models
Expand All @@ -17,11 +17,29 @@ class Migration(migrations.Migration):
name='RasterStyle',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=256)),
('name', models.CharField(max_length=256, unique=True)),
('type', models.CharField(choices=[('ramp', 'ramp'), ('values', 'values'), ('intervals', 'intervals')], max_length=16)),
('browse_types', models.ManyToManyField(related_name='raster_styles', to='coverages.BrowseType')),
('title', models.TextField(blank=True, null=True)),
('abstract', models.TextField(blank=True, null=True)),
],
),
migrations.CreateModel(
name='RasterStyleToBrowseTypeThrough',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('style_name', models.CharField(blank=True, max_length=256, null=True)),
('browse_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='coverages.browsetype')),
('raster_style', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='coverages.rasterstyle')),
],
options={
'unique_together': {('raster_style', 'browse_type', 'style_name')},
},
),
migrations.AddField(
model_name='rasterstyle',
name='browse_types',
field=models.ManyToManyField(related_name='raster_styles', through='coverages.RasterStyleToBrowseTypeThrough', to='coverages.BrowseType'),
),
migrations.CreateModel(
name='RasterStyleColorEntry',
fields=[
Expand Down
16 changes: 14 additions & 2 deletions eoxserver/resources/coverages/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,18 +223,30 @@ class RasterStyle(models.Model):
("values", "values"),
("intervals", "intervals"),
)
browse_types = models.ManyToManyField(BrowseType, related_name="raster_styles")
browse_types = models.ManyToManyField(BrowseType, related_name="raster_styles", through="RasterStyleToBrowseTypeThrough")
name = models.CharField(max_length=256, unique=True, **mandatory)
type = models.CharField(max_length=16, choices=TYPE_CHOICES, **mandatory)
title = models.TextField(**optional)
abstract = models.TextField(**optional)

def __str__(self):
return self.name


RGB_REGEX = re.compile('^#?((?:[0-F]{3}){1,2})$', re.IGNORECASE)
class RasterStyleToBrowseTypeThrough(models.Model):
raster_style = models.ForeignKey(RasterStyle, on_delete=models.CASCADE)
browse_type = models.ForeignKey(BrowseType, on_delete=models.CASCADE)
style_name = models.CharField(max_length=256, **optional)

class Meta:
unique_together = (
('raster_style', 'browse_type', 'style_name'),
)


class RasterStyleColorEntry(models.Model):
RGB_REGEX = re.compile('^#?((?:[0-F]{3}){1,2})$', re.IGNORECASE)

Check warning

Code scanning / CodeQL

Overly permissive regular expression range Medium

Suspicious character range that is equivalent to \[0-9:;<=>?@A-F\].

raster_style = models.ForeignKey(RasterStyle, related_name="color_entries", on_delete=models.CASCADE)
value = models.FloatField()
color = models.CharField(max_length=7, validators=[RegexValidator(RGB_REGEX)])
Expand Down
60 changes: 40 additions & 20 deletions eoxserver/services/ows/wms/layermapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
from eoxserver.render.coverage.objects import Coverage as RenderCoverage
from eoxserver.render.coverage.objects import Mosaic as RenderMosaic
from eoxserver.render.browse.objects import (
Browse, GeneratedBrowse, Mask, MaskedBrowse, DEFAULT_EOXS_LAYER_SUFFIX_SEPARATOR
Browse, GeneratedBrowse, Mask, MaskedBrowse, DEFAULT_EOXS_LAYER_SUFFIX_SEPARATOR,
RasterStyle,
)
from eoxserver.resources.coverages import models

Expand All @@ -59,7 +60,6 @@ class NoSuchPrefix(NoSuchLayer):
locator = 'layer'



class LayerMapper(object):
""" Default layer mapper.
"""
Expand All @@ -73,13 +73,13 @@ def __init__(self, supported_layer_types, suffix_separator=None):
)
self.suffix_separator = suffix_separator

def get_layer_description(self, eo_object, raster_styles, geometry_styles):
def get_layer_description(self, eo_object, default_raster_styles, default_geometry_styles):
if isinstance(eo_object, models.Coverage):
coverage = RenderCoverage.from_model(eo_object)
return LayerDescription.from_coverage(coverage, raster_styles)
return LayerDescription.from_coverage(coverage, default_raster_styles)
elif isinstance(eo_object, models.Mosaic):
coverage = RenderCoverage.from_model(eo_object)
return LayerDescription.from_mosaic(coverage, raster_styles)
return LayerDescription.from_mosaic(coverage, default_raster_styles)
elif isinstance(eo_object, (models.Product, models.Collection)):
dimensions = {}
if eo_object.begin_time and eo_object.end_time:
Expand All @@ -105,13 +105,6 @@ def get_layer_description(self, eo_object, raster_styles, geometry_styles):
browse_type_qs = models.BrowseType.objects.none()
mask_type_qs = models.MaskType.objects.none()

browse_types_name_and_is_gray = browse_type_qs.annotate(
is_gray=Case(
When(green_expression__isnull=True, then=Value(True)),
default=Value(False),
output_field=BooleanField()
)
).values_list('name', 'is_gray').distinct()
mask_type_names = mask_type_qs.values_list(
'name', flat=True
).distinct()
Expand All @@ -121,27 +114,43 @@ def get_layer_description(self, eo_object, raster_styles, geometry_styles):
"%s%soutlines" % (
eo_object.identifier, self.suffix_separator
),
styles=geometry_styles,
styles=default_geometry_styles,
queryable=True,
dimensions=dimensions,
),
LayerDescription(
"%s%soutlined" % (
eo_object.identifier, self.suffix_separator
),
styles=geometry_styles,
styles=default_geometry_styles,
queryable=True,
dimensions=dimensions,
)
]
for name, is_gray in browse_types_name_and_is_gray:
for browse_type in browse_type_qs:
used_styles = []
raster_style_throughs = \
models.RasterStyleToBrowseTypeThrough.objects.filter(
browse_type=browse_type
).prefetch_related("raster_style")

used_styles = [
RasterStyle.from_model(
raster_style_through.raster_style,
raster_style_through.style_name
)
for raster_style_through in raster_style_throughs
]

is_gray = not bool(browse_type.green_expression)

sub_layers.append(
LayerDescription(
"%s%s%s" % (
eo_object.identifier, self.suffix_separator,
name
) if name else eo_object.identifier,
styles=raster_styles if is_gray else [],
browse_type.name
) if browse_type.name else eo_object.identifier,
styles=used_styles or default_raster_styles if is_gray else [],
dimensions=dimensions,
)
)
Expand All @@ -153,7 +162,7 @@ def get_layer_description(self, eo_object, raster_styles, geometry_styles):
eo_object.identifier, self.suffix_separator,
mask_type_name
),
styles=geometry_styles,
styles=default_geometry_styles,
dimensions=dimensions,
)
)
Expand Down Expand Up @@ -610,7 +619,8 @@ class LayerMapperConfigReader(config.Reader):
color = config.Option(type=str, default='grey')


def _generate_browse_from_browse_type(product: models.Product, browse_type: models.BrowseType, variables):
def _generate_browse_from_browse_type(
product: models.Product, browse_type: models.BrowseType, variables):
if not browse_type.red_or_grey_expression:
return None

Expand Down Expand Up @@ -657,6 +667,15 @@ def _generate_browse_from_browse_type(product: models.Product, browse_type: mode
nodata_values.append(browse_type.alpha_nodata_value)
field_names.extend(alpha_bands)

raster_styles = {}
raster_style_througs = models.RasterStyleToBrowseTypeThrough.objects.filter(
browse_type=browse_type
).prefetch_related("raster_style")
for raster_style_throug in raster_style_througs:
raster_styles[raster_style_throug.style_name] = RasterStyle.from_model(
raster_style_throug.raster_style, raster_style_throug.style_name
)

coverages, fields_and_coverages = _lookup_coverages(product, field_names)

# only return a browse instance if coverages were found
Expand All @@ -668,6 +687,7 @@ def _generate_browse_from_browse_type(product: models.Product, browse_type: mode
fields_and_coverages,
product,
variables,
raster_styles,
show_out_of_bounds_data=browse_type.show_out_of_bounds_data,
)
return None
Expand Down
Loading

0 comments on commit 0e87b7c

Please sign in to comment.