Table of Contents

Class GmlDatasetProcessorBase<TFeature>

Namespace
EncDotNet.S100.Datasets.Pipelines
Assembly
EncDotNet.S100.Datasets.Pipelines.dll

Abstract base class for GML-based dataset processors that share the standard S-100 Part 9 vector portrayal pipeline template: parse → catalogue setup → FeatureXML projection → XSLT pipeline → Mapsui display-list rendering.

public abstract class GmlDatasetProcessorBase<TFeature> : IDatasetProcessor, IVectorPortrayalSource, IHeadlessImageRenderer, IDisplayModeAwareDatasetProcessor, ILoadedDatasetProjection where TFeature : IS100Feature

Type Parameters

TFeature

The concrete feature type constrained to IS100Feature.

Inheritance
GmlDatasetProcessorBase<TFeature>
Implements
Derived
Inherited Members

Remarks

Subclasses provide the spec-specific pieces (dataset parsing, catalogue creation, feature XML source) via abstract/virtual members. The base handles the shared pipeline orchestration, feature-info construction, enumeration, and extent computation.

Constructors

GmlDatasetProcessorBase(GmlPortrayalCatalogueBase, FeatureCatalogueDecoder?, string, IDisplayPlaneAuthorityProvider, string)

Initializes the shared processor state. Called by subclass constructors after parsing the dataset and creating the catalogue.

protected GmlDatasetProcessorBase(GmlPortrayalCatalogueBase catalogue, FeatureCatalogueDecoder? decoder, string fileName, IDisplayPlaneAuthorityProvider authorityProvider, string specName)

Parameters

catalogue GmlPortrayalCatalogueBase

Portrayal catalogue used by the XSLT pipeline.

decoder FeatureCatalogueDecoder

Optional feature-catalogue decoder for attribute decoding.

fileName string

Dataset file name (used in feature info).

authorityProvider IDisplayPlaneAuthorityProvider

Resolves the default S-98 display plane for this dataset's content. Required — processors receive the provider via DI rather than reaching for a static singleton. The Mapsui-typed cross-dataset sort authority lives in the renderer package; the processor only needs the plane.

specName string

Product specification name, e.g. "S-124".

Properties

AxisOrder

The outcome of the dataset-level axis-order check the reader ran on the dataset's positions (issue #650), or null when the product has no positions to check or the reader does not run it. A Swapped outcome means the reader swapped latitude and longitude for the whole dataset (when Normalized is set). Surfaced by hosts (CLI info, the viewer's dataset list) and, when swapped or inconsistent, as a {product}-PROJ-AXIS warning in Validate().

public AxisOrderAssessment? AxisOrder { get; }

Property Value

AxisOrderAssessment

Catalogue

The portrayal catalogue for this processor.

protected GmlPortrayalCatalogueBase Catalogue { get; }

Property Value

GmlPortrayalCatalogueBase

DeclaredDisplayModeIds

The set of display-mode ids declared by the dataset's portrayal catalogue, in no particular order. Empty when the catalogue declares no display modes (the common case; portrayal then always uses the single default look).

public IReadOnlyCollection<string> DeclaredDisplayModeIds { get; }

Property Value

IReadOnlyCollection<string>

Decoder

The feature catalogue decoder, if available.

protected FeatureCatalogueDecoder? Decoder { get; }

Property Value

FeatureCatalogueDecoder

Features

The parsed features from the dataset.

protected abstract IReadOnlyList<TFeature> Features { get; }

Property Value

IReadOnlyList<TFeature>

FileName

The dataset file name.

protected string FileName { get; }

Property Value

string

Metadata

The lightweight, product-agnostic DatasetMetadata for this dataset — its declared specification and, where cheaply derivable, its geographic extent, horizontal CRS, display-scale window, and temporal coverage. Overriding processors derive it once from the already-parsed dataset (never a second parse) and cache it, so hosts can frame a viewport, register a layer, draw an out-of-scale indicator, or gate visibility without re-reading the dataset. The default implementation does not override this and instead returns a fresh, minimal value carrying only Spec on each access; processors that can supply an extent (and other facts) override it with the derive-once-and-cache behaviour above. See issue #467 / #460.

public DatasetMetadata Metadata { get; }

Property Value

DatasetMetadata

MinExtentPadding

Minimum extent padding in degrees. Default is 0.01; override for specs that need wider padding (e.g. S-421 uses 0.05).

protected virtual double MinExtentPadding { get; }

Property Value

double

PreferLiveDrawing

Whether this product's datasets are sparse overlays a renderer may draw directly every frame (PreferLiveDrawing). Off by default; a product turns it on only when every dataset it can produce is small.

protected virtual bool PreferLiveDrawing { get; }

Property Value

bool

ProductDescription

Human-readable product description for info strings (e.g. "Navigational Warnings", "Marine Aids to Navigation").

protected abstract string ProductDescription { get; }

Property Value

string

Spec

The product specification (name + edition) this dataset declares conformance to. Each per-spec processor returns a fixed name (e.g. "S-101"); the edition portion is populated from the dataset itself (HDF5 productSpecification attribute, GML application namespace, S-101 ProductSpecificationEdition, etc.) when known, otherwise defaults to SpecVersion default.

public SpecRef Spec { get; }

Property Value

SpecRef

VersionAssessment

How the dataset's declared product specification edition (Spec) relates to the version of the catalogue used to process it, or null when no catalogue self-describes its version (so no assessment can be made). Computed once at construction — catalogue resolution is a one-shot per processor — and surfaced by hosts (CLI info/render, viewer layer list) to warn on a version mismatch per S-100 Edition 5.2.1 Part 2 §6.

public SpecVersionAssessment? VersionAssessment { get; }

Property Value

SpecVersionAssessment

Methods

BuildEggCode(TFeature)

Builds the WMO / SIGRID-3 ice "egg code" projection for a feature, when the product supports it. Returns null by default; S-411 overrides this to project sea-ice / lake-ice concentration, stage, and form attributes into an IceEggCode for the pick report.

protected virtual IceEggCode? BuildEggCode(TFeature feature)

Parameters

feature TFeature

Returns

IceEggCode

BuildFeatureReferences(TFeature)

Builds feature references for the pick UI. Override to expose xlink/information references as navigable links.

protected virtual IReadOnlyList<FeatureReference> BuildFeatureReferences(TFeature feature)

Parameters

feature TFeature

Returns

IReadOnlyList<FeatureReference>

BuildInfoSuffix()

Appends spec-specific lines to the info string. Override to add counts like "Information types: N".

protected virtual string BuildInfoSuffix()

Returns

string

BuildNavigationalWarning(TFeature)

Builds the navigational warning message for a feature, when the product carries warnings. Returns null by default; S-124 overrides this for NavwarnPart features.

protected virtual NavigationalWarningInfo? BuildNavigationalWarning(TFeature feature)

Parameters

feature TFeature

Returns

NavigationalWarningInfo

BuildSupplementalPortrayalAsync(RenderContext?, CancellationToken)

Builds portrayal from a second, non-XSLT source that is merged with the product's own XSLT output — e.g. S-125, whose own catalogue portrays only AtoN status indications, supplements it with the S-101 AtoN rules. Returns null (the default) when the product has no supplemental portrayal. Called under the render gate.

protected virtual Task<GmlDatasetProcessorBase<TFeature>.SupplementalPortrayal?> BuildSupplementalPortrayalAsync(RenderContext? context, CancellationToken cancellationToken)

Parameters

context RenderContext

The render context (palette, mariner settings, ECDIS display state).

cancellationToken CancellationToken

Cancellation token.

Returns

Task<GmlDatasetProcessorBase<TFeature>.SupplementalPortrayal>

BuildVectorPortrayalAsync(RenderContext?, CancellationToken)

Builds an immutable, Mapsui-free snapshot of this dataset's vector portrayal for the supplied render context. Implementations run under the processor's render gate and snapshot all mutable portrayal state (palette, ECDIS display, asset pre-warm) so the result is safe to convert to Mapsui layers off the gate.

public Task<VectorPortrayalResult> BuildVectorPortrayalAsync(RenderContext? context = null, CancellationToken cancellationToken = default)

Parameters

context RenderContext

Optional render context (palette, scales, ECDIS, mariner).

cancellationToken CancellationToken

Cancellation token.

Returns

Task<VectorPortrayalResult>

The Mapsui-free vector portrayal result.

ComputeGeographicExtent()

Computes the padded geographic extent (lat / lon degrees) of all features. The Mapsui renderer projects this to Spherical Mercator; keeping it Mapsui-free lets the headless path share the same bounds. Derives from the memoized raw envelope, so repeated renders do not re-scan every coordinate (issue #467, WS1).

protected GeographicBounds ComputeGeographicExtent()

Returns

GeographicBounds

CreateFeatureXmlSource()

Creates the spec-appropriate feature XML source.

protected abstract IFeatureXmlSource CreateFeatureXmlSource()

Returns

IFeatureXmlSource

CreateFeatureXmlSource(RenderContext?)

Creates the feature XML source for one render. The default ignores context and calls CreateFeatureXmlSource(); S-124 overrides it to leave out HiddenFeatures. Called under the render gate.

protected virtual IFeatureXmlSource CreateFeatureXmlSource(RenderContext? context)

Parameters

context RenderContext

The render context, if any.

Returns

IFeatureXmlSource

CreateLoadedData()

Allocates the LoadedDatasetData variant wrapping this processor's already-parsed model. Never re-reads the source bytes.

public abstract LoadedDatasetData CreateLoadedData()

Returns

LoadedDatasetData

The catalog payload wrapping the resident model (e.g. an S125DatasetData or S102CoverageData).

Remarks

Each GML subclass wraps its resident, already-parsed model in the matching LoadedDatasetData variant (e.g. new S125DatasetData(_dataset)) — no second parse.

EnumerateFeatures()

Enumerates a lightweight summary of every feature exposed by this processor — used to seed the viewer's feature-search index without forcing a full GetFeatureInfo(string) call per feature.

public IEnumerable<FeatureSummary> EnumerateFeatures()

Returns

IEnumerable<FeatureSummary>

Remarks

Coverage products (S-102, S-104, S-111) and any processor that does not expose discrete features should leave this returning the default empty enumeration.

GetFeatureGeometryAt(int)

Returns the renderer-neutral geometry of the feature at the given enumeration ordinal — the same index reported by Ordinal and carried by Ordinal, so a pick resolves to its outline the same collision-free way GetFeatureInfoAt(int) resolves its attributes. Lets a host highlight or outline a picked feature without reaching into a product's feature model.

public S100FeatureGeometry? GetFeatureGeometryAt(int ordinal)

Parameters

ordinal int

Returns

S100FeatureGeometry

The feature's geometry, or null when the ordinal is out of range or this processor does not expose discrete vector features (coverage / non-vector products leave the default and pick through GetCoverageInfo(double, double, DateTime?)).

GetFeatureInfo(string)

Returns information about a feature identified by its reference string (as stored in the rendered feature via the renderer's feature-ref key), or null if the feature cannot be found.

public FeatureInfo? GetFeatureInfo(string featureRef)

Parameters

featureRef string

Returns

FeatureInfo

Remarks

Real-world datasets occasionally contain features that share a gml:id (a producer bug), so callers that already know the feature's position within the dataset should prefer GetFeatureInfoAt(int) for a collision-free lookup.

GetFeatureInfoAt(int)

Returns information about the feature at the given enumeration ordinal — the same index reported via Ordinal from EnumerateFeatures(). Used by the search index so duplicate gml:ids (a real producer bug) still route to the correct feature.

public FeatureInfo? GetFeatureInfoAt(int ordinal)

Parameters

ordinal int

Returns

FeatureInfo

Feature info, or null when the ordinal is out of range or this processor does not expose discrete features.

GetNavigationalWarningMessage()

Returns the dataset's navigational warning as a whole (S-124: its NavwarnPreamble and References), with no part's validity, text or geometry. A cancellation or an in-force bulletin may have no parts, so GetNavigationalWarnings() doesn't list it, yet it ends other warnings; build a NavigationalWarningSet from it.

public virtual NavigationalWarningInfo? GetNavigationalWarningMessage()

Returns

NavigationalWarningInfo

The warning, or null when the product carries none.

GetNavigationalWarnings()

Returns every navigational warning part in the dataset (each S-124 NavwarnPart) with its message, in dataset order, including parts with no geometry. Lets a host list warnings without picking them: a part with no location draws nothing, so a pick can never reach it.

public IReadOnlyList<NavigationalWarningFeature> GetNavigationalWarnings()

Returns

IReadOnlyList<NavigationalWarningFeature>

One entry per warning part, or an empty list when the product carries no warnings.

GetSuppressionInfo(RenderContext?)

Called before the pipeline runs. Return a non-null info string to suppress rendering (the dataset contributes no portrayal for this context, e.g. S-411 hides when the time slider is before the issue date); the returned text becomes the dataset's status line.

protected virtual string? GetSuppressionInfo(RenderContext? context)

Parameters

context RenderContext

Returns

string

HitTestFeatures(double, double, double)

Hit-tests this dataset's vector features against a geographic point, returning every feature whose geometry contains the point (area features) or lies within radiusMeters of it (point / curve features), in feature-enumeration order. The reusable geographic complement to GetCoverageInfo(double, double, DateTime?): a caller resolves each hit to full attributes via GetFeatureInfoAt(int) (or GetFeatureInfo(string)) and ranks the results across datasets itself — for example by a rendering paint stack, which the processor contract deliberately knows nothing about.

public IReadOnlyList<FeatureGeometryHit> HitTestFeatures(double latitude, double longitude, double radiusMeters)

Parameters

latitude double

Pick latitude in WGS-84 degrees.

longitude double

Pick longitude in WGS-84 degrees.

radiusMeters double

Search tolerance for point/curve features in metres; area features use exact containment and ignore it. Negative or non-finite values are treated as 0.

Returns

IReadOnlyList<FeatureGeometryHit>

The matching features in enumeration order — each Ordinal aligns with GetFeatureInfoAt(int) — or an empty list. Vector processors override this; coverage / non-vector processors leave the default empty implementation and expose picks through GetCoverageInfo(double, double, DateTime?) instead.

IsPickable(TFeature)

Whether HitTestFeatures(double, double, double) may report feature. The default is true; S-124 returns false for features its last portrayal left out, so what isn't drawn isn't picked.

protected virtual bool IsPickable(TFeature feature)

Parameters

feature TFeature

The feature hit.

Returns

bool

PostProcessInstructions(IReadOnlyList<DrawingInstruction>)

Post-processes drawing instructions after the pipeline runs. Override to apply fallback fills or other transformations (e.g. S-129 area fill fallback).

protected virtual IReadOnlyList<DrawingInstruction> PostProcessInstructions(IReadOnlyList<DrawingInstruction> instructions)

Parameters

instructions IReadOnlyList<DrawingInstruction>

Returns

IReadOnlyList<DrawingInstruction>

RenderHeadlessAsync(int, int, RenderContext?, RgbaColor?, CancellationToken)

Renders this dataset to a standalone SKBitmap through the headless, backend-agnostic Skia vector core (VectorSceneBuilder → SkiaDisplayListRenderer), bypassing Mapsui entirely. This is the vector analogue of the direct-Skia coverage renderer and the basis for a headless tile-serving API.

public Task<SKBitmap> RenderHeadlessAsync(int widthPixels, int heightPixels, RenderContext? context = null, RgbaColor? background = null, CancellationToken cancellationToken = default)

Parameters

widthPixels int

Output bitmap width in pixels.

heightPixels int

Output bitmap height in pixels.

context RenderContext

Optional render context (palette, symbol/text scale, ECDIS display settings).

background RgbaColor?

Optional background fill; defaults to opaque white.

cancellationToken CancellationToken

Cancellation token.

Returns

Task<SKBitmap>

A newly allocated bitmap owned by the caller.

Remarks

Tiled-symbol pattern area-fills are rasterised through RasterizePatternTile(string, AreaFill, double) and tiled across the polygon, anchored to a global world-space origin so adjacent polygons sharing a pattern align seamlessly. Unlike the Mapsui path, the headless renderer does not perform NetTopologySuite priority-clipping of overlapping patterns or land-occlusion, so patterns may visibly bleed across opaque overlay fills. The viewport is auto-fitted to the dataset extent and padded to the output aspect ratio.

SetAxisOrder(AxisOrderAssessment?)

Records the axis-order assessment the reader attached to the parsed dataset (issue #650). Subclasses call this once, from their constructor, after parsing the dataset.

protected void SetAxisOrder(AxisOrderAssessment? axisOrder)

Parameters

axisOrder AxisOrderAssessment

SetContentHash(string)

Records the hash of the dataset's raw bytes (see EncDotNet.S100.Datasets.Pipelines.DatasetContentHash), from which ContentScope is built. Subclasses call this once, from their constructor, before parsing the dataset. Until it is called the dataset reports no content scope, so its layers bypass the warm tile disk cache.

protected void SetContentHash(string contentHash)

Parameters

contentHash string

SetDeclaredEdition(string?)

Records the dataset's declared product-spec edition (parsed from the GML productEdition / application-schema namespace) and computes the VersionAssessment against the edition this build implements. Subclasses call this once, from their constructor, after parsing the dataset. Also emits the one-shot catalogue-resolution telemetry for this processor instance.

protected void SetDeclaredEdition(string? declaredEdition)

Parameters

declaredEdition string

The declared edition string (e.g. "2.0.0"), or null when the dataset declares none.

Validate()

Runs the product's normative rule pack (ValidateDataset()) and appends the axis-order finding (AxisOrderFindings) for AxisOrder. The report is computed once and cached. Returns null when the product has no rule pack and the reader ran no axis-order check.

public ValidationReport? Validate()

Returns

ValidationReport

ValidateDataset()

Runs the product's rule pack, or returns null when the product has none. Called once by Validate(), which caches the result.

protected virtual ValidationReport? ValidateDataset()

Returns

ValidationReport

Remarks

Overrides should typically delegate to ValidationRunner.Run(...) so that an InvalidOperationException from the typed projection (e.g. an empty dataset) surfaces as an empty report rather than propagating out of Validate().