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
klik hier om te downloaden -> 08_gis/structured/IJmuiden.gpkg

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 -> ")
klik hier om te downloaden -> 08_gis/structured/model_struc_qgis.nc

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 -> ")
klik hier om te downloaden -> 08_gis/vertex/model_vert_qgis.nc

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.