Starting from QGIS 3.14 TimeManager is dead, long live the Temporal Controller! There are a lot of tutorials out there how "Temporal Controller" deals with vector layers, which looks nice. However, I'm working a lot with raster data and I cannot find any information, how to get a similar behaviour as it was in TimeManager.

I have a NetCDF like this:

ncdump -h SLA_ts.nc 
netcdf SLA_ts {
    time = 124 ;
    lat = 5 ;
    lon = 5 ;
    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" ;

When I load this file in QGIS, the time steps are loaded as bands (in the Symbology-Tab, they are displayed as Band 1: time=52244 (days since 1858-11-17 00:00:00) and so on...). However, the Temporal-Tab offers only a fixed time range as shown in the screenshot:enter image description here

I would expect that the Temporal Controller automatically recognizes that the layers are time steps but the button "Automatic" is inactive. Has anyone any experience with raster layers in "Temporal Controller"? How can I modify my data to be interpreted correctly? Or is this functionality simply not implemented (as the Temporal Controller is quite new)?

  • In my understanding of the time controller per layer - if it's not a hybrid layer like a netCDF - your only logical choice is to set a fixed time range, since there a no attributes, which would controll an automatic setting - may be the layers name could be one, but not supported at the moment I guess. The temporal controller will show then the layers fall into the actual time set by the temporal controller.
    – eurojam
    Oct 20, 2020 at 19:08
  • If you have a few grids for only a handful of timesteps, this makes sence. However, for a real time series of grids, this is far from beeing a handy solution. Therefore I wonder what the "Automatic" option is and when it applies. Is the "temporal controller" at all able to select a raster layer according to the chosen time?
    – Ludwig
    Oct 21, 2020 at 8:50
  • may be you can consider to use a python script to set the time slots to the layers
    – eurojam
    Oct 21, 2020 at 10:12
  • This sounds at least like a kind of workaround. I didn't know that it is possible to modify layer properties via a python script. Could you maybe send me a link to some docs or tutorials? If I understand you right, this script should load the raster 124 times (for each time step in my example) and modify the name, the band and the time interval, right?
    – Ludwig
    Oct 21, 2020 at 14:02
  • you can have a look to Anita Graser's Blog, she describes some Python Code to manipulate the temporal settings for a vectorLayer, but the temporalProperties() are also available for raster layers: anitagraser.com/tag/pyqgis
    – eurojam
    Oct 21, 2020 at 15:33

2 Answers 2


I wrote a PyQGIS script to handle the workaround for NetCDF files which uses temporal controller's updateTemporalRange signal. Just copy the following script to QGIS Python editor window, edit your dates, run the script and use Temporal Controller as usual.

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)
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()
    if t_range.begin().timeSpec() == 0 or t_range.end().timeSpec() == 0:
        begin = t_range.begin()
        end = t_range.end()
        t_range = QgsDateTimeRange(begin, end)
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()
# 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))) 

Here is a simple gif animation showing the usage:

gif animation of usage

  • 1
    I didn't care about this topic for a while as I managed to get TimeManager to work with newer QGIS versions (just modify "qgisMaximumVersion" in ~/.local/share/QGIS/QGIS3/profiles/default/python/plugins/timemanager/metadata.txt) but now a new QGIS LTR appeared and I had to deal with this topic again. This code works indeed, now the Temporal Controller can loop through the layers as in your example. Great job! However, there is one problem left: Every time you go to a new time step, the layer style is reset to default. Do you see any posibility to keep the styling as it is?
    – Ludwig
    Mar 3, 2022 at 10:58
  • You might want to try to add the netcdf file as QgsMeshLayer and it might just work (assuming that the first band is spatial and that the time dimension is called "time"). Then the temporal controller should work and the style will be kept.
    – Joonala
    Mar 3, 2022 at 13:34

While the question says raster data, your text body says NetCDF. Be aware that NetCDF can be loaded as "Mesh" layer in QGIS. If loaded like that, the temporal aspects should be detected properly and it should work automagically out of the box.

For making it easier to use temporal features with "dumb" raster layers where the timestamp might be just in the filename, I made a feature request for expression support: https://github.com/qgis/QGIS/issues/42309

  • 2
    Thanks, this is very close to the functionality I would wish. When loading the NetCDF as a mesh I can indeed use Temporal Controller as expected. The only drawback is that my data is raster data. It is not correctly displayed when loaded as a mesh. The blurring effect of the raster cells due to the interpolation is no serious problem. However, empty cells ("NaN") can cause significant problems. If there are some isolated cells, surrounded by NaNs, they are not shown anymore (obviously clusters of at least 3 valid values are needed). However, this is a nice workaround for data without NaNs.
    – Ludwig
    Mar 20, 2021 at 14:30
  • Could you file a issue for that? It mirrors some of my puzzlement about mesh layers and might make a nice point for improvement. Mar 21, 2021 at 14:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.