Skip to content

Simple geometries

The geometry model implemented by the geobase package is based on Simple Feature Access - Part 1: Common Architecture published by the Open Geospatial Consortium (OGC).

The basic primitives are point, line string (or a polyline) and polygon, and their multi-part representations. A geometry collection may contain any type of other geometry objects.

🧩 Geometry types

Geometry primitive types supported by this package (with samples adapted from the samples of the Wikipedia page about WKT, and compatible also with GeoJSON):

GeometryShapeDart code to build objects
PointPoint.build([30.0, 10.0])
LineStringLineString.build([30, 10, 10, 30, 40, 40])
PolygonPolygon.build([[30, 10, 40, 40, 20, 40, 10, 20, 30, 10]])
Polygon (with a hole)Polygon.build([[35, 10, 45, 45, 15, 40, 10, 20, 35, 10], [20, 30, 35, 35, 30, 20, 20, 30]])

Also multipart geometry classes are supported:

GeometryShapeDart code to build objects
MultiPointMultiPoint.build([[10, 40], [40, 30], [20, 20], [30, 10]])
MultiLineStringMultiLineString.build([[10, 10, 20, 20, 10, 40], [40, 40, 30, 30, 40, 20, 30, 10]])
MultiPolygonMultiPolygon.build([[[30, 20, 45, 40, 10, 40, 30, 20]], [[15, 5, 40, 10, 10, 20, 5, 10, 15, 5]]])
MultiPolygon (with a hole)MultiPolygon.build([[[40, 40, 20, 45, 45, 30, 40, 40]], [[20, 35, 10, 30, 10, 10, 30, 5, 45, 20, 20, 35], [30, 20, 20, 15, 20, 25, 30, 20]]])
GeometryCollectionGeometryCollection([Point.build([30.0, 10.0]), LineString.build([10, 10, 20, 20, 10, 40]), Polygon.build([[40, 40, 20, 45, 45, 30, 40, 40]])])

πŸ”§ Building geometry objects

Samples above expect 2D coordinates (x and y coordinates - or longitude and latitude).

When data contains more coordinates, like also z in 3D data, then the type parameter in build methods (for geometries other than Point) must always be used explicitely to define the coordinate type.

A line string with 3 points (2D coordinates with x and y) from the table above:

LineString.build([30, 10, 10, 30, 40, 40]);

In this call there was no need to specify the coordinate type, but the same example adjusted to contain 3D coordinates (x, y and z) requires explicitely also the type parameter (here each point has the z value of 5.5):

LineString.build([30, 10, 5.5, 10, 30, 5.5, 40, 40, 5.5], type: Coords.xyz);

This sample even extended, a line string with 3D and measured coordinates (x, y, z and m) is created below (here the m value grows from 3.1 to 3.3):

LineString.build(
[30, 10, 5.5, 3.1, 10, 30, 5.5, 3.2, 40, 40, 5.5, 3.3],
type: Coords.xyzm,
);

Geometry objects can be created also from iterables of Position objects (instances of Position itself, or subtypes Projected and Geographic):

// A line string with 3 points (2D coordinates with x and y).
LineString.from([
[30.0, 10.0].xy, // xy => Position.view()
[10.0, 30.0].xy,
[40.0, 40.0].xy,
]);
// A line string with 3 points (3D coordinates with x, y and z).
LineString.from([
Geographic(lon: 30, lat: 10, elev: 5.5), // x = lon, y = lat, z = elev
Geographic(lon: 10, lat: 30, elev: 5.5),
Geographic(lon: 40, lat: 40, elev: 5.5),
]);
// A line string with 3 points (3D and measured coordinates: x, y, z and m).
LineString.from([
Projected(x: 30, y: 10, z: 5.5, m: 3.1),
Projected(x: 10, y: 30, z: 5.5, m: 3.2),
Projected(x: 40, y: 40, z: 5.5, m: 3.3),
]);

In all geometry classes there are also some other ways to create objects:

  • default constructors: creates a geometry object using coordinate arrays
  • parse: parses a geometry object from text conforming to some text format like GeoJSON or WKT
  • decode: decodes a geometry object from bytes conforming to some binary format like WKB

Alternative ways to construct geometries are shown for 3D lines strings.

// a line string from a PositionSeries created from coordinate values (3D)
LineString(
[
10.0, 20.0, 30.0, // (x, y, z) for position 0
12.5, 22.5, 32.5, // (x, y, z) for position 1
15.0, 25.0, 35.0, // (x, y, z) for position 2
].positions(Coords.xyz),
// `.positions()` on a double list creates a `PositionSeries` instance.
);
// a line string from 3D position objects
LineString.from([
[10.0, 20.0, 30.0].xyz, // `.xyz` creates a `Position` instance
[12.5, 22.5, 32.5].xyz,
[15.0, 25.0, 35.0].xyz,
]);
// a line string from 3D positions encoded as GeoJSON text
LineString.parse(
format: GeoJSON.geometry,
'''
{
"type": "LineString",
"coordinates": [
[10.0,20.0,30.0],
[12.5,22.5,32.5],
[15.0,25.0,35.0]
]
}
''',
);
// a line string from 3D positions encoded as WKT text
LineString.parse(
format: WKT.geometry,
'LINESTRING Z (10.0 20.0 30.0,12.5 22.5 32.5,15.0 25.0 35.0)',
);
// a line string from 3D positions encoded as a comma delimited string
LineString.parseCoords(
// values for three (x, y, z) positions
'10.0,20.0,30.0,12.5,22.5,32.5,15.0,25.0,35.0',
type: Coords.xyz,
);

All other geometry objects have similar factories too.

🟦 Calculating bounding boxes

Geometry objects also know how to calculate their minimum bounding boxes.

// a line string from 2D positions (without a bounding box stored)
final line = LineString.build(
[
10.0, 20.0, // (x, y) for position 0
12.5, 22.5, // (x, y) for position 1
15.0, 25.0, // (x, y) for position 2
],
type: Coords.xy,
);
// calculate the bounding box when needed
final bounds = line.calculateBounds();
// `bounds` would be equal with this one:
Box.create(minX: 10.0, minY: 20.0, maxX: 15.0, maxY: 25.0);

If bounding boxes are needed frequently it might be more efficient to populate bounding boxes on geometry objects.

// a line string from 2D positions (with bounding box populated and stored)
final line = LineString.build(
[
10.0, 20.0, // (x, y) for position 0
12.5, 22.5, // (x, y) for position 1
15.0, 25.0, // (x, y) for position 2
],
type: Coords.xy,
).populated(onBounds: true);
// as the line string already contains it's bounds, you can just access it
final bounds = line.bounds;

When reading external data, it’s possible that a minimum bounding box is included in data already. The last example shows how to use external data for bounding box.

// a line string from 2D positions (with a bounding box given explicitly)
final line = LineString.build(
[
10.0, 20.0, // (x, y) for position 0
12.5, 22.5, // (x, y) for position 1
15.0, 25.0, // (x, y) for position 2
],
type: Coords.xy,
// a bounding box (minX, minY, maxX, maxY)
bounds: Box.build(10.0, 20.0, 15.0, 25.0),
)

πŸ–‡οΈ Geometry data model

The following class diagram describes key members of Point, LineString and Polygon geometry classes:

Primitive geometry classes described by the diagram:

  • Point with a single position represented by Position
  • LineString with a chain of positions (at least two positions) represented by PositionSeries
  • Polygon with an array of linear rings
    • exactly one exterior ring represented by PositionSeries
    • 0 to N interior rings (holes) with each represented by PositionSeries

The PositionSeries class is described in the appendix about coordinate arrays and the SimpleGeometryContent interface visible in the diagram in content interfaces. The usage of project() method is described in the chapter about projections.

See also the class diagram about multi and collection geometries below:

For example MultiLineString stores chains of positions for all line strings as a list of PositionSeries. It’s also possible to get a mapped iterable of LineString objects using the lineStrings getter.