Read and write ZMAP files#

The venerable ZMAP format looks like this:

! Landmark Zmap grid file name:   .DATANSLCU.dat
! Created/converted by Oasis Montaj, Geosoft Inc.
@.DATANSLCU.dat, GRID, 3
20, 1e+30, , 7, 1
208, 435, -630000.0, 672000.0, 2000000.0, 2621000.0
0.0, 0.0, 0.0
@
      -16481.9570313      -16283.9033203      -16081.5751953
      -15856.7861328      -15583.7167969      -15255.7343750
      -14869.3769531      -14426.1513672      -13915.8769531

Files often have a the extension .DAT. The first two lines shown are comments. The lines between the @ signs give the name of the grid, with some metadata such as null value, precision, and so on.

gio wraps the zmapio library. There are several functions:

  • read_zmap(): read a ZMAP file.

  • array_to_zmap(): write a NumPy array as a ZMAP file.

  • dataarray_to_zmap(): write an Xarray DataArray as a ZMAP file.

  • xarray.DataArray.gio.to_zmap(): an Xarray accessor providing another way to save a DataArray as a ZMAP file.

Let’s read some data from a ZMAP file:

!head ../../data/ZMAP/NStopo.dat
! Landmark Zmap grid file name:   .\DATA\NStopo.dat
! Created/converted by Oasis Montaj, Geosoft Inc.
@.\DATA\NStopo.dat  ,GRID,  4
        20,   1.0E+30,       ,     7,       1
       208,       435,  -630000.0000,   672000.0000,  2000000.0000,  2621000.0000,
        0.0000,        0.0000,        0.0000,
@
         -67.2144775         -67.5697021         -67.1472168         -69.0812378
         -73.1813660         -74.3084717         -72.7660828         -72.0339966
         -70.5137329         -68.5551758         -66.1948547         -62.7756653
import gio

z = gio.read_zmap('../../data/ZMAP/NStopo.dat')

z.plot()
<matplotlib.collections.QuadMesh at 0x7f9df010a160>
../_images/63e446d785ce4531a49592163f4c7916c60cefc04f41199c8b32130717bf8774.png

Let’s take a look at the xarray object too:

z
<xarray.DataArray '.\\DATA\\NStopo.dat' (Y: 208, X: 435)>
array([[  -50.138916 ,   -43.8048401,   -39.550293 , ...,   702.7687378,
          656.3284302,   581.6201782],
       [  -50.7771301,   -43.4010925,   -40.3817444, ...,   626.8185425,
          587.7715454,   528.4921265],
       [  -49.3357849,   -47.8291626,   -43.6928101, ...,   472.8615723,
          492.1585693,   453.5387573],
       ...,
       [  -67.1472168,   -72.3484497,   -78.2303467, ..., -2397.1206055,
        -2378.3427734, -2362.1647949],
       [  -67.5697021,   -72.6132202,   -81.2595825, ..., -2409.6945801,
        -2391.8217773, -2376.5600586],
       [  -67.2144775,   -74.9806519,   -82.2666016, ..., -2416.8630371,
        -2398.3891602, -2387.4147949]])
Coordinates:
  * X        (X) float64 -6.3e+05 -6.27e+05 -6.24e+05 ... 6.69e+05 6.72e+05
  * Y        (Y) float64 2e+06 2.003e+06 2.006e+06 ... 2.618e+06 2.621e+06
Attributes:
    fname:       ../../data/ZMAP/NStopo.dat
    null_value:  1e+30
    comment:     [' Landmark Zmap grid file name:   .\\DATA\\NStopo.dat', ' C...

Transform the surface#

The units are feet; let’s transform them to metres and save a new file.

z_m = 0.3048 * z

Use the xarray accessory#

If your surface is held in an xarray.DataArray then the accessory is the easiest way to save it in ZMAP format.

To use it, access the to_zmap() function in the gio accessory. After importing gio, the accessory is attached to every instance of a DataArray.

z_m.gio.to_zmap('NStopo_m.dat')

We can take a peek at this file using IPython to see if it looks legit:

!head ./NStopo_m.dat
!File created by gio.to_zmap xarray accessory
@.\DATA\NStopo.dat, GRID, 6
12, -999.25, , 3, 1
208, 435, -630000.0, 672000.0, 2000000.0, 2621000.0
0.0, 0.0, 0.0
@
     -20.487     -20.595     -20.466     -21.056     -22.306     -22.649
     -22.179     -21.956     -21.493     -20.896     -20.176     -19.134
     -18.187     -17.888     -17.495     -17.983     -17.928     -17.516
     -18.176     -21.011     -21.595     -20.317     -19.193     -18.450

Use the function#

If your surface is a NumPy array, then you can use the array_to_zmap() function.

Note that if you want the ZMAP file to have real-world coordinates, you will need to provide the extent as a list or tuple of min X, max X, min Y, max Y. For example:

arr = z_m.data

gio.array_to_zmap('NStopo_m.dat', arr, extent=[-630000.0, 672000.0, 2000000.0, 2621000.0])

It should look like the file we got before:

!head ./NStopo_m.dat
!File created by gio.array_to_zmap
@Z, GRID, 6
12, -999.25, , 3, 1
208, 435, -630000.0, 672000.0, 2000000.0, 2621000.0
0.0, 0.0, 0.0
@
     -20.487     -20.595     -20.466     -21.056     -22.306     -22.649
     -22.179     -21.956     -21.493     -20.896     -20.176     -19.134
     -18.187     -17.888     -17.495     -17.983     -17.928     -17.516
     -18.176     -21.011     -21.595     -20.317     -19.193     -18.450