Skip to content

Commit

Permalink
Merge pull request #23 from erick-otenyo/dev
Browse files Browse the repository at this point in the history
Implement setting legend type for raster style and selecting labeled values
  • Loading branch information
erick-otenyo authored Feb 29, 2024
2 parents 8ba3d94 + 43890b0 commit 6e8886b
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 200 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.10 on 2024-02-29 07:58

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('geomanager', '0045_additionalmapboundarydata_active_and_more'),
]

operations = [
migrations.AddField(
model_name='colorvalue',
name='show_on_legend',
field=models.BooleanField(default=True, verbose_name='Show label on Legend'),
),
migrations.AddField(
model_name='rasterstyle',
name='legend_type',
field=models.CharField(choices=[('basic', 'Basic'), ('choropleth', 'Choropleth'), ('gradient', 'Gradient')], default='choropleth', max_length=100, verbose_name='Legend Type'),
),
]
1 change: 1 addition & 0 deletions geomanager/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .vector_file import *
from .vector_tile import *
from .wms import *
from .raster_style import *

logger = logging.getLogger(__name__)

Expand Down
201 changes: 2 additions & 199 deletions geomanager/models/raster_file.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,30 @@
import os

from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django_extensions.db.models import TimeStampedModel
from modelcluster.fields import ParentalKey
from modelcluster.models import ClusterableModel
from wagtail.admin.panels import FieldPanel, FieldRowPanel, MultiFieldPanel, InlinePanel
from wagtail.admin.panels import FieldPanel, MultiFieldPanel
from wagtail.api.v2.utils import get_full_url
from wagtail.fields import StreamField
from wagtail.images.blocks import ImageChooserBlock
from wagtail.images.models import Image
from wagtail.models import Orderable
from wagtail_color_panel.edit_handlers import NativeColorPanel
from wagtail_color_panel.fields import ColorField
from wagtail_modeladmin.helpers import AdminURLHelper

from geomanager.blocks import (
FileLayerPointAnalysisBlock,
FileLayerAreaAnalysisBlock, InlineLegendBlock
)
from geomanager.forms import RasterStyleModelForm
from geomanager.helpers import get_raster_layer_files_url
from geomanager.models.raster_style import RasterStyle
from geomanager.models.core import Dataset, BaseLayer
from geomanager.settings import geomanager_settings
from geomanager.storage import OverwriteStorage
from geomanager.utils import DATE_FORMAT_CHOICES
from geomanager.validators import validate_directory_name
from geomanager.widgets import RasterStyleWidget


class RasterFileLayer(TimeStampedModel, BaseLayer):
Expand Down Expand Up @@ -374,193 +367,3 @@ def __str__(self):
return f"{self.dataset} - {self.created}"


class RasterStyle(TimeStampedModel, ClusterableModel):
base_form_class = RasterStyleModelForm

name = models.CharField(max_length=256, verbose_name=_("name"),
help_text=_("Style name for identification"))
unit = models.CharField(max_length=100, blank=True, null=True, verbose_name=_("data unit"),
help_text=_("Data unit"))
min = models.IntegerField(default=0, verbose_name=_("minimum value"), help_text=_("minimum value"))
max = models.IntegerField(default=100, verbose_name=_("maximum value"), help_text=_("maximum value"))
steps = models.IntegerField(default=5, validators=[MinValueValidator(3), MaxValueValidator(20), ], null=True,
blank=True, verbose_name=_("steps"), help_text=_("Number of steps"))
use_custom_colors = models.BooleanField(default=False, verbose_name=_("Use Custom Colors"))
palette = models.TextField(blank=True, null=True, verbose_name=_("Color Palette"))
interpolate = models.BooleanField(default=False, verbose_name=_("interpolate"), help_text="Interpolate colorscale")
custom_color_for_rest = ColorField(blank=True, null=True, default="#ff0000",
verbose_name=_("Color for the rest of values"),
help_text=_(
"Color for values greater than the values defined above, "
"as well as values greater than the maximum defined value"))

class Meta:
verbose_name = _("Raster Style")
verbose_name_plural = _("Raster Styles")

def __str__(self):
return self.name

panels = [
FieldPanel("name"),
FieldPanel("unit"),
FieldRowPanel(
[
FieldPanel("min"),
FieldPanel("max"),
], _("Data values")
),
FieldPanel("steps"),
FieldPanel("palette", widget=RasterStyleWidget),
FieldPanel("use_custom_colors"),
MultiFieldPanel([
InlinePanel("color_values", heading=_("Color Values"), label=_("Color Value")),
NativeColorPanel("custom_color_for_rest"),
], _("Custom Color Values")),

# FieldPanel("interpolate")
]

def get_palette_list(self):
if not self.use_custom_colors:
return self.palette.split(",")
return self.get_custom_palette()

@property
def min_value(self):
return self.min

@property
def max_value(self):
max_value = self.max
if self.min == max_value:
max_value += 0.1
return max_value

@property
def scale_value(self):
return 254 / (self.max_value - self.min_value)

@property
def offset_value(self):
return -self.min_value

@property
def clip_value(self):
return self.max_value + self.offset_value

def get_custom_color_values(self):
values = []
color_values = self.color_values.order_by('threshold')

for i, c_value in enumerate(color_values):
value = c_value.value
# if not the first one, add prev value for later comparison
if i == 0:
value["min_value"] = None
else:
value["min_value"] = color_values[i - 1].threshold
value["max_value"] = value["threshold"]
values.append(value)
return values

def get_custom_palette(self):
colors = []
for i in range(256):
color = self.get_color_for_index(i)
colors.append(color)

return colors

def get_color_for_index(self, index_value):
values = self.get_custom_color_values()

for value in values:
max_value = value["max_value"] + self.offset_value

if max_value > self.clip_value:
max_value = self.clip_value

if max_value < 0:
max_value = 0

max_value = self.scale_value * max_value

if value["min_value"] is None:
if index_value <= max_value:
return values[0]["color"]

if value["min_value"] is not None:
min_value = value["min_value"] + self.offset_value

if min_value > self.clip_value:
min_value = self.clip_value

if min_value < 0:
min_value = 0

min_value = self.scale_value * min_value

if min_value < index_value <= max_value:
return value["color"]

return self.custom_color_for_rest

def get_style_as_json(self):
palette = self.get_palette_list()
style = {
"bands": [
{
"band": 1,
"min": self.min,
"max": self.max,
"palette": palette,
"scheme": "discrete",
}
]
}
return style

def get_legend_config(self):
items = []
if self.use_custom_colors:
values = self.get_custom_color_values()
count = len(values)

if count > 1:
for value in values:
item = {
"name": value['label'] if value.get('label') else value['threshold'],
"color": value['color']
}
items.append(item)
rest_item = {"name": "", "color": self.custom_color_for_rest}
items.append(rest_item)

return {"type": "choropleth", "items": items}


class ColorValue(TimeStampedModel, Orderable):
layer = ParentalKey(RasterStyle, related_name='color_values')
threshold = models.FloatField(verbose_name=_("Threshold value"), help_text=_(
"Values less than or equal to the input value, will be assigned the chosen color"))
color = ColorField(default="#ff0000", verbose_name=_("color"))
label = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('Optional Label'))

class Meta:
verbose_name = _("Color Value")
verbose_name_plural = _("Color Values")

panels = [
FieldPanel("threshold"),
NativeColorPanel("color"),
FieldPanel("label")
]

@property
def value(self):
return {
"threshold": self.threshold,
"color": self.color,
"label": self.label
}
Loading

0 comments on commit 6e8886b

Please sign in to comment.