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.
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.
|
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).
|
FIgure 2. Open NetCDF Data in QGIS
|
|
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.
|
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.
|
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.
|
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.
|
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).
|
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!
NetCDF
QGIS
Tutorial