EncDotNet.S100.Datasets.S57
0.22.0
See the version list below for details.
dotnet add package EncDotNet.S100.Datasets.S57 --version 0.22.0
NuGet\Install-Package EncDotNet.S100.Datasets.S57 -Version 0.22.0
<PackageReference Include="EncDotNet.S100.Datasets.S57" Version="0.22.0" />
<PackageVersion Include="EncDotNet.S100.Datasets.S57" Version="0.22.0" />
<PackageReference Include="EncDotNet.S100.Datasets.S57" />
paket add EncDotNet.S100.Datasets.S57 --version 0.22.0
#r "nuget: EncDotNet.S100.Datasets.S57, 0.22.0"
#:package EncDotNet.S100.Datasets.S57@0.22.0
#addin nuget:?package=EncDotNet.S100.Datasets.S57&version=0.22.0
#tool nuget:?package=EncDotNet.S100.Datasets.S57&version=0.22.0
EncDotNet.S100.Datasets.S57
Reader and S-101 translator for IHO S-57 Electronic Navigational Chart (ENC) datasets.
Overview
This library reads S-57 (Edition 3.1) ENC base cells via the
EncDotNet.S57 NuGet package
(itself layered on top of EncDotNet.Iso8211) and translates the parsed
records into the S-101 in-memory document model so the existing S-100 Part 9A
Lua portrayal pipeline (provided by EncDotNet.S100.Datasets.S101) can render
them. This is not an S-52 implementation — symbology is whatever the S-101
portrayal catalogue produces when fed the translated data.
Mappings follow the IHO S-57 to S-101 Conversion Guidance (S-101PT6 INF02A, draft 2021).
Key types:
S57Dataset— entry point; opens a.000file and exposes the parsedEncDotNet.S57.S57Documentfrom the upstream package, plus a staticIsS57File(path)discriminator used byEncDotNet.S100.Datasets.Pipelinesto disambiguate.000files that could otherwise be S-101. Also exposes a cheapReadMetadata"peek" path (issue #460) that folds the WGS-84 extent from the raw spatial coordinates (via the DSPM coordinate multiplication factor) and the compilation-scale (CSCL) display window without the S-57 → S-101 translation or any portrayal — so a host can frame a viewport over a loose folder of S-57 cells before deciding to load each in full.S57ToS101Translator— translates anS57Document(package type) into anS101Documentby remapping object/attribute codes, exploding multi-point soundings, synthesising theinformationcomplex attribute from textual fields, and converting nodes/edges/area-rings into S-101 spatial primitives.S57S101Mapping— embedded code-mapping table sourced from IHO's S-57 → S-101 conversion guidance.S101AllowedEnumValues— lazy-loaded helper that consults the bundled S-101 Feature Catalogue to drop emitted enumerated attribute values that aren't permitted by the destination FC binding.
Translation behaviour
| Aspect | Notes |
|---|---|
| Object/attribute codes | Looked up via the embedded S57S101Mapping rules (S-57 numeric code → S-101 acronym/code). Unknown S-57 attribute codes pass through; unknown enumerated values are dropped if the S-101 FC declares an allowable list. |
Multi-point soundings (SOUNDG) |
Exploded into S-101 MultiPoint spatial records and a Sounding feature so each depth value is independently portrayable. |
INFORM / TXTDSC (English) |
Carried on a shared NauticalInformation information type (not on the feature) as a single information complex instance with text and/or fileReference and language = "eng"; the feature binds it via an AdditionalInformation / theInformation information association. |
NINFOM / NTXTDS (national language) |
Carried as a second information instance on the same NauticalInformation record with empty language (S-57 has no language tag; Data Producers can populate it post-conversion). |
OBJNAM (English) |
Carried as an S-101 featureName complex attribute instance with name and language = "eng". |
NOBJNM (national language) |
Carried as a separate featureName instance with empty language. |
LITCHR / SIGGRP / SIGPER (light features) |
On feature classes that bind it (LightAllAround, LightFogDetector, LightAirObstruction), assembled into a single rhythmOfLight complex attribute instance: lightCharacteristic (mandatory), plus signalGroup / signalPeriod when present, and any SIGSEQ-derived signalSequence phases nested inside (see below). An out-of-range LITCHR code drops the instance (its mandatory sub-attribute would be missing). On non-light features (FogSignal, RadarTransponderBeacon) SIGGRP / SIGPER remain directly-bound simple attributes. |
DATSTA / DATEND (date ranges) |
On feature classes that bind it, assembled into a fixedDateRange complex attribute instance with dateStart and/or dateEnd (both optional). |
PERSTA / PEREND (periodic date ranges) |
On feature classes that bind it, assembled into a periodicDateRange complex attribute instance. Both dateStart and dateEnd are mandatory in the S-101 FC, so the instance is emitted only when both endpoints are present; otherwise the lone endpoint is dropped (and reported by the translation diagnostics). |
SURSTA / SUREND (survey date ranges) |
On feature classes that bind it (QualityOfBathymetricData, QualityOfNonBathymetricData, QualityOfSurvey), assembled into a surveyDateRange complex attribute instance. dateEnd (SUREND) is mandatory, so the instance is emitted only when it is present; dateStart (SURSTA) is optional. |
CATZOC (zone of confidence) |
On QualityOfBathymetricData (the sole feature class binding the complex), carried as the categoryOfZoneOfConfidenceInData sub-attribute of a zoneOfConfidence complex attribute instance. The S-57 and S-101 enumerations are identical (1=A1, 2=A2, 3=B, 4=C, 5=D, 6=U); an out-of-range code drops the instance. For quantified zones (A1–C) the nested horizontalPositionUncertainty and verticalUncertainty complexes are populated from the CATZOC-implied accuracy values of the IHO CATZOC table (IHO S-4 §B-290), each as an uncertaintyFixed fixed-metre term plus, where the table defines one, an uncertaintyVariableFactor percentage-of-depth term (e.g. A1 → ±5 m + 5% horizontal, 0.5 m + 1% vertical). ZOC D and U are unquantified, so only the category is emitted. fixedDateRange has no CATZOC-side source and is left unpopulated. |
CATPRA (category of production area) |
Feature-dependent. On ProductionStorageArea (PRDARE) it passes through to categoryOfProductionArea, whose enumeration shares codes 1–12 with S-57 CATPRA. On OffshoreProductionArea (OSPARE) the FC binds the distinct categoryOfOffshoreProductionArea enumeration, so the value is redirected and remapped (8 Tank Farm → 4, 9 Wind Farm → 1, 12 Solar Farm → 6); S-57 production categories with no offshore equivalent (quarry, mine, stockpile, power station, refinery, timber yard, factory, slag/spoil, production plant) are dropped. |
MORFAC (mooring/warping facility) + CATMOR |
S-101 has no MooringWarpingFacility class (it survives only in the sister product S-131); the S-101 portrayal catalogue wires the MORFAC symbols to Dolphin.lua (point) and ShorelineConstruction.lua (line/area), which sets the geometry default: point → Dolphin, line/area → ShorelineConstruction. CATMOR (a near-universal ~99.98% of corpus instances) refines specific point facilities that have their own S-101 class — 3 bollard → Bollard, 5 post/pile → Pile, 7 mooring buoy → MooringBuoy — which redirect regardless of the geometry default (all such instances are points in practice). On the remaining point Dolphins, CATMOR maps to categoryOfDolphin, but the S-57 and S-101 enumerations diverge (S-101: Mooring/Deviation/Berthing/Fender; S-57: dolphin/deviation-dolphin/bollard/tie-up-wall/pile/chain/mooring-buoy), so only the two coincident meanings are carried (1 dolphin → 1 Mooring Dolphin, 2 deviation dolphin → 2 Deviation Dolphin); 4 tie-up wall and 6 chain/wire/cable leave a Dolphin with no category. CATMOR is dropped wherever MORFAC redirects to a class that does not bind categoryOfDolphin (Bollard, Pile, MooringBuoy, ShorelineConstruction). |
NATSUR / NATQUA (seabed) |
On SeabedArea (SBDARE, the sole feature class binding the complex), assembled into one or more surfaceCharacteristics complex attribute instances. The two S-57 lists are paired positionally: position i yields an instance carrying natureOfSurface = NATSUR[i] and natureOfSurfaceQualifyingTerms = NATQUA[i]. Both sub-attributes are optional, so a position with only one populated (e.g. NATQUA without NATSUR, the most common corpus case) still forms a valid instance; out-of-range enumerate codes are dropped individually. SeabedArea does not bind a top-level natureOfSurface, so on that feature NATSUR is routed exclusively into the complex; on other feature classes (Coastline, LandRegion, …) NATSUR remains a directly-bound simple natureOfSurface. |
SIGSEQ (signal sequence) |
Parsed into signalSequence complex attribute instances. The S-57 value is a +-separated list of phase durations in seconds where a parenthesised duration denotes an eclipse/silence phase; each phase becomes one instance carrying signalDuration (real seconds) and signalStatus (1 = Lit/Sound for a bare duration, 2 = Eclipsed/Silent for a parenthesised one). On the light feature classes that bind rhythmOfLight the phases are nested inside that complex (its last sub-attribute per the FC), and are therefore emitted only when a rhythmOfLight instance is present (a valid LITCHR); on FogSignal / RadarTransponderBeacon signalSequence binds at the top level. Phases that do not parse as a real duration are dropped and reported. |
Sector lights (SECTR1 / SECTR2 / COLOUR / VALNMR / LITVIS / LITCHR / SIGGRP / SIGPER / SIGSEQ) |
A LIGHTS object carrying a sector arc (SECTR1/SECTR2 present) is redirected from LightAllAround to LightSectored, and its light attributes are assembled into the mandatory sectorCharacteristics [1..] complex: lightCharacteristic (from LITCHR, mandatory) plus optional signalGroup/signalPeriod/signalSequence, and one lightSector carrying colour (from the COLOUR list), valueOfNominalRange (VALNMR), lightVisibility (from the LITVIS list), and a sectorLimit whose sectorLimitOne/sectorLimitTwo sectorBearing come from SECTR1/SECTR2 (three levels of nesting). Since each S-57 LIGHTS object encodes a single sector, one LightSectored feature is emitted per S-57 sector-light with one lightSector (conformant, as lightSector is [1..]); co-located sector arcs of one physical light — several LIGHTS objects sharing the same S-101 point — are merged into a single LightSectored feature carrying one sectorCharacteristics instance per arc (see Sector-light merge below). LightSectored binds none of these attributes at the top level, so all are diverted into the complex and none pass through as flat simple attributes. |
TOPSHP / COLOUR / COLPAT (topmark) |
A TOPMAR object is not an S-101 feature; it is a slave of a buoy/beacon/LightFloat master (linked by the master's FFPT with the master/slave relationship indicator). Its attributes are folded into the master's topmark [0..1] complex: topmarkDaymarkShape (from TOPSHP, the mandatory [1..1] sub-attribute), colour (from the COLOUR list, split into occurrences), and colourPattern (from COLPAT). The fold happens only when the master resolves to one of the 12 S-101 classes binding topmark; a TOPMAR referenced by a non-binding master (or a non-slave relationship) stays unmapped. An instance whose mandatory topmarkDaymarkShape is missing or fails S-101 enumeration validation is rolled back and reported. The absorbed TOPMAR emits no feature of its own; the TopmarksAbsorbed diagnostic counts every TOPMAR consumed by a topmark-binding master (a rolled-back instance is still counted as absorbed even though no topmark is emitted). |
HORCLR (horizontal clearance) |
Assembled into the mandatory horizontalClearanceValue [1..1] sub-attribute of a horizontal-clearance complex, selected per feature by the S-101 FC binding: horizontalClearanceOpen on Gate (GATCON) or horizontalClearanceFixed on the fixed-span classes that bind it (SpanFixed, SpanOpening, Tunnel, ShorelineConstruction, StructureOverNavigableWater, Canal, DockArea, LockBasin). The value is a real carried verbatim; the optional horizontalDistanceUncertainty has no S-57 source and is left unpopulated. On a feature binding neither complex (e.g. Bridge, which S-101 decomposes into spans that carry the clearance) HORCLR has no conformant home and stays unmapped. |
SORDAT (source date) |
Mapped to the top-level reportedDate simple attribute (an S100_TruncatedDate), the S-57 YYYYMMDD value carried verbatim (matching the fidelity of dateStart/dateEnd). Because SORDAT is a near-universal S-57 attribute but the FC binds reportedDate on only ~50 feature classes, the mapping is gated on the resolved feature actually binding reportedDate; on a feature that does not (e.g. Coastline, SeabedArea, LandRegion, DepthContour, the light classes) SORDAT has no conformant home and stays unmapped. SORIND (source indication) has no general S-101 equivalent (the FC's source attribute binds only UpdateInformation) and is intentionally not mapped. |
VALLMA (value of local magnetic anomaly) |
Assembled into the valueOfLocalMagneticAnomaly complex on LocalMagneticAnomaly (LOCMAG, the only feature that binds it). The value feeds the mandatory magneticAnomalyValue [1..1] real sub-attribute verbatim; the optional referenceDirection enum has no S-57 source and is left unpopulated. |
RADWAL (radar wave length) |
Assembled into the radarWaveLength complex on RadarTransponderBeacon (RTPBCN, binding it [0..2]). The list-typed S-57 value is a set of value-band pairs (e.g. 0.03-X or 0.03-X,0.10-S); each pair yields one complex instance carrying the mandatory waveLengthValue (real) and radarBand (text). A pair that does not split into both parts is dropped. |
CURVEL (current velocity) |
Assembled into the speed complex on CurrentNonGravitational (CURENT) and TidalStreamFloodEbb (TS_FEB). The value feeds the mandatory speedMaximum [1..1] real sub-attribute verbatim; the optional speedMinimum has no S-57 source and is left unpopulated. |
MLTYLT (multiplicity of lights) |
Assembled into the multiplicityOfFeatures complex on the light classes that bind it (LightAllAround, LightSectored, LightAirObstruction). The S-57 count feeds the optional numberOfFeatures integer verbatim and the mandatory multiplicityKnown boolean is set true. |
C_AGGR (aggregation) → RangeSystem + RangeSystemAggregation |
An S-57 C_AGGR collection whose members (resolved via its FFPT peer feature-pointers, keyed by LNAM) are all permitted RangeSystemAggregation components and include at least one navigational track (NavigationLine, RecommendedTrack, or RecommendedRouteCentreline) is mapped to a synthesised geometry-less RangeSystem collection feature (FC binds theCollection to a dedicated RangeSystem class, not to a member). One RangeSystemAggregation / theComponent feature association is emitted from the RangeSystem to each resolved member (populating the previously-empty FeatureAssociationCatalogue / RoleCatalogue). A two-phase pass allocates and registers every qualifying RangeSystem's record before wiring associations, so nested C_AGGR members (RangeSystem is itself a permitted component) resolve without dangling references. A C_AGGR that has no track member or any non-permitted member has no S-101 home and stays unmapped (counted under UnmappedObjectClasses). C_ASSO (whose NOAA-corpus majority is LandArea-partition groupings) has no S-101 named-association equivalent and is left unmapped. |
| Spatial relationships | S-57 vector pointer records (VRPT, FSPT) translated to S-101 spatial associations and ring orientation. |
List-valued enumerations (COLOUR, NATSUR, CATLIT, …) |
S-57 encodes multiple enumerate selections as a comma-separated string (e.g. COLOUR = "3,1"). Each code is split into a separate S-101 attribute occurrence (distinct ATIX) and validated independently, so one invalid code no longer discards the whole attribute. |
Limitations
- Sibling updates are applied on the exchange-set and CLI paths. Within an exchange set, each base cell's in-set sequential updates (
.001,.002, …) are folded in viaS57Document.ApplyChangesbefore translation. Thes100 s57 convertCLI likewise auto-discovers and folds sibling update files sitting next to a loose base.000. The low-levelS57Dataset.Open(string)overload still reads a bare base cell only; useS57Dataset.Open(Stream, IReadOnlyList<Stream>)to apply updates programmatically. M_COVR"no coverage available" is dropped. AnM_COVRmeta-object flaggedCATCOV = 2("no coverage available") is not translated. S-101 has nocategoryOfCoverageconcept — itsDataCoveragefeature always asserts coverage — so emitting aDataCoveragefor a no-data region would falsely claim data over the cell's hole and drive cross-cell scale-band overlap suppression to blank the coarser overlapping cell there (issue #438). Coverage-availableM_COVR(CATCOV = 1, or absent) still translates toDataCoveragenormally; the drop is counted by theRuleDroppedObjectClassesdiagnostic.- Breadth-first feature coverage. Feature-class coverage is derived from the bundled S-101 Feature Catalogue's
<S100FC:alias>(S-57 acronym) bridge and validated against a 3,636-cell NOAA ENC corpus: ~99% of feature instances now translate. The classes that remain unmapped are ones that are aggregation/collection objects with no S-101 named-association home (C_ASSO, and anyC_AGGRthat is not a range system), or are meta objects with no S-101 equivalent (M_CSCL). Objects that become S-101 attributes rather than features are folded into their host:TOPMARfolds into the master buoy/beacon'stopmarkcomplex via the S-57 master/slave relationship (see the attribute table above). Geometry-conditional splits are handled where the S-101 model requires them (e.g.MORFAC→Dolphin/ShorelineConstructionby primitive, refined byCATMOR; see the attribute table above), and range-systemC_AGGRcollections are synthesised intoRangeSystemfeatures (see below). - Simple attributes plus selected complex attributes. Most attribute rules cover S-57 attributes that map to a directly feature-bound simple S-101 attribute. The
featureName(OBJNAM/NOBJNM),rhythmOfLight(LITCHR/SIGGRP/SIGPER) with its nestedsignalSequence(SIGSEQ), the three date-range complex attributes —fixedDateRange(DATSTA/DATEND),periodicDateRange(PERSTA/PEREND), andsurveyDateRange(SURSTA/SUREND) — thezoneOfConfidence(CATZOC) complex — including its nestedhorizontalPositionUncertainty/verticalUncertaintyuncertainty complexes derived from the IHO CATZOC table —surfaceCharacteristics(NATSUR/NATQUA), the three-levelsectorCharacteristics/lightSector/sectorLimitsector geometry onLightSectored(SECTR1/SECTR2/COLOUR/VALNMR/LITVISplus the light-characteristic attributes), and thehorizontalClearanceOpen/horizontalClearanceFixedcomplexes (HORCLR), thevalueOfLocalMagneticAnomalycomplex (VALLMA) onLocalMagneticAnomaly, theradarWaveLengthcomplex (RADWAL) onRadarTransponderBeacon, thespeedcomplex (CURVEL) onCurrentNonGravitational/TidalStreamFloodEbb, and themultiplicityOfFeaturescomplex (MLTYLT) on the light classes are assembled, gated on the S-101 Feature Catalogue's per-feature attribute bindings.signalSequencealso binds at the top level onFogSignal/RadarTransponderBeacon. Assembled complex sub-attributes are validated against the destination simple attribute's global enumeration rather than the (sometimes narrower) per-complexpermittedValuessubset, matching the fidelity of the rest of the pipeline. Attributes that are sub-attributes of the remaining S-101 complex attributes are still left unmapped pending further complex-attribute assembly.SORDAT→reportedDateis mapped as a top-level simple attribute, gated on the resolved feature bindingreportedDate(the ~50 feature classes the FC allows); on other featuresSORDATstays unmapped.SORINDhas no general S-101 equivalent and is intentionally not mapped. - Listed-value remapping is best-effort. Some S-57 enumerated attribute values have different numeric codes in S-101; values that aren't permitted by the destination S-101 FC binding are dropped (and reported by the translation diagnostics). Multi-valued (list-type) S-57 enumerations are split into individual S-101 occurrences first, so only the specific invalid codes are dropped rather than the whole attribute.
- Complex-attribute synthesis covers the common cases. The most frequent S-101 complex attributes are assembled (see the table above), including sectored-light geometry (
LightSectored). Co-located sector arcs of one physical light — multiple S-57LIGHTSobjects that each carry a single sector (SECTR1/SECTR2) and resolve to the same S-101 point — are folded into a singleLightSectoredfeature carrying onesectorCharacteristicsinstance per arc (FC allows [1..*]); the lowest-document-order light is the primary and the rest are absorbed (they emit no feature of their own). TheSectorLightsMergeddiagnostic counts the absorbed members. Non-sector attributes (height, status, name) come from the primary, matching the corpus where these are invariant within a physical light. The nested uncertainty complexes ofzoneOfConfidence(horizontalPositionUncertainty/verticalUncertainty) are populated for quantified zones (A1–C) from the CATZOC-implied IHO CATZOC accuracy table; ZOC D/U remain category-only as their accuracy is unquantified. INFORM/TXTDSC/NINFOM/NTXTDSare emitted as aNauticalInformationinformation type bound to the feature by anAdditionalInformation/theInformationinformation association (the "fuller path" in the conversion guidance), rather than as an inlineinformationcomplex on the feature. A feature with any of the four textual attributes yields oneNauticalInformationrecord (Englishinformationinstance and/or a national-language instance); theNauticalInformationTypesEmitteddiagnostic counts the records emitted. Portrayal is unchanged — the S-101 portrayal reads the associatedNauticalInformation(ProcessNauticalInformation), so text/pictorial notes render identically to the former inline encoding.- Area rings are reassembled by node contiguity. An S-57 area's boundary edges (
FSPT) are chained into rings by shared begin/end node identity — reversing individual edges as needed — rather than by relying on theFSPTlisting order. Because S-57 lists every interior edge consecutively (withUSAG = interior) regardless of which hole it belongs to, grouping byUSAGalone would merge all of an area's holes into a single boundary; flattening that merged ring to coordinates then jumps between holes and renders as long "spike" artifacts through a curve. Chaining reconstructs one closed ring per hole, so each interior boundary becomes its own S-101 interior ring. - Range-system aggregations are synthesised; other collections are not. A qualifying
C_AGGR(all members are permittedRangeSystemAggregationcomponents and at least one is a navigational track) is emitted as a geometry-lessRangeSystemcollection feature with oneRangeSystemAggregation/theComponentfeature association per member; theRangeSystemsEmitteddiagnostic counts them. Across the 7,184-cell NOAA base corpus this synthesises 2,083RangeSystemfeatures (931 cells) with 6,690 component associations and zero dangling or component-less collections.C_AGGRgroupings that are not range systems, and allC_ASSOassociations (whose corpus majority isLandArea-partition groupings), have no S-101 named-association equivalent and stay unmapped.
Translation diagnostics
S57ToS101Translator.Translate has an optional overload that accepts an
S57TranslationDiagnostics sink. When supplied, the translator records — as
compact aggregate counters — exactly what it dropped and why, so a caller (for
example a corpus-wide conversion audit) can quantify coverage gaps in the
embedded S57S101Mapping without re-deriving the translator's logic:
var diagnostics = new S57TranslationDiagnostics();
var s101 = new S57ToS101Translator().Translate(s57, diagnostics);
// Object classes present in the data with no mapping rule (a coverage gap):
foreach (var (objl, count) in diagnostics.UnmappedObjectClasses) { /* … */ }
Passing null (or using the parameterless Translate overloads) disables
collection with zero overhead. The counters distinguish a genuine gap (no
rule — UnmappedObjectClasses / UnmappedAttributes) from a by-design drop
(a rule exists but resolves to nothing — RuleDroppedObjectClasses /
RuleDroppedAttributes), and separately report FC-rejected enumerate values
(DroppedEnumValues), geometry loss (FeaturesDroppedForNoGeometry), and
sounding point accounting. Portrayal-affecting synthesis is also counted:
SectorLightsMerged (co-located sector arcs absorbed into one
LightSectored), TopmarksAbsorbed (TOPMAR slaves consumed by a topmark-binding
master), NauticalInformationTypesEmitted (NauticalInformation
records created for INFORM/TXTDSC/NINFOM/NTXTDS), and
RangeSystemsEmitted (geometry-less RangeSystem collection features
synthesised from range-system C_AGGR aggregations).
Validation
S57DatasetProcessor.Validate() runs validation in two phases:
- Pre-translation —
S57PreTranslationRules.Defaultchecks the rawEncDotNet.S57.S57Documentfor things that don't survive translation (DSID / DSPM presence and a positive compilation scale; the presence of anM_COVRmeta-feature). - Post-translation — the translated S-101 document is fed into
S101DatasetRules.Default(the same pack that runs against native S-101 datasets) and the resulting findings are rebadged with the prefixS101-as-S57/so the user can tell whether a problem originated in the raw S-57 input or in the translated S-101 projection.
Pre-translation rules shipped by S57PreTranslationRules.Default:
| Rule id | Severity | Checks |
|---|---|---|
S57-R-1.1 |
Error | DSID record present AND DSPM compilation scale denominator (CSCL) > 0. |
S57-R-1.2 |
Warning | At least one M_COVR meta-feature is present in the cell. |
S57-PROJ-PARSE |
— | Placeholder reserving the namespace for future parser-diagnostic findings. |
The two reports are concatenated by ConcatReports.Concat(pre, post, rebadgePrefix: "S101-as-S57/") in
EncDotNet.S100.Datasets.Pipelines
and cached on the processor.
Exchange-set integrity verification
S57ExchangeSetVerification integrity-verifies an S-57 / S-63 exchange
set (a folder containing a CATALOG.031) and maps the result onto the
same S-100 ExchangeSetVerificationResult
model used for native S-100 exchange sets, so both products surface
uniformly through the CLI (s100 validate) and any future viewer wiring.
using EncDotNet.S100.Datasets.S57;
using EncDotNet.S100.ExchangeSets;
ExchangeSetVerificationResult result =
await S57ExchangeSetVerification.VerifyAsync("/path/to/s57set");
bool integrity = result.IntegrityVerified; // no CRC mismatches / missing files
bool signed = result.AllValid; // strict: every file signature Ok
It is a deliberately thin adapter, not a shared interface: the
upstream S-57 verifier (EncDotNet.S57 0.5.0+) is rooted at a directory
path (CATALOG.031 plus the files it references), whereas the S-100
verifier is IAssetSource-based. Forcing both behind one interface would
distort both, so only the result is mapped.
Mapping notes:
- Outcomes are mapped by name (
S57VerificationOutcome→VerificationOutcome). The two enums are kept byte-identical; mapping by name means a future divergence fails loudly rather than silently mis-mapping. - Both the signature dimension (
Outcome) and the checksum dimension (ChecksumOutcome) are preserved independently. - S-57 integrity uses a CRC-32 (the
CATALOG.031field), not the SHA-256 digest the S-100 model carries, so the per-file CRC values are surfaced throughFileVerificationResult.DetailandComputedSha256is leftnull. - The verdict semantics match S-100:
NoChecksum/NotSignedare non-failing (an unsigned-but-intact set hasIntegrityVerified == truebutAllValid == false); onlyChecksumMismatch/FileMissing/Error/ invalid signatures fail integrity. This is identical to the upstream S-57AllValid. - Today the upstream S-57 verifier reports every file as
NotSigned(S-63 signature verification is a seam); CRC checking is fully wired.
Exchange-set cell enumeration
S57ExchangeSetCatalog is the loading-side companion to the verification
adapter above. It reads a CATALOG.031 (via EncDotNet.S57's
S57CatalogReader) and groups the catalogued CATD files into renderable
base cells, each with its in-set sequential updates and (optionally) its
extent:
using EncDotNet.S100.Datasets.S57;
IReadOnlyList<S57ExchangeSetCell> cells =
S57ExchangeSetCatalog.ReadBaseCells("/path/to/s57set");
foreach (S57ExchangeSetCell cell in cells)
{
// cell.RelativePath → "…/US5MA1BO.000" (platform-normalised)
// cell.UpdateRelativePaths → ["…/US5MA1BO.001", "…/US5MA1BO.002"]
// cell.BoundingBox → EPSG:4326 extent, or null
}
BoundingBox? union = S57ExchangeSetCatalog.UnionBoundingBox(cells);
Files are grouped by their 8-character cell name; the .000 file is the base
and .001, .002, … are its updates in application order. Non-dataset
entries (.TXT, certificates, the catalogue itself) are ignored, and update
files with no matching base are skipped. The pure grouping logic is exposed as
SelectBaseCells(S57Catalog) for unit testing without a real catalogue on
disk. The viewer pairs this enumeration with a FileSystemAssetSource rooted
at the exchange-set directory so each cell flows through the same
S57DatasetProcessor code path as a single dropped .000 file (with its
in-set updates folded in via S57Document.ApplyChanges before translation to
S-101).
Like the verification adapter, this is a deliberately thin adapter over the directory-rooted S-57 model rather than a shared interface.
Usage
using EncDotNet.S100.Datasets.S57;
using EncDotNet.S100.Datasets.S101;
var s57 = S57Dataset.Open("US5NY16M.000");
var s101Document = new S57ToS101Translator().Translate(s57);
var dataset = S101Dataset.FromDocument(s101Document);
// Use the existing S-101 pipeline from here:
// S101LuaRuleExecutor, FeatureGeometryProvider<Feature>, etc.
Installation
dotnet add package EncDotNet.S100.Datasets.S57
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- EncDotNet.Iso8211 (>= 0.6.0)
- EncDotNet.S100.Datasets.S101 (>= 0.22.0)
- EncDotNet.S100.ExchangeSets (>= 0.22.0)
- EncDotNet.S100.Specifications (>= 0.22.0)
- EncDotNet.S57 (>= 0.5.1)
-
net8.0
- EncDotNet.Iso8211 (>= 0.6.0)
- EncDotNet.S100.Datasets.S101 (>= 0.22.0)
- EncDotNet.S100.ExchangeSets (>= 0.22.0)
- EncDotNet.S100.Specifications (>= 0.22.0)
- EncDotNet.S57 (>= 0.5.1)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on EncDotNet.S100.Datasets.S57:
| Package | Downloads |
|---|---|
|
EncDotNet.S100.Datasets.Pipelines
Per-spec IDatasetProcessor implementations, the DatasetPipelineFactory (file -> processor detection), the headless image-render capability, the S-98 interoperability authority, and the validation runner for IHO S-100 product datasets. Consumed by the EncDotNet.S100 convenience package, the viewer, and the s100 CLI. |
|
|
EncDotNet.S100.Collections
Libraries for manipulating S-100 based nautical charts. |
GitHub repositories
This package is not used by any popular GitHub repositories.