s57

package
v0.100.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 19, 2025 License: MIT Imports: 3 Imported by: 2

Documentation

Overview

Package s57 provides a parser for IHO S-57 Electronic Navigational Charts.

This package is designed for chart rendering applications. It provides fast spatial queries, feature grouping, and a clean API optimized for viewport-based rendering.

Basic Usage

parser := s57.NewParser()
chart, err := parser.Parse("US5MA22M.000")
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Chart: %s covers %+v\n", chart.DatasetName(), chart.Bounds())

Rendering Workflow

The typical rendering workflow uses spatial queries to efficiently render only visible features:

// 1. Query features in viewport
viewport := s57.Bounds{
    MinLon: -71.5, MaxLon: -71.0,
    MinLat: 42.0, MaxLat: 42.5,
}
visibleFeatures := chart.FeaturesInBounds(viewport)

// 2. Pass to S-52 presentation library for rendering
// S-52 handles all grouping, ordering, and symbology based on its lookup tables
s52.Render(visibleFeatures, displaySettings)

Spatial Queries

The chart automatically builds a spatial index for fast viewport queries:

// Get chart coverage
bounds := chart.Bounds()

// Query visible features
visible := chart.FeaturesInBounds(viewport)

// Features are returned as a slice - no allocation overhead for iteration

Feature Access

Access all features or query by object class:

// Get all features in the chart
allFeatures := chart.Features()

// Each feature contains everything needed for S-52 symbology lookup:
for _, feature := range allFeatures {
    class := feature.ObjectClass()        // "ACHARE", "DEPARE", "LNDARE"
    attrs := feature.Attributes()         // All feature attributes
    geom := feature.Geometry()            // Geometry with type and coordinates
    // Pass to S-52 for symbology lookup and rendering
}

Accessing Feature Data

for _, feature := range visibleFeatures {
    id := feature.ID()
    class := feature.ObjectClass()    // "DEPCNT", "LIGHTS", etc.
    geom := feature.Geometry()

    // Access coordinates
    for _, coord := range geom.Coordinates {
        lon, lat := coord[0], coord[1]
        // ... project and render
    }

    // Access attributes for styling
    if depth, ok := feature.Attribute("DRVAL1"); ok {
        // Apply depth-based color
    }
}

Integration with S-52 Presentation Library

This library handles S-57 parsing only. Features are designed to work directly with S-52 presentation libraries for symbology lookup and rendering.

// Parse S-57 chart
chart, _ := s57Parser.Parse("chart.000")

// S-52 uses ObjectClass + Attributes + Geometry for lookup
for _, feature := range chart.Features() {
    // S-52 looks up: ObjectClass + GeometryType + Attributes → Symbology
    symbology := s52.Lookup(feature.ObjectClass(), feature.GeometryType(), feature.Attributes())
    render(feature.Geometry(), symbology)
}

Performance

- Spatial index built automatically during parsing - Viewport queries are O(n) with low constant factor (simple bounding box checks) - No allocations during iteration - Features parsed eagerly (charts fit in memory)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Bounds

type Bounds struct {
	MinLon float64 // Western edge
	MaxLon float64 // Eastern edge
	MinLat float64 // Southern edge
	MaxLat float64 // Northern edge
}

Bounds represents a geographic bounding box in WGS-84 coordinates.

Coordinates are in decimal degrees.

func (Bounds) Contains

func (b Bounds) Contains(lon, lat float64) bool

Contains returns true if the point (lon, lat) is within the bounds.

func (Bounds) Expand

func (b Bounds) Expand(margin float64) Bounds

Expand returns a new Bounds expanded by the given margin in all directions.

Margin is in decimal degrees.

func (Bounds) Intersects

func (b Bounds) Intersects(other Bounds) bool

Intersects returns true if the given bounds intersects with this bounds.

func (Bounds) Union

func (b Bounds) Union(other Bounds) Bounds

Union returns a new Bounds that encompasses both this bounds and the other.

The resulting bounds will be the smallest bounding box that contains both inputs.

type Chart

type Chart struct {
	// contains filtered or unexported fields
}

Chart represents a parsed S-57 Electronic Navigational Chart.

A chart contains metadata (cell name, edition, dates, etc.) and a collection of navigational features (depth contours, buoys, lights, hazards, etc.).

Access metadata via methods like DatasetName(), Edition(), IssueDate(). Access features via Features(), FeaturesInBounds(), or FeatureCount().

All fields are private to maintain encapsulation.

func Parse added in v0.100.0

func Parse(filename string) (*Chart, error)

Parse reads an S-57 file from the OS filesystem and returns the parsed chart. This is a convenience function equivalent to:

parser := s57.NewParser()
chart, err := parser.Parse(filename)

Example:

chart, err := s57.Parse("US5MA22M.000")

func ParseFS added in v0.100.0

func ParseFS(fs afero.Fs, filename string) (*Chart, error)

ParseFS reads an S-57 file from a custom filesystem and returns the parsed chart. This allows using custom filesystem implementations such as afero.NewMemMapFs() for testing or specialized storage systems.

The filesystem is used for both the base file and any update files (.001, .002, etc.) if ApplyUpdates is enabled in the options.

Example with in-memory filesystem:

fs := afero.NewMemMapFs()
afero.WriteFile(fs, "/chart.000", data, 0644)
chart, err := s57.ParseFS(fs, "/chart.000")

Example with custom options:

fs := afero.NewMemMapFs()
afero.WriteFile(fs, "/chart.000", data, 0644)
opts := s57.DefaultParseOptions()
opts.Fs = fs
opts.ApplyUpdates = false
chart, err := s57.ParseWithOptions("/chart.000", opts)

func ParseWithOptions added in v0.100.0

func ParseWithOptions(filename string, opts ParseOptions) (*Chart, error)

ParseWithOptions reads an S-57 file with custom options. This is a convenience function equivalent to:

parser := s57.NewParser()
chart, err := parser.ParseWithOptions(filename, opts)

Example:

opts := s57.DefaultParseOptions()
opts.SkipUnknownFeatures = true
chart, err := s57.ParseWithOptions("US5MA22M.000", opts)

func (*Chart) ApplicationProfile

func (c *Chart) ApplicationProfile() string

ApplicationProfile returns human-readable application profile.

Examples: "EN (ENC New)", "ER (ENC Revision)"

func (*Chart) Bounds

func (c *Chart) Bounds() Bounds

Bounds returns the geographic coverage area of the chart.

This represents the minimum bounding box containing all features.

func (*Chart) Comment

func (c *Chart) Comment() string

Comment returns the metadata comment field.

func (*Chart) CompilationScale

func (c *Chart) CompilationScale() int32

CompilationScale returns the compilation scale denominator of the chart.

For example, a value of 50000 indicates the chart was compiled at 1:50,000 scale. This helps determine appropriate display scales and SCAMIN filtering.

S-57 §7.3.2.1: CSCL field in DSPM record. Returns 0 if not specified.

func (*Chart) CoordinateUnits

func (c *Chart) CoordinateUnits() CoordinateUnits

CoordinateUnits returns the coordinate system used in the chart.

Most ENC charts use CoordinateUnitsLatLon (lat/lon in WGS-84). Some charts may use CoordinateUnitsEastNorth for projected coordinates.

S-57 §7.3.2.1: COUN field in DSPM record.

func (*Chart) DatasetName

func (c *Chart) DatasetName() string

DatasetName returns the chart's dataset name (cell identifier).

Example: "US5MA22M", "GB5X01NE"

func (*Chart) Edition

func (c *Chart) Edition() string

Edition returns the chart's edition number.

func (*Chart) ExchangePurpose

func (c *Chart) ExchangePurpose() string

ExchangePurpose returns human-readable exchange purpose.

Returns "New" for new datasets or "Revision" for updates.

func (*Chart) FeatureCount

func (c *Chart) FeatureCount() int

FeatureCount returns the number of features in the chart.

func (*Chart) Features

func (c *Chart) Features() []Feature

Features returns all features in the chart.

Features include depth contours, buoys, lights, hazards, restricted areas, and all other navigational objects defined in the S-57 Object Catalogue.

Each feature contains ObjectClass, Attributes, and Geometry needed for S-52 presentation library symbology lookup and rendering.

func (*Chart) FeaturesInBounds

func (c *Chart) FeaturesInBounds(bounds Bounds) []Feature

FeaturesInBounds returns all features that intersect the given bounding box.

This is the primary method for viewport-based rendering. Only features that could be visible in the viewport are returned.

Example:

viewport := s57.Bounds{
    MinLon: -71.5, MaxLon: -71.0,
    MinLat: 42.0, MaxLat: 42.5,
}
visibleFeatures := chart.FeaturesInBounds(viewport)
for _, feature := range visibleFeatures {
    render(feature)
}

func (*Chart) HorizontalDatum

func (c *Chart) HorizontalDatum() int

HorizontalDatum returns the horizontal geodetic datum code.

Common values:

  • 2: WGS-84 (most common for modern ENCs)
  • Other values defined in S-57 Part 3 Table 3.1

S-57 §7.3.2.1: HDAT field in DSPM record.

func (*Chart) IssueDate

func (c *Chart) IssueDate() string

IssueDate returns the chart issue date in YYYYMMDD format.

This is when the dataset was released by the producing agency.

func (*Chart) ProducingAgency

func (c *Chart) ProducingAgency() int

ProducingAgency returns the producing agency code.

Example: 550 = NOAA (United States)

Full agency list available in IHO S-57 Appendix A.

func (*Chart) ProductSpecification

func (c *Chart) ProductSpecification() string

ProductSpecification returns human-readable product specification.

Typically "ENC" for Electronic Navigational Charts.

func (*Chart) S57Edition

func (c *Chart) S57Edition() string

S57Edition returns the S-57 standard edition used.

Example: "03.1" for S-57 Edition 3.1

func (*Chart) UpdateDate

func (c *Chart) UpdateDate() string

UpdateDate returns the update application date in YYYYMMDD format.

All updates dated on or before this date must be applied for current data.

func (*Chart) UpdateNumber

func (c *Chart) UpdateNumber() string

UpdateNumber returns the chart's update number.

"0" indicates a base cell, higher numbers indicate applied updates.

func (*Chart) UsageBand

func (c *Chart) UsageBand() UsageBand

UsageBand returns the ENC usage band of this chart.

This indicates the intended usage and appropriate scale range:

  • Overview: ≥1:1,500,000 (route planning)
  • General: 1:350,000-1:1,500,000 (open ocean)
  • Coastal: 1:90,000-1:350,000 (coastal navigation)
  • Approach: 1:22,000-1:90,000 (approaching ports)
  • Harbour: 1:4,000-1:22,000 (harbour navigation)
  • Berthing: ≤1:4,000 (final approach)

Applications should load the appropriate band based on zoom level.

type CoordinateUnits

type CoordinateUnits int

CoordinateUnits indicates how coordinates are encoded in the chart.

S-57 §7.3.2.1: COUN field in DSPM record defines coordinate units. Reference: S-57 Part 3 Table 3.2

const (
	// CoordinateUnitsLatLon indicates coordinates are in latitude/longitude (WGS-84).
	// This is the most common format for ENC charts.
	// Coordinates are decimal degrees, typically scaled by 10^7.
	CoordinateUnitsLatLon CoordinateUnits = 1

	// CoordinateUnitsEastNorth indicates coordinates are in projected Easting/Northing.
	// Less common; requires DSPR record to specify projection parameters.
	CoordinateUnitsEastNorth CoordinateUnits = 2

	// CoordinateUnitsUnknown indicates coordinate units are not specified.
	// Treat as lat/lon by default (S-57 default assumption).
	CoordinateUnitsUnknown CoordinateUnits = 0
)

func (CoordinateUnits) String

func (c CoordinateUnits) String() string

String returns a human-readable name for the coordinate units.

type Feature

type Feature struct {
	// contains filtered or unexported fields
}

Feature represents a navigational object from an S-57 chart.

Features include depth contours, buoys, lights, hazards, restricted areas, and all other objects defined in the S-57 Object Catalogue.

Access feature data via methods:

  • ID() returns the unique identifier
  • ObjectClass() returns the S-57 object class (e.g., "DEPCNT", "LIGHTS")
  • Geometry() returns the spatial representation
  • Attributes() returns all attributes
  • Attribute(name) returns a specific attribute value

func (*Feature) Attribute

func (f *Feature) Attribute(name string) (interface{}, bool)

Attribute returns a specific attribute value by name.

Returns the value and true if the attribute exists, or nil and false if not found.

Example:

if depth, ok := feature.Attribute("DRVAL1"); ok {
    fmt.Printf("Depth: %v meters\n", depth)
}

func (*Feature) Attributes

func (f *Feature) Attributes() map[string]interface{}

Attributes returns all feature attributes as a map.

Common attributes:

  • "DRVAL1": Depth range value 1 (minimum depth)
  • "DRVAL2": Depth range value 2 (maximum depth)
  • "COLOUR": Color code
  • "OBJNAM": Object name

Attribute meanings are defined in the S-57 Object Catalogue.

func (*Feature) Geometry

func (f *Feature) Geometry() Geometry

Geometry returns the spatial representation of the feature.

func (*Feature) ID

func (f *Feature) ID() int64

ID returns the unique feature identifier.

func (*Feature) ObjectClass

func (f *Feature) ObjectClass() string

ObjectClass returns the S-57 object class code.

Common examples:

  • "DEPCNT": Depth contour
  • "DEPARE": Depth area
  • "BOYCAR": Buoy, cardinal
  • "LIGHTS": Light
  • "OBSTRN": Obstruction
  • "RESARE": Restricted area

type Geometry

type Geometry struct {
	// Type indicates the geometry type (Point, LineString, or Polygon).
	Type GeometryType

	// Coordinates contains [longitude, latitude] pairs.
	//
	// For Point: Single coordinate pair
	// For LineString: Array of coordinate pairs forming a line
	// For Polygon: Array of coordinate pairs forming a closed ring
	//
	// Note: Coordinates follow GeoJSON convention [lon, lat], not [lat, lon].
	Coordinates [][]float64
}

Geometry represents the spatial representation of a feature.

Coordinates follow GeoJSON convention: [longitude, latitude] pairs. All coordinates are in WGS-84 decimal degrees.

type GeometryType

type GeometryType int

GeometryType represents the type of geometry.

const (
	// GeometryTypePoint represents a single point location.
	GeometryTypePoint GeometryType = iota

	// GeometryTypeLineString represents a line composed of connected points.
	GeometryTypeLineString

	// GeometryTypePolygon represents a closed polygon area.
	GeometryTypePolygon
)

func (GeometryType) String

func (g GeometryType) String() string

String returns the string representation of the geometry type.

type ParseOptions

type ParseOptions struct {
	SkipUnknownFeatures bool
	ValidateGeometry    bool
	ObjectClassFilter   []string

	// ApplyUpdates controls whether to automatically discover and apply
	// update files (.001, .002, etc.) when parsing a base cell (.000).
	// Default is true - updates are automatically applied.
	//
	// When true, the parser looks for sequential update files in the same
	// directory as the base file and applies them in order.
	//
	// Set to false to parse only the base cell without updates.
	ApplyUpdates bool

	// Fs is the filesystem to use for reading files.
	// If nil, the OS filesystem is used (afero.NewOsFs()).
	// This allows using custom filesystem implementations for testing
	// (e.g., afero.NewMemMapFs()) or specialized storage systems.
	//
	// Example with in-memory filesystem:
	//   fs := afero.NewMemMapFs()
	//   afero.WriteFile(fs, "/test.000", data, 0644)
	//   opts := s57.ParseOptions{Fs: fs}
	//   parser := s57.NewParser()
	//   chart, err := parser.ParseWithOptions("/test.000", opts)
	Fs afero.Fs
}

ParseOptions configures parsing behavior.

func DefaultParseOptions

func DefaultParseOptions() ParseOptions

DefaultParseOptions returns default options.

type Parser

type Parser interface {
	// Parse reads an S-57 file and returns the parsed chart.
	//
	// The filename should point to an S-57 base cell (.000) or update file (.001, .002, etc.).
	// Returns an error if the file cannot be read or parsed according to S-57 Edition 3.1.
	Parse(filename string) (*Chart, error)

	// ParseWithOptions parses an S-57 file with custom options.
	//
	// Use ParseOptions to control validation, error handling, and feature filtering.
	ParseWithOptions(filename string, opts ParseOptions) (*Chart, error)
}

Parser parses S-57 Electronic Navigational Chart files.

Create a parser with NewParser and use Parse or ParseWithOptions to read charts.

func NewParser

func NewParser() Parser

NewParser creates a new S-57 parser with default settings.

Example:

parser := s57.NewParser()
chart, err := parser.Parse("US5MA22M.000")

type UsageBand

type UsageBand int

UsageBand defines the ENC usage band (navigational purpose) of the chart.

ENC cells are organized by usage band, which determines the level of detail and appropriate display scale. Applications should load the appropriate band based on the current zoom level.

Reference: S-57 Part 3 §7.3.1.1 (INTU field) and S-52 Section 3.4

const (
	// UsageBandUnknown indicates the band is not specified.
	UsageBandUnknown UsageBand = 0

	// UsageBandOverview - For overview navigation (≥ 1:1,500,000).
	// Provides general context and route planning.
	UsageBandOverview UsageBand = 1

	// UsageBandGeneral - For general navigation (1:350,000 - 1:1,500,000).
	// Used for open ocean and offshore navigation.
	UsageBandGeneral UsageBand = 2

	// UsageBandCoastal - For coastal navigation (1:90,000 - 1:350,000).
	// Used for navigation along coastlines and approaching ports.
	UsageBandCoastal UsageBand = 3

	// UsageBandApproach - For approach navigation (1:22,000 - 1:90,000).
	// Used when approaching ports, harbours, and pilot stations.
	UsageBandApproach UsageBand = 4

	// UsageBandHarbour - For harbour navigation (1:4,000 - 1:22,000).
	// Used for navigation within harbours and restricted waters.
	UsageBandHarbour UsageBand = 5

	// UsageBandBerthing - For berthing (≤ 1:4,000).
	// Used for final approach to berth and detailed harbour navigation.
	UsageBandBerthing UsageBand = 6
)

func (UsageBand) ScaleRange

func (ub UsageBand) ScaleRange() (min, max int)

ScaleRange returns the recommended scale range for this usage band.

Returns (minScale, maxScale) where scales are denominators (e.g., 1:90000 returns 90000). For overview and berthing (open-ended ranges), one value may be 0.

func (UsageBand) String

func (ub UsageBand) String() string

String returns the human-readable name of the usage band.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL