Planned redesigned PositionData class definition as a new type PositionSeries:
/// A fixed-length (and random-access) view to a series of positions.
///
/// Implementations of this abstract class can use at least two different
/// structures to store coordinate values of positions contained in a series:
/// * A list of [Position] objects (each object contain x and y coordinates, and
/// optionally z and m too).
/// * A list of [double] values as a flat structure. For example for 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.
///
/// It's also possible to create a position data instance using factory methods
/// [PositionSeries.view] and [PositionSeries.parse] that create an instance
/// storing coordinate values of positions in a double array. The factory
/// [PositionSeries.from] creates an instance storing positions objects in an
/// array.
///
/// See [Position] for description about supported coordinate values.
///
/// For [PositionSeries] and sub classes equality by `operator ==` and
/// `hashCode` is not testing coordinate values for contained positions. Methods
/// [equalsCoords], [equals2D] and [equals3D] should be used to test coordinate
/// values between two [PositionSeries] instances.
abstract class PositionSeries implements Positionable {
/// A series of positions as a view backed by [source] containing coordinate
/// values of positions.
///
/// The [source] collection contains coordinate values of positions as a flat
/// structure. For example for `Coords.xyz` the first three coordinate values
/// are x, y and z of the first position, the next three coordinate values are
/// x, y and z of the second position, and so on.
///
/// The `type.coordinateDimension` (either 2, 3 or 4) property defines the
/// number of coordinate values for each position. The number of positions
/// contained by the view is calculated as
/// `source.length ~/ type.coordinateDimension`. If there are zero values or
/// less coordinate values than `type.coordinateDimension`, then the view is
/// considered empty.
///
/// An iterable collection of [source] may be represented by a [List] or any
/// [Iterable] with efficient `length` and `elementAt` implementations. A lazy
/// iterable with a lot of coordinate values may produce very poor
/// performance.
///
/// See [Position] for description about supported coordinate values.
factory PositionSeries.view(Iterable<double> source, {required Coords type});
/// A series of positions as a view backed by [source] containing [Position]
/// objects.
///
/// If given [type] is null then the coordinate type of [source] positions is
/// resolved from those positions (a type returned is such that it's valid
/// for all positions).
///
/// An iterable collection of [source] may be represented by a [List] or any
/// [Iterable] with efficient `length` and `elementAt` implementations. A lazy
/// iterable with a lot of position objects may produce very poor performance.
///
/// See [Position] for description about supported coordinate values.
factory PositionSeries.from(Iterable<Position> source, {Coords? type});
/// Parses a series of positions from [text] containing coordinate values of
/// positions.
///
/// Coordinate values in [text] are separated by [delimiter].
///
/// See [Position] for description about supported coordinate values.
///
/// Throws FormatException if coordinates are invalid.
factory PositionSeries.parse(
String text, {
Pattern? delimiter = ',',
Coords type = Coords.xy,
});
/// The number of positions in this series.
int get length;
/// Returns true if this series has no positions.
bool get isEmpty;
/// Returns true if this series has at least one position.
bool get isNotEmpty;
/// All positions in this series as an iterable.
///
/// See also [positionsAs] that allow typing position object as subtypes of
/// [Position].
Iterable<Position> get positions;
/// All positions in this series as an iterable of positions typed as [R]
/// using [to] factory.
///
/// See also [positions] that always returns position objects as [Position].
Iterable<R> positionsAs<R extends Position>({
required CreatePosition<R> to,
});
/// The position at the given index.
///
/// The index must be a valid index in this series; `0 <= index < length`.
///
/// See also [get] that allow typing the position object as subtypes of
/// [Position].
Position operator [](int index);
/// The position at the given index as an object of [R] using [to] factory.
///
/// The index must be a valid index in this series; `0 <= index < length`.
///
/// Examples when `series` is an instance of [PositionSeries]:
///
/// ```dart
/// // get a position at index 3 as a `Projected` position
/// series.get(3, to: Projected.create);
///
/// // get a position at index 3 as a `Geographic` position
/// series.get(3, to: Geographic.create);
/// ```
R get<R extends Position>(
int index, {
required CreatePosition<R> to,
});
/// The first position or null (if empty collection).
Position? get firstOrNull;
/// The last position or null (if empty collection).
Position? get lastOrNull;
/// The `x` coordinate of the position at the given index.
///
/// For geographic coordinates x represents *longitude*.
///
/// The index must be a valid index in this series; `0 <= index < length`.
double x(int index);
/// The `y` coordinate of the position at the given index.
///
/// For geographic coordinates y represents *latitude*.
///
/// The index must be a valid index in this series; `0 <= index < length`.
double y(int index);
/// The `z` coordinate of the position at the given index.
///
/// Returns zero if z is not available for a valid index. You can also use
/// [optZ] that returns z coordinate as a nullable value.
///
/// For geographic coordinates z represents *elevation* or *altitude*.
///
/// The index must be a valid index in this series; `0 <= index < length`.
double z(int index);
/// The `z` coordinate of the position at the given index.
///
/// Returns null if z is not available for a valid index.
///
/// For geographic coordinates z represents *elevation* or *altitude*.
///
/// The index must be a valid index in this series; `0 <= index < length`.
double? optZ(int index);
/// The `m` coordinate of the position at the given index.
///
/// Returns zero if m is not available for a valid index. You can also use
/// [optM] that returns m coordinate as a nullable value.
///
/// `m` represents a measurement or a value on a linear referencing system
/// (like time).
///
/// The index must be a valid index in this series; `0 <= index < length`.
double m(int index);
/// The `m` coordinate of the position at the given index.
///
/// Returns null if m is not available for a valid index.
///
/// `m` represents a measurement or a value on a linear referencing system
/// (like time).
///
/// The index must be a valid index in this series; `0 <= index < length`.
double? optM(int index);
/// Coordinate values of all positions in this series as an iterable.
///
/// Each position contains 2, 3 or 4 coordinate values indicated by [type] of
/// this series.
///
/// For example if data contains positions (x: 1.0, y: 1.1), (x: 2.0, y: 2.1),
/// and (x: 3.0, y: 3.1), then a returned iterable would be
/// `[1.0, 1.1, 2.0, 2.1, 3.0, 3.1]`.
///
/// For projected or cartesian coordinates, the coordinate ordering is:
/// (x, y), (x, y, z), (x, y, m) or (x, y, z, m).
///
/// For geographic coordinates, the coordinate ordering is:
/// (lon, lat), (lon, lat, elev), (lon, lat, m) or (lon, lat, elev, m).
///
/// See also [valuesByType] that returns coordinate values of all positions
/// according to a given coordinate type.
Iterable<double> get values;
/// Coordinate values of all positions in this series as an iterable.
///
/// Each position contains 2, 3 or 4 coordinate values indicated by given
/// [type].
///
/// See [values] (that returns coordinate values according to the coordinate
/// type of this bounding box) for description of possible return values.
Iterable<double> valuesByType(Coords type);
/// True if the first and last position equals in 2D.
bool get isClosed;
/// True if the first and last position equals in 2D within [toleranceHoriz].
bool isClosedBy([double toleranceHoriz = defaultEpsilon]);
/// Returns true if this and [other] contain exactly same coordinate values
/// (or both are empty) in the same order and with the same coordinate type.
bool equalsCoords(PositionSeries other);
/// True if this series of positions equals with [other] by testing 2D
/// coordinates of all positions (that must be in same order in both series).
///
/// Returns false if this or [other] is empty ([isEmpty] is true).
///
/// Differences on 2D coordinate values (ie. x and y, or lon and lat) between
/// this and [other] must be within [toleranceHoriz].
///
/// Tolerance values must be positive (>= 0.0).
bool equals2D(
PositionSeries other, {
double toleranceHoriz = defaultEpsilon,
});
/// True if this series of positions equals with [other] by testing 3D
/// coordinates of all positions (that must be in same order in both views).
///
/// Returns false if this or [other] is empty ([isEmpty] is true).
///
/// Returns false if this or [other] do not contain 3D coordinates.
///
/// Differences on 2D coordinate values (ie. x and y, or lon and lat) between
/// this and [other] must be within [toleranceHoriz].
///
/// Differences on vertical coordinate values (ie. z or elev) between
/// this and [other] must be within [toleranceVert].
///
/// Tolerance values must be positive (>= 0.0).
bool equals3D(
PositionSeries other, {
double toleranceHoriz = defaultEpsilon,
double toleranceVert = defaultEpsilon,
});
}
See also #199.
PositionData
shall be deprecated.Planned redesigned
PositionData
class definition as a new typePositionSeries
: