Skip to content

Add GeoPackage Related Tables Extension support#211

Merged
trasch merged 7 commits into
mainfrom
issue_189_related_tables
Jun 23, 2026
Merged

Add GeoPackage Related Tables Extension support#211
trasch merged 7 commits into
mainfrom
issue_189_related_tables

Conversation

@trasch

@trasch trasch commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds support for the GeoPackage Related Tables Extension (OGC 18-000), enabling non-spatial attribute tables, media blobs, and foreign key relationships between feature tables and associated data tables.

New files (3)

File Contents
RelationshipTable.swift RelationshipType enum, RelationRow struct, GeoPackage.readRelations/writeRelation/registerRelatedTablesExtension, Feature.relatedAttributes(from:using:), FeatureCollection.loadRelatedAttributes/loadRelatedMedia/relationships
AttributeTable.swift AttributeTable struct, GeoPackage.readAttributeTable/writeAttributeTable with optional rowId filter for SQL-level filtering
MediaTable.swift MediaTable struct, GeoPackage.readMediaTable/writeMediaTable

Modified files (2)

File Change
GeoPackage.swift Added gpkgext_relations, gpkg_data_columns, gpkg_data_column_constraints to createMetadataSQL / dropMetadataSQL
GeoPackageReader.swift Sets feature.id = .int(rowId) when reading features (enables per-feature related data lookup)

Key design

  • readAttributeTable(from:table:rowId:) uses WHERE id = ? at the SQL level when filtering by a specific row — no in-memory filtering
  • Feature.id stores the GeoPackage row ID directly (no foreignMembers hack)
  • FeatureCollection.loadRelatedAttributes/loadRelatedMedia resolve gpkgext_relations entries and load the full related table
  • Feature.relatedAttributes(from:using:) reads only the matching row via SQL filter

Tests (6)

attributeTableRoundTrip, mediaTableRoundTrip, relationshipsRoundTrip, attributeTableValidation, loadRelatedAttributesViaFeatureCollection, featureRelatedAttributes

Closes #189

- Add gpkgext_relations, gpkg_data_columns, gpkg_data_column_constraints
  to createMetadataSQL / dropMetadataSQL
- Add RelationshipType enum, RelationRow struct, and GeoPackage helpers
  for reading/writing relationships and registering the extension
- Add AttributeTable struct with GeoPackage.readAttributeTable/
  writeAttributeTable (supports optional rowId filter for fast lookup)
- Add MediaTable struct with GeoPackage.readMediaTable/writeMediaTable
- Store GeoPackage row ID in Feature.id when reading features
- Add Feature.relatedAttributes(from:using:) for per-feature lookup
- Add FeatureCollection.loadRelatedAttributes/loadRelatedMedia by table
  name resolving gpkgext_relations entries
- 6 tests: attribute/media round-trip, relationships, validation,
  FeatureCollection.loadRelatedAttributes, Feature.relatedAttributes
@trasch trasch self-assigned this Jun 22, 2026
@trasch trasch added the enhancement New feature or request label Jun 22, 2026
trasch added 6 commits June 23, 2026 09:06
- SQLiteDB: @unchecked Sendable with internal serial DispatchQueue
  for thread-safe C API access
- GeoPackageConnection: actor (all public methods are async)
- FeatureCollection(geopackage:): now async throws
- FeatureStreamAsyncIterator: uses SQLiteDB directly
- Add write(sql:values:) convenience method on SQLiteDB
- All existing convenience APIs (GeoPackage.readAttributeTable,
  GeoPackage.validate, etc.) remain synchronous — they create
  their own SQLiteDB internally
- Internal helpers (GeoPackageReader, GeoPackageWriter, TileReader,
  TileWriter) stay synchronous
- Fix all test call sites with await where needed
- Restore GeoPackageError named associated values (path:, detail:, type:)
- Restore DocC comments on GeoPackageValidationIssue cases
- Remove return keyword from single-expression switch arms
- Re-add GeoPackage.validate(in: SQLiteDB) for connection-based usage
- Add gpkgTableName property on Feature via foreignMembers
- Add MediaRow struct for single media entry representation
- Add rowId filter to readMediaTable (SQL-level WHERE id = ?)
- Set gpkgTableName automatically in GeoPackageReader and FeatureStream
- Replace Feature.relatedMedia/relatedAttributes with connection-based
  auto-resolve versions using gpkgTableName and id
- Add GeoPackageConnection.readMediaRows method
- Remove old URL-based RelationRow-requiring Feature methods
- Add DocC documentation to all public types, functions,
  parameters, and enum cases across the GeoPackage module
- Add GeoPackageConnection.write(features: [Feature], to:) for
  incremental/streaming writes (first call creates table, subsequent
  calls append)
- Refactor GeoPackageWriter into reusable building blocks
  (createFeatureTable, writeFeatureRows, buildSpatialIndex,
  updateContentsBounds, writeContentsMetadata)
- writeGeopackage now async throws via GeoPackageConnection
- GeoPackageConnection.init validates metadata by default
  (skipValidation: Bool to opt out)
- Add featureTables() and tileTables() convenience filters
- SQLiteDB: @unchecked Sendable with internal serial queue
- Make GeoPackageConnection an actor
- Fix LEAST/GREATEST SQLite compatibility in bounds update
- Use INSERT OR REPLACE / INSERT OR IGNORE for idempotent writes
- Clean up test files: strip () from #function, add dbUrl pattern
  with FileManager.default.removeItem(at:) before each test
- All 1583 tests pass, zero warnings
Add FeatureCollection.writeGeopackage(into:table:createSpatialIndex:)
so users can write features and read them back on the same
GeoPackageConnection without reopening the file.
- Add gpkgRowId convenience property on Feature backed by
  foreignMembers["_gpkg_rowid"]
- Stop setting feature.id = .int(rowId) — Feature.id is now
  free for GeoJSON "id" values
- Update Feature.relatedAttributes/relatedMedia to use gpkgRowId
  instead of case .int(let rowId) = id
- Update test to check feature.gpkgRowId instead of feature.id
@trasch trasch merged commit acf10c7 into main Jun 23, 2026
1 of 2 checks passed
@trasch trasch deleted the issue_189_related_tables branch June 23, 2026 11:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add GeoPackage related tables support

1 participant