Skip to content

Add typed DSID/DSPM codes and a lexical-level helper - #52

Merged
philliphoff merged 1 commit into
mainfrom
claude/s57-typed-dsid-codes
Nov 3, 2026
Merged

philliphoff merged 1 commit into
mainfrom
claude/s57-typed-dsid-codes

Conversation

@philliphoff

Copy link
Copy Markdown
Owner

Summary

The "Smaller, nice to have" items of #49. Items 1–3 are in a separate PR.

Typed codes. Each enum value is the exact S-57 code, so callers can cast to and from the existing int properties, and codes an enum doesn't name still cast through as numbers. The int properties and the records' ToString output are unchanged; each property's XML docs now name its enum.

Subfield Enum
EXPP S57ExchangePurpose (New 1, Revision 2)
PRSP S57ProductSpecification (Enc 1, DataDictionary 2, InlandEnc 10)
PROF S57ApplicationProfile (EncNew 1, EncRevision 2, DataDictionary 3)
DSTR S57DataStructure (1–4, TopologyNotRelevant 255)
HDAT S57HorizontalDatum (all 131 HORDAT values; WGS84 = 2)
VDAT/SDAT S57VerticalDatum (all 30 VERDAT values)
DUNI/HUNI/PUNI S57DepthUnits, S57HeightUnits, S57PositionalAccuracyUnits
COUN S57CoordinateUnits

S57ProductSpecification is there for writers. S57ProductKind is for reading and can't be cast back to a code: its InlandEnc is 3, not 10. The AML codes 51–57 stay with AmlProduct, whose values are already PRSP codes.

Lexical levels.

  • S57LexicalLevel names the levels: Ascii 0, Latin1 1, Ucs2 2.
  • S57LexicalLevels.Lowest(IEnumerable<string?>) returns the lowest level that can encode every string. An empty set gives Ascii.
  • S57LexicalLevels.CanEncode(string?, S57LexicalLevel) checks a single string.
  • S57LexicalLevels.WithLowestLevels(S57Document) is an opt-in way to set AALL/NALL from a document's ATTF/NATF values. It throws if an ATTF value needs UCS-2, which ATTF doesn't allow. The writer's default output is unchanged.
  • Characters outside the Basic Multilingual Plane, and unpaired surrogates, can't be encoded at any level, so Lowest throws ArgumentException.
  • These methods use the writer's own encoder (S57FieldEncoder), so the two can't disagree. Its surrogate check moved into a shared method; the bytes written are the same.

This replaces EncDotNet.S100's S101ToS57LexicalLevels.

Docs. In docs/writing-s57-charts.md, the example uses the enums, there's a subfield-to-enum table, and WithLowestLevels is explained.

Left open:

  • S57ApplicationProfile names only the S-57 codes 1–3. The AML profile codes 4–20 cast through as unnamed values.
  • There's no TryLowest variant for non-BMP text.

Standards alignment

  • ISO/IEC 8211 (iso8211)
  • S-57 records, topology, updates or exchange sets (s57-enc)
  • Legacy chart viewer, maintenance only (chart-viewer)
  • N/A: build, CI, docs or tooling only

Standard section references cited in code or docs: S-57 Part 3 §7.3.1.1 (DSID/DSSI), §7.3.2.1 (DSPM) and clause 2.4 (lexical levels); Appendix A attributes HORDAT and VERDAT.

Tests

  • Added or updated xUnit tests under tests/. They cover:
    • every enum value against the spec, including no gaps in HORDAT/VERDAT
    • lexical levels for ASCII, Latin-1, BMP, non-BMP, unpaired surrogates and an empty set
    • writer round trips, including WithLowestLevels reproducing the golden cell byte for byte
  • Test input is built in memory; no real ENC cells are committed
  • dotnet test --configuration Release passes: 1036 unit tests and 14 performance tests. It also passes merged with the other EncDotNet.S57: attribute catalogue, object codes 161+ and long-name helpers #49 PR.

Documentation

  • Updated the affected package's src/<project>/README.md
  • Updated guides under docs/ if user-facing behavior changed
  • New public APIs have XML doc comments
  • docfx docfx.json --warningsAsErrors passes, if docs changed

Dependencies

  • No new NuGet dependencies

Breaking changes

None. All the types are new, and existing properties and writer output are unchanged. dotnet pack package validation against 0.8.1 passes.


Generated by Claude Code

Closes the "smaller, nice to have" items of #49.

Typed codes: enums whose values are the exact S-57 codes, so callers can
cast to and from the existing int properties on S57DataSetIdentification
and S57DataSetParameters (left unchanged, with no new properties, so the
API baseline and the record ToString fingerprints are unaffected):

- S57ExchangePurpose (EXPP), S57ProductSpecification (PRSP: ENC, ODD,
  Inland ENC 10; AML 51-57 stay with AmlProduct), S57ApplicationProfile
  (PROF), S57DataStructure (DSTR)
- S57HorizontalDatum (HDAT, HORDAT's 131 values), S57VerticalDatum
  (VDAT/SDAT, VERDAT's 30 values), S57DepthUnits, S57HeightUnits,
  S57PositionalAccuracyUnits, S57CoordinateUnits (COUN)

Each int property's docs name its enum.

Lexical levels: S57LexicalLevel names AALL/NALL levels 0-2, and
S57LexicalLevels offers Lowest(IEnumerable<string?>), CanEncode and an
opt-in WithLowestLevels(S57Document) that sets AALL/NALL from the
document's ATTF/NATF values. They share S57FieldEncoder's checks with the
writer. Characters outside the BMP (and unpaired surrogates) can't be
encoded at any level, so Lowest throws ArgumentException for them. The
writer's default output is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAmJZFjoPbwaxxeU38tpqC
@philliphoff
philliphoff merged commit 9f4f763 into main Nov 3, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants