Class GmlDatasetProcessorBase<TFeature>
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
TFeatureThe 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
catalogueGmlPortrayalCatalogueBasePortrayal catalogue used by the XSLT pipeline.
decoderFeatureCatalogueDecoderOptional feature-catalogue decoder for attribute decoding.
fileNamestringDataset file name (used in feature info).
authorityProviderIDisplayPlaneAuthorityProviderResolves 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.
specNamestringProduct 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
Catalogue
The portrayal catalogue for this processor.
protected GmlPortrayalCatalogueBase Catalogue { get; }
Property Value
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
Decoder
The feature catalogue decoder, if available.
protected FeatureCatalogueDecoder? Decoder { get; }
Property Value
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
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
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
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
ProductDescription
Human-readable product description for info strings (e.g. "Navigational Warnings", "Marine Aids to Navigation").
protected abstract string ProductDescription { get; }
Property Value
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
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
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
featureTFeature
Returns
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
featureTFeature
Returns
BuildInfoSuffix()
Appends spec-specific lines to the info string. Override to add counts like "Information types: N".
protected virtual string BuildInfoSuffix()
Returns
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
featureTFeature
Returns
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
contextRenderContextThe render context (palette, mariner settings, ECDIS display state).
cancellationTokenCancellationTokenCancellation 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
contextRenderContextOptional render context (palette, scales, ECDIS, mariner).
cancellationTokenCancellationTokenCancellation 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
CreateFeatureXmlSource()
Creates the spec-appropriate feature XML source.
protected abstract IFeatureXmlSource CreateFeatureXmlSource()
Returns
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
contextRenderContextThe render context, if any.
Returns
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
S125DatasetDataorS102CoverageData).
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
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
ordinalint
Returns
- S100FeatureGeometry
The feature's geometry, or
nullwhen 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
featureRefstring
Returns
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
ordinalint
Returns
- FeatureInfo
Feature info, or
nullwhen 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
nullwhen 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
contextRenderContext
Returns
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
latitudedoublePick latitude in WGS-84 degrees.
longitudedoublePick longitude in WGS-84 degrees.
radiusMetersdoubleSearch 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
featureTFeatureThe feature hit.
Returns
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
instructionsIReadOnlyList<DrawingInstruction>
Returns
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
widthPixelsintOutput bitmap width in pixels.
heightPixelsintOutput bitmap height in pixels.
contextRenderContextOptional render context (palette, symbol/text scale, ECDIS display settings).
backgroundRgbaColor?Optional background fill; defaults to opaque white.
cancellationTokenCancellationTokenCancellation token.
Returns
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
axisOrderAxisOrderAssessment
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
contentHashstring
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
declaredEditionstringThe declared edition string (e.g.
"2.0.0"), ornullwhen 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
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
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().