From e3861cecabde2f984c1e650b1060e790119120f9 Mon Sep 17 00:00:00 2001 From: navibyte <88932567+navispatial@users.noreply.github.com> Date: Fri, 29 Jul 2022 10:46:48 +0300 Subject: [PATCH] refactor(geobase): simplify positions as iterable coords #136 --- .../geobase/lib/src/coordinates/base/box.dart | 12 ++ .../lib/src/coordinates/base/position.dart | 12 ++ .../src/coordinates/geographic/geobox.dart | 2 +- .../coordinates/geographic/geographic.dart | 2 +- .../src/coordinates/projected/projbox.dart | 2 +- .../src/coordinates/projected/projected.dart | 2 +- .../src/vector_data/array/coordinates.dart | 11 +- .../vector_data/array/geographic_coords.dart | 122 ------------- .../src/vector_data/array/position_array.dart | 40 +++-- .../vector_data/array/position_coords.dart | 108 +++++++++-- .../vector_data/array/projected_coords.dart | 97 ---------- .../lib/src/vector_data/coords/lonlat.dart | 31 +++- .../lib/src/vector_data/coords/xy.dart | 19 +- .../test/coordinates/position_test.dart | 10 +- .../test/vector_data/position_array_test.dart | 48 ++--- .../vector_data/position_coords_test.dart | 167 +++--------------- 16 files changed, 244 insertions(+), 441 deletions(-) delete mode 100644 dart/geobase/lib/src/vector_data/array/geographic_coords.dart delete mode 100644 dart/geobase/lib/src/vector_data/array/projected_coords.dart diff --git a/dart/geobase/lib/src/coordinates/base/box.dart b/dart/geobase/lib/src/coordinates/base/box.dart index 9681ff82..6dec8106 100644 --- a/dart/geobase/lib/src/coordinates/base/box.dart +++ b/dart/geobase/lib/src/coordinates/base/box.dart @@ -64,6 +64,18 @@ typedef CreateBox = T Function({ /// xyz | west, south, minElev, east, north, maxElev /// xym | west, south, minM, east, north, maxM /// xyzm | west, south, minElev, minM, east, north, maxElev, maxM +/// +/// Sub classes containing coordinate values mentioned above, should implement +/// equality and hashCode methods as: +/// +/// ```dart +/// @override +/// bool operator ==(Object other) => +/// other is Box && Box.testEquals(this, other); +/// +/// @override +/// int get hashCode => Box.hash(this); +/// ``` abstract class Box extends Positionable { /// Default `const` constructor to allow extending this abstract class. const Box(); diff --git a/dart/geobase/lib/src/coordinates/base/position.dart b/dart/geobase/lib/src/coordinates/base/position.dart index 9a5d0c23..1a33acac 100644 --- a/dart/geobase/lib/src/coordinates/base/position.dart +++ b/dart/geobase/lib/src/coordinates/base/position.dart @@ -71,6 +71,18 @@ typedef TransformPosition = T Function(T source); /// 1 | y | lat /// 2 | z | elev /// 3 | m | m +/// +/// Sub classes containing coordinate values mentioned above, should implement +/// equality and hashCode methods as: +/// +/// ```dart +/// @override +/// bool operator ==(Object other) => +/// other is Position && Position.testEquals(this, other); +/// +/// @override +/// int get hashCode => Position.hash(this); +/// ``` abstract class Position extends Positionable { /// Default `const` constructor to allow extending this abstract class. const Position(); diff --git a/dart/geobase/lib/src/coordinates/geographic/geobox.dart b/dart/geobase/lib/src/coordinates/geographic/geobox.dart index b104ee3e..3b00ad32 100644 --- a/dart/geobase/lib/src/coordinates/geographic/geobox.dart +++ b/dart/geobase/lib/src/coordinates/geographic/geobox.dart @@ -268,7 +268,7 @@ class GeoBox extends Box { @override bool operator ==(Object other) => - other is GeoBox && Box.testEquals(this, other); + other is Box && Box.testEquals(this, other); @override int get hashCode => Box.hash(this); diff --git a/dart/geobase/lib/src/coordinates/geographic/geographic.dart b/dart/geobase/lib/src/coordinates/geographic/geographic.dart index ad1efd77..2bc15b1d 100644 --- a/dart/geobase/lib/src/coordinates/geographic/geographic.dart +++ b/dart/geobase/lib/src/coordinates/geographic/geographic.dart @@ -217,7 +217,7 @@ class Geographic extends Position { @override bool operator ==(Object other) => - other is Geographic && Position.testEquals(this, other); + other is Position && Position.testEquals(this, other); @override int get hashCode => Position.hash(this); diff --git a/dart/geobase/lib/src/coordinates/projected/projbox.dart b/dart/geobase/lib/src/coordinates/projected/projbox.dart index 007141e7..3ac96907 100644 --- a/dart/geobase/lib/src/coordinates/projected/projbox.dart +++ b/dart/geobase/lib/src/coordinates/projected/projbox.dart @@ -209,7 +209,7 @@ class ProjBox extends Box { @override bool operator ==(Object other) => - other is ProjBox && Box.testEquals(this, other); + other is Box && Box.testEquals(this, other); @override int get hashCode => Box.hash(this); diff --git a/dart/geobase/lib/src/coordinates/projected/projected.dart b/dart/geobase/lib/src/coordinates/projected/projected.dart index 44239f35..8dbe6143 100644 --- a/dart/geobase/lib/src/coordinates/projected/projected.dart +++ b/dart/geobase/lib/src/coordinates/projected/projected.dart @@ -190,7 +190,7 @@ class Projected extends Position { @override bool operator ==(Object other) => - other is Projected && Position.testEquals(this, other); + other is Position && Position.testEquals(this, other); @override int get hashCode => Position.hash(this); diff --git a/dart/geobase/lib/src/vector_data/array/coordinates.dart b/dart/geobase/lib/src/vector_data/array/coordinates.dart index 083e933b..8a015738 100644 --- a/dart/geobase/lib/src/vector_data/array/coordinates.dart +++ b/dart/geobase/lib/src/vector_data/array/coordinates.dart @@ -16,16 +16,15 @@ import '/src/utils/num.dart'; part 'box_coords.dart'; part 'coordinates_mixin.dart'; -part 'geographic_coords.dart'; part 'position_array.dart'; part 'position_coords.dart'; -part 'projected_coords.dart'; -/// Geospatial coordinate values as an iterable collection. +/// Geospatial coordinate values as an iterable collection of double values. /// -/// There are two known sub classes; [PositionCoords] containing coordinate -/// values of a single position and [PositionArray] containing coordinate values -/// of 0 to N positions. +/// See also sub classes: +/// * [PositionArray]: coordinate values of 0 to N positions in a flat structure +/// * [PositionCoords]: coordinate values of a single position +/// * [BoxCoords]: coordinate values of a single bounding box abstract class Coordinates extends Iterable implements Positionable {} typedef _CreateAt = T Function( diff --git a/dart/geobase/lib/src/vector_data/array/geographic_coords.dart b/dart/geobase/lib/src/vector_data/array/geographic_coords.dart deleted file mode 100644 index 033d5213..00000000 --- a/dart/geobase/lib/src/vector_data/array/geographic_coords.dart +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2020-2022 Navibyte (https://navibyte.com). All rights reserved. -// Use of this source code is governed by a “BSD-3-Clause”-style license that is -// specified in the LICENSE file. -// -// Docs: https://github.com/navibyte/geospatial - -part of 'coordinates.dart'; - -/// A geographic position as an iterable collection of coordinate values. -/// -/// Such position is a valid [Geographic] implementation and represents -/// coordinate values also as a collection of `Iterable` (containing 2, -/// 3, or 4 items). -/// -/// See [Geographic] for description about supported coordinate values. -/// -/// See also specialized sub classes: -/// -/// Class | 2D/3D | Coords | Values | lon (x) | lat (y) | elev (z) | m -/// ------------- | ----- | ------ | -------- | ------- | ------- | -------- | - -/// `LonLat` | 2D | 2 | `double` | + | + | | -/// `LonLatElev` | 3D | 3 | `double` | + | + | + | -/// `LonLatM` | 2D | 3 | `double` | + | + | | + -/// `LonLatElevM` | 3D | 4 | `double` | + | + | + | + -class GeographicCoords extends PositionCoords implements Geographic { - /// A geographic position with coordinate values as a view backed by `source`. - /// - /// An iterable collection of `source` may be represented by a [List] or any - /// [Iterable] with efficient `length` and `elementAt` implementations. - const GeographicCoords.view(super.source, {super.type = Coords.xy}) - : super._(); - - /// A geographic position as an iterable collection of [x], [y], and optional - /// [z] and [m] values. - /// - /// This factory is compatible with `CreatePosition` function type. - factory GeographicCoords.create({ - required num x, - required num y, - num? z, - num? m, - }) => - _doCreate( - to: GeographicCoords.view, - x: x, - y: y, - z: z, - m: m, - ); - - /// A geographic position as an iterable collection parsed from [text]. - /// - /// Coordinate values in [text] are separated by [delimiter]. - /// - /// Supported coordinate value combinations for [text] are: - /// (lon, lat), (lon, lat, elev), (lon, lat, m) or (lon, lat, elev, m) - /// - /// Use an optional [type] to explicitely set the coordinate type. If not - /// provided and [text] has 3 items, then (lon, lat, elev) coordinates are - /// assumed. - /// - /// Throws FormatException if coordinates are invalid. - factory GeographicCoords.fromText( - String text, { - Pattern? delimiter = ',', - Coords? type, - }) => - _doCreateFromText( - text, - to: GeographicCoords.view, - delimiter: delimiter, - type: type, - ); - - @override - double get x => lon; - - @override - double get y => lat; - - @override - double get z => elev; - - @override - double? get optZ => optElev; - - @override - double get lon => _data.elementAt(0); - - @override - double get lat => _data.elementAt(1); - - @override - double get elev => is3D ? _data.elementAt(2) : 0.0; - - @override - double? get optElev => is3D ? _data.elementAt(2) : null; - - @override - Iterable get values => _data; - - @override - GeographicCoords copyWith({num? x, num? y, num? z, num? m}) => _doCopyWith( - from: this, - to: GeographicCoords.view, - x: x, - y: y, - z: z, - m: m, - ); - - @override - GeographicCoords transform(TransformPosition transform) => - transform.call(this); - - @override - bool operator ==(Object other) => - other is Geographic && Position.testEquals(this, other); - - @override - int get hashCode => Position.hash(this); -} diff --git a/dart/geobase/lib/src/vector_data/array/position_array.dart b/dart/geobase/lib/src/vector_data/array/position_array.dart index f8aab83d..f3e9da79 100644 --- a/dart/geobase/lib/src/vector_data/array/position_array.dart +++ b/dart/geobase/lib/src/vector_data/array/position_array.dart @@ -11,9 +11,8 @@ part of 'coordinates.dart'; /// The collection implements `Iterable` with coordinate values of /// positions as a flat structure (each position containing 2, 3, or 4 values). /// -/// Position data is also accessible as projected coordinates via [projected] -/// and as geographic coordinates via [geographic] properties. You can use -/// [data] to map coordinate values to custom position types. +/// Position data is also accessible as position coordinates via [data]. You can +/// also use [dataTo] to map coordinate values to custom position types. /// /// See [Position] for description about supported coordinate values. class PositionArray with _CoordinatesMixin { @@ -63,22 +62,16 @@ class PositionArray with _CoordinatesMixin { type: type, ); - /// Access positions as projected coordinates. + /// Access coordinate values of positions. /// - /// See [Projected] for description about supported coordinate values. - PositionData get projected => - _PositionArrayData(_data, _type, ProjectedCoords.view); - - /// Access positions as geographic coordinates. - /// - /// See [Geographic] for description about supported coordinate values. - PositionData get geographic => - _PositionArrayData(_data, _type, GeographicCoords.view); + /// See [Position] for description about supported coordinate values. + PositionData get data => + _PositionArrayData(_data, _type, PositionCoords.view); /// Access positions as custom type of [T]. /// /// The [factory] is used to create instances of [T] as needed. - PositionData data( + PositionData dataTo( CreatePosition factory, ) => _PositionArrayData(_data, _type, _adapt(factory)); @@ -167,4 +160,23 @@ class _PositionArrayData with PositionData { @override bool get isMeasured => type.isMeasured; + + @override + Iterable get all sync* { + final dim = coordinateDimension; + final len = data.length; + var start = 0; + var end = dim; + while (end <= len) { + yield _doCreateRange( + data, + to: factory, + type: type, + start: start, + end: end, + ); + start += dim; + end += dim; + } + } } diff --git a/dart/geobase/lib/src/vector_data/array/position_coords.dart b/dart/geobase/lib/src/vector_data/array/position_coords.dart index 1566b910..be4e9cc8 100644 --- a/dart/geobase/lib/src/vector_data/array/position_coords.dart +++ b/dart/geobase/lib/src/vector_data/array/position_coords.dart @@ -6,28 +6,39 @@ part of 'coordinates.dart'; -/// A geospatial position as an iterable collection of coordinate values. +/// A base class for positions as an iterable collection of coordinate values. /// -/// Such position is a valid [Position] implementation and represents -/// coordinate values also as a collection of `Iterable` (containing 2, -/// 3, or 4 items). +/// See [PositionCoords] for a concrete implementation. /// -/// See [Position] for description about supported coordinate values. -abstract class PositionCoords extends Position with _CoordinatesMixin { +/// See also specialized sub classes for projected coordinates: +/// +/// Class | 2D/3D | Coords | Values | x | y | z | m +/// ------ | ----- | ------ | -------- | - | - | - | - +/// `XY` | 2D | 2 | `double` | + | + | | +/// `XYZ` | 3D | 3 | `double` | + | + | + | +/// `XYM` | 2D | 3 | `double` | + | + | | + +/// `XYZM` | 3D | 4 | `double` | + | + | + | + +/// +/// And there are also specialized sub classes for geographic coordinates: +/// +/// Class | 2D/3D | Coords | Values | lon (x) | lat (y) | elev (z) | m +/// ------------- | ----- | ------ | -------- | ------- | ------- | -------- | - +/// `LonLat` | 2D | 2 | `double` | + | + | | +/// `LonLatElev` | 3D | 3 | `double` | + | + | + | +/// `LonLatM` | 2D | 3 | `double` | + | + | | + +/// `LonLatElevM` | 3D | 4 | `double` | + | + | + | + +abstract class BasePositionCoords extends Position with _CoordinatesMixin { @override final Iterable _data; @override final Coords _type; - const PositionCoords._(Iterable source, {Coords type = Coords.xy}) + /// Default `const` constructor to allow extending this abstract class. + const BasePositionCoords(Iterable source, {Coords type = Coords.xy}) : _data = source, _type = type; - /// A geospatial position with coordinate values as a view backed by [source]. - factory PositionCoords.view(Iterable source, {Coords type}) = - _PositionCoordsImpl.view; - @override double get x => _data.elementAt(0); @@ -102,10 +113,64 @@ abstract class PositionCoords extends Position with _CoordinatesMixin { int get length; } +/// A geospatial position as an iterable collection of coordinate values. +/// +/// Such position is a valid [Position] implementation and represents +/// coordinate values also as a collection of `Iterable` (containing 2, +/// 3, or 4 items). +/// +/// See [Position] for description about supported coordinate values. @immutable -class _PositionCoordsImpl extends PositionCoords { - const _PositionCoordsImpl.view(super.source, {super.type = Coords.xy}) - : super._(); +class PositionCoords extends BasePositionCoords { + /// A geospatial position with coordinate values as a view backed by `source`. + /// + /// An iterable collection of `source` may be represented by a [List] or any + /// [Iterable] with efficient `length` and `elementAt` implementations. + const PositionCoords.view(super.source, {super.type = Coords.xy}) : super(); + + /// A geospatial position as an iterable collection of [x], [y], and optional + /// [z] and [m] values. + /// + /// This factory is compatible with `CreatePosition` function type. + factory PositionCoords.create({ + required num x, + required num y, + num? z, + num? m, + }) => + _doCreate( + to: PositionCoords.view, + x: x, + y: y, + z: z, + m: m, + ); + + /// A geospatial position as an iterable collection parsed from [text]. + /// + /// Coordinate values in [text] are separated by [delimiter]. + /// + /// Supported coordinate value combinations for [text] are: + /// (x, y), (x, y, z), (x, y, m) and (x, y, z, m). + /// + /// Or for geographic coordinates values combinations are: + /// (lon, lat), (lon, lat, elev), (lon, lat, m) or (lon, lat, elev, m) + /// + /// Use an optional [type] to explicitely set the coordinate type. If not + /// provided and [text] has 3 items, then xyz coordinates are assumed. + /// + /// Throws FormatException if coordinates are invalid. + factory PositionCoords.fromText( + String text, { + Pattern? delimiter = ',', + Coords? type, + }) => + _doCreateFromText( + text, + to: PositionCoords.view, + delimiter: delimiter, + type: type, + ); @override Iterable get values => _data; @@ -121,7 +186,14 @@ class _PositionCoordsImpl extends PositionCoords { ); @override - PositionCoords transform(TransformPosition transform) => transform.call(this); + BasePositionCoords transform(TransformPosition transform) => + transform.call(this); + + /// Copies this position to as a new projected position. + Projected get asProjected => copyTo(Projected.create); + + /// Copies this position to as a new geographic position. + Geographic get asGeographic => copyTo(Geographic.create); @override bool operator ==(Object other) => @@ -131,7 +203,7 @@ class _PositionCoordsImpl extends PositionCoords { int get hashCode => Position.hash(this); } -T _doCreate({ +T _doCreate({ required _CreateAt to, required num x, required num y, @@ -175,7 +247,7 @@ T _doCreate({ } } -T _doCreateFromText( +T _doCreateFromText( String text, { required _CreateAt to, Pattern? delimiter = ',', @@ -194,7 +266,7 @@ T _doCreateFromText( ); } -T _doCopyWith({ +T _doCopyWith({ required T from, required _CreateAt to, num? x, diff --git a/dart/geobase/lib/src/vector_data/array/projected_coords.dart b/dart/geobase/lib/src/vector_data/array/projected_coords.dart deleted file mode 100644 index 86837223..00000000 --- a/dart/geobase/lib/src/vector_data/array/projected_coords.dart +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2020-2022 Navibyte (https://navibyte.com). All rights reserved. -// Use of this source code is governed by a “BSD-3-Clause”-style license that is -// specified in the LICENSE file. -// -// Docs: https://github.com/navibyte/geospatial - -part of 'coordinates.dart'; - -/// A projected position as an iterable collection of coordinate values. -/// -/// Such position is a valid [Projected] implementation and represents -/// coordinate values also as a collection of `Iterable` (containing 2, -/// 3, or 4 items). -/// -/// See [Projected] for description about supported coordinate values. -/// -/// See also specialized sub classes: -/// -/// Class | 2D/3D | Coords | Values | x | y | z | m -/// ------ | ----- | ------ | -------- | - | - | - | - -/// `XY` | 2D | 2 | `double` | + | + | | -/// `XYZ` | 3D | 3 | `double` | + | + | + | -/// `XYM` | 2D | 3 | `double` | + | + | | + -/// `XYZM` | 3D | 4 | `double` | + | + | + | + -class ProjectedCoords extends PositionCoords implements Projected { - /// A projected position with coordinate values as a view backed by `source`. - /// - /// An iterable collection of `source` may be represented by a [List] or any - /// [Iterable] with efficient `length` and `elementAt` implementations. - const ProjectedCoords.view(super.source, {super.type = Coords.xy}) - : super._(); - - /// A projected position as an iterable collection of [x], [y], and optional - /// [z] and [m] values. - /// - /// This factory is compatible with `CreatePosition` function type. - factory ProjectedCoords.create({ - required num x, - required num y, - num? z, - num? m, - }) => - _doCreate( - to: ProjectedCoords.view, - x: x, - y: y, - z: z, - m: m, - ); - - /// A projected position as an iterable collection parsed from [text]. - /// - /// Coordinate values in [text] are separated by [delimiter]. - /// - /// Supported coordinate value combinations for [text] are: - /// (x, y), (x, y, z), (x, y, m) and (x, y, z, m). - /// - /// Use an optional [type] to explicitely set the coordinate type. If not - /// provided and [text] has 3 items, then xyz coordinates are assumed. - /// - /// Throws FormatException if coordinates are invalid. - factory ProjectedCoords.fromText( - String text, { - Pattern? delimiter = ',', - Coords? type, - }) => - _doCreateFromText( - text, - to: ProjectedCoords.view, - delimiter: delimiter, - type: type, - ); - - @override - Iterable get values => _data; - - @override - ProjectedCoords copyWith({num? x, num? y, num? z, num? m}) => _doCopyWith( - from: this, - to: ProjectedCoords.view, - x: x, - y: y, - z: z, - m: m, - ); - - @override - ProjectedCoords transform(TransformPosition transform) => - transform.call(this); - - @override - bool operator ==(Object other) => - other is Projected && Position.testEquals(this, other); - - @override - int get hashCode => Position.hash(this); -} diff --git a/dart/geobase/lib/src/vector_data/coords/lonlat.dart b/dart/geobase/lib/src/vector_data/coords/lonlat.dart index f2413d57..8a540f80 100644 --- a/dart/geobase/lib/src/vector_data/coords/lonlat.dart +++ b/dart/geobase/lib/src/vector_data/coords/lonlat.dart @@ -4,6 +4,8 @@ // // Docs: https://github.com/navibyte/geospatial +import 'package:meta/meta.dart'; + import '/src/codes/coords.dart'; import '/src/coordinates/base.dart'; import '/src/coordinates/geographic.dart'; @@ -18,7 +20,8 @@ import '/src/vector_data/array.dart'; /// `Iterable` with exactly 2 items. /// /// See [Geographic] for description about supported coordinate values. -class LonLat extends GeographicCoords { +@immutable +class LonLat extends BasePositionCoords implements Geographic { /// A geographic position as an iterable collection of [lon] and [lat] values. factory LonLat(double lon, double lat) { // create a fixed list of 2 items @@ -50,9 +53,9 @@ class LonLat extends GeographicCoords { /// `elementAt` implementations. const LonLat.view(super.source) : assert(source.length == 2, 'LonLat must have exactly 2 values'), - super.view(); + super(); - const LonLat._(super.source) : super.view(); + const LonLat._(super.source) : super(); /// A geographic position as an iterable collection parsed from [text]. /// @@ -95,6 +98,18 @@ class LonLat extends GeographicCoords { @override Coords get type => Coords.xy; + @override + double get x => lon; + + @override + double get y => lat; + + @override + double get z => elev; + + @override + double? get optZ => optElev; + @override double get lon => elementAt(0); @@ -113,6 +128,16 @@ class LonLat extends GeographicCoords { @override double? get optM => null; + @override + Iterable get values => this; + + @override + bool operator ==(Object other) => + other is Position && Position.testEquals(this, other); + + @override + int get hashCode => Position.hash(this); + @override String toString() => '$lon,$lat'; } diff --git a/dart/geobase/lib/src/vector_data/coords/xy.dart b/dart/geobase/lib/src/vector_data/coords/xy.dart index 006eaa6e..34e4b5a0 100644 --- a/dart/geobase/lib/src/vector_data/coords/xy.dart +++ b/dart/geobase/lib/src/vector_data/coords/xy.dart @@ -4,6 +4,8 @@ // // Docs: https://github.com/navibyte/geospatial +import 'package:meta/meta.dart'; + import '/src/codes/coords.dart'; import '/src/coordinates/base.dart'; import '/src/coordinates/projected.dart'; @@ -18,7 +20,8 @@ import '/src/vector_data/array.dart'; /// `Iterable` with exactly 2 items. /// /// See [Projected] for description about supported coordinate values. -class XY extends ProjectedCoords { +@immutable +class XY extends BasePositionCoords implements Projected { /// A projected position as an iterable collection of [x] and [y] values. factory XY(double x, double y) { // create a fixed list of 2 items @@ -44,9 +47,9 @@ class XY extends ProjectedCoords { /// `elementAt` implementations. const XY.view(super.source) : assert(source.length == 2, 'XY must have exactly 2 values'), - super.view(); + super(); - const XY._(super.source) : super.view(); + const XY._(super.source) : super(); /// A projected position as an iterable collection parsed from [text]. /// @@ -107,8 +110,18 @@ class XY extends ProjectedCoords { @override double? get optM => null; + @override + Iterable get values => this; + @override String toString() => '$x,$y'; + + @override + bool operator ==(Object other) => + other is Position && Position.testEquals(this, other); + + @override + int get hashCode => Position.hash(this); } /// A projected position as an iterable collection of x, y and z values. diff --git a/dart/geobase/test/coordinates/position_test.dart b/dart/geobase/test/coordinates/position_test.dart index c2b023f6..24ed3b41 100644 --- a/dart/geobase/test/coordinates/position_test.dart +++ b/dart/geobase/test/coordinates/position_test.dart @@ -81,7 +81,7 @@ void main() { // copy to expect(p1, p1.copyTo(Projected.create)); - expect(p1, isNot(p1.copyTo(Geographic.create))); + expect(p1, p1.copyTo(Geographic.create)); expect(p1, p1.copyTo(_TestXYZM.create)); expect(t1, t1.copyTo(Projected.create)); expect(t1, t1.copyTo(_TestXYZM.create)); @@ -100,8 +100,8 @@ void main() { const p7 = Geographic(lon: 1.0, lat: 2.0, elev: 3.0, m: 4.0); expect(p1, isNot(p5)); expect(p1, isNot(p6)); - expect(p1, isNot(p7)); - expect(p5, isNot(p6)); + expect(p1, p7); + expect(p5, p6); expect(p6, isNot(p7)); final p8 = const Projected(x: 1.0, y: 2.0); @@ -191,7 +191,7 @@ void main() { // copy to expect(p1, p1.copyTo(Geographic.create)); - expect(p1, isNot(p1.copyTo(Projected.create))); + expect(p1, p1.copyTo(Projected.create)); }); test('Equals with tolerance', () { @@ -434,7 +434,7 @@ class _TestXYZM implements Projected { @override bool operator ==(Object other) => - other is Projected && Position.testEquals(this, other); + other is Position && Position.testEquals(this, other); @override int get hashCode => Position.hash(this); diff --git a/dart/geobase/test/vector_data/position_array_test.dart b/dart/geobase/test/vector_data/position_array_test.dart index fb1715cd..777d9d8d 100644 --- a/dart/geobase/test/vector_data/position_array_test.dart +++ b/dart/geobase/test/vector_data/position_array_test.dart @@ -29,10 +29,10 @@ void main() { expect(array3.type, type); }); - test('Access projected positions', () { - final projected = array3.projected; - expect(projected.length, 3); - expect(projected.type, type); + test('Access positions as PositionData', () { + final positions = array3.data; + expect(positions.length, 3); + expect(positions.type, type); final tests = type == Coords.xyz ? [ @@ -61,26 +61,28 @@ void main() { ]; for (final test in tests) { - expect(projected.all, test); + expect(positions.all, test); for (var index = 0; index < 3; index++) { - expect(projected[index], test[index]); - expect(projected.get(index, to: Projected.create), tests[1][index]); + expect(positions[index], test[index]); + expect(positions[index].asProjected, test[index]); + expect(positions[index].asGeographic, test[index]); + expect(positions.get(index, to: Projected.create), tests[1][index]); expect( - projected.get(index, to: Geographic.create), - isNot(tests[1][index]), + positions.get(index, to: Geographic.create), + tests[1][index], ); - expect(projected[index].x, test[index].x); - expect(projected[index].y, test[index].y); - expect(projected[index].z, test[index].z); - expect(projected[index].optZ, test[index].optZ); - expect(projected[index].m, test[index].m); - expect(projected[index].optM, test[index].optM); + expect(positions[index].x, test[index].x); + expect(positions[index].y, test[index].y); + expect(positions[index].z, test[index].z); + expect(positions[index].optZ, test[index].optZ); + expect(positions[index].m, test[index].m); + expect(positions[index].optM, test[index].optM); } } }); - test('Access geographic positions', () { - final geographic = array3.geographic; + test('Access as geographic positions', () { + final geographic = array3.dataTo(Geographic.create); expect(geographic.length, 3); expect(geographic.type, type); @@ -120,7 +122,7 @@ void main() { ); expect( geographic.get(index, to: Projected.create), - isNot(tests[1][index]), + tests[1][index], ); expect(geographic[index].lon, test[index].lon); expect(geographic[index].lat, test[index].lat); @@ -146,8 +148,8 @@ void main() { expect(xyArray3.type, Coords.xy); }); - test('Access projected positions', () { - final projected = xyArray3.projected; + test('Access as projected positions', () { + final projected = xyArray3.dataTo(Projected.create); expect(projected.length, 3); expect(projected.type, Coords.xy); @@ -171,7 +173,7 @@ void main() { expect(projected.get(index, to: Projected.create), tests[1][index]); expect( projected.get(index, to: Geographic.create), - isNot(tests[1][index]), + tests[1][index], ); expect(projected[index].x, test[index].x); expect(projected[index].y, test[index].y); @@ -213,7 +215,7 @@ void main() { }); test('Access projected positions', () { - final projected = xyzmArray3.data(Projected.create); + final projected = xyzmArray3.dataTo(Projected.create); expect(projected.length, 3); expect(projected.type, Coords.xyzm); @@ -237,7 +239,7 @@ void main() { expect(projected.get(index, to: Projected.create), tests[1][index]); expect( projected.get(index, to: Geographic.create), - isNot(tests[1][index]), + tests[1][index], ); expect(projected[index].x, test[index].x); expect(projected[index].y, test[index].y); diff --git a/dart/geobase/test/vector_data/position_coords_test.dart b/dart/geobase/test/vector_data/position_coords_test.dart index 7822f43a..547ef65c 100644 --- a/dart/geobase/test/vector_data/position_coords_test.dart +++ b/dart/geobase/test/vector_data/position_coords_test.dart @@ -12,7 +12,7 @@ import 'package:geobase/vector_data.dart'; import 'package:test/test.dart'; void main() { - group('Projected coords as iterable', () { + group('Position coords as iterable', () { test('Coordinate values as iterable', () { // xyz and xym coordinates const xyzData = [15.0, 30.1, 45.2]; @@ -112,16 +112,16 @@ void main() { [null, null, null, null], ); - expect(const ProjectedCoords.view([1.0, 2.0]), p1); - expect(const ProjectedCoords.view([1.0, 2.0, 3.0]), p2); - expect(const ProjectedCoords.view([1.0, 2.0, 4.0]) == p3, false); - expect(const ProjectedCoords.view([1.0, 2.0, 4.0], type: Coords.xym), p3); - expect(const ProjectedCoords.view([1.0, 2.0, 3.0, 4.0]), p4); + expect(const PositionCoords.view([1.0, 2.0]), p1); + expect(const PositionCoords.view([1.0, 2.0, 3.0]), p2); + expect(const PositionCoords.view([1.0, 2.0, 4.0]) == p3, false); + expect(const PositionCoords.view([1.0, 2.0, 4.0], type: Coords.xym), p3); + expect(const PositionCoords.view([1.0, 2.0, 3.0, 4.0]), p4); - expect(ProjectedCoords.fromText('1.0,2.0'), p1); - expect(ProjectedCoords.fromText('1.0,2.0,3.0'), p2); - expect(ProjectedCoords.fromText('1.0,2.0,4.0', type: Coords.xym), p3); - expect(ProjectedCoords.fromText('1.0,2.0,3.0,4.0'), p4); + expect(PositionCoords.fromText('1.0,2.0'), p1); + expect(PositionCoords.fromText('1.0,2.0,3.0'), p2); + expect(PositionCoords.fromText('1.0,2.0,4.0', type: Coords.xym), p3); + expect(PositionCoords.fromText('1.0,2.0,3.0,4.0'), p4); expect(XY.fromText(p1.toString()), p1); expect(XYZ.fromText(p2.toString()), p2); @@ -129,10 +129,10 @@ void main() { expect(XYZM.fromText(p4.toString()), p4); expect(XYZM.fromText('1.0 2.0 3.0 4.0', delimiter: ' '), p4); - expect(() => const ProjectedCoords.view([1.0]).y, throwsRangeError); - expect(() => ProjectedCoords.fromText('1.0'), throwsFormatException); + expect(() => const PositionCoords.view([1.0]).y, throwsRangeError); + expect(() => PositionCoords.fromText('1.0'), throwsFormatException); expect( - () => ProjectedCoords.fromText('1.0,2.0,x'), + () => PositionCoords.fromText('1.0,2.0,x'), throwsFormatException, ); }); @@ -154,8 +154,8 @@ void main() { expect(p1.equals3D(p3), false); // copy to - expect(p1, p1.copyTo(ProjectedCoords.create)); - expect(p1 == p1.copyTo(GeographicCoords.create), false); + expect(p1, p1.copyTo(PositionCoords.create)); + expect(p1, p1.copyTo(Geographic.create)); // with some coordinates missing or other type const p5 = Projected(x: 1.0, y: 2.0, z: 3.0); @@ -163,8 +163,8 @@ void main() { const p7 = Geographic(lon: 1.0, lat: 2.0, elev: 3.0, m: 4.0); expect(p1, isNot(p5)); expect(p1, isNot(p6)); - expect(p1, isNot(p7)); - expect(p5, isNot(p6)); + expect(p1, p7); + expect(p5, p6); expect(p6, isNot(p7)); final p8 = XY.create(x: 1.0, y: 2.0); @@ -173,125 +173,8 @@ void main() { }); test('Equals with tolerance', () { - final p1 = ProjectedCoords.create(x: 1.0002, y: 2.0002, z: 3.002, m: 4.0); - final p2 = ProjectedCoords.create(x: 1.0003, y: 2.0003, z: 3.003, m: 4.0); - expect(p1.equals2D(p2), false); - expect(p1.equals3D(p2), false); - expect(p1.equals2D(p2, toleranceHoriz: 0.00011), true); - expect(p1.equals3D(p2, toleranceHoriz: 0.00011), false); - expect(p1.equals2D(p2, toleranceHoriz: 0.00011), true); - expect( - p1.equals3D(p2, toleranceHoriz: 0.00011, toleranceVert: 0.0011), - true, - ); - expect(p1.equals2D(p2, toleranceHoriz: 0.00009), false); - expect( - p1.equals3D(p2, toleranceHoriz: 0.00011, toleranceVert: 0.0009), - false, - ); - }); - - test('Copy with', () { - expect( - XY.create(x: 1, y: 1).copyWith(), - XY.create(x: 1, y: 1), - ); - expect( - XY.create(x: 1, y: 1).copyWith(y: 2), - XY.create(x: 1, y: 2), - ); - expect( - XY.create(x: 1, y: 1).copyWith(z: 2), - XY.create(x: 1, y: 1), - ); - }); - }); - - group('Geographic coordinates as iterable', () { - test('Coordinate access and factories', () { - final p1 = LonLat.create(x: 1.0, y: 2.0); - final p2 = LonLatElev.create(x: 1.0, y: 2.0, z: 3.0); - final p3 = LonLatM.create(x: 1.0, y: 2.0, m: 4.0); - final p4 = LonLatElevM.create(x: 1.0, y: 2.0, z: 3.0, m: 4.0); - expect([p1.x, p1.y], p1.values); - expect([p2.x, p2.y, p2.z], p2.values); - expect([p3.x, p3.y, p3.m], p3.values); - expect([p4.x, p4.y, p4.z, p4.m], p4.values); - expect([p1.x, p1.y, 0, 0], [p1[0], p1[1], p1[2], p1[3]]); - expect([p2.x, p2.y, p2.z, 0], [p2[0], p2[1], p2[2], p2[3]]); - expect([p3.x, p3.y, p3.m, 0], [p3[0], p3[1], p3[2], p3[3]]); - expect([p4.x, p4.y, p4.z, p4.m], [p4[0], p4[1], p4[2], p4[3]]); - expect( - [p1.optZ, p1.optM, p2.optM, p3.optZ], - [null, null, null, null], - ); - - expect(const GeographicCoords.view([1.0, 2.0]), p1); - expect(const GeographicCoords.view([1.0, 2.0, 3.0]), p2); - expect(const GeographicCoords.view([1.0, 2.0, 4.0]) == p3, false); - expect( - const GeographicCoords.view([1.0, 2.0, 4.0], type: Coords.xym), - p3, - ); - expect(const GeographicCoords.view([1.0, 2.0, 3.0, 4.0]), p4); - - expect(GeographicCoords.fromText('1.0,2.0'), p1); - expect(GeographicCoords.fromText('1.0,2.0,3.0'), p2); - expect(GeographicCoords.fromText('1.0,2.0,4.0', type: Coords.xym), p3); - expect(GeographicCoords.fromText('1.0,2.0,3.0,4.0'), p4); - - expect(LonLat.fromText(p1.toString()), p1); - expect(LonLatElev.fromText(p2.toString()), p2); - expect(LonLatM.fromText(p3.toString()), p3); - expect(LonLatElevM.fromText(p4.toString()), p4); - expect(LonLatElevM.fromText('1.0 2.0 3.0 4.0', delimiter: ' '), p4); - - expect(() => const GeographicCoords.view([1.0]).lat, throwsRangeError); - expect(() => GeographicCoords.fromText('1.0'), throwsFormatException); - expect( - () => GeographicCoords.fromText('1.0,2.0,x'), - throwsFormatException, - ); - }); - - test('Equals and hashCode', () { - // test Position itself - final one = 1.0; - final two = 2.0; - final p1 = LonLatElevM(1.0, 2.0, 3.0, 4.0); - final p2 = LonLatElevM(one, 2.0, 3.0, 4.0); - final p3 = LonLatElevM(two, 2.0, 3.0, 4.0); - expect(p1, p2); - expect(p1, isNot(p3)); - expect(p1.hashCode, p2.hashCode); - expect(p1.hashCode, isNot(p3.hashCode)); - expect(p1.equals2D(p2), true); - expect(p1.equals2D(p3), false); - expect(p1.equals3D(p2), true); - expect(p1.equals3D(p3), false); - - // copy to - expect(p1, p1.copyTo(GeographicCoords.create)); - expect(p1 == p1.copyTo(ProjectedCoords.create), false); - - // with some coordinates missing or other type - const p5 = Geographic(lon: 1.0, lat: 2.0, elev: 3.0); - const p6 = Projected(x: 1.0, y: 2.0, z: 3.0); - const p7 = Projected(x: 1.0, y: 2.0, z: 3.0, m: 4.0); - expect(p1, isNot(p5)); - expect(p1, isNot(p6)); - expect(p1, isNot(p7)); - expect(p5, isNot(p6)); - expect(p6, isNot(p7)); - - final p8 = LonLat.create(x: 1.0, y: 2.0); - expect(p1.equals2D(p8), true); - expect(p1.equals3D(p8), false); - }); - - test('Equals with tolerance', () { - final p1 = ProjectedCoords.create(x: 1.0002, y: 2.0002, z: 3.002, m: 4.0); - final p2 = ProjectedCoords.create(x: 1.0003, y: 2.0003, z: 3.003, m: 4.0); + final p1 = PositionCoords.create(x: 1.0002, y: 2.0002, z: 3.002, m: 4.0); + final p2 = PositionCoords.create(x: 1.0003, y: 2.0003, z: 3.003, m: 4.0); expect(p1.equals2D(p2), false); expect(p1.equals3D(p2), false); expect(p1.equals2D(p2, toleranceHoriz: 0.00011), true); @@ -353,7 +236,7 @@ void _testCoordinateOrder( Iterable coords, [ Coords? type, ]) { - final factories = [ProjectedCoords.create, GeographicCoords.create]; + final factories = [PositionCoords.create]; for (final factory in factories) { final fromCoords = @@ -372,15 +255,7 @@ void _testCoordinateOrder( ); expect( - ProjectedCoords.view( - coords, - type: type ?? Coords.fromDimension(coords.length), - ), - fromCoords, - ); - - expect( - GeographicCoords.view( + PositionCoords.view( coords, type: type ?? Coords.fromDimension(coords.length), ),