DHI.Services.MIKECore – Internal Guide¶
This doc shows how to open and work with MIKE Zero files (dfs0/dfs2/dfsu) through
DHI.Services.MIKECore. It covers the architecture, capabilities, configuration, file/ID conventions, and usage patterns. Per-domain API details still live with those services (TimeSeries, GIS, Map, Mesh, etc.); this is the big picture and how to leverage the providers.
What MIKECore is¶
A set of lightweight providers that read/write MIKE Zero files via the managed MIKE APIs:
- dfs0 (point time series)
- dfs2 (2D gridded rasters, time-varying)
- dfsu (unstructured meshes, 2D/3D, time-varying)
MIKECore focuses on:
- Direct file access through
IFileSource(local folders by default; can be swapped for cloud/backed stores). - No ORM / no DB—it reads/writes MIKE files directly.
- Vendor libraries:
Generic.MikeZero.DFS.*,SkiaSharpfor map rendering, and DHI GIS utilities for projections and geometry.
Key building blocks¶
IFileSource & file addressing¶
All providers receive either:
- an
IFileSource+ relative path/prefix, or - a connection string that is usually just a file path (or a folder for map sources).
Internally each provider uses:
_fileSource.ReadDfs0 / ReadDfs2 / ReadDfsu(path, dfsFile => { ... })
This guarantees safe open/close and lets implementations hook alternative storage.
Projections & coordinates¶
- The MIKE projection (WKT, lon/lat, orientation) is read from the file header.
- The
Cartographyhelpers handle Geo <-> projected transforms plus true-north rotation corrections where needed.
Delete values¶
- dfs files encode missing data (
DeleteValueFloat/DeleteValueDouble). - Providers translate delete values to nulls where it makes sense (e.g., time series values).
Time axes¶
- Both equidistant and non-equidistant calendar axes are supported for reading.
- When writing dfs0, we enforce equidistance if the target file axis is equidistant.
Providers at a glance¶
| Area | Type | Class | Read | Write | Notes |
|---|---|---|---|---|---|
| Maps (dfs2) | Map source | Dfs2MapSource |
✓ | — | Renders SKBitmap tiles/overlays. EPSG:3857 only for map output. |
| Maps (dfsu) | Map source | DfsuMapSource |
✓ | — | Contour rendering over meshes; EPSG:3857 output. |
| Time series (dfs0) | Repo | Dfs0TimeSeriesRepository |
✓ | ✓ | Single file, single/multi item; add/set using temp file+builder. |
| Time series groups (dfs0) | Repo | Dfs0GroupedTimeSeriesRepository |
✓ | ✓ | One dfs0 per “group”; merges items across files by path. |
| Time series (dfs2) | Repo | Dfs2TimeSeriesRepository |
✓ | — | Extracts a cell/area time series from rasters (with rotation fixes). |
| Time series (dfsu) | Repo | DfsuTimeSeriesRepository |
✓ | — | Extracts element/area time series (2D/3D layer aware). |
| Features (dfs2) | Repo | Dfs2FeatureRepository |
✓ | — | Builds polygon features for each cell at a time step; carries item attributes. |
| Features (dfsu) | Repo | DfsuFeatureRepository |
✓ | — | Builds element polygons; attributes per selected item/time. |
| Features (dfsu, contour polygons) | Repo | DfsuContouringFeatureRepository |
✓ | — | Generates contour areas from dfsu item values. |
| Rasters (dfs2) | TimeStep server | Dfs2TimeStepServer |
✓ | — | Returns a Raster (values + georef bounding box) per item & time. |
| Meshes (dfsu) | Mesh repo | DfsuMeshRepository |
✓ | — | Mesh metadata, time stamps, point/polygon aggregation, mesh data. |
Registration patterns¶
Map example (dfs2)¶
var dfs2File = Path.Combine(appData, "dfs2", "R20141001.dfs2");
ServiceLocator.Register(
new MapService(
new Dfs2MapSource(dfs2File), // or Dfs2MapSource(new FileSource(root), "relative/path.dfs2")
new MapStyleService(
new MapStyleRepository(Path.Combine(appData, "styles.json"), SerializerOptionsDefault.Options)
)
),
"dfs2-map"
);
TimeSeries (dfs2) via config¶
"dfs2": {
"$type": "DHI.Services.TimeSeries.WebApi.TimeSeriesServiceConnection, DHI.Services.TimeSeries.WebApi",
"ConnectionString": "C:\\Temp\\R20141001.dfs2",
"RepositoryType": "DHI.Services.Provider.MIKECore.Dfs2TimeSeriesRepository, DHI.Services.Provider.MIKECore",
"Name": "MIKE dfs2 time series connection",
"Id": "dfs2"
}
Use the assembly-qualified type names that match your build (namespace here is
…MIKECore).
ID conventions & selection “dialects”¶
Most repos accept an ID string that encapsulates file, what to extract, and how to locate it. The exact parser classes (e.g., Dfs0TimeSeriesId, Dfs0GroupedTimeSeriesId, Dfs2DTimeSeriesId, DfsuFeatureCollectionId) live in the domain packages; here’s how they behave:
dfs0 (point time series)¶
Dfs0TimeSeriesRepositoryGetAll()→ oneTimeSeriesper item.GetIds()can return short (itemName) or long IDs (includes quantity) based onDefaultLongId.Get(id)/GetValues(id)→ match by item number/name/quantity viaDfs0TimeSeriesId.
Dfs0GroupedTimeSeriesRepository- IDs are two parts: relative file path + item path (
ObjId), wrapped byDfs0GroupedTimeSeriesId. - Groups map to subfolders; each
.dfs0under that path is another group/file.
- IDs are two parts: relative file path + item path (
dfs2 / dfsu (grids / meshes)¶
Selections are expressed via Dfs2DTimeSeriesId/DfsuFeatureCollectionId etc., supporting:
- Item: by name or number.
- Request types:
- Point selection:
- Geo:
Longitude, Latitude - XY: local/projected X,Y
- Geo:
- Polygon selection:
- PolygonGeo: arrays of lon/lat
- PolygonXY: arrays of X,Y
- Element (dfsu): integer element id; for 3D you can also pass LayerNumber.
- Point selection:
- Area statistic for polygons:
Average | Maximum | Minimum. - Reprojection (dfs2 vector fields):
NoneUV(rotate U/V and return one component)SpeedDirection(apply rotation to direction, keep speed)
The concrete text format of the ID is handled by the
…Idclasses—construct them explicitly in code if you’re not calling the Web API parser.
Using each provider¶
1) Dfs2MapSource / DfsuMapSource (maps)¶
CRS restriction: map output must be requested in EPSG:3857.
var map = new Dfs2MapSource("C:\\data\\R20141001.dfs2");
var dates = map.GetDateTimes(null); // or pass sub-path/id when using a folder+prefix
var bmp = map.GetMap(
style: mapStyle,
crs: "EPSG:3857",
boundingBox: new BoundingBox(minX, minY, maxX, maxY),
width: 1024,
height: 768,
filePath: "", // for folder/prefix sources: "subdir/file.dfs2"
dateTime: dates.Max, // pick timestep
item: "Current speed", // or "1" by item number
parameters: new Parameters {
{ "includevector", true },
{ "isoline", "None" }, // None | Major10 | Major5 | Major2
{ "vectorcolor", "#000000" },
{ "usevectorfixedlength", false },
{ "vectormaxlength", 10 },
{ "vectornumberhorizontal", 0 },
{ "scale", 1d },
{ "coloringtype", "DiscreteColoring" }, // or ContinuousColoring
{ "shadingtype", "ShadedContour" } // or FlatShading, etc.
}
);
- Thresholds come from the
MapStylepalette; we convert palette stops into(value, bandColor)for the contouring engine. GetLayerInfo()returns layer bounds and projection from the file.
2) Dfs0TimeSeriesRepository (single file)¶
Read values:
var repo = new Dfs0TimeSeriesRepository("C:\\data\\gauge.dfs0");
foreach (var id in repo.GetIds()) { /* ids per item */ }
var maybe = repo.Get(id);
var ts = maybe.HasValue ? maybe.Value : null; // TimeSeries<string,double>
var values = repo.GetValues(id).Value; // ITimeSeriesData<double> (nulls for deletes)
Write/Update:
// SetValues overwrites the file with the single item’s values.
// If file axis is equidistant, incoming datetimes must be equidistant.
repo.SetValues(id, values);
// Add creates a new dfs0 (builder) when file doesn’t exist.
repo.Add(new TimeSeries<string,double>(id, "ItemName") { Data = values });
// Not implemented in this repo (throws): Remove/Update per item.
// Use Grouped repo for multi-item editing (below).
3) Dfs0GroupedTimeSeriesRepository (multiple files, grouped)¶
Treat each .dfs0 under a root as a group, and each item inside as a separate series.
var grp = new Dfs0GroupedTimeSeriesRepository("C:\\data\\ts-root");
// Add or update a series into the right file (merges items).
grp.Add(series);
// Bulk fetch values efficiently by grouping per file internally
var bag = grp.GetValues(new[]{ id1, id2, id3 }); // IDictionary<string, ITimeSeriesData<double>>
// Rename/update by replacing one item
grp.Update(series);
// Remove a series (rewrites the file without that item)
grp.Remove(id);
How it works: we copy the target file to a temp, compute the new set of items (read old + apply change), write the full set to temp using dfs0 builder, then save back through _fileSource.
4) Dfs2TimeSeriesRepository (extract time series from grids)¶
Create one time series from a cell or polygon:
- Point (
Geo/XY/Element) → we map to cell indices(j,k)accounting for grid spacing and true-north rotation. - Polygon (
PolygonGeo/PolygonXY) → we collect all covered cells and aggregate per time step (Average/Min/Max). - Vector items → U/V reprojection is supported. We can:
- return rotated
UorVcomponents (ReprojectType.UV), or - correct direction for
SpeedDirectionfields.
- return rotated
var repo = new Dfs2TimeSeriesRepository("C:\\data\\R20141001.dfs2");
var id = /* build Dfs2DTimeSeriesId for Item="U", RequestType=Geo, Lon/Lat, Reproject=UV with UItem="U", VItem="V" */;
var meta = repo.Get(id); // metadata shell (name)
var values = repo.GetValues(id).Value; // ITimeSeriesData<double>
var times = repo.GetDateTimes(id).Value; // SortedSet<DateTime>
5) DfsuTimeSeriesRepository (extract time series from meshes)¶
- Select by element id, point (finds intersecting element), or polygon (multiple elements).
- Supports 3D via
LayerNumber(top layer is computed and offset). - Aggregation over multiple elements:
Average | Min | Max.
var repo = new DfsuTimeSeriesRepository("C:\\data\\HD.dfsu");
var id = /* Dfs2DTimeSeriesId-like with Item="Water Level", RequestType=PolygonGeo, coords... */;
var ts = repo.Get(id).Value; // series metadata
var vals = repo.GetValues(id).Value; // values with nulls for delete
var tms = repo.GetDateTimes(id).Value;
6) Dfs2FeatureRepository (cells as polygons with attributes)¶
Build a FeatureCollection<string> at a specific time:
- Each grid cell becomes a polygon with attributes for each item’s value at that timestep.
- Respects an optional BoundingBox filter.
var repo = new Dfs2FeatureRepository("C:\\data\\R20141001.dfs2");
var featureId = /* Dfs2FeatureCollectionId with File, DateTime, optional BoundingBox */;
var fc = repo.Get(featureId.ToString()).Value;
7) DfsuFeatureRepository (elements as polygons)¶
- Returns polygon per mesh element.
- You can request a specific item or all items (values attached as attributes).
- Optional DateTime filter (otherwise 0).
var repo = new DfsuFeatureRepository("C:\\data\\HD.dfsu");
var featureId = /* DfsuFeatureCollectionId with File, optional Item, optional DateTime */;
var fc = repo.Get(featureId.ToString()).Value;
8) DfsuContouringFeatureRepository (contour polygons)¶
- Generates contour polygons for a dfsu item/time step using
DfsContouring. - Use
ContourLevelsin the ID to control bands. - Boundary elements get set to a floor value to close contours at the domain edge.
var repo = new DfsuContouringFeatureRepository("C:\\data\\HD.dfsu");
var featureId = /* DfsuFeatureCollectionId with Item, DateTime, ContourLevels=[…] */;
var contours = repo.Get(featureId.ToString()).Value; // features with "Value" attribute
9) Dfs2TimeStepServer (rasters per item/time)¶
- Returns a
Raster(values array + georef metadata) for a given item and DateTime. - Reprojects the envelope to geographic.
- Replaces file delete values with 0 in the raster payload (you can post-process that to null).
var ts = new Dfs2TimeStepServer("C:\\data\\R20141001.dfs2");
var dtList = ts.GetDateTimes();
var items = ts.GetItems();
var rasterMaybe = ts.Get(items.First().Id, dtList.First());
var raster = (Dfs2TimeStepServer.Raster)rasterMaybe.Value;
// raster.Size, raster.GeoCenter, raster.Metadata[…], raster.ToBitmap()
10) DfsuMeshRepository (mesh metadata, values & aggregations)¶
- MeshInfo: items, full date range, bounding box, projection.
- Point extraction: time series at a geo point (element intersection).
- Polygon aggregation: Average/Min/Max over intersected elements. Average = weighted by area (or area intersection if polygon provided).
- Batch polygon aggregation is parallelized.
var repo = new DfsuMeshRepository(new FileSource("C:\\data"));
foreach (var m in repo.GetAll()) { /* mesh files under root */ }
var id = "C:\\data\\HD.dfsu";
var dates = repo.GetDateTimes(id);
var p = new Spatial.Point(12.34, 55.67); // lon/lat
var dr = new DateRange(dates.First(), dates.Last());
var seriesAtPoint = repo.GetValues(id, p, dr);
var avgOverPoly = repo.GetAggregatedValues(id, AggregationType.Average, "Water Level", dr); // whole domain
var avgOverAOI = repo.GetAggregatedValues(id, AggregationType.Average, "Water Level", myPolygon, dr); // AOI
Writing dfs0 files – how it’s done¶
- We always write through a temp file then stream it via
_fileSource.Save(...). - For equidistant axes, we validate that incoming timestamps are equidistant.
- For multi-item editing, use
Dfs0GroupedTimeSeriesRepository, which:- Reads all items,
- Applies your add/update/remove,
- Writes the complete item set back to temp,
- Saves the file.
Metadata defaults applied if missing when creating new files:
FileTitle,ApplicationTitle,ApplicationVersionBuilderDataType,TimeAxis(defaults toCalendarNonEquidistant)StatType(defaults toNoStat)
Performance notes¶
- Sequential reads: Where possible we sort items by
ItemNumberbefore reading to minimize file seeking (e.g., in dfs2 UV pairing). - Bulk extraction: For grouped dfs0 values, we batch per file.
- dfsu polygon aggregation: we precompute weights (either element area or area overlap) and then read each time step once, applying weights for each polygon.
- Parallelism: Some repo calls parallelize over ids (with
Partitioner)—wrap callers in try/catch forAggregateException.
Swapping storage¶
All providers are built on IFileSource:
- Local:
new FileSource(rootFolder) - Cloud or remote: implement
IFileSourcewithExists,GetFilePaths,ReadDfs*,Save,Copy,Build, etc., and pass it into the constructors. Everything else stays the same.