Skip to content

Notes on GeoPackage 1.4 compliance

Nathan Russell edited this page Jan 5, 2026 · 1 revision

It appears that the Java, iOS and Javascript libraries were updated to declare GeoPackage 1.4 compatibility in 2024. These libraries were updated to set USER_VERSION to 1.4, and the RTree Index triggers were updated according to the spec. However it does not appear that the libraries were updated to support "empty" Point geometry (Points with coordinates declared as NaN), which also appears to be a requirement for full adherence to the 1.4 spec. The following is an analysis of the changes that would be required for supporting "empty" Point geometry.

Java libraries

simple-features-java

Point

  • Add a new field (explicityEmpty) and a public method (setExplicitlyEmpty) that allows an application to indicate when a Point should be set as “empty”, which indicates that coordinates should be set to NaN
  • The “isEmpty” method should not be hardcoded to false. Instead it should check whether explicityEmpty is set to true (for writing new data), or whether the x or y coordinates are set to NaN (for reading existing data)

Geometry

  • Modify the “getEnvelope” method to return null when isEmpty() is true, to prevent corrupted NaN envelopes from being returned
  • Modify the "getDimension" method to return -1 when isEmpty() is true
  • Modify the "getCentroid" method to return null when isEmpty() is true

GeometryEnvelope

  • Modify the "contains(Point point, double epsilon)" method to return false when isEmpty() is true
  • Modify the "overlap(GeometryEnvelope envelope, boolean allowEmpty)" method to return null if the GeometryEnvelope object contains NaN values for min or max X or Y values
  • Modify the "union(GeometryEnvelope envelope)" method to return null if the GeometryEnvelope object contains NaN values for min or max X or Y values

GeometryEnvelopeBuilder

  • Modify the “addPoint” method to early return when isEmpty() is true, to prevent comparison of NaN coordinate values

GeometryUtils

  • Each method that accesses point coordinates for calculations or processing should check for isEmpty() to prevent attempted calculation on coordinates containing NaN values

  • Modify the “bearing(Point point1, Point point2)” method to return Double.NaN if isEmpty() is true for either Point object

  • Modify the “bound(Point point, GeometryEnvelope envelope)” method to early return when isEmpty() is true

  • Modify the “degreesToMeters(Point point)” method to return a copy of the passed in Point object if isEmpty() is true

  • Modify the “degreesToRadians(Point point)” method to return a copy of the passed in Point object if isEmpty() is true

  • Modify the "distance(Point point1, Point point2)” method to return Double.NaN if isEmpty() is true for either Point object

  • Modify the “distanceHaversine(Point point1, Point point2)” method to return Double.NaN if isEmpty() is true for either Point object

  • Modify the "geodesicMidpoint(Point point1, Point point2)" method to return null if isEmpty() is true for either Point object

  • Modify the "geodesicMidpointRadians(Point point1, Point point2)" method to return null if isEmpty() is true for either Point object

  • Modify the "geodesicPath(List points, double maxDistance)" method to skip segments when isEmpty() is true for either Point object

  • Modify the "geodesicPath(Point point1, Point point2, double maxDistance)" method to return an empty list if isEmpty() is true for either Point object

  • Modify the “metersToDegrees(Point point)” method to return a copy of the passed in Point object if isEmpty() is true

  • Modify the "perpendicularDistance(Point point, Point lineStart, Point lineEnd)” method to return Double.NaN if isEmpty() is true for any of the Point objects

  • Modify the “pointOnPath(Point point, Point point1, Point point2)” method to return false if isEmpty() is true for any of the Point objects

  • Modify the “pointOnPath(Point point, Point point1, Point point2, double epsilon)” method to return false if isEmpty() is true for any of the Point objects

  • Modify the “radiansToDegrees(Point point)” method to return a copy of the passed in Point object if isEmpty() is true

  • Modify the "simplifyPoints(List points, double tolerance)" to filter the list of Point objects to remove any Point object where isEmpty() is true

simple-features-wkb-java

GeometryWriter

  • Modify the “writePoint(Point point)” method to write NaN values for the coordinates when isEmpty() is true

geopackage-core-java

GeoPackageGeometryData

  • Modify the "fromBytes(byte[] bytes)" method to consume the WKB for "empty" Point geometry. When the geometry is identified as "empty" based on the header flag, the current logic will assume that no WKB is present after the header. Logic should be added to check for the presence of additional bytes after the header when the geometry type is Point, and if so consume the WKB to create a Point object, which should contain NaN coordinates values. This should handle both cases where a geopackage file created against an older version may have a missing WKB payload for "empty" Points, as well as 1.4 geopackage files that may have a WKB payload with NaN coordinates for "empty" Points
  • Modify the "toBytes()" method to enforce the creation of WKB with NaN coordinates for "empty" Point objects. The current logic will not create WKB for geometry marked as "empty"

iOS libraries

simple-features-ios

SFPoint

  • Requires similar modifications as simple-features-java's Point class (isEmpty method, explicitlyEmpty property, etc)

SFGeometry

  • Modify the “envelope” method to early return nil when isEmpty() is true, to prevent corrupted NaN envelopes from being returned
  • Modify the "dimension" method to return -1 when isEmpty() is true
  • Modify the "centroid" method to return nil when isEmpty() is true

SFGeometryEnvelope

  • Modify the "isEmpty" method to check for NaN and nil values
  • Modify the "initWithMinX:andMinY" methods to check for coordinates with NaN values, and set them to nil if so

SFGeometryEnvelopeBuilder

  • Modify the "buildEnvelopeWithGeometry" method to early return if isEmpty is true

SFGeometryUtils

  • Requires similar modifications as simple-features-java's GeometryUtils class (calculations should not be attempted on NaN coordinates values)

simple-features-wkb-ios

SFWBGeometryWriter

  • Modify the "writePoint" method to write NaN values for the coordinates when isEmpty is true

geopackage-ios

GPKGGeometryData

  • Requires similar modifications as geopackage-core-java's GeoPackageGeometryData class (modify the "fromData" and "toData" methods to read and write "empty" Points, respectively)

Javascript libraries

simple-features-js

Point

  • Requires similar modifications as simple-features-java's Point class (isEmpty method, explicitlyEmpty property, etc)
  • Initialize coordinates to NaN instead of 0

Geometry

  • Modify the “getEnvelope” method to return null when isEmpty() is true, to prevent corrupted NaN envelopes from being returned
  • Modify the "getDimension" method to return -1 when isEmpty() is true
  • Modify the "getCentroid" method to return null when isEmpty() is true

GeometryEnvelope

  • Modify the "isEmpty" method to check for NaN and undefined values
  • Modify constructor blocks to check for coordinates with NaN values, and set them to undefined if so

GeometryEnvelopeBuilder

  • Modify the "buildEnvelopeWithEnvelope" method to early return if isEmpty is true

GeometryUtils

  • Requires similar modifications as simple-features-java's GeometryUtils class (calculations should not be attempted on NaN coordinates values)

simple-features-wkb-js

GeometryWriter

  • Modify the "writePoint" method to write NaN values for the coordinates when isEmpty is true

geopackage-js

geoPackageGeometryData

  • Requires similar modifications as geopackage-core-java's GeoPackageGeometryData class (modify the "fromBuffer" and "toBuffer" methods to read and write "empty" Points, respectively)