Skip to content

Advanced topics

πŸ—ΊοΈ Using cartesian positions

Position objects can be manipulated in cartesian coordinate system using operators and functions available.

// a position containing x, y and z
final pos = [708221.0, 5707225.0, 45.0].xyz;
// multiplication operator - prints "708.221,5707.225,0.045" (values in km)
// (the operand is a factor value applied to all coordinate values)
print(pos * 0.001);
// negate operator - prints "-708221.0,-5707225.0,-45.0"
print(-pos);
// following operators expect an operand to be another position object
// add operator - prints "708231.0,5707245.0,50.0"
print(pos + [10.0, 20.0, 5.0].xyz);
// subtraction operator - prints "708211.0,5707205.0,40.0"
print(pos - [10.0, 20.0, 5.0].xyz);
// division operator - prints "708.221,5707.225,45.0" (x and y values in km)
print(pos / [1000.0, 1000.0, 1.0].xyz);
// modulo operator - prints "221.0,225.0,45.0"
print(pos % [1000.0, 1000.0, 1000.0].xyz);
// there is support also for basic calculations in cartesian coordinates
// other point 1000.0 meters to the direction of 45Β° (north-east)
final other = pos.destinationPoint2D(distance: 1000.0, bearing: 45.0);
// distance between points - prints "1000.0"
print(pos.distanceTo2D(other).toStringAsFixed(1));
// bearing from point to another - prints "45.0"
print(pos.bearingTo2D(other).toStringAsFixed(1));
// midpoint between two points - prints "708574.6,5707578.6"
print(pos.midPointTo(other).toText(decimals: 1));
// intermediate point between two point (fraction range: 0.0 to 1.0)
// prints "708397.8,5707401.8"
print(pos.intermediatePointTo(other, fraction: 0.25).toText(decimals: 1));

Other options to apply coordinate transforms on position objects are to use transform (with a custom transform as a parameter) or project (geospatial projections, discussed in the separate chapter).

Also PositionSeries has some helpful operators and functions worth mentioning here.

// a closed linear ring with positions in the counterclockwise (CCW) order
final polygon = [
[1.0, 6.0].xy,
[3.0, 1.0].xy,
[7.0, 2.0].xy,
[4.0, 4.0].xy,
[8.0, 5.0].xy,
[1.0, 6.0].xy,
].series();
// the area of a polygon formed by the linear ring - prints "16.5"
print(polygon.signedArea2D());
// the perimeter of a polygon - prints "24.3"
print(polygon.length2D().toStringAsFixed(1));
// the centroid position of a polygon - prints "3.9,3.7"
print(polygon.centroid2D()!.toText(decimals: 1));
// a closed linear ring with positions in the clockwise (CW) order
final reversed = polygon.reversed();
// a line string omitting the last position of `reversed`
final line = reversed.range(0, reversed.positionCount - 1);
// the length of a line string - prints "18.9"
print(line.length2D().toStringAsFixed(1));
// the line string modified by replacing positions at indexes 1 ja 2
final lineModified = line.rangeReplaced(1, 3, [
[3.5, 1.5].xy,
[7.5, 2.5].xy,
]);
// coordinate values of a line string multiplied by 100.0
final lineModified2 = lineModified * 100.0;
// get position count and a position by index - prints "5" and "350.0,150.0"
print(lineModified2.positionCount);
print(lineModified2[1]);

Also PositionSeries objects can be transformed using transform and project too, just like single positions. Some other methods are available too to add, insert, replace and remove positions on a series, or to filter it. Please consult the package documentation for more information.

🌎 Calculations along the Earth surface

Cartesian geometry used in the previous section is not enough when measuring distances between geographic positions located far apart.

The geobase package provides geodesy functions that are based on calculations on a spherical earth model (with errors up to 0.3% compared to an ellipsoidal earth model). Distance, bearing, destination point and midpoint are provided both for great circle paths and rhumb lines. Intermediate points, intersections and areas are available for great circle paths only.

There’s a separate chapter about these calculations.

🌐 Antimeridian issues

When manipulating geographic coordinates you also have to handle geometries and bounding boxes spanning the antimeridian (longitude +180Β°). The rules specified by RFC 7946 about GeoJSON are respected. For example bounding boxes may have west longitude coordinate (minX) larger than east longitude coordinate (maxX).

// The bounding box of the Fiji archipelago spans the antimeridian.
GeoBox(west: 177.0, south: -20.0, east: -178.0, north: -16.0);

Special logic is also applied when merging geographic bounding boxes.

// a sample merging two boxes on both sides on the antimeridian
// (the result equal with p3 is then spanning the antimeridian)
const b1 = GeoBox(west: 177.0, south: -20.0, east: 179.0, north: -16.0);
const b2 = GeoBox(west: -179.0, south: -20.0, east: -178.0, north: -16.0);
const b3 = GeoBox(west: 177.0, south: -20.0, east: -178.0, north: -16.0);
b1.mergeGeographically(b2) == b3; // true
// a sample merging two boxes without need for antimeridian logic
const b4 = GeoBox(west: 40.0, south: 10.0, east: 60.0, north: 11.0);
const b5 = GeoBox(west: 55.0, south: 19.0, east: 70.0, north: 20.0);
const b6 = GeoBox(west: 40.0, south: 10.0, east: 70.0, north: 20.0);
b4.mergeGeographically(b5) == b6; // true

πŸ”’ Coordinate arrays

Position and bounding box classes introduced in the coordinates chapter are used when handling positions or bounding boxes (bounds) individually.

However to handle coordinate data in geometry objects and geospatial data formats also, efficient array data structures for coordinate values (as double numeric values) are needed. These structures are mostly used when building or writing coordinate data of geometry objects described in the simple geometry chapter.

Following factory methods allow creating PositionSeries, Position and Box instances from coordinate arrays of double values.

Factory methodDescription
PositionSeries.viewCoordinate values of 0 to N positions as a flat structure.
Position.viewCoordinate values of a single position.
Box.viewCoordinate values of a single bounding box.

For example series of positions can be created as:

// A position series with three positions each with x and y coordinates.
PositionSeries.view(
[
10.0, 11.0, // (x, y) for position 0
20.0, 21.0, // (x, y) for position 1
30.0, 31.0, // (x, y) for position 2
],
type: Coords.xy,
);
// A shortcut to create a position series with three positions (with x and y).
[
10.0, 11.0, // (x, y) for position 0
20.0, 21.0, // (x, y) for position 1
30.0, 31.0, // (x, y) for position 2
].positions(Coords.xy);
// A position series with three positions each with x, y and z coordinates.
PositionSeries.view(
[
10.0, 11.0, 12.0, // (x, y, z) for position 0
20.0, 21.0, 22.0, // (x, y, z) for position 1
30.0, 31.0, 32.0, // (x, y, z) for position 2
],
type: Coords.xyz,
);

The coordinate type (using a Coords enum value) must be defined when creating series of positions. Expected coordinate values (exactly in this order) for each type are described below:

TypeProjected valuesGeographic values
Coords.xyx, ylon, lat
Coords.xyzx, y, zlon, lat, elev
Coords.xymx, y, mlon, lat, m
Coords.xyzmx, y, z, mlon, lat, elev, m

See also specialized extension methods or getters on List<double>:

Method/getterCreated objectDescription
positions()PositionSeriesAn array of 0 to N positions from a flat structure of coordinate values.
positionPositionA single position.
boxBoxA single bounding box.

For single positions there are also some more extension getters on List<double> to create instances of Position:

Getter2D/3DCoordsValuesxyzm
.xy2D2double++
.xyz3D3double+++
.xym2D3double+++
.xyzm3D4double++++

For geographic coordinates same getters on List<double> are used:

Getter2D/3DCoordsValueslon (x)lat (y)elev (z)m
.xy2D2double++
.xyz3D3double+++
.xym2D3double+++
.xyzm3D4double++++

🧩 Content interfaces

Content interfaces allows writing geometry, property and feature data to format encoders and object builders. They are used in this package for encoding geometries and features to GeoJSON (text), WKT (text) and WKB (binary) representations, and decoding geometry and feature objects from GeoJSON and WKB representations.

Content interfaceDescription
CoordinateContentWrite coordinate data to format encoders and object builders.
SimpleGeometryContentWrite simple geometry data to format encoders and object builders.
GeometryContentWrite geometry (both simple and collection geometries) data to format encoders and object builders.
FeatureContentWrite geospatial feature objects to format encoders and object builders.