Coordinates
Geographic and projected coordinates can be represented as single positions, series of positions and bounding boxes (with minimum and maximum positions).
This chapter discusses using and creating such objects.
Geometric objects commonly used in geospatial applications like like points, line strings (or polylines) and polygons are introduced in the separate chapter about simple geometries.
📍 Position data
The basic building blocks to represent position data in this package are:
Class | Description |
---|---|
Position | A position with 2 to 4 coordinate values (x and y are required, z and m are optional) representing an exact location in some coordinate reference system. |
PositionSeries | A series of 0 to N positions built from a coordinate value array or a list of position objects. |
Box | A bounding box with 4 to 8 coordinate values (minX, minY, maxX and maxY are required, minZ, minM, maxZ and maxM are optional). |
These classes are used by simple geometry classes too as internal data structures to store single positions and boxes, and series of positions.
Some basic samples to create position objects:
// A position as a view on a coordinate array containing x and y. Position.view([708221.0, 5707225.0]);
// A position as a view on a coordinate array containing x, y and z. Position.view([708221.0, 5707225.0, 45.0]);
// A position as a view on a coordinate array containing x, y, z and m. Position.view([708221.0, 5707225.0, 45.0, 123.0]);
// The samples above can be shorted using extension methods on `List<double>`. [708221.0, 5707225.0].xy; [708221.0, 5707225.0, 45.0].xyz; [708221.0, 5707225.0, 45.0, 123.0].xyzm;
// There are also some other factory methods. Position.create(x: 708221.0, y: 5707225.0, z: 45.0, m: 123.0); Position.parse('708221.0,5707225.0,45.0,123.0'); Position.parse('708221.0 5707225.0 45.0 123.0', delimiter: ' ');
When a position contains geographic coordinates, then x
represents
longitude
, y
represents latitude
, and z
represents elevation
(or
height or altitude) when manipulating data with classes introduced here
(external data formats may use different
axis orders).
Bounding boxes have similar factory methods too:
// The same bounding box (limits on x and y) created with different factories. Box.view([70800.0, 5707200.0, 70900.0, 5707300.0]); Box.create(minX: 70800.0, minY: 5707200.0, maxX: 70900.0, maxY: 5707300.0); Box.parse('70800.0,5707200.0,70900.0,5707300.0'); Box.parse('70800.0 5707200.0 70900.0 5707300.0', delimiter: ' ');
// The same box using extension methods on `List<double>`. [70800.0, 5707200.0, 70900.0, 5707300.0].box;
🪣 Series of positions
PositionSeries
is a fixed-length (and random-access) view to a series of
positions. There are two main structures to store coordinate values of positions
contained in a series:
- A list of
Position
objects (each object contains x and y coordinates, and optionally z and m too). - A list of
double
values as a flat structure. For example a double list could contain coordinates like[x0, y0, z0, x1, y1, z1, x2, y2, z2]
that represents three positions each with x, y and z coordinates.
These two structures are demonstrated by code:
// A position series from a flat coordinate value array. PositionSeries.view( [ 70800.0, 5707200.0, // (x, y) coordinate values for position 0 70850.0, 5707250.0, // (x, y) coordinate values for position 1 70900.0, 5707300.0, // (x, y) coordinate values for position 2 ], type: Coords.xy, );
// A position series from an array of position objects. PositionSeries.from( [ [70800.0, 5707200.0].xy, // position 0 with (x, y) coordinate values [70850.0, 5707250.0].xy, // position 1 with (x, y) coordinate values [70900.0, 5707300.0].xy, // position 2 with (x, y) coordinate values ], type: Coords.xy, );
When manipulating positions in code it may be easier to handle lists or
iterables of Position
objects. However coordinate value arrays in a flat
structure is used by default when decoding position data from geospatial data
formats.
Coordinate value arrays represented as List<double>
also provides more
memory-efficient structure than List<Position>
. This is further enhanced by
using optimized Float64List
(a list of double-precision floating-point numbers)
or Float32List
(a list of single-precision floating-point numbers, takes even
less space but sacrifices accuracy a bit) data structures defined by the
standard dart:typed_data
package.
Building PositionSeries
objects from coordinate value arrays can be also
shortened. This can be handy when specifying position data in Dart code.
// A position series from a flat coordinate value array (2D positions). [ 70800.0, 5707200.0, // (x, y) coordinate values for position 0 70850.0, 5707250.0, // (x, y) coordinate values for position 1 70900.0, 5707300.0, // (x, y) coordinate values for position 2 ].positions(Coords.xy);
// A position series from a flat coordinate value array (3D positions). [ 70800.0, 5707200.0, 40.0, // (x, y, z) coordinate values for position 0 70850.0, 5707250.0, 45.0, // (x, y, z) coordinate values for position 1 70900.0, 5707300.0, 50.0, // (x, y, z) coordinate values for position 2 ].positions(Coords.xyz);
See also the advanced topic about coordinate arrays for more information about handling coordinate value arrays for a single position, series of positions and a single bounding box.
Classes described above can be used to represented position data in various coordinate reference systems, including geographic, projected and local systems.
There are also very specific subtypes of Position
and Box
classes.
Projected
(extending Position
) and ProjBox
(extending Box
) can be used
to represent projected or cartesian (XYZ) coordinates. Similarily Geographic
and GeoBox
can be used to represent geographic coordinates.
These special purpose subtypes for positions and boxes are discussed in next few sections.
🌐 Geographic coordinates
Geographic coordinates are based on a spherical or ellipsoidal coordinate
system representing positions on the Earth as longitude (lon
) and latitude
(lat
).
Elevation (elev
) in meters and measure (m
) coordinates are optional.
Geographic positions:
// A geographic position with longitude and latitude. Geographic(lon: -0.0014, lat: 51.4778);
// A geographic position with longitude, latitude and elevation. Geographic(lon: -0.0014, lat: 51.4778, elev: 45.0);
// A geographic position with longitude, latitude, elevation and measure. Geographic(lon: -0.0014, lat: 51.4778, elev: 45.0, m: 123.0);
// The last sample also from a double list or text (order: lon, lat, elev, m). Geographic.build([-0.0014, 51.4778, 45.0, 123.0]); Geographic.parse('-0.0014,51.4778,45.0,123.0'); Geographic.parse('-0.0014 51.4778 45.0 123.0', delimiter: ' ');
Geographic bounding boxes:
// A geographic bbox (-20 .. 20 in longitude, 50 .. 60 in latitude). GeoBox(west: -20, south: 50, east: 20, north: 60);
// A geographic bbox with limits (100 .. 200) on the elevation coordinate too. GeoBox(west: -20, south: 50, minElev: 100, east: 20, north: 60, maxElev: 200);
// The last sample also from a double list or text. GeoBox.build([-20, 50, 100, 20, 60, 200]); GeoBox.parse('-20,50,100,20,60,200');
🔢 Geographic string representations (DMS)
A geographic position can also be parsed from sexagesimal degrees (latitude and longitude subdivided to degrees, minutes and seconds):
// Decimal degrees (DD) with signed numeric degree values. Geographic.parseDms(lat: '51.4778', lon: '-0.0014');
// Decimal degrees (DD) with degree and cardinal direction symbols (N/E/S/W). Geographic.parseDms(lat: '51.4778°N', lon: '0.0014°W');
// Degrees and minutes (DM). Geographic.parseDms(lat: '51°28.668′N', lon: '0°00.084′W');
// Degrees, minutes and seconds (DMS). Geographic.parseDms(lat: '51° 28′ 40″ N', lon: '0° 00′ 05″ W');
Format geographic coordinates as string representations (DD, DM, DMS):
const p = Geographic(lat: 51.4778, lon: -0.0014);
// all three samples print decimal degrees: 51.4778°N 0.0014°W print(p.latLonDms(separator: ' ')); print('${p.latDms()} ${p.lonDms()}'); print('${Dms().lat(51.4778)} ${Dms().lon(-0.0014)}');
// prints degrees and minutes: 51°28.668′N, 0°00.084′W const dm = Dms(type: DmsType.degMin, decimals: 3); print(p.latLonDms(format: dm));
// prints degrees, minutes and seconds: 51° 28′ 40″ N, 0° 00′ 05″ W const dms = Dms.narrowSpace(type: DmsType.degMinSec); print(p.latLonDms(format: dms));
// 51 degrees 28 minutes 40 seconds to N, 0 degrees 0 minutes 5 seconds to W const dmsTextual = Dms( type: DmsType.degMinSec, separator: ' ', decimals: 0, zeroPadMinSec: false, degree: ' degrees', prime: ' minutes', doublePrime: ' seconds to', ); print(p.latLonDms(format: dmsTextual));
Parsing and formatting is supported also for geographic bounding boxes:
// Parses box from decimal degrees (DD) with cardinal direction symbols. final box = GeoBox.parseDms(west: '20°W', south: '50°N', east: '20°E', north: '60°N');
// prints degrees and minutes: 20°0′W 50°0′N, 20°0′E 60°0′N const dm0 = Dms(type: DmsType.degMin, decimals: 0, zeroPadMinSec: false); print('${box.westDms(dm0)} ${box.southDms(dm0)}' ' ${box.eastDms(dm0)} ${box.northDms(dm0)}');
In the previous example dm
, dm0
, dms
and dmsTextual
are instances of the
Dms
class that implements DmsFormat
. This defines multiple methods for
parsing and formatting decimal degrees and sexagesimal degrees
(degrees/minutes/seconds) on latitude, longitude and bearing values.
The default format used by Geographic
and GeoBox
classes formats values as
decimal degrees with cardinal direction symbols. To use other formats
(degrees/minutes or degrees/minutes/seconds), or to set other parameters (like
separators, symbol characters, the number of decimals, zero padding or value
signing) you should create a custom Dms
instance.
See the API documentation and DMS test cases for more samples.
🗺️ Projected coordinates

Projected coordinates represent projected or cartesian (XYZ) coordinates with
an optional measure (m) coordinate. For projected map positions x
often
represents easting (E) and y
represents northing (N), however a coordinate
reference system might specify something else too.
The m
(measure) coordinate represents a measurement or a value on a linear
referencing system (like time). It could be associated with a 2D position
(x, y, m) or a 3D position (x, y, z, m).
Projected positions:
// A projected position with x and y. Projected(x: 708221.0, y: 5707225.0);
// A projected position with x, y and z. Projected(x: 708221.0, y: 5707225.0, z: 45.0);
// A projected position with x, y, z and m. Projected(x: 708221.0, y: 5707225.0, z: 45.0, m: 123.0);
// The last sample also from a double list or text (order: x, y, z, m). Projected.build([708221.0, 5707225.0, 45.0, 123.0]); Projected.parse('708221.0,5707225.0,45.0,123.0'); Projected.parse('708221.0 5707225.0 45.0 123.0', delimiter: ' ');
Projected bounding boxes:
// A projected bbox with limits on x and y. ProjBox(minX: 10, minY: 10, maxX: 20, maxY: 20);
// A projected bbox with limits on x, y and z. ProjBox(minX: 10, minY: 10, minZ: 10, maxX: 20, maxY: 20, maxZ: 20);
// The last sample also from a double list or text. ProjBox.build([10, 10, 10, 20, 20, 20]); ProjBox.parse('10,10,10,20,20,20');
🗄️ Scalable coordinates
Scalable coordinates are coordinates associated with a level of detail (LOD)
or a zoom
level. They are used for example by
tiling schemes to represent pixels or
tiles in tile matrices.
The Scalable2i
class represents projected x
, y
coordinates at zoom
level, with all values as integers.
// A pixel with a zoom level (or LOD = level of detail) coordinates. const pixel = Scalable2i(zoom: 9, x: 23, y: 10);
// Such coordinates can be scaled to other zoom levels. pixel.zoomIn(); // => Scalable2i(zoom: 10, x: 46, y: 20); pixel.zoomOut(); // => Scalable2i(zoom: 8, x: 11, y: 5); pixel.zoomTo(13); // => Scalable2i(zoom: 13, x: 368, y: 160));
⌖ Coordinates summary
Classes representing position, bounding box and scalable coordinates:
Coordinate values in position classes (projected and geographic):
Class | Required coordinates | Optional coordinates | Values |
---|---|---|---|
Position | x, y | z, m | double |
Projected | x, y | z, m | double |
Geographic | lon, lat | elev, m | double |
Coordinate values in bounding box classes (projected and geographic):
Class | Required coordinates | Optional coordinates | Values |
---|---|---|---|
Box | minX, minY, maxX, maxY | minZ, minM, maxZ, maxM | double |
ProjBox | minX, minY, maxX, maxY | minZ, minM, maxZ, maxM | double |
GeoBox | west, south, east, north | minElev, minM, maxElev, maxM | double |
Coordinate values in scalable classes:
Class | Required coordinates | Optional coordinates | Values |
---|---|---|---|
Scalable2i | zoom, x, y | int |
Coordinates are stored as double
values in all position and bounding box
classes but Scalable2i
uses int
coordinate values.
The Position
class is a super type for Projected
and Geographic
, and
the Box
class is a super type for ProjBox
and GeoBox
. Please see more
information about them in the API reference.
📡 Coordinate reference systems
According to Wikipedia a Coordinate reference system is a coordinate-based local, regional or global system used to locate geographical entities.
Coordinate reference systems are identified by String
identifiers. Such ids
are specified by registries like The EPSG dataset and
OGC CRS registry. Also W3C has a
good introduction
about coordinate reference systems.
The package also contains CoordRefSys
class that has constant instaces for:
Constant | Description |
---|---|
CRS84 | WGS 84 geographic coordinates (order: longitude, latitude). |
CRS84h | WGS 84 geographic coordinates (order: longitude, latitude) with ellipsoidal height (elevation). |
EPSG_4326 | WGS 84 geographic coordinates (order: latitude, longitude). |
EPSG_4258 | ETRS89 geographic coordinates (order: latitude, longitude). |
EPSG_3857 | WGS 84 projected (Web Mercator) metric coordinates based on “spherical development of ellipsoidal coordinates”. |
EPSG_3395 | WGS 84 projected (World Mercator) metric coordinates based on “ellipsoidal coordinates”. |
These constants can be used to check id
, axisOrder
and whether to swapXY
when dealing with external data sources like GeoJSON data. However currently
geodetic datum and projection parameters are not available.
Other identifiers can be added by creating a custom class implementing
CoordRefSysResolver
and by registering it’s global instance using
CoordRefSysResolver.register()
on startup routines of your app.
Please note that CRS84
and EPSG_4326
(or “EPSG:4326”) constants both refer
to the WGS 84 geographic coordinate system, but in external data representations
their axis order differs.
📅 Temporal coordinate reference systems
There is also a type TemporalRefSys
for specifying a temporal coordinate
reference system. A custom logic can be registered using
TemporalRefSysResolver.register()
.
Currently there is only one constant identifier defined by TemporalRefSys
:
Constant | Description |
---|---|
gregorian | References temporal coordinates, dates or timestamps, that are in the Gregorian calendar and conform to RFC 3339. |