Repository: DHI/mikeio

Issue:
Write Dataset to dfs1 does not write Geographical Information
subj

Discussion:

Patch:
diff --git a/docs/conf.py b/docs/conf.py
index 9a6f82d4..1f794c18 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -56,5 +56,3 @@ html_theme = "sphinx_rtd_theme"
 # html_static_path = ["_static"]
 
 # html_logo = "../images/logo/SVG/MIKE-IO-Logo-Pos-RGB.svg"
-
-myst_heading_anchors = 3
diff --git a/docs/data-structures.md b/docs/data-structures.md
deleted file mode 100644
index 3179253c..00000000
--- a/docs/data-structures.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Data Structures
-
-MIKE IO has these primary data structures: 
-
-* [Dataset](Dataset) - a collection of DataArrays corresponding to the contents of a dfs file; typically obtained from `mikeio.read()`
-* [DataArray](DataArray) - data and metadata corresponding to one "item" in a dfs file. 
-* **Geometry** - spatial description of the data in a dfs file; comes in different flavours: [Grid1D](Grid1D), [Grid2D](Grid2D), [Grid3D](Grid3D), [GeometryFM](GeometryFM), [GeometryFM3D](GeometryFM3D), etc. corresponding to different types of dfs files. 
-* **Dfs** - an object returned by `dfs = mikeio.open()` containing the metadata (=header) of a dfs file ready for reading the data (which can be done with `dfs.read()`); exists in different specialized versions: [Dfs0](Dfs0), [Dfs1](Dfs1), [Dfs2](Dfs2), [Dfs3](Dfs3), [Dfsu2DH](Dfsu2DH), [Dfsu3D](Dfsu3D), [Dfsu2DV](Dfsu2DV), [DfsuSpectral](DfsuSpectral), 
-
diff --git a/docs/dataarray.md b/docs/dataarray.md
deleted file mode 100644
index 0b9f4b5d..00000000
--- a/docs/dataarray.md
+++ /dev/null
@@ -1,154 +0,0 @@
-# DataArray
-
-The [DataArray](DataArray) is the common MIKE IO data structure 
-for *item* data from dfs files. 
-The `mikeio.read()` methods returns a Dataset as a container of DataArrays (Dfs items)
-
-Each DataArray have the following properties:
-* **item** - an  [`ItemInfo`](ItemInfo) with name, type and unit
-* **time** - a [pandas.DateimeIndex](https://pandas.pydata.org/docs/reference/api/pandas.DatetimeIndex.html) with the time instances of the data
-* **geometry** - a Geometry object with the spatial description of the data
-* **values** - a NumPy array
-
-Use DataArray's string representation to get an overview of the DataArray
-
-
-```python
->>> import mikeio
->>> ds = mikeio.read("testdata/HD2D.dfsu")
->>> da = ds["Surface Elevation"]
->>> da
-<mikeio.DataArray>
-name: Surface elevation
-dims: (time:9, element:884)
-time: 1985-08-06 07:00:00 - 1985-08-07 03:00:00 (9 records)
-geometry: Dfsu2D (884 elements, 529 nodes)
-```
-
-
-## Temporal selection
-
-```python
->>> da.sel(time="1985-08-06 12:00")
-<mikeio.DataArray>
-name: Surface elevation
-dims: (element:884)
-time: 1985-08-06 12:00:00 (time-invariant)
-geometry: Dfsu2D (884 elements, 529 nodes)
-values: [0.1012, 0.1012, ..., 0.105]
-
->>> da["1985-8-7":]
-<mikeio.DataArray>
-name: Surface elevation
-dims: (time:2, element:884)
-time: 1985-08-07 00:30:00 - 1985-08-07 03:00:00 (2 records)
-geometry: Dfsu2D (884 elements, 529 nodes)
-
-```
-
-## Spatial selection
-
-The `sel` method finds the nearest element.
-
-```python
->>> da.sel(x=607002, y=6906734)
-<mikeio.DataArray>
-name: Surface elevation
-dims: (time:9)
-time: 1985-08-06 07:00:00 - 1985-08-07 03:00:00 (9 records)
-geometry: GeometryPoint2D(x=607002.7094112666, y=6906734.833048992)
-values: [0.4591, 0.8078, ..., -0.6311]
-```
-
-## Plotting
-
-The plotting of a DataArray is context-aware meaning that plotting behaviour depends on the geometry of the DataArray being plotted. 
-
-```python
->>> da = mikeio.read("testdata/HD2D.dfsu")["Surface Elevation"]
->>> da.plot()
->>> da.plot.contourf()
->>> da.plot.mesh()
-```
-
-See details in the [API specification](_DatasetPlotter) below and in the bottom of the relevant pages e.g. [DataArray Plotter Grid2D API](_DataArrayPlotterGrid2D) on the dfs2 page.
-
-
-
-## Properties
-
-The DataArray has several properties:
-
-* n_items - Number of items
-* n_timesteps - Number of timesteps
-* n_elements - Number of elements
-* start_time - First time instance (as datetime)
-* end_time - Last time instance (as datetime)
-* is_equidistant - Is the time series equidistant in time
-* timestep - Time step in seconds (if is_equidistant)
-* shape - Shape of each item
-* deletevalue - File delete value (NaN value)
-
-
-
-## Methods
-
-DataArray has several useful methods for working with data, 
-including different ways of *selecting* data:
-
-* [`sel()`](DataArray.sel) - Select subset along an axis
-* [`isel()`](DataArray.isel) - Select subset along an axis with an integer
-
-*Aggregations* along an axis:
-
-* [`mean()`](DataArray.mean) - Mean value along an axis
-* [`nanmean()`](DataArray.nanmean) - Mean value along an axis (NaN removed)
-* [`max()`](DataArray.max) - Max value along an axis
-* [`nanmax()`](DataArray.nanmax) - Max value along an axis (NaN removed)
-* [`min()`](DataArray.min) - Min value along an axis
-* [`nanmin()`](DataArray.nanmin) - Min value along an axis (NaN removed)
-* [`aggregate()`](DataArray.aggregate) - Aggregate along an axis
-* [`quantile()`](DataArray.quantile) - Quantiles along an axis
-
-*Mathematical operations* +, - and * with numerical values:
-
-* ds + value
-* ds - value
-* ds * value
-
-and + and - between two DataArrays (if number of items and shapes conform):
-
-* ds1 + ds2
-* ds1 - ds2
-
-Other methods that also return a DataArray:
-
-* [`interp_time()`](DataArray.interp_time) - Temporal interpolation (see [Time interpolation notebook](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Time%20interpolation.ipynb))
-* [`dropna()`](DataArray.dropna) - Remove time steps where all items are NaN
-* [`squeeze()`](DataArray.squeeze) - Remove axes of length 1
-
-*Conversion* methods:
-
-* [`to_xarray()`](DataArray.to_xarray) - Convert DataArray to a xarray DataArray (great for Dfs2)
-* [`to_dfs()`](DataArray.to_dfs) - Write DataArray to a Dfs file
-
-
-## DataArray API
-
-```{eval-rst}
-.. autoclass:: mikeio.DataArray
-	:members:
-```
-
-
-
-## DataArray Plotter API
-
-A DataArray `da` can be plotted using `da.plot`. 
-
-```{eval-rst}
-.. autoclass:: mikeio.dataarray._DataArrayPlotter
-	:members:
-```
-
-
diff --git a/docs/dataset.md b/docs/dataset.md
index 3bc735aa..9e42d73c 100644
--- a/docs/dataset.md
+++ b/docs/dataset.md
@@ -1,14 +1,13 @@
 # Dataset
 
-The [Dataset](Dataset) is the MIKE IO data structure 
+The [Dataset](Dataset) is the common MIKE IO data structure 
 for data from dfs files. 
-The `mikeio.read()` methods returns a Dataset as a container of [DataArrays](dataarray) (Dfs items). Each DataArray has the properties, *item*, *time*, *geometry* and *values*. The time and geometry are common to all DataArrays in the Dataset. 
+The `mikeio.read()` methods returns a Dataset as a container of DataArrays (Dfs items)
 
-The Dataset has the following primary properties: 
-
-* **items** - a list of the DataArray items
+Each DataArray have the following properties:
+* **item** - an  [`ItemInfo`](ItemInfo) with name, type and unit
 * **time** - a pandas.DateTimeIndex with the time instances of the data
-* **geometry** - a Geometry object with the spatial description of the data
+* **values** - a NumPy array
 
 Use Dataset's string representation to get an overview of the Dataset
 
@@ -18,35 +17,33 @@ Use Dataset's string representation to get an overview of the Dataset
 >>> ds = mikeio.read("testdata/HD2D.dfsu")
 >>> ds
 <mikeio.Dataset>
-dims: (time:9, element:884)
-time: 1985-08-06 07:00:00 - 1985-08-07 03:00:00 (9 records)
-geometry: Dfsu2D (884 elements, 529 nodes)
-items:
-  0:  Surface elevation <Surface Elevation> (meter)
-  1:  U velocity <u velocity component> (meter per sec)
-  2:  V velocity <v velocity component> (meter per sec)
-  3:  Current speed <Current Speed> (meter per sec)
+Geometry: Dfsu2D
+Dimensions: (time:9, element:884)
+Time: 1985-08-06 07:00:00 - 1985-08-07 03:00:00 (9 records)
+Items:
+  0:  Surface elevation <Surface Elevation> (meter)        
+  1:  U velocity <u velocity component> (meter per sec)    
+  2:  V velocity <v velocity component> (meter per sec)    
+  3:  Current speed <Current Speed> (meter per sec)   
 ```
 
-## Selecting items
-
-Selecting a specific item "itemA" (at position 0) from a Dataset ds can be done with:
+Selecting items
+---------------
+Selecting a specific item "itemA" (at position 0) from a Dataset ds can be 
+done with:
 
 * `ds[["itemA"]]` - returns a new Dataset with "itemA"
-* `ds["itemA"]` - returns "itemA" DataArray
+* `ds["itemA"]` - returns the data of "itemA"
 * `ds[[0]]` - returns a new Dataset with "itemA" 
-* `ds[0]` - returns "itemA" DataArray
-* `ds.itemA` - returns "itemA" DataArray
-
-We recommend the use *named* items for readability. 
+* `ds[0]` - returns the data of "itemA"
 
 ```
 >>> ds.Surface_elevation
 <mikeio.DataArray>
-name: Surface elevation
-dims: (time:9, element:884)
-time: 1985-08-06 07:00:00 - 1985-08-07 03:00:00 (9 records)
-geometry: Dfsu2D (884 elements, 529 nodes)
+Name: Surface elevation
+Geometry: Dfsu2D
+Dimensions: (time:9, element:884)
+Time: 1985-08-06 07:00:00 - 1985-08-07 03:00:00 (9 records)
 ```
 
 Negative index e.g. ds[-1] can also be used to select from the end. 
@@ -60,8 +57,6 @@ Note that this behavior is similar to pandas and xarray.
 
 ## Temporal selection
 
-A time slice of a Dataset can be selected in several different ways. 
-
 ```python
 >>> ds.sel(time="1985-08-06 12:00")
 <mikeio.Dataset>
@@ -104,19 +99,8 @@ items:
   3:  Current speed <Current Speed> (meter per sec)
 ```
 
-
-## Plotting
-
-In most cases, you will *not* plot the Dataset, but rather it's DataArrays. But there are two exceptions: 
-
-* dfs0-Dataset : plot all items as timeseries with ds.plot()
-* scatter : compare two items using ds.plot.scatter(x="itemA", y="itemB")
-
-See details in the [API specification](_DatasetPlotter) below.
-
-
 ## Properties
-The Dataset (and DataArray) has several properties:
+The Dataset/DataArray has several properties:
 
 * n_items - Number of items
 * n_timesteps - Number of timesteps
@@ -130,9 +114,9 @@ The Dataset (and DataArray) has several properties:
 
 
 
-## Methods
-
-Dataset (and DataArray) has several useful methods for working with data, 
+Methods
+-------
+Dataset and DataArray has several useful methods for working with data, 
 including different ways of *selecting* data:
 
 * [`sel()`](Dataset.sel) - Select subset along an axis
@@ -176,19 +160,16 @@ Other methods that also return a Dataset:
 
 
 
-## Dataset API
-
+Dataset API
+-----------
 ```{eval-rst}
 .. autoclass:: mikeio.Dataset
 	:members:
 ```
 
-
-## Dataset Plotter API
-
+DataArray API
+-----------
 ```{eval-rst}
-.. autoclass:: mikeio.dataset._DatasetPlotter
+.. autoclass:: mikeio.DataArray
 	:members:
-```
-
-
+```
\ No newline at end of file
diff --git a/docs/design.md b/docs/design.md
index 2b418df2..0d56e020 100644
--- a/docs/design.md
+++ b/docs/design.md
@@ -36,10 +36,10 @@ Examples are available in two forms:
 * [Jupyter notebooks](https://nbviewer.jupyter.org/github/DHI/mikeio/tree/main/notebooks/)
 
 ## Open Source​
-MIKE IO is an open source project licensed under the [BSD-3 license] (https://github.com/DHI/mikeio/blob/main/License.txt).
+MIKE IO is an open source project licensed under the `BSD-3 license <https://github.com/DHI/mikeio/blob/main/License.txt>`_.
 The software is provided free of charge with the source code available for inspection and modification.
 
-Contributions are welcome, more details can be found in our [contribution guidelines](https://github.com/DHI/mikeio/blob/main/CONTRIBUTING.md).
+Contributions are welcome, more details can be found in our `contribution guidelines <https://github.com/DHI/mikeio/blob/main/CONTRIBUTING.md>`_.
 
 ## Easy to collaborate
 By developing MIKE IO on GitHub along with a completely open discussion, we believe that the collaboration between developers and end-users results in a useful library.
@@ -50,7 +50,7 @@ By providing the historical versions of MIKE IO on PyPI it is possible to reprod
 Install specific version::
 
 ```
-pip install mikeio==0.12.2
+pip install mikeio==0.4.3
 ```
 
 ## Easy access to new features
diff --git a/docs/dfs-overview.md b/docs/dfs-overview.md
deleted file mode 100644
index 26d5fa50..00000000
--- a/docs/dfs-overview.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Dfs Overview
-
-[DFS file system specification](https://docs.mikepoweredbydhi.com/core_libraries/dfs/dfs-file-system)
-
-
-## MIKE IO Dfs classes
-
-All Dfs classes (and the Dataset) class are representations of timeseries and 
-share these properties: 
-
-* items - a list of [ItemInfo](ItemInfo) with name, type and unit of each item
-* n_items - Number of items
-* n_timesteps - Number of timesteps
-* start_time - First time instance (as datetime)
-* end_time - Last time instance (as datetime)
-* geometry - spatial description of the data in the file ([Grid1D](Grid1D), [Grid2D](Grid2D), etc ... )
-* deletevalue - File delete value (NaN value)
-
-
-
-## Open or read? 
-
-Dfs files contain data and metadata. 
-
-If the file is small (e.g. <100 MB), you probably just want to get all the data at once with `mikeio.read(...)` which will return a `Dataset` for further processing.   
-
-If the file is big, you will typically get the file *header* with `dfs = mikeio.open(...)` which will return a MIKE IO Dfs class, before reading any data. When you have decided what to read (e.g. specific time steps, an sub area or selected elements), you can the get the Dataset `ds` you need with `ds = dfs.read(...)`.
-
-## Open and read API
-
-```{eval-rst}
-.. automodule:: mikeio
-	:members:
-```
diff --git a/docs/dfs0.md b/docs/dfs0.md
index 3a187a11..68b053fb 100644
--- a/docs/dfs0.md
+++ b/docs/dfs0.md
@@ -8,13 +8,14 @@ Working with data from dfs0 files are conveniently done in one of two ways:
 * [`pandas.DataFrame`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html) - utilize all the powerful methods of pandas
 
 
-## Read Dfs0 to Dataset
-
+Read Dfs0 to Dataset
+--------------------
 
 ```python
 >>> import mikeio
 >>> ds = mikeio.read("da_diagnostic.dfs0")
 >>> ds
+>>> ds
 <mikeio.Dataset>
 dims: (time:744)
 time: 2017-10-27 00:00:00 - 2017-10-29 18:00:00 (744 non-equidistant records)
@@ -25,7 +26,8 @@ items:
   3:  MeasurementSign. Wave Height <Significant wave height> (meter)
 ```
 
-## From Dfs0 to pandas DataFrame
+From Dfs0 to pandas DataFrame
+-----------------------------
 
 ```python
 >>> df = ds.to_dataframe()
@@ -39,7 +41,8 @@ items:
 
 ```
 
-## From pandas DataFrame to Dfs0
+From pandas DataFrame to Dfs0
+-----------------------------
 
 ```python
 >>> import mikeio
@@ -47,19 +50,10 @@ items:
 >>> df.to_dfs0("mauna_loa_co2.dfs0")
 ```
 
-## Dfs0 example notebooks
-
+Dfs0 example notebooks
+----------------------
 * [Dfs0](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfs0%20-%20Timeseries.ipynb) - read, write, to_dataframe, non-equidistant, accumulated timestep, extrapolation
 * [Dfs0 Relative-time](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfs0%20-%20Relative%20time.ipynb) - read file with relative time axis
 * [Dfs0 | getting-started-with-mikeio](https://dhi.github.io/getting-started-with-mikeio/dfs0.html) - Course literature
 
 
-
-
-## Dfs0 API
-
-```{eval-rst}
-.. autoclass:: mikeio.Dfs0
-	:members:
-	:inherited-members:
-```
\ No newline at end of file
diff --git a/docs/dfs1.md b/docs/dfs1.md
deleted file mode 100644
index 8e2e29aa..00000000
--- a/docs/dfs1.md
+++ /dev/null
@@ -1,66 +0,0 @@
-# Dfs1
-
-A dfs1 file contains node-based line series data. Dfs1 files do not contain enough metadata to determine their geographical position, but have a relative distance from the origo. 
-
-
-```python
->>> import mikeio
->>> ds = mikeio.read("tide1.dfs1")
->>> ds
-<mikeio.Dataset>
-dims: (time:97, x:10)
-time: 2019-01-01 00:00:00 - 2019-01-03 00:00:00 (97 records)
-geometry: Grid1D (n=10, dx=0.06667)
-items:
-  0:  Level <Water Level> (meter)
-```
-
-## Grid 1D
-
-The spatial information is available in the `geometry` attribute (accessible from Dfs1, Dataset, and DataArray), which in the case of a dfs1 file is a [`Grid1D`](Grid1D) geometry. 
-
-```python
->>> ds.geometry
-<mikeio.Grid1D>
-x: [0, 0.06667, ..., 0.6] (nx=10, dx=0.06667)
-```
-
-Grid1D's primary properties and methods are: 
-
-* `x` 
-* `nx`
-* `dx`
-* `find_index()`
-* `isel()`
-
-See [API specification](Grid1D) below for details.
-
-
-
-
-## Dfs1 API
-
-```{eval-rst}
-.. autoclass:: mikeio.Dfs1
-	:members:
-	:inherited-members:
-```
-
-## Grid1D API
-
-```{eval-rst}
-.. autoclass:: mikeio.Grid1D
-	:members:
-	:inherited-members:
-```
-
-
-## DataArray Plotter Grid1D API
-
-A DataArray `da` with a Grid1D geometry can be plotted using `da.plot`. 
-
-```{eval-rst}
-.. autoclass:: mikeio.dataarray._DataArrayPlotterGrid1D
-	:members:
-	:inherited-members:
-```
\ No newline at end of file
diff --git a/docs/dfs2.md b/docs/dfs123.md
similarity index 65%
rename from docs/dfs2.md
rename to docs/dfs123.md
index a6079a21..62739d5a 100644
--- a/docs/dfs2.md
+++ b/docs/dfs123.md
@@ -1,27 +1,33 @@
+# Dfs1, Dfs2 and Dfs3
 
-# Dfs2
+MIKE IO has a similar API for the three gridded dfs file types: Dfs1, Dfs2 and Dfs2. 
 
-A dfs2 file is also called a grid series file. Values in a dfs2 file are ‘element based’, i.e. values are defined in the centre of each grid cell. 
+All Dfs classes (and the Dataset) class are representations of timeseries and 
+share these properties: 
+
+* items - a list of [`ItemInfo`](ItemInfo) with name, type and unit of each item
+* n_items - Number of items
+* n_timesteps - Number of timesteps
+* start_time - First time instance (as datetime)
+* end_time - Last time instance (as datetime)
+* deletevalue - File delete value (NaN value)
+
+
+Dfs2
+----
 
+A dfs2 file is also called a grid series file. Values in a dfs2 file are ‘element based’, i.e. values are defined in the centre of each grid cell. 
+The spatial information is available in the `DataArray.geometry` attribute, which in the case of a Dfs2 file is a [`Grid2D`](Grid2D) geometry. 
 
 ```python
 >>> import mikeio
 >>> ds = mikeio.read("gebco_sound.dfs2")
->>> ds
 <mikeio.Dataset>
 dims: (time:1, y:264, x:216)
 time: 2020-05-15 11:04:52 (time-invariant)
 geometry: Grid2D (ny=264, nx=216)
 items:
   0:  Elevation <Total Water Depth> (meter)
-```
-
-
-## Grid2D
-
-The spatial information is available in the `geometry` attribute (accessible from Dfs2, Dataset, and DataArray), which in the case of a dfs2 file is a [`Grid2D`](Grid2D) geometry. 
-
-```python
 >>> ds.geometry
 <mikeio.Grid2D>
 x: [12.2, 12.21, ..., 13.1] (nx=216, dx=0.004167)
@@ -29,59 +35,21 @@ y: [55.2, 55.21, ..., 56.3] (ny=264, dy=0.004167)
 projection: LONG/LAT
 ```
 
-Grid2D's primary properties and methods are: 
-
-* `x` 
-* `nx`
-* `dx`
-* `y`
-* `ny`
-* `dy`
-* `origin`
-* `projection`
-* `xy` 
-* `bbox`
-* `contains()`
-* `find_index()`
-* `isel()`
-* `to_mesh()`
 
-See [API specification](Grid2D) below for details.
-
-
-## Dfs2 Example notebooks
-
-* [Dfs2 | getting-started-with-mikeio](https://dhi.github.io/getting-started-with-mikeio/dfs2.html)
+Dfs2 Example notebooks
+----------------------
 * [Dfs2-Bathymetry](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfs2%20-%20Bathymetry.ipynb) - GEBCO NetCDF/xarray to dfs2 
 * [Dfs2-Boundary](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfs2%20-%20Boundary.ipynb) - Vertical transect dfs2, interpolation in time 
 * [Dfs2-Export-to-netCDF](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfs2%20-%20Export%20to%20netcdf.ipynb) Export dfs2 to NetCDF
 * [Dfs2-GFS](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfs2%20-%20Global%20Forecasting%20System.ipynb) - GFS NetCDF/xarray to dfs2
 * [Dfs2-SST](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfs2%20-%20Sea%20surface%20temperature.ipynb) - DMI NetCDF/xarray to dfs2 
+* [Dfs2 | getting-started-with-mikeio](https://dhi.github.io/getting-started-with-mikeio/dfs2.html)
 
 
-
-## Dfs2 API
-
-```{eval-rst}
-.. autoclass:: mikeio.Dfs2
-	:members:
-	:inherited-members:
-```
-
-## Grid2D API
-
+Grid2D
+--------
 ```{eval-rst}
 .. autoclass:: mikeio.Grid2D
 	:members:
 	:inherited-members:
-```
-
-## DataArray Plotter Grid2D API
-
-A DataArray `da` with a Grid2D geometry can be plotted using `da.plot`. 
-
-```{eval-rst}
-.. autoclass:: mikeio.dataarray._DataArrayPlotterGrid2D
-	:members:
-	:inherited-members:
 ```
\ No newline at end of file
diff --git a/docs/dfs3.md b/docs/dfs3.md
deleted file mode 100644
index 1a8795e9..00000000
--- a/docs/dfs3.md
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
-# Dfs3
-
-A dfs3 file contains 3D gridded data.  
-
-
-```python
->>> import mikeio
->>> ds = mikeio.read("dissolved_oxygen.dfs3")
->>> ds
-<mikeio.Dataset>
-dims: (time:1, z:17, y:112, x:91)
-time: 2001-12-28 00:00:00 (time-invariant)
-geometry: Grid3D(nz=17, ny=112, nx=91)
-items:
-  0:  Diss. oxygen (mg/l) <Concentration 3> (mg per liter)
-```
-
-A specific layer can be read with the `layers` argument, in which case a 2D Dataset will be returned: 
-
-```python
->>> import mikeio
->>> mikeio.read("dissolved_oxygen.dfs2", layers="bottom")
-<mikeio.Dataset>
-dims: (time:1, y:112, x:91)
-time: 2001-12-28 00:00:00 (time-invariant)
-geometry: Grid2D (ny=112, nx=91)
-items:
-  0:  Diss. oxygen (mg/l) <Concentration 3> (mg per liter)
-```
-
-## Grid3D
-
-The spatial information is available in the `geometry` attribute (accessible from Dfs3, Dataset, and DataArray), which in the case of a dfs3 file is a [`Grid3D`](Grid3D) geometry. 
-
-```python
->>> dfs = mikeio.open("dissolved_oxygen.dfs3")
->>> dfs.geometry
-<mikeio.Grid3D>
-x: [0, 150, ..., 1.35e+04] (nx=91, dx=150)
-y: [0, 150, ..., 1.665e+04] (ny=112, dy=150)
-z: [0, 1, ..., 16] (nz=17, dz=1)
-origin: (10.37, 55.42), orientation: 18.125
-projection: PROJCS["UTM-32",GEOGCS["Unused",DATUM["UTM...
-```
-
-Grid3D's primary properties and methods are: 
-
-* `x` 
-* `nx`
-* `dx`
-* `y`
-* `ny`
-* `dy`
-* `z`
-* `nz`
-* `dz`
-* `origin`
-* `projection`
-* `contains()`
-* `isel()`
-
-See [API specification](Grid3D) below for details.
-
-## Dfs3 Example notebooks
-
-* [Dfs3-Basic](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfs3%20-%20Basic.ipynb)  
-
-
-
-## Dfs3 API
-
-```{eval-rst}
-.. autoclass:: mikeio.Dfs3
-	:members:
-	:inherited-members:
-```
-
-## Grid3D API
-
-```{eval-rst}
-.. autoclass:: mikeio.Grid3D
-	:members:
-	:inherited-members:
-```
-
diff --git a/docs/dfsu-1dv-vertical-column.md b/docs/dfsu-1dv-vertical-column.md
deleted file mode 100644
index 075bd628..00000000
--- a/docs/dfsu-1dv-vertical-column.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Dfsu 1DV Vertical Column
-
-
-
-
-## FM Geometry Vertical Column API
-
-```{eval-rst}
-.. autoclass:: mikeio.spatial.FM_geometry.GeometryFMVerticalColumn
-	:members:
-	:inherited-members:
-```
-
-
diff --git a/docs/dfsu-2d.md b/docs/dfsu-2d.md
deleted file mode 100644
index 40e226ae..00000000
--- a/docs/dfsu-2d.md
+++ /dev/null
@@ -1,69 +0,0 @@
-# Dfsu 2D
-
-
-## Dfsu functionality
-
-A Dfsu class (e.g. Dfsu2DH) is returned by `mikeio.open()` if the argument is a dfsu file. 
-
-Apart from the common [dfsu-geometry properties and methods](./dfu-mesh-overview.md#mike-io-flexible-mesh-geometry), Dfsu2DH has the following *properties*:
-
-
-```{eval-rst}
-.. autosummary::
-    :nosignatures:
-
-    mikeio.dfsu._Dfsu.deletevalue
-    mikeio.dfsu._Dfsu.n_items
-    mikeio.dfsu._Dfsu.items
-    mikeio.dfsu._Dfsu.n_timesteps
-    mikeio.dfsu._Dfsu.start_time
-    mikeio.dfsu._Dfsu.end_time
-    mikeio.dfsu._Dfsu.timestep
-    mikeio.dfsu._Dfsu.is_2d
-```
-
-Dfsu2DH has the following *methods*:
-
-```{eval-rst}
-.. autosummary::
-    :nosignatures:
-
-    mikeio.dfsu._Dfsu.read
-    mikeio.dfsu._Dfsu.write
-    mikeio.dfsu._Dfsu.write_header
-    mikeio.dfsu._Dfsu.close
-```
-
-See the [API specification](Dfsu2DH) below for a detailed description. 
-
-See the [Dfsu Read Example notebook](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfsu%20-%20Read.ipynb) for basic dfsu functionality.
-
-
-
-## Dfsu 2DH API
-
-```{eval-rst}
-.. autoclass:: mikeio.dfsu.Dfsu2DH
-	:members:
-	:inherited-members:
-```
-
-
-## Flexible Mesh Geometry API
-
-See [Flexible Mesh Geometry API](GeometryFM)
-
-
-## FM Geometry Plotter API
-
-See [FM Geometry Plotter API](_GeometryFMPlotter)
-
-## DataArray Plotter FM API
-
-A DataArray `da` with a GeometryFM geometry can be plotted using `da.plot`. 
-
-```{eval-rst}
-.. autoclass:: mikeio.dataarray._DataArrayPlotterFM
-	:members:
-	:inherited-members:
-```
\ No newline at end of file
diff --git a/docs/dfsu-2dv-vertical-profile.md b/docs/dfsu-2dv-vertical-profile.md
deleted file mode 100644
index f4f3bfbf..00000000
--- a/docs/dfsu-2dv-vertical-profile.md
+++ /dev/null
@@ -1,69 +0,0 @@
-# Dfsu 2DV Vertical Profile
-
-
-In addition to the common [dfsu-geometry properties and methods](./dfu-mesh-overview.md#mike-io-flexible-mesh-geometry), Dfsu2DV has the below additional *properties* (from it's geometry [GeometryFMVerticalProfile](GeometryFMVerticalProfile)): 
-
-
-
-```{eval-rst}
-.. autosummary::
-    :nosignatures:
-
-    mikeio.dfsu_layered.Dfsu2DV.n_layers
-    mikeio.dfsu_layered.Dfsu2DV.n_sigma_layers
-    mikeio.dfsu_layered.Dfsu2DV.n_z_layers
-    mikeio.dfsu_layered.Dfsu2DV.layer_ids
-    mikeio.dfsu_layered.Dfsu2DV.top_elements
-    mikeio.dfsu_layered.Dfsu2DV.bottom_elements
-    mikeio.dfsu_layered.Dfsu2DV.n_layers_per_column
-    mikeio.dfsu_layered.Dfsu2DV.e2_e3_table
-    mikeio.dfsu_layered.Dfsu2DV.elem2d_ids
-```
-
-
-And in addition to the basic dfsu functionality, Dfsu2DV has the below additional *methods*: 
-
-```{eval-rst}
-.. autosummary::
-    :nosignatures:
-
-    mikeio.dfsu_layered.Dfsu2DV.get_layer_elements    
-```
-
-
-
-```{warning}
-In MIKE Zero, layer ids are 1-based. In MIKE IO, all ids are **0-based**following standard Python indexing. The bottom layer is 0. In early versionsof MIKE IO, layer ids was 1-based! From release 0.10 all ids are 0-based.
-```
-
-## Vertical Profile Dfsu example notebooks
-
-* [Dfsu - Vertical Profile.ipynb](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfsu%20-%20Vertical%20Profile.ipynb) 
-
-
-
-## Dfsu 2DV Vertical Profile API
-
-```{eval-rst}
-.. autoclass:: mikeio.dfsu_layered.Dfsu2DV
-	:members:
-	:inherited-members:
-```
-
-## FM Geometry 2DV Vertical Profile API
-
-```{eval-rst}
-.. autoclass:: mikeio.spatial.FM_geometry.GeometryFMVerticalProfile
-	:members:
-	:inherited-members:
-```
-
-## DataArray Plotter FM Vertical Profile API
-
-A DataArray `da` with a GeometryFMVerticalProfile geometry can be plotted using `da.plot`. 
-
-```{eval-rst}
-.. autoclass:: mikeio.dataarray._DataArrayPlotterFMVerticalProfile
-	:members:
-	:inherited-members:
-```
\ No newline at end of file
diff --git a/docs/dfsu-3d.md b/docs/dfsu-3d.md
deleted file mode 100644
index c5c2c314..00000000
--- a/docs/dfsu-3d.md
+++ /dev/null
@@ -1,60 +0,0 @@
-# Dfsu 3D
-
-
-In addition to the common [dfsu-geometry properties and methods](./dfu-mesh-overview.md#mike-io-flexible-mesh-geometry), Dfsu3D has the below additional *properties* (from it's geometry [GeometryFM3D](GeometryFM3D)): 
-
-```{eval-rst}
-.. autosummary::
-    :nosignatures:
-
-    mikeio.dfsu_layered.Dfsu3D.n_layers
-    mikeio.dfsu_layered.Dfsu3D.n_sigma_layers
-    mikeio.dfsu_layered.Dfsu3D.n_z_layers
-    mikeio.dfsu_layered.Dfsu3D.layer_ids
-    mikeio.dfsu_layered.Dfsu3D.top_elements
-    mikeio.dfsu_layered.Dfsu3D.bottom_elements
-    mikeio.dfsu_layered.Dfsu3D.n_layers_per_column
-    mikeio.dfsu_layered.Dfsu3D.geometry2d
-    mikeio.dfsu_layered.Dfsu3D.e2_e3_table
-    mikeio.dfsu_layered.Dfsu3D.elem2d_ids
-```
-
-
-And in addition to from the basic dfsu functionality, Dfsu3D has the below additional *methods*: 
-
-```{eval-rst}
-.. autosummary::
-    :nosignatures:
-
-    mikeio.dfsu_layered.Dfsu3D.get_layer_elements
-    mikeio.dfsu_layered.Dfsu3D.find_nearest_profile_elements
-```
-
-
-
-```{warning}
-In MIKE Zero, layer ids are 1-based. In MIKE IO, all ids are **0-based**following standard Python indexing. The bottom layer is 0. In early versionsof MIKE IO, layer ids was 1-based! From release 0.10 all ids are 0-based.
-```
-
-
-## Dfsu 3D example notebooks
-
-See the [Dfsu - 3D sigma-z.ipynb](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfsu%20-%203D%20sigma-z.ipynb) for 3d dfsu functionality.
-
-
-## Dfsu 3D API
-
-```{eval-rst}
-.. autoclass:: mikeio.dfsu_layered.Dfsu3D
-	:members:
-	:inherited-members:
-```
-
-## FM Geometry 3D API
-
-```{eval-rst}
-.. autoclass:: mikeio.spatial.FM_geometry.GeometryFM3D
-	:members:
-	:inherited-members:
-```
-
diff --git a/docs/dfsu-mesh-overview.md b/docs/dfsu-mesh-overview.md
deleted file mode 100644
index 1039dc62..00000000
--- a/docs/dfsu-mesh-overview.md
+++ /dev/null
@@ -1,119 +0,0 @@
-# Dfsu and Mesh Overview
-
-Dfsu and mesh files are both flexible mesh file formats used by MIKE 21/3 engines. 
-The .mesh file is an ASCII file for storing the flexible mesh geometry. 
-The .dfsu file is a binary dfs file with data on this mesh. The mesh geometry is 
-available in a .dfsu file as static items.  
-
-For a detailed description of the .mesh and .dfsu file specification see the [flexible file format documentation](https://manuals.mikepoweredbydhi.help/2021/General/FM_FileSpecification.pdf).
-
-
-## The flexible mesh
-
-The mesh geometry in a .mesh or a .dfsu file consists of a list of nodes and a list of elements.
-
-Each node has:
-
-* Node id
-* x,y,z coordinates
-* Code (0 for internal water points, 1 for land, >1 for open boundary)
-
-Each element has:
-
-* Element id
-* Element table; specifies for each element the nodes that defines the element. 
-(the number of nodes defines the type: triangular, quadrilateral, prism etc.)
-
-
-```{warning} 
-In MIKE Zero, node ids, element ids and layer ids are 1-based.  In MIKE IO, all ids are **0-based** following standard Python indexing. That means, as an example, that when finding the element closest to a point its id will be 1 lower in MIKE IO compared to examining the file in MIKE Zero.
-```
-
-## MIKE IO Flexible Mesh Geometry 
-
-MIKE IO has a Flexible Mesh Geometry class, `GeometryFM`, containing the list of node coordinates and the element table which defines the mesh, as well as a number of derived properties (e.g. element coordinates) and methods making it convenient to work with the mesh. 
-
-| Property  |      Description     |
-|----------|--------------|
-| `n_nodes` | Number of nodes | 
-| `node_coordinates` | Coordinates (x,y,z) of all nodes | 
-| `codes` | Codes of all nodes (0:water, 1:land, >=2:open boundary) | 
-| `boundary_polylines` | Lists of closed polylines defining domain outline | 
-| `n_elements` | Number of elements | 
-| `element_coordinates` | Center coordinates of each element | 
-| `element_table` | Element to node connectivity | 
-| `max_nodes_per_element` | The maximum number of nodes for an element | 
-| `is_tri_only` | Does the mesh consist of triangles only? | 
-| `projection_string` | The projection string | 
-| `is_geo` | Are coordinates geographical (LONG/LAT)? | 
-| `is_local_coordinates` | Are coordinates relative (NON-UTM)? | 
-| `type_name` | Type name, e.g. Dfsu2D| 
-
-
-| Method  |      Description     |
-|----------|--------------|
-| `contains()` | test if a list of points are contained by mesh | 
-| `find_index()` | Find index of elements containing points/area|
-| `isel()` | Get subset geometry for list of indicies |
-| `find_nearest_points()` | Find index of nearest elements (optionally for a list) |
-| `plot` | Plot the geometry |
-| `get_overset_grid()` | Get a Grid2D covering the domain |
-| `to_shapely()` | Export mesh as shapely MultiPolygon | 
-| `get_element_area()` | Calculate the horizontal area of each element | 
-
-
-These properties and methods are accessible from the geometry, but also from the Mesh/Dfsu object. 
-
-If a .dfsu file is *read* with `mikeio.read()`, the returned Dataset ds will contain a Flexible Mesh Geometry `geometry`. If a .dfsu or a .mesh file is *opened* with mikeio.open, the returned object will also contain a Flexible Mesh Geometry `geometry`. 
-
-```python
->>> import mikeio
->>> ds = mikeio.read("oresundHD_run1.dfsu")
->>> ds.geometry
-Flexible Mesh Geometry: Dfsu2D
-number of nodes: 2046
-number of elements: 3612
-projection: UTM-33
-
->>> dfs = mikeio.open("oresundHD_run1.dfsu")
->>> dfs.geometry
-Flexible Mesh Geometry: Dfsu2D
-number of nodes: 2046
-number of elements: 3612
-projection: UTM-33
-```
-
-
-
-
-## Common Dfsu and Mesh properties
-
-MIKE IO has Dfsu classes for .dfsu files 
-and a [Mesh class](mikeio.Mesh) for .mesh files which both 
-have a [Flexible Mesh Geometry](GeometryFM) accessible through the ´geometry´ accessor. 
-
-
-
-
-## Dfsu types
-
-The following dfsu file types are supported by MIKE IO.
-
-* 2D horizontal. 
-* 3D layered. 
-* 2D vertical profile - a vertical slice through a 3D layered file. 
-* 1D vertical column - a vertical dfs1 file and is produced by taking out one column of a 3D layered file.
-* 3D/4D SW, two horizontal dimensions and 1-2 spectral dimensions. Output from MIKE 21 SW.
-
-When a dfsu file is opened with mikeio.open() the returned dfs object will be a specialized class [Dfsu2DH](Dfsu2DH), [Dfsu3D](Dfsu3D), [Dfsu2DV](Dfsu2DV), or [DfsuSpectral](DfsuSpectral) according to the type of dfsu file. 
-
-The layered files (3d, 2d/1d vertical) can have both sigma- and z-layers or only sigma-layers. 
-
-In most cases values are stored in cell centers and vertical (z) information in nodes, 
-but the following values types exists: 
-
-* Standard value type, storing values on elements and/or nodes. This is the default type.
-* Face value type, storing values on element faces. This is used e.g. for HD decoupling files, to store the discharge between elements.
-* Spectral value type, for each node or element, storing vales for a number of frequencies and/or directions. This is the file type for spectral output from the MIKE 21 SW. 
-
-
diff --git a/docs/dfsu-spectral.md b/docs/dfsu-spectral.md
deleted file mode 100644
index 2b7404ec..00000000
--- a/docs/dfsu-spectral.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Dfsu Spectral
-
-
-MIKE 21 SW can output spectral information in *points*, along *lines* or in an *area*. If the full (2d) spectra are stored, the dfsu files will have two additional axes: frequency and directions. 
-
-
-## Spectral Dfsu example notebooks
-
-* [Dfsu - Spectral data.ipynb](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfsu%20-%20Spectral%20data.ipynb) 
-* [Dfsu - Spectral data other formats.ipynb](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfsu%20-%20Spectral%20data%20other%20formats.ipynb) 
-
-
-
-## Dfsu Spectral API
-
-```{eval-rst}
-.. autoclass:: mikeio.dfsu_spectral.DfsuSpectral
-	:members:
-	:inherited-members:
-```
-
-
-## FM Geometry Point Spectrum API
-
-```{eval-rst}
-.. autoclass:: mikeio.spatial.FM_geometry.GeometryFMPointSpectrum
-	:members:
-	:inherited-members:
-```
-
-## FM Geometry Line Spectrum API
-
-```{eval-rst}
-.. autoclass:: mikeio.spatial.FM_geometry.GeometryFMLineSpectrum
-	:members:
-	:inherited-members:
-```
-
-## FM Geometry Area Spectrum API
-
-```{eval-rst}
-.. autoclass:: mikeio.spatial.FM_geometry.GeometryFMAreaSpectrum
-	:members:
-	:inherited-members:
-```
-
diff --git a/docs/dfsu.rst b/docs/dfsu.rst
new file mode 100644
index 00000000..642ce43f
--- /dev/null
+++ b/docs/dfsu.rst
@@ -0,0 +1,222 @@
+.. _dfsu:
+
+Dfsu and Mesh
+*************
+
+.. warning::
+    TODO Not updated to MIKE IO 1.0
+
+Dfsu and mesh files are both flexible mesh file formats used by MIKE 21/3 engines. 
+The .mesh file is an ASCII format for storing the flexible mesh geometry. 
+The .dfsu file is a binary dfs file with data on this mesh. The mesh geometry is 
+available in a .dfsu file as static items.  
+
+For a detailed description of the .mesh and .dfsu file specification see the `flexible file format documentation <https://manuals.mikepoweredbydhi.help/2021/General/FM_FileSpecification.pdf>`_.
+
+
+The flexible mesh
+---------------------
+
+The mesh geometry in a .mesh or a .dfsu file consists of a number of nodes and a number of elements.
+
+Each node has:
+
+* Node id
+* X,Y,Z coordinate
+* Code for the boundary
+
+Each element has:
+
+* Element id
+* Element type; triangular, quadrilateral, prism etc.
+* Element table; specifies for each element the nodes that defines the element. 
+
+.. warning::
+    In MIKE Zero, node ids, element ids and layer ids are 1-based. 
+    In MIKE IO, all ids are **0-based** following standard Python indexing. 
+    That means, as an example, that when finding the element closest to a 
+    point its id will be 1 lower in MIKE IO compared to examining the file in MIKE Zero.
+
+
+
+Common Dfsu and Mesh properties
+-------------------------------
+
+MIKE IO has a `Dfsu class <#mikeio.Dfsu>`_ for handling .dfsu files 
+and a `Mesh class <#mikeio.Mesh>`_ for handling .mesh files both they inherit from the 
+same base class and have the same core functionality. 
+
+.. autosummary::
+    :nosignatures:
+
+    mikeio.Mesh.n_nodes
+    mikeio.Mesh.node_coordinates
+    mikeio.Mesh.codes
+    mikeio.Mesh.boundary_polylines
+    mikeio.Mesh.n_elements
+    mikeio.Mesh.element_coordinates
+    mikeio.Mesh.element_table
+    mikeio.Mesh.max_nodes_per_element
+    mikeio.Mesh.is_tri_only
+    mikeio.Mesh.projection_string
+    mikeio.Mesh.is_geo
+    mikeio.Mesh.is_local_coordinates
+    mikeio.Mesh.type_name    
+
+
+Common Dfsu and Mesh methods
+----------------------------
+
+
+.. autosummary::
+    :nosignatures:
+
+    mikeio.Mesh.contains
+    mikeio.Mesh.find_nearest_elements
+    mikeio.Mesh.plot
+    mikeio.Mesh.to_shapely
+    mikeio.Mesh.get_overset_grid
+    mikeio.Mesh.get_2d_interpolant
+    mikeio.Mesh.interp2d
+    mikeio.Mesh.get_element_area
+    mikeio.Mesh.elements_to_geometry
+
+
+Mesh functionality
+------------------
+
+The Mesh class is initialized with a mesh or a dfsu file. 
+
+
+.. code-block:: python
+
+    >>> msh = Mesh("../tests/testdata/odense_rough.mesh")
+    >>> msh
+    Number of elements: 654
+    Number of nodes: 399
+    Projection: UTM-33
+
+
+Apart from the common flexible file functionality, 
+the Mesh object has the following methods and properties:
+
+.. autosummary::
+    :nosignatures:
+
+    mikeio.Mesh.write
+    mikeio.Mesh.plot_boundary_nodes
+    mikeio.Mesh.zn
+
+See the `Mesh API specification <#mikeio.Mesh>`_ below for a detailed description. 
+See the `Mesh Example notebook <https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Mesh.ipynb>`_ for more Mesh operations (including shapely examples).
+
+
+Dfsu functionality
+------------------
+
+The Dfsu class is initialized with a mesh or a dfsu file. 
+
+Apart from the common flexible file functionality, the Dfsu has the following *properties*:
+
+.. autosummary::
+    :nosignatures:
+
+    mikeio.Dfsu.deletevalue
+    mikeio.Dfsu.n_items
+    mikeio.Dfsu.items
+    mikeio.Dfsu.n_timesteps
+    mikeio.Dfsu.start_time
+    mikeio.Dfsu.end_time
+    mikeio.Dfsu.timestep
+    mikeio.Dfsu.is_2d
+
+
+Apart from the common flexible file functionality, the Dfsu has the following *methods*:
+
+.. autosummary::
+    :nosignatures:
+
+    mikeio.Dfsu.read
+    mikeio.Dfsu.write
+    mikeio.Dfsu.write_header
+    mikeio.Dfsu.close
+    mikeio.Dfsu.extract_track
+
+See the `Dfsu API specification <#mikeio.Dfsu>`_ below for a detailed description. 
+See the `Dfsu Read Example notebook <https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Dfsu%20-%20Read.ipynb>`_ for basic dfsu functionality.
+
+
+
+Dfsu types
+----------
+
+The following dfsu file types are supported by MIKE IO.
+
+* 2D horizontal. 
+* 3D layered. 
+* 2D vertical profile - a vertical slice through a 3D layered file. 
+* 1D vertical column - a vertical dfs1 file and is produced by taking out one column of a 3D layered file.
+* 3D/4D SW, two horizontal dimensions and 1-2 spectral dimensions. Output from MIKE 21 SW.
+
+The layered files (3d, 2d/1d vertical) can have both sigma- and z-layers or only sigma-layers. 
+
+In most cases values are stored in cell centers and vertical (z) information in nodes, 
+but the following values types exists: 
+
+* Standard value type, storing values on elements and/or nodes. This is the default type.
+* Face value type, storing values on element faces. This is used e.g. for HD decoupling files, to store the discharge between elements.
+* Spectral value type, for each node or element, storing vales for a number of frequencies and/or directions. This is the file type for spectral output from the MIKE 21 SW. 
+
+
+
+
+Layered dfsu files
+------------------ 
+
+There are three type of layered dfsu files: 3D dfsu, 2d vertical slices and 1d vertical profiles.
+
+Apart from the basic dfsu functionality, layered dfsu have the below additional *properties*: 
+
+.. autosummary::
+    :nosignatures:
+
+    mikeio.Dfsu.n_layers
+    mikeio.Dfsu.n_sigma_layers
+    mikeio.Dfsu.n_z_layers
+    mikeio.Dfsu.layer_ids
+    mikeio.Dfsu.top_elements
+    mikeio.Dfsu.bottom_elements
+    mikeio.Dfsu.n_layers_per_column
+    mikeio.Dfsu.geometry2d
+    mikeio.Dfsu.e2_e3_table
+    mikeio.Dfsu.elem2d_ids
+
+Apart from the basic dfsu functionality, layered dfsu have the below additional *methods*: 
+
+.. autosummary::
+    :nosignatures:
+
+    mikeio.Dfsu.get_layer_elements
+    mikeio.Dfsu.find_nearest_profile_elements
+    mikeio.Dfsu.plot_vertical_profile
+
+.. warning::
+    In MIKE Zero, layer ids are 1-based. 
+    In MIKE IO, all ids are **0-based** following standard Python indexing. 
+    The bottom layer is 0. 
+    In previous versions of MIKE IO, layer ids was 1-based! 
+    From release 0.10 all ids are 0-based.  
+
+
+
+Dfsu API
+--------
+.. autoclass:: mikeio.Dfsu
+	:members:
+	:inherited-members:
+
+Mesh API
+--------
+.. autoclass:: mikeio.Mesh
+	:members:
+	:inherited-members:
\ No newline at end of file
diff --git a/docs/eum.md b/docs/eum.md
index 57c4b4a7..a9509beb 100644
--- a/docs/eum.md
+++ b/docs/eum.md
@@ -39,15 +39,11 @@ mm per day
 2004
 ```
 
-
-
-## EUM example notebooks
-
 See the [Units notebook](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Units.ipynb) for more examples.
 
 
-## EUM API
-
+EUM API
+-------
 ```{eval-rst}
 .. automodule:: mikeio.eum
 	:members:
diff --git a/docs/generic.md b/docs/generic.md
index 0cf06403..96a34ce7 100644
--- a/docs/generic.md
+++ b/docs/generic.md
@@ -19,13 +19,15 @@ All methods in the generic module creates a new dfs file.
 >>> generic.concat(["fileA.dfs2", "fileB.dfs2"], "new_file.dfs2")
 ```
 
-## Generic example notebooks
-
 See the [Generic notebook](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Generic.ipynb) for more examples.
 
+
 ## Generic API
 
 ```{eval-rst}
+.. automodule:: mikeio
+	:members:
+
 .. automodule:: mikeio.generic
 	:members:
 ```
diff --git a/docs/getting-started.md b/docs/getting_started.md
similarity index 90%
rename from docs/getting-started.md
rename to docs/getting_started.md
index 833a35f5..75631c7d 100644
--- a/docs/getting-started.md
+++ b/docs/getting_started.md
@@ -11,7 +11,7 @@
 
 ## Dataset
 The [Dataset](Dataset) is the common MIKE IO data structure for data read from dfs files. 
-The  `mikeio.read()` method returns a Dataset with a [DataArray](dataarray) for each item.
+The  `mikeio.read()` method returns a Dataset with a [DataArray](DataArray) for each item.
 
 The DataArray have all the relevant information, e.g:
 
@@ -76,11 +76,11 @@ Items:
   0:  Elevation <Total Water Depth> (meter)
 ```
 
-Read more on the [Dfs2 page](dfs2).
+Read more on the [Dfs123 page](dfs123.rst).
 
 
 ## Generic dfs
-MIKE IO has [`generic`](generic.md) functionality that works for all dfs files: 
+MIKE IO has [`generic`](mikeio.generic) functionality that works for all dfs files: 
 
 * [`concat()`](generic.concat) - Concatenates files along the time axis
 * [`extract()`](generic.extract) - Extract timesteps and/or items to a new dfs file
@@ -97,4 +97,4 @@ from mikeio import generic
 generic.concat(["fileA.dfs2", "fileB.dfs2"], "new_file.dfs2")
 ```
 
-See [Generic page](generic.md) and the [Generic notebook](<https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Generic.ipynb>) for more examples.
+See [Generic page](mikeio.generic) and the [Generic notebook](<https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Generic.ipynb>) for more examples.
diff --git a/docs/index.md b/docs/index.md
index 52c0e4f0..74259782 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -29,7 +29,7 @@ $ pip install mikeio
 >>>  df = ds.to_dataframe()
 ```
 
-Read more in the [getting started guide](getting-started).
+Read more in the [getting started guide](getting_started).
 
 
 Where can I get help?
@@ -44,22 +44,12 @@ Where can I get help?
    :caption: Contents:
    :hidden:
 
-   getting-started
+   getting_started
    design
-   data-structures
    dataset
-   dataarray
-   dfs-overview
    dfs0
-   dfs1
-   dfs2
-   dfs3
-   dfsu-mesh-overview
-   mesh
-   dfsu-2d
-   dfsu-3d
-   dfsu-2dv-vertical-profile
-   dfsu-spectral
+   dfs123
+   dfsu   
    eum
    generic
 ```
\ No newline at end of file
diff --git a/docs/mesh.md b/docs/mesh.md
deleted file mode 100644
index f9c849bf..00000000
--- a/docs/mesh.md
+++ /dev/null
@@ -1,62 +0,0 @@
-# Mesh
-
-
-## Mesh functionality
-
-The Mesh class is returned by `mikeio.open("my.mesh")` if the argument is a mesh file (or previously using `mikeio.Mesh()` given a mesh or a dfsu file). 
-
-```python
->>> msh = mikeio.open("../tests/testdata/odense_rough.mesh")
->>> msh
-Number of elements: 654
-Number of nodes: 399
-Projection: UTM-33
-```
-
-In addition to the common [dfsu-geometry properties and methods](./dfu-mesh-overview.md#mike-io-flexible-mesh-geometry), `Mesh` has the following properties and methods:
-
-
-```{eval-rst}
-.. autosummary::
-    :nosignatures:
-
-    mikeio.Mesh.write
-    mikeio.Mesh.zn
-```
-
-See the [Mesh API specification](mikeio.Mesh) below for a detailed description. 
-
-
-
-## Mesh example notebooks
-
-See the [Mesh Example notebook](https://nbviewer.jupyter.org/github/DHI/mikeio/blob/main/notebooks/Mesh.ipynb) for more Mesh operations (including shapely examples).
-
-
-
-## Mesh API
-
-```{eval-rst}
-.. autoclass:: mikeio.Mesh
-	:members:
-	:inherited-members:
-```
-
-
-## Flexible Mesh Geometry API
-
-A mesh object 
-
-```{eval-rst}
-.. autoclass:: mikeio.spatial.FM_geometry.GeometryFM
-	:members:
-	:inherited-members:
-```
-
-## FM Geometry Plotter API
-
-```{eval-rst}
-.. autoclass:: mikeio.spatial.FM_geometry._GeometryFMPlotter
-	:members:
-	:inherited-members:
-```
\ No newline at end of file
diff --git a/mikeio/dataarray.py b/mikeio/dataarray.py
index 3158a084..e8d5b7c9 100644
--- a/mikeio/dataarray.py
+++ b/mikeio/dataarray.py
@@ -32,7 +32,7 @@ from .data_utils import DataUtilsMixin
 
 
 class _DataArrayPlotter:
-    """Context aware plotter (sensible plotting according to geometry)"""
+    """Context aware plotter"""
 
     def __init__(self, da: "DataArray") -> None:
         self.da = da
@@ -121,7 +121,6 @@ class _DataArrayPlotter:
         return result
 
     def line(self, ax=None, figsize=None, **kwargs):
-        """Plot data as lines (timeseries if time is present)"""
         fig, ax = self._get_fig_ax(ax, figsize)
         if self.da._has_time_axis:
             return self._timeseries(self.da.values, fig, ax, **kwargs)
@@ -157,19 +156,6 @@ class _DataArrayPlotter:
 
 
 class _DataArrayPlotterGrid1D(_DataArrayPlotter):
-    """Plot a DataArray with a Grid1D geometry
-
-    Examples
-    --------
-    >>> da = mikeio.read("tide1.dfs1")["Level"]
-    >>> da.plot()
-    >>> da.plot.line()
-    >>> da.plot.timeseries()
-    >>> da.plot.imshow()
-    >>> da.plot.pcolormesh()
-    >>> da.plot.hist()
-    """
-
     def __init__(self, da: "DataArray") -> None:
         super().__init__(da)
 
@@ -181,19 +167,16 @@ class _DataArrayPlotterGrid1D(_DataArrayPlotter):
             return self.pcolormesh(ax, **kwargs)
 
     def line(self, ax=None, figsize=None, **kwargs):
-        """Plot as spatial lines"""
         _, ax = self._get_fig_ax(ax, figsize)
         return self._lines(ax, **kwargs)
 
     def timeseries(self, ax=None, figsize=None, **kwargs):
-        """Plot as timeseries"""
         if self.da.n_timesteps == 1:
             raise ValueError("Not possible with single timestep DataArray")
         fig, ax = self._get_fig_ax(ax, figsize)
         return super()._timeseries(self.da.values, fig, ax, **kwargs)
 
     def imshow(self, ax=None, figsize=None, **kwargs):
-        """Plot as 2d"""
         if not self.da._has_time_axis:
             raise ValueError(
                 "Not possible without time axis. DataArray only has 1 dimension."
@@ -237,20 +220,6 @@ class _DataArrayPlotterGrid1D(_DataArrayPlotter):
 
 
 class _DataArrayPlotterGrid2D(_DataArrayPlotter):
-    """Plot a DataArray with a Grid2D geometry
-
-    If DataArray has multiple time steps, the first step will be plotted.
-
-    Examples
-    --------
-    >>> da = mikeio.read("gebco_sound.dfs2")["Elevation"]
-    >>> da.plot()
-    >>> da.plot.contour()
-    >>> da.plot.contourf()
-    >>> da.plot.pcolormesh()
-    >>> da.plot.hist()
-    """
-
     def __init__(self, da: "DataArray") -> None:
         super().__init__(da)
 
@@ -258,7 +227,6 @@ class _DataArrayPlotterGrid2D(_DataArrayPlotter):
         return self.pcolormesh(ax, figsize, **kwargs)
 
     def contour(self, ax=None, figsize=None, **kwargs):
-        """Plot data as contour lines"""
         _, ax = self._get_fig_ax(ax, figsize)
 
         x, y = self._get_x_y()
@@ -271,7 +239,6 @@ class _DataArrayPlotterGrid2D(_DataArrayPlotter):
         return ax
 
     def contourf(self, ax=None, figsize=None, **kwargs):
-        """Plot data as filled contours"""
         fig, ax = self._get_fig_ax(ax, figsize)
 
         x, y = self._get_x_y()
@@ -283,7 +250,6 @@ class _DataArrayPlotterGrid2D(_DataArrayPlotter):
         return ax
 
     def pcolormesh(self, ax=None, figsize=None, **kwargs):
-        """Plot data as coloured patches"""
         fig, ax = self._get_fig_ax(ax, figsize)
 
         xn, yn = self._get_xn_yn()
@@ -328,56 +294,32 @@ class _DataArrayPlotterGrid2D(_DataArrayPlotter):
 
 
 class _DataArrayPlotterFM(_DataArrayPlotter):
-    """Plot a DataArray with a GeometryFM geometry
-
-    If DataArray has multiple time steps, the first step will be plotted.
-
-    If DataArray is 3D the surface layer will be plotted.
-
-    Examples
-    --------
-    >>> da = mikeio.read("HD2D.dfsu")["Surface elevation"]
-    >>> da.plot()
-    >>> da.plot.contour()
-    >>> da.plot.contourf()
-
-    >>> da.plot.mesh()
-    >>> da.plot.outline()
-    >>> da.plot.hist()
-    """
-
     def __init__(self, da: "DataArray") -> None:
         super().__init__(da)
 
     def __call__(self, ax=None, figsize=None, **kwargs):
-        """Plot data as coloured patches"""
         ax = self._get_ax(ax, figsize)
         return self._plot_FM_map(ax, **kwargs)
 
     def patch(self, ax=None, figsize=None, **kwargs):
-        """Plot data as coloured patches"""
         ax = self._get_ax(ax, figsize)
         kwargs["plot_type"] = "patch"
         return self._plot_FM_map(ax, **kwargs)
 
     def contour(self, ax=None, figsize=None, **kwargs):
-        """Plot data as contour lines"""
         ax = self._get_ax(ax, figsize)
         kwargs["plot_type"] = "contour"
         return self._plot_FM_map(ax, **kwargs)
 
     def contourf(self, ax=None, figsize=None, **kwargs):
-        """Plot data as filled contours"""
         ax = self._get_ax(ax, figsize)
         kwargs["plot_type"] = "contourf"
         return self._plot_FM_map(ax, **kwargs)
 
     def mesh(self, ax=None, figsize=None, **kwargs):
-        """Plot mesh only"""
         return self.da.geometry.plot.mesh(figsize=figsize, ax=ax, **kwargs)
 
     def outline(self, ax=None, figsize=None, **kwargs):
-        """Plot domain outline (using the boundary_polylines property)"""
         return self.da.geometry.plot.outline(figsize=figsize, ax=ax, **kwargs)
 
     def _plot_FM_map(self, ax, **kwargs):
@@ -410,21 +352,6 @@ class _DataArrayPlotterFM(_DataArrayPlotter):
 
 
 class _DataArrayPlotterFMVerticalColumn(_DataArrayPlotter):
-    """Plot a DataArray with a GeometryFMVerticalColumn geometry
-
-    If DataArray has multiple time steps, the first step will be plotted.
-
-    Examples
-    --------
-    >>> ds = mikeio.read("oresund_sigma_z.dfsu")
-    >>> dsp = ds.sel(x=333934.1, y=6158101.5)
-    >>> da = dsp["Temperature"]
-    >>> dsp.plot()
-    >>> dsp.plot(extrapolate=False, marker='o')
-    >>> dsp.plot.pcolormesh()
-    >>> dsp.plot.hist()
-    """
-
     def __init__(self, da: "DataArray") -> None:
         super().__init__(da)
 
@@ -433,7 +360,6 @@ class _DataArrayPlotterFMVerticalColumn(_DataArrayPlotter):
         return self.line(ax, **kwargs)
 
     def line(self, ax=None, figsize=None, extrapolate=True, **kwargs):
-        """Plot data as vertical lines"""
         ax = self._get_ax(ax, figsize)
         return self._line(ax, extrapolate=extrapolate, **kwargs)
 
@@ -466,7 +392,6 @@ class _DataArrayPlotterFMVerticalColumn(_DataArrayPlotter):
         return ax
 
     def pcolormesh(self, ax=None, figsize=None, **kwargs):
-        """Plot data as coloured patches"""
         fig, ax = self._get_fig_ax(ax, figsize)
         ze = self.da.geometry.calc_ze()
         pos = ax.pcolormesh(
@@ -484,18 +409,6 @@ class _DataArrayPlotterFMVerticalColumn(_DataArrayPlotter):
 
 
 class _DataArrayPlotterFMVerticalProfile(_DataArrayPlotter):
-    """Plot a DataArray with a 2DV GeometryFMVerticalProfile geometry
-
-    If DataArray has multiple time steps, the first step will be plotted.
-
-    Examples
-    --------
-    >>> da = mikeio.read("oresund_vertical_slice.dfsu")["Temperature"]
-    >>> da.plot()
-    >>> da.plot.mesh()
-    >>> da.plot.hist()
-    """
-
     def __init__(self, da: "DataArray") -> None:
         super().__init__(da)
 
diff --git a/mikeio/dfs1.py b/mikeio/dfs1.py
index 18683a8d..f8fa22ca 100644
--- a/mikeio/dfs1.py
+++ b/mikeio/dfs1.py
@@ -23,7 +23,15 @@ class Dfs1(_Dfs123):
 
         if filename:
             self._read_dfs1_header()
-            self.geometry = Grid1D(x0=self._x0, dx=self._dx, nx=self._nx)
+            origin = self._longitude, self._latitude
+            self.geometry = Grid1D(
+                x0=self._x0,
+                dx=self._dx,
+                nx=self._nx,
+                projection=self._projstr,
+                origin=origin,
+                orientation=self._orientation,
+            )
 
     def __repr__(self):
         out = ["<mikeio.Dfs1>"]
@@ -52,6 +60,7 @@ class Dfs1(_Dfs123):
             raise FileNotFoundError(self._filename)
 
         self._dfs = DfsFileFactory.Dfs1FileOpen(self._filename)
+        self._x0 = self._dfs.SpatialAxis.X0
         self._dx = self._dfs.SpatialAxis.Dx
         self._nx = self._dfs.SpatialAxis.XCount
 
@@ -139,7 +148,17 @@ class Dfs1(_Dfs123):
             )
         )
 
+    @property
+    def x0(self):
+        """Start point of x values (often 0)"""
+        return self._x0
+
     @property
     def dx(self):
         """Step size in x direction"""
         return self._dx
+
+    @property
+    def nx(self):
+        """Number of node values"""
+        return self._nx
diff --git a/mikeio/spatial/FM_geometry.py b/mikeio/spatial/FM_geometry.py
index f528d1d9..3dd03e01 100644
--- a/mikeio/spatial/FM_geometry.py
+++ b/mikeio/spatial/FM_geometry.py
@@ -75,36 +75,19 @@ class GeometryFMPointSpectrum(_Geometry):
 
 
 class _GeometryFMPlotter:
-    """Plot GeometryFM
-
-    Examples
-    --------
-    >>> ds = mikeio.read("HD2D.dfsu")
-    >>> g = ds.geometry
-    >>> g.plot()          # bathymetry (as patches)
-    >>> g.plot.contour()  # bathymetry contours
-    >>> g.plot.contourf() # filled bathymetry contours
-    >>> g.plot.mesh()     # mesh only
-    >>> g.plot.outline()  # domain outline only
-    >>> g.plot.boundary_nodes()
-    """
-
     def __init__(self, geometry: "GeometryFM") -> None:
         self.g = geometry
 
     def __call__(self, ax=None, figsize=None, **kwargs):
-        """Plot bathymetry as coloured patches"""
         ax = self._get_ax(ax, figsize)
         return self._plot_FM_map(ax, **kwargs)
 
     def contour(self, ax=None, figsize=None, **kwargs):
-        """Plot bathymetry as contour lines"""
         ax = self._get_ax(ax, figsize)
         kwargs["plot_type"] = "contour"
         return self._plot_FM_map(ax, **kwargs)
 
     def contourf(self, ax=None, figsize=None, **kwargs):
-        """Plot bathymetry as filled contours"""
         ax = self._get_ax(ax, figsize)
         kwargs["plot_type"] = "contourf"
         return self._plot_FM_map(ax, **kwargs)
@@ -136,7 +119,6 @@ class _GeometryFMPlotter:
         )
 
     def mesh(self, title="Mesh", figsize=None, ax=None):
-        """Plot mesh only"""
         from matplotlib.collections import PatchCollection
 
         ax = self._get_ax(ax=ax, figsize=figsize)
@@ -156,7 +138,6 @@ class _GeometryFMPlotter:
         return ax
 
     def outline(self, title="Outline", figsize=None, ax=None):
-        """Plot domain outline (using the boundary_polylines property)"""
         ax = self._get_ax(ax=ax, figsize=figsize)
         ax.set_aspect(self._plot_aspect())
 
@@ -172,7 +153,9 @@ class _GeometryFMPlotter:
         return ax
 
     def boundary_nodes(self, boundary_names=None, figsize=None, ax=None):
-        """Plot mesh boundary nodes and their code values"""
+        """
+        Plot mesh boundary nodes and their codes
+        """
         import matplotlib.pyplot as plt
 
         ax = self._get_ax(ax=ax, figsize=figsize)
@@ -527,6 +510,7 @@ class GeometryFM(_Geometry):
 
         Parameters
         ----------
+
         x: float or array(float)
             X coordinate(s) (easting or longitude)
         y: float or array(float)
@@ -553,21 +537,16 @@ class GeometryFM(_Geometry):
 
         Examples
         --------
-        >>> g = dfs.geometry
-        >>> id = g.find_nearest_elements(3, 4)
-        >>> ids = g.find_nearest_elements([3, 8], [4, 6])
-        >>> ids = g.find_nearest_elements(xy)
-        >>> ids = g.find_nearest_elements(3, 4, n_nearest=4)
-        >>> ids, d = g.find_nearest_elements(xy, return_distances=True)
-
-        >>> ids = g.find_nearest_elements(3, 4, z=-3)
-        >>> ids = g.find_nearest_elements(3, 4, layer=4)
-        >>> ids = g.find_nearest_elements(xyz)
-        >>> ids = g.find_nearest_elements(xyz, n_nearest=3)
-
-        See Also
-        --------
-        find_index : find element indicies for points or an area
+        >>> id = dfs.find_nearest_elements(3, 4)
+        >>> ids = dfs.find_nearest_elements([3, 8], [4, 6])
+        >>> ids = dfs.find_nearest_elements(xy)
+        >>> ids = dfs.find_nearest_elements(3, 4, n_nearest=4)
+        >>> ids, d = dfs.find_nearest_elements(xy, return_distances=True)
+
+        >>> ids = dfs.find_nearest_elements(3, 4, z=-3)
+        >>> ids = dfs.find_nearest_elements(3, 4, layer=4)
+        >>> ids = dfs.find_nearest_elements(xyz)
+        >>> ids = dfs.find_nearest_elements(xyz, n_nearest=3)
         """
         idx, d2d = self._find_n_nearest_2d_elements(x, y, n=n_nearest)
 
@@ -970,31 +949,9 @@ class GeometryFM(_Geometry):
         bnd_face_id = face_counts == 1
         return all_faces[uf_id[bnd_face_id]]
 
-    def isel(self, idx=None, axis="elements", keepdims=False):
-        """export a selection of elements to a new geometry
-
-        Typically not called directly, but by Dataset/DataArray's
-        isel() or sel() methods.
-
-        Parameters
-        ----------
-        idx : list(int)
-            list of element indicies
-        keepdims : bool, optional
-            Should the original Geometry type be kept (keepdims=True)
-            or should it be reduced e.g. to a GeometryPoint2D if possible
-            (keepdims=False), by default False
-
-        Returns
-        -------
-        Geometry
-            geometry subset
+    def isel(self, idx=None, axis="elements", simplify=True):
 
-        See Also
-        --------
-        find_index : find element indicies for points or an area
-        """
-        if (np.isscalar(idx) or len(idx)) == 1 and (not keepdims):
+        if (np.isscalar(idx) or len(idx)) == 1 and simplify:
             coords = self.element_coordinates[idx].flatten()
 
             if self.is_layered:
@@ -1008,44 +965,7 @@ class GeometryFM(_Geometry):
                 return self.elements_to_geometry(elements=idx, node_layers=None)
 
     def find_index(self, x=None, y=None, coords=None, area=None):
-        """Find element indicies for a number of points or within an area
-
-        This method will return elements *containing* the argument
-        points/area, which is not necessarily the same as the nearest.
-
-        Typically not called directly, but by Dataset/DataArray's
-        sel() method.
 
-        Parameters
-        ----------
-        x: float or array(float)
-            X coordinate(s) (easting or longitude)
-        y: float or array(float)
-            Y coordinate(s) (northing or latitude)
-        coords : np.array(float,float), optional
-            As an alternative to specifying x, and y individually,
-            the argument coords can be used instead.
-            (x,y)-coordinates of points to be found,
-            by default None
-        area : (float, float, float, float), optional
-            Bounding box of coordinates (left lower and right upper)
-            to be selected, by default None
-
-        Returns
-        -------
-        np.array
-            indicies of containing elements
-
-        Examples
-        --------
-        >>> g = dfs.geometry
-        >>> id = dfs.find_index(x=3.1, y=4.3)
-
-        See Also
-        --------
-        isel : get subset geometry for specific indicies
-        find_nearest_elements : find nearest instead of containing elements
-        """
         if (coords is not None) or (x is not None) or (y is not None):
             if area is not None:
                 raise ValueError(
diff --git a/mikeio/spatial/grid_geometry.py b/mikeio/spatial/grid_geometry.py
index 7021e1dc..dee7e16c 100644
--- a/mikeio/spatial/grid_geometry.py
+++ b/mikeio/spatial/grid_geometry.py
@@ -25,7 +25,7 @@ def _parse_grid_axis(name, x, x0=0.0, dx=None, nx=None):
         x = np.asarray(x)
         _check_equidistant(x)
         if len(x) > 1 and x[0] > x[-1]:
-            raise ValueError(f"{name} values must be increasing")
+            raise ValueError("{name} values must be increasing")
         x0 = x[0]
         dx = x[1] - x[0] if len(x) > 1 else 1.0
         nx = len(x)
@@ -98,7 +98,6 @@ class Grid1D(_Geometry):
         return f"Grid1D (n={self.nx}, dx={self.dx:.4g})"
 
     def find_index(self, x: float, **kwargs) -> int:
-        """Find nearest point"""
 
         d = (self.x - x) ** 2
         return np.argmin(d)
@@ -142,7 +141,6 @@ class Grid1D(_Geometry):
         return self._orientation
 
     def isel(self, idx, axis=0):
-        """Get a subset geometry from this geometry"""
 
         if not np.isscalar(idx):
             nc = None if self._nc is None else self._nc[idx, :]
@@ -480,8 +478,7 @@ class Grid2D(_Geometry):
 
     def find_index(self, x: float = None, y: float = None, coords=None, area=None):
         """Find nearest index (i,j) of point(s)
-
-        -1 is returned if point is outside grid
+           -1 is returned if point is outside grid
 
         Parameters
         ----------
@@ -823,7 +820,6 @@ class Grid3D(_Geometry):
         )
 
     def isel(self, idx, axis):
-        """Get a subset geometry from this geometry"""
         if not np.isscalar(idx):
             d = np.diff(idx)
             if np.any(d < 1) or not np.allclose(d, d[0]):


Test patch:
diff --git a/tests/test_dfs1.py b/tests/test_dfs1.py
index fe4be616..02e9639c 100644
--- a/tests/test_dfs1.py
+++ b/tests/test_dfs1.py
@@ -1,14 +1,12 @@
-import datetime
 import os
 
 import numpy as np
-import pandas as pd
 import pytest
 
 import mikeio
 
-from mikeio import Dfs1, Dataset
-from mikeio.eum import EUMType, EUMUnit, ItemInfo
+from mikeio import Dfs1
+from mikeio.eum import EUMType, EUMUnit
 
 
 def test_filenotexist():
@@ -37,6 +35,40 @@ def test_repr_empty():
     assert "Dfs1" in text
 
 
+def test_properties():
+    filename = r"tests/testdata/tide1.dfs1"
+    dfs = mikeio.open(filename)
+
+    assert dfs.dx == 0.06666692346334457
+    assert dfs.x0 == 0.0
+    assert dfs.nx == 10
+    assert dfs.projection_string == "LONG/LAT"
+    assert dfs.longitude == -5.0
+    assert dfs.latitude == 51.20000076293945
+    assert dfs.orientation == 180
+
+    g = dfs.geometry
+    assert isinstance(g, mikeio.Grid1D)
+    assert g.dx == 0.06666692346334457
+    assert g._x0 == 0.0
+    assert g.nx == 10
+    assert g.projection == "LONG/LAT"
+    assert g.origin == (-5.0, 51.20000076293945)
+    assert g.orientation == 180
+
+
+def test_read_write_properties(tmpdir):
+    # test that properties are the same after read-write
+    filename = r"tests/testdata/tide1.dfs1"
+    ds1 = mikeio.read(filename)
+
+    outfilename = os.path.join(tmpdir.dirname, "tide1.dfs1")
+    ds1.to_dfs(outfilename)
+    ds2 = mikeio.read(outfilename)
+
+    assert ds1.geometry == ds2.geometry
+
+
 def test_read():
 
     filename = r"tests/testdata/random.dfs1"