NetCDF Temporal Data Visualization with QGIS (Global Monthly Temperature Showcase)

What is NetCDF Data?

Network Common Data Form (NetCDF) is a file format for storing multiple layer scientific data like temperature, precipitation, air pressure, etc. It was developed in 1988 by University Corporation for Atmospheric Research (UCAR). Basically NetCDF is an extended version of  Common Data Format (CDF) and Hierarchical Data Format (HDF) file format. With this extension, NetCDF can store a huge amount of data for multiple datasets. This capability made NetCDF as a compact file to keep temporal data in multiple bands such as daily or monthly mean temperature, precipitation  and other properties.
 
One benefit from temporal dataset is enabled us to visualize or animate temporal change from time to time. In this post I will show you how to visualize NetCDF temporal data for global monthly average temperature from 1948 with QGIS as in the following animation.
 
Monthly Average Temperature Animation
For this tutorial I used QGIS 3.14 Pi. Let's get started!

Open NetCDF Data in QGIS

The data for this tutorial can be downloaded from NOAA Physical Science Laboratory. To open NetCDF data in QGIS is the same with opening Raster Data. Therefore from QGIS Layer Menu select  Data Source Manager as shown in figure 1.
 
Data Source Manager QGIS
Figure 1. QGIS Data Source Manager


 
From the data source manager window as seen in figure 2. Select Raster and then browse to the NetCDF file to be opened. Click Add button to open the file. Might be after clicking the Add button, a CRS window will be opened as in figure 3. We need to specify a Coordinate Reference System for the data. In this case I chose Geographic Coordinate System with WGS 1984 Datum (EPSG:4326).
 
Open NetCDF Data in QGIS
FIgure 2. Open NetCDF Data in QGIS

Coordinate Reference System Selector
Figure 3. Coordinate Reference System Selector

After selecting the coordinate system through Coordinate Reference System Selector window, the NetCDF data will be added into QGIS map canvas as shown in figure 4.

NetCDF data added into QGIS map canvas
Figure 4. NetCDF data added into QGIS map canvas

Get More Information About NetCDF Data

Great, the data has been added to QGIS, now let's explore the data to get to know more about the data. To do this, Right Click the added NetCDF data layer and select Properties. The Layer properties window will be shown as in figure 5. From the properties window select Information option on the left menu. Then on the right side the related information about the data will be appeared. Scroll down to More Information section. In this section will be found some description about the data such as: long name of the data, property units, time range, time interval of each layer, etc. All those information are standardized as attribute information that describe about the respective NetCDF data. More explanation about NetCDF data attribute can be found here.
 
NetCDF Information
Figure 5. NetCDF Information

Visualize NetCDF Temporal Change

We already added the NetCDF data into QGIS and see attribute information of the data. Now let's visualize or animate the temporal change.

To visualize temporal change, can be used Temporal Controller tool. To open this tool select View menu, Panels and check Temporal Controller

Unfortunately after playing a little bit with this tool, I have a conclusion that the temporal controller tool can't be used directly to render a band with a respective date. It's different from vector data which has time attribute for each feature. After searching for solution, finally I found the following Python code that can be used to overcome this problem. I changed a little bit the code with adding input for start and end date/time. The original python code can be found here

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import datetime

#INPUT START AND END DATE/TIME
#FOR DATE/TIME USED SINGLE DIGIT FOR SINGLE NUMBER (i.e 4 NOT 04)
start_date='1948:1:1' #'yyyy:m:d' 
end_date='2021:2:28'
start_time='0:0:0' #'h:m:s'
end_time='0:0:0'

#PARSING INPUT DATE/TIME
date_time=[start_date,start_time,end_date,end_time]
dt_dict={}
key=0
for i in date_time:
    a=i.split(':')
    for j in range(len(a)):
        key +=1
        dt_dict[key]=int(a[j])


# 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(dt_dict[1], dt_dict[2], dt_dict[3], dt_dict[4], dt_dict[5],dt_dict[6]), datetime.datetime(dt_dict[7], dt_dict[8], dt_dict[9], dt_dict[10], dt_dict[11], dt_dict[12]))) 

Back to QGIS dekstop. Open Python Console. Copy the code and paste it into an editor  as in figure 6. Change the start and end date/time with respective with NetCDF data. You can check the start and end date/time from attribute/layer's properties information. The time information in properties information is in unix timestamp like this: 1297320(hours since 1800:01:01 00:00:00), so we need to convert it into date and time format (yyyy:mm:dd hh:mm:ss) if we want to use it. But to make life more simple, in this case, I looked at the file download page and found out the data range time is from 1948 - Feb 2021.

QGIS Python Console
Figure 6. QGIS Python Console

After changing the start and end date/time. Run the code and close the Python console if you'd like to. Open Temporal Controller again. Click the Fixed range temporal navigation button and then click Animated temporal animation (See animated gif in figure 7). Look at the start and end date/time. If it's correct as input, it means everything is fine and we are ready to play the animation. Lastly before pushing the Play button set the step unit to months, because the data interval is every 1 month.

Animated Monthly Temperature in QGIS
Figure 7. Animated Monthly Temperature in QGIS
 
If you want to make the animation faster, we need to render more frame in a second. For that open Temporal Controller Setting (the yellow gear button on the top right of temporal controller widget).  In the settings change the frame rate more than 1 to make the animation faster (see figure 8).
 
Temporal Controller Settings
Figure 8. Temporal Controller Settings

That's all this tutorial how to visualize or animate NetCDF data in QGIS. We had discussed a little bit about NetCDF data, how to add NetCDF data into QGIS, seeing infomation about the data and visualize it with a help of python code and using temporal controller. I hope you enjoy this tutorial and thanks for reading!
Link for video tutorial    

Related Posts

Disqus Comments