Exporting model data to GIS
This notebook shows how to export model data so it can be viewed in GIS (QGIS or ArcMAP).
import os
import xarray as xr
from IPython.display import FileLink
import nlmod
nlmod.util.get_color_logger("INFO")
nlmod.show_versions()
Python version : 3.11.14
NumPy version : 2.4.4
Xarray version : 2026.4.0
Matplotlib version : 3.10.9
Flopy version : 3.10.0
nlmod version : 0.11.3dev
Model types
structured grid
source_ws_struc = os.path.join("..", "examples", "01_basic_model")
model_name = "IJmuiden"
output_ws = "08_gis"
os.makedirs(output_ws, exist_ok=True)
ds_struc = xr.load_dataset(
os.path.join(source_ws_struc, f"{model_name}.nc"), mask_and_scale=False
)
ds_struc
<xarray.Dataset> Size: 10MB
Dimensions: (y: 60, x: 100, layer: 40, time: 1)
Coordinates:
* y (y) float64 480B 5e+05 4.998e+05 ... 4.942e+05 4.94e+05
* x (x) float64 800B 9.505e+04 9.515e+04 ... 1.05e+05
* layer (layer) <U8 1kB 'AAOP' 'NASC' ... 'PZWAz4' 'MSz1'
* time (time) datetime64[ns] 8B 2019-12-31
spatial_ref int64 8B 0
Data variables: (12/17)
top (y, x) float64 48kB -18.75 -18.75 -18.75 ... 0.25 0.25
botm (layer, y, x) float64 2MB -18.75 -18.75 ... -218.5
kh (layer, y, x) float64 2MB 1.0 1.0 1.0 ... 6.1 6.11 6.11
kv (layer, y, x) float64 2MB 0.1 0.1 0.1 ... 0.611 0.611
area (y, x) float64 48kB 1e+04 1e+04 1e+04 ... 1e+04 1e+04
steady (time) int64 8B 1
... ...
rws_oppwater_area (y, x) float64 48kB 1e+04 1e+04 1e+04 ... 0.0 0.0 0.0
rws_oppwater_cond (y, x) float64 48kB 1e+03 1e+03 1e+03 ... 0.0 0.0 0.0
rws_oppwater_stage (y, x) float64 48kB 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0
ahn (y, x) float32 24kB 3.403e+38 3.403e+38 ... 0.1605
edge_mask (layer, y, x) int64 2MB 0 0 0 0 0 0 0 ... 1 1 1 1 1 1 1
recharge (time, y, x) float64 48kB 0.0006068 ... 0.0006068
Attributes:
extent: [ 95000. 105000. 494000. 500000.]
gridtype: structured
model_name: IJmuiden
mfversion: mf6
created_on: 20260513_15:11:18
exe_name: /home/docs/checkouts/readthedocs.org/u...
model_ws: 01_basic_model
figdir: 01_basic_model/figure
cachedir: 01_basic_model/cache
transport: 0
surface_drn_resistance: 10.0
model_dataset_written_to_disk_on: 20260513_15:11:21# create gisdir
gisdir_struc = os.path.join(output_ws, "structured")
os.makedirs(gisdir_struc, exist_ok=True)
vertex grid
source_ws_vert = os.path.join("..", "examples", "03_local_grid_refinement")
model_name = "IJm_planeten"
ds_vert = xr.load_dataset(
os.path.join(source_ws_vert, f"{model_name}.nc"), mask_and_scale=False
)
ds_vert
<xarray.Dataset> Size: 11MB
Dimensions: (icell2d: 6231, layer: 40, time: 6, iv: 6459, icv: 7)
Coordinates:
* icell2d (icell2d) int64 50kB 0 1 2 3 4 ... 6227 6228 6229 6230
x (icell2d) float64 50kB 9.505e+04 9.515e+04 ... 1.05e+05
y (icell2d) float64 50kB 5e+05 5e+05 ... 4.94e+05 4.94e+05
* layer (layer) <U8 1kB 'AAOP' 'NASC' ... 'PZWAz4' 'MSz1'
* time (time) datetime64[ns] 48B 2015-01-02 ... 2015-01-07
spatial_ref int64 8B 0
Dimensions without coordinates: iv, icv
Data variables: (12/20)
top (icell2d) float64 50kB -18.75 -18.75 ... 0.25 0.25
botm (layer, icell2d) float64 2MB -18.75 -18.75 ... -218.5
kh (layer, icell2d) float64 2MB 1.0 1.0 1.0 ... 6.11 6.11
kv (layer, icell2d) float64 2MB 0.1 0.1 0.1 ... 0.611 0.611
steady (time) int64 48B 1 0 0 0 0 0
nstp (time) int64 48B 1 1 1 1 1 1
... ...
rws_oppwater_area (icell2d) float64 50kB 1e+04 1e+04 1e+04 ... 0.0 0.0 0.0
rws_oppwater_cond (icell2d) float64 50kB 1e+03 1e+03 1e+03 ... 0.0 0.0 0.0
rws_oppwater_stage (icell2d) float64 50kB 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0
ahn (icell2d) float64 50kB nan nan nan ... 0.8357 0.1605
edge_mask (layer, icell2d) int64 2MB 1 1 1 1 1 1 1 ... 1 1 1 1 1 1
recharge (time, icell2d) float64 299kB 0.0019 0.0019 ... 0.0014
Attributes:
extent: [ 95000. 105000. 494000. 500000.]
gridtype: vertex
model_name: IJm_planeten
mfversion: mf6
created_on: 20260513_15:11:59
exe_name: /home/docs/checkouts/readthedocs.org/u...
model_ws: 03_local_grid_refinement
figdir: 03_local_grid_refinement/figure
cachedir: 03_local_grid_refinement/cache
transport: 0
surface_drn_resistance: 10.0
model_dataset_written_to_disk_on: 20260513_15:12:27# create gisdir
gisdir_vert = os.path.join(output_ws, "vertex")
os.makedirs(gisdir_vert, exist_ok=True)
Export vector data
structured
# write model data to a geopackage
fname_geopackage = nlmod.gis.ds_to_vector_file(ds_struc, gisdir=gisdir_struc)
# get download link
FileLink(fname_geopackage, result_html_prefix="klik hier om te downloaden -> ")
INFO:pyogrio._io.write:Created 6,000 records
INFO:pyogrio._io.write:Created 6,000 records
INFO:pyogrio._io.write:Created 6,000 records
INFO:pyogrio._io.write:Created 6,000 records
INFO:pyogrio._io.write:Created 6,000 records
INFO:pyogrio._io.write:Created 6,000 records
INFO:pyogrio._io.write:Created 6,000 records
INFO:pyogrio._io.write:Created 6,000 records
INFO:pyogrio._io.write:Created 6,000 records
INFO:pyogrio._io.write:Created 6,000 records
INFO:pyogrio._io.write:Created 6,000 records
vertex grid
# write model data to a geopackage
fname_geopackage = nlmod.gis.ds_to_vector_file(ds_vert, gisdir=gisdir_vert)
INFO:pyogrio._io.write:Created 6,231 records
INFO:pyogrio._io.write:Created 6,231 records
INFO:pyogrio._io.write:Created 6,231 records
INFO:pyogrio._io.write:Created 6,231 records
INFO:pyogrio._io.write:Created 6,231 records
INFO:pyogrio._io.write:Created 6,231 records
INFO:pyogrio._io.write:Created 6,231 records
INFO:pyogrio._io.write:Created 6,231 records
INFO:pyogrio._io.write:Created 6,231 records
INFO:pyogrio._io.write:Created 6,231 records
INFO:pyogrio._io.write:Created 6,231 records
Export griddata
The model data can be exported to a netcdf file that can be visualised in Qgis. For a structured model the standard model dataset (xarray.Dataset) can be exported to a netdf file. For a vertex model you have to convert the model dataset to a certain format before you can write it to a netcdf and read it with Qgis. With the code below we export the vertex model dataset to a netcdf file (‘model_qgis.nc’) that can be read using Qgis.
structured grid
In order to load a structured grid netcdf file in Qgis use the “Add Raster Layer..” option.
# write model data to a netcdf file
fname = os.path.join(gisdir_struc, "model_struc_qgis.nc")
ds_struc.to_netcdf(fname)
# get download link
FileLink(fname, result_html_prefix="klik hier om te downloaden -> ")
vertex grid
In order to load a vertex netcdf file in Qgis use the “Add Mesh Layer..” option.
# write model data to a netcdf file
fname = os.path.join(gisdir_vert, "model_vert_qgis.nc")
out = nlmod.gis.ds_to_ugrid_nc_file(ds_vert, fname)
# get download link
FileLink(fname, result_html_prefix="klik hier om te downloaden -> ")
Add symbology (QGIS)
It is always nice to have automatic symbology for your vector data. Some thoughts:
QGIS can save symbology of a single shapefile in a .qml file
In QGIS you can add a .qml file to a geopackage thus saving the symbology to a single file.
You can create a .qml file in QGIS from existing symbology.
a .qml file is an xml file so theoretically it is possible to modify a .qml file with Python based on the properties of the data.
Some limitations of the current gis functions:
when exporting shapefiles to gis, attributes cannot have names longer than 10 characters. Now the automatic character shortening of fiona is used. This is not optimal.
when exporting data variables with dimension time only the mean values in time are exported in the shapefile to avoid extremely large files.