Skip to content

Commit

Permalink
Merge pull request pblottiere#42 from pblottiere/temporal
Browse files Browse the repository at this point in the history
[draft] Add support for WMS TIME parameter
  • Loading branch information
pblottiere authored Jun 26, 2024
2 parents 9bf9a4a + d37625f commit 6ab1146
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 19 deletions.
19 changes: 18 additions & 1 deletion qsa-api/qsa_api/api/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from jsonschema.exceptions import ValidationError
from flask import send_file, Blueprint, jsonify, request

from qgis.PyQt.QtCore import QDateTime

from ..wms import WMS
from ..project import QSAProject

Expand Down Expand Up @@ -271,6 +273,7 @@ def project_add_layer(name):
"crs": {"type": "number"},
"type": {"type": "string"},
"overview": {"type": "boolean"},
"datetime": {"type": "string"},
},
}

Expand All @@ -292,8 +295,22 @@ def project_add_layer(name):
if "overview" in data:
overview = data["overview"]

datetime = None
if "datetime" in data:
# check format "yyyy-MM-dd HH:mm:ss"
datetime = QDateTime.fromString(
data["datetime"], "yyyy-MM-dd HH:mm:ss"
)
if not datetime.isValid():
return {"error": "Invalid datetime"}, 415

rc, err = project.add_layer(
data["datasource"], data["type"], data["name"], crs, overview
data["datasource"],
data["type"],
data["name"],
crs,
overview,
datetime,
)
if rc:
return jsonify(rc), 201
Expand Down
8 changes: 4 additions & 4 deletions qsa-api/qsa_api/api/symbology.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ def symbology_raster_singlebandpseudocolor():
props = {}
props["band"] = {"band": 1, "min": 0.0, "max": 1.0}
props["ramp"] = {
"name" : f"Spectral ({ramps})",
"color1": "0,0,0,255",
"color2": "255,255,255,255",
"interpolation": "Linear (Linear, Discrete, Exact)"
"name": f"Spectral ({ramps})",
"color1": "0,0,0,255",
"color2": "255,255,255,255",
"interpolation": "Linear (Linear, Discrete, Exact)",
}
props["contrast_enhancement"] = {
"limits_min_max": "MinMax (MinMax, UserDefined)",
Expand Down
1 change: 1 addition & 0 deletions qsa-api/qsa_api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def __init__(self) -> None:
def run(self):
app.run(host="0.0.0.0", threaded=False)


qsa = QSA()


Expand Down
18 changes: 17 additions & 1 deletion qsa-api/qsa_api/mapproxy/mapproxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import shutil
from pathlib import Path

from qgis.PyQt.QtCore import Qt, QDateTime

from ..utils import config, qgisserver_base_url


Expand Down Expand Up @@ -50,7 +52,12 @@ def clear_cache(self, layer_name: str) -> None:
shutil.rmtree(d)

def add_layer(
self, name: str, bbox: list, srs: int, is_raster: bool
self,
name: str,
bbox: list,
srs: int,
is_raster: bool,
datetime: QDateTime | None,
) -> (bool, str):
if self.cfg is None:
return False, "Invalid MapProxy configuration"
Expand All @@ -61,6 +68,12 @@ def add_layer(
self.cfg["sources"] = {}

lyr = {"name": name, "title": name, "sources": [f"{name}_cache"]}
if datetime and is_raster:
lyr["dimensions"] = {}
lyr["dimensions"]["time"] = {
"values": [datetime.toString(Qt.ISODate)]
}

self.cfg["layers"].append(lyr)

c = {"grids": ["webmercator"], "sources": [f"{name}_wms"]}
Expand All @@ -79,6 +92,9 @@ def add_layer(
},
"coverage": {"bbox": bbox, "srs": f"EPSG:{srs}"},
}
if datetime and is_raster:
s["forward_req_params"] = ["TIME"]

self.cfg["sources"][f"{name}_wms"] = s

return True, ""
Expand Down
16 changes: 14 additions & 2 deletions qsa-api/qsa_api/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import sqlite3
from pathlib import Path

from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtCore import Qt, QDateTime
from qgis.core import (
Qgis,
QgsSymbol,
Expand All @@ -18,12 +18,14 @@
QgsVectorLayer,
QgsRasterLayer,
QgsMarkerSymbol,
QgsDateTimeRange,
QgsRasterMinMaxOrigin,
QgsContrastEnhancement,
QgsSingleSymbolRenderer,
QgsSimpleFillSymbolLayer,
QgsSimpleLineSymbolLayer,
QgsSimpleMarkerSymbolLayer,
QgsRasterLayerTemporalProperties,
)

from .mapproxy import QSAMapProxy
Expand Down Expand Up @@ -321,6 +323,7 @@ def add_layer(
name: str,
epsg_code: int,
overview: bool,
datetime: QDateTime | None,
) -> (bool, str):
t = self._layer_type(layer_type)
if t is None:
Expand Down Expand Up @@ -349,6 +352,15 @@ def add_layer(
return False, err
else:
self.debug("Overviews already exist")

if datetime:
self.debug("Activate temporal properties")
mode = QgsRasterLayerTemporalProperties.ModeFixedTemporalRange
props = lyr.temporalProperties()
props.setMode(mode)
dt_range = QgsDateTimeRange(datetime, datetime)
props.setFixedTemporalRange(dt_range)
props.setIsActive(True)
else:
return False, "Invalid layer type"

Expand Down Expand Up @@ -403,7 +415,7 @@ def add_layer(
return False, err

rc, err = mp.add_layer(
name, bbox, epsg_code, t == Qgis.LayerType.Raster
name, bbox, epsg_code, t == Qgis.LayerType.Raster, datetime
)
if not rc:
return False, err
Expand Down
50 changes: 39 additions & 11 deletions qsa-api/qsa_api/raster/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
class RasterSymbologyRenderer:
class Type(Enum):
SINGLE_BAND_GRAY = QgsSingleBandGrayRenderer(None, 1).type()
SINGLE_BAND_PSEUDOCOLOR = QgsSingleBandPseudoColorRenderer(None, 1).type()
SINGLE_BAND_PSEUDOCOLOR = QgsSingleBandPseudoColorRenderer(
None, 1
).type()
MULTI_BAND_COLOR = QgsMultiBandColorRenderer(None, 1, 1, 1).type()

def __init__(self, name: str) -> None:
Expand All @@ -46,7 +48,9 @@ def __init__(self, name: str) -> None:
self.renderer = QgsSingleBandGrayRenderer(None, 1)
elif name == RasterSymbologyRenderer.Type.MULTI_BAND_COLOR.value:
self.renderer = QgsMultiBandColorRenderer(None, 1, 1, 1)
elif name == RasterSymbologyRenderer.Type.SINGLE_BAND_PSEUDOCOLOR.value:
elif (
name == RasterSymbologyRenderer.Type.SINGLE_BAND_PSEUDOCOLOR.value
):
self.renderer = QgsSingleBandPseudoColorRenderer(None, 1)

@property
Expand Down Expand Up @@ -127,7 +131,10 @@ def style_to_json(path: Path) -> (dict, str):
props = RasterSymbologyRenderer._multibandcolor_properties(
renderer
)
elif renderer_type == RasterSymbologyRenderer.Type.SINGLE_BAND_PSEUDOCOLOR:
elif (
renderer_type
== RasterSymbologyRenderer.Type.SINGLE_BAND_PSEUDOCOLOR
):
props = RasterSymbologyRenderer._singlebandpseudocolor_properties(
renderer
)
Expand Down Expand Up @@ -253,8 +260,12 @@ def _singlebandpseudocolor_properties(renderer) -> dict:

props["ramp"] = {}
shader_fct = renderer.shader().rasterShaderFunction()
color_1 = shader_fct.sourceColorRamp().properties()["color1"].split("rgb")[0]
color_2 = shader_fct.sourceColorRamp().properties()["color2"].split("rgb")[0]
color_1 = (
shader_fct.sourceColorRamp().properties()["color1"].split("rgb")[0]
)
color_2 = (
shader_fct.sourceColorRamp().properties()["color2"].split("rgb")[0]
)
props["ramp"]["color1"] = color_1
props["ramp"]["color2"] = color_2

Expand Down Expand Up @@ -294,21 +305,30 @@ def _refresh_min_max_multibandcolor(self, layer: QgsRasterLayer) -> None:
if min_max_origin == QgsRasterMinMaxOrigin.Limits.MinMax:
red_band = renderer.redBand()
red_stats = layer.dataProvider().bandStatistics(
red_band, QgsRasterBandStats.Min | QgsRasterBandStats.Max, layer.extent(), 250000
red_band,
QgsRasterBandStats.Min | QgsRasterBandStats.Max,
layer.extent(),
250000,
)
red_ce.setMinimumValue(red_stats.minimumValue)
red_ce.setMaximumValue(red_stats.maximumValue)

green_band = renderer.greenBand()
green_stats = layer.dataProvider().bandStatistics(
green_band, QgsRasterBandStats.Min | QgsRasterBandStats.Max, layer.extent(), 250000
green_band,
QgsRasterBandStats.Min | QgsRasterBandStats.Max,
layer.extent(),
250000,
)
green_ce.setMinimumValue(green_stats.minimumValue)
green_ce.setMaximumValue(green_stats.maximumValue)

blue_band = renderer.blueBand()
blue_stats = layer.dataProvider().bandStatistics(
blue_band, QgsRasterBandStats.Min | QgsRasterBandStats.Max, layer.extent(), 250000
blue_band,
QgsRasterBandStats.Min | QgsRasterBandStats.Max,
layer.extent(),
250000,
)
blue_ce.setMinimumValue(blue_stats.minimumValue)
blue_ce.setMaximumValue(blue_stats.maximumValue)
Expand All @@ -333,21 +353,29 @@ def _refresh_min_max_singlebandgray(self, layer: QgsRasterLayer) -> None:
if min_max_origin == QgsRasterMinMaxOrigin.Limits.MinMax:
# Accuracy : estimate
stats = layer.dataProvider().bandStatistics(
1, QgsRasterBandStats.Min | QgsRasterBandStats.Max, layer.extent(), 250000
1,
QgsRasterBandStats.Min | QgsRasterBandStats.Max,
layer.extent(),
250000,
)

ce.setMinimumValue(stats.minimumValue)
ce.setMaximumValue(stats.maximumValue)

layer.renderer().setContrastEnhancement(ce)

def _refresh_min_max_singlebandpseudocolor(self, layer: QgsRasterLayer) -> None:
def _refresh_min_max_singlebandpseudocolor(
self, layer: QgsRasterLayer
) -> None:
# compute min/max
min_max_origin = layer.renderer().minMaxOrigin().limits()
if min_max_origin == QgsRasterMinMaxOrigin.Limits.MinMax:
# Accuracy : estimate
stats = layer.dataProvider().bandStatistics(
1, QgsRasterBandStats.Min | QgsRasterBandStats.Max, layer.extent(), 250000
1,
QgsRasterBandStats.Min | QgsRasterBandStats.Max,
layer.extent(),
250000,
)

layer.renderer().setClassificationMin(stats.minimumValue)
Expand Down

0 comments on commit 6ab1146

Please sign in to comment.