QGIS Temporal Controller z danymi rastrowymi
Począwszy od QGIS 3.14 TimeManager jest martwy, niech żyje kontroler czasowy! Istnieje wiele samouczków, jak „Temporal Controller” radzi sobie z warstwami wektorowymi, co wygląda ładnie. Jednak dużo pracuję z danymi rastrowymi i nie mogę znaleźć żadnych informacji, jak uzyskać podobne zachowanie jak w TimeManager.
Mam taki NetCDF:
ncdump -h SLA_ts.nc
netcdf SLA_ts {
dimensions:
time = 124 ;
lat = 5 ;
lon = 5 ;
variables:
char crs ;
crs:grid_mapping_name = "latitude_longitude" ;
crs:long_name = "CRS definition" ;
crs:semi_major_axis = 6378136.3 ;
crs:inverse_flattening = 298.257 ;
float time(time) ;
time:long_name = "time" ;
time:calendar = "standard" ;
time:units = "days since 1858-11-17 00:00:00.0" ;
time:actual_range = 52244., 55987.735625 ;
float lat(lat) ;
lat:standard_name = "latitude" ;
lat:long_name = "latitude" ;
lat:units = "degrees_north" ;
lat:actual_range = 53.8, 54.2 ;
float lon(lon) ;
lon:standard_name = "longitude" ;
lon:long_name = "longitude" ;
lon:units = "degrees_east" ;
lon:actual_range = 7.5, 7.9 ;
float sla(time, lat, lon) ;
sla:_FillValue = NaNf ;
sla:grid_mapping = "crs" ;
sla:long_name = "sea level anomaly" ;
sla:unit = "m" ;
// global attributes:
:Conventions = "CF-1.5" ;
}
Kiedy ładuję ten plik do QGIS, kroki czasowe są ładowane jako pasma (w zakładce Symbolika są wyświetlane jako Band 1: time=52244 (days since 1858-11-17 00:00:00)i tak dalej ...). Jednak karta Temporal-Tab oferuje tylko stały zakres czasu, jak pokazano na zrzucie ekranu:
Spodziewałbym się, że kontroler czasowy automatycznie rozpozna, że warstwy są krokami czasowymi, ale przycisk „Automatyczny” jest nieaktywny. Czy ktoś ma jakieś doświadczenie z warstwami rastrowymi w „Temporal Controller”? Jak mogę zmodyfikować moje dane, aby były poprawnie interpretowane? A może ta funkcja po prostu nie została zaimplementowana (ponieważ kontroler czasowy jest całkiem nowy)?
Odpowiedzi
Napisałem skrypt PyQGIS do obsługi obejścia dla plików NetCDF, które używają updateTemporalRangesygnału kontrolera czasowego . Po prostu skopiuj poniższy skrypt do okna edytora QGIS Python, edytuj daty, uruchom skrypt i jak zwykle użyj kontrolera Temporal Controller.
import datetime
# These functions are part of https://github.com/GispoCoding/qgis_plugin_tools/blob/master/tools/raster_layers.py
def set_raster_renderer_to_singleband(layer: QgsRasterLayer, band: int = 1) -> None:
"""
Set raster renderer to singleband
:param layer: raster layer
"""
# https://gis.stackexchange.com/a/377631/123927 and https://gis.stackexchange.com/a/157573/123927
provider: QgsRasterDataProvider = layer.dataProvider()
renderer: QgsSingleBandGrayRenderer = QgsSingleBandGrayRenderer(layer.dataProvider(), band)
stats: QgsRasterBandStats = provider.bandStatistics(band, QgsRasterBandStats.All, layer.extent(), 0)
min_val = max(stats.minimumValue, 0)
max_val = max(stats.maximumValue, 0)
enhancement = QgsContrastEnhancement(renderer.dataType(band))
contrast_enhancement = QgsContrastEnhancement.StretchToMinimumMaximum
enhancement.setContrastEnhancementAlgorithm(contrast_enhancement, True)
enhancement.setMinimumValue(min_val)
enhancement.setMaximumValue(max_val)
layer.setRenderer(renderer)
layer.renderer().setContrastEnhancement(enhancement)
layer.triggerRepaint()
def set_band_based_on_range(layer: QgsRasterLayer, t_range: QgsDateTimeRange) -> int:
"""
:param layer: raster layer
:param t_range: temporal range
:return: band number
"""
band_num = 1
tprops: QgsRasterLayerTemporalProperties = layer.temporalProperties()
if tprops.isVisibleInTemporalRange(t_range) and t_range.begin().isValid() and t_range.end().isValid():
if tprops.mode() == QgsRasterLayerTemporalProperties.ModeFixedTemporalRange:
layer_t_range: QgsDateTimeRange = tprops.fixedTemporalRange()
start: datetime.datetime = layer_t_range.begin().toPyDateTime()
end: datetime.datetime = layer_t_range.end().toPyDateTime()
delta = (end - start) / layer.bandCount()
band_num = int((t_range.begin().toPyDateTime() - start) / delta) + 1
set_raster_renderer_to_singleband(layer, band_num)
return band_num
def set_fixed_temporal_range(layer: QgsRasterLayer, t_range: QgsDateTimeRange) -> None:
"""
Set fixed temporal range for raster layer
:param layer: raster layer
:param t_range: fixed temporal range
"""
mode = QgsRasterLayerTemporalProperties.ModeFixedTemporalRange
tprops: QgsRasterLayerTemporalProperties = layer.temporalProperties()
tprops.setMode(mode)
if t_range.begin().timeSpec() == 0 or t_range.end().timeSpec() == 0:
begin = t_range.begin()
end = t_range.end()
begin.setTimeSpec(Qt.TimeSpec(1))
end.setTimeSpec(Qt.TimeSpec(1))
t_range = QgsDateTimeRange(begin, end)
tprops.setFixedTemporalRange(t_range)
tprops.setIsActive(True)
def temporal_range_changed(t_range: QgsDateTimeRange):
layer = iface.activeLayer()
if isinstance(layer, QgsRasterLayer):
set_band_based_on_range(layer, t_range)
def set_range():
mode = QgsRasterLayerTemporalProperties.ModeFixedTemporalRange
temporal_controller: QgsTemporalController = iface.mapCanvas().temporalController()
temporal_controller.updateTemporalRange.connect(temporal_range_changed)
# Add one second to make the last frame visible
set_fixed_temporal_range(iface.activeLayer(), QgsDateTimeRange(datetime.datetime(2020, 11, 2, 15, 0), datetime.datetime(2020, 11, 3, 11, 0, 1)))
Oto prosta animacja gif pokazująca użycie: