diff --git a/dart/geobase/CHANGELOG.md b/dart/geobase/CHANGELOG.md index 5811f7b3..2e5d6522 100644 --- a/dart/geobase/CHANGELOG.md +++ b/dart/geobase/CHANGELOG.md @@ -8,6 +8,7 @@ NOTE: Version 0.6.0 currently [under development](https://github.com/navibyte/ge * [Testing feature and geometry equality #188](https://github.com/navibyte/geospatial/issues/188) * [Factories on Position and Box to create from double arrays #199](https://github.com/navibyte/geospatial/issues/199) * [Redesigned PositionData supporting positions from coordinate value array and position objects #200](https://github.com/navibyte/geospatial/issues/200) +* [Coordinates using data structures from typed data #203](https://github.com/navibyte/geospatial/issues/203) ⚠️ Breaking changes: * [Allow a position stored as "Position" in Point, and a bounding box as "Box" in Geometry classes #192](https://github.com/navibyte/geospatial/issues/192) diff --git a/dart/geobase/lib/src/coordinates/base/box.dart b/dart/geobase/lib/src/coordinates/base/box.dart index c028ec58..e696c40d 100644 --- a/dart/geobase/lib/src/coordinates/base/box.dart +++ b/dart/geobase/lib/src/coordinates/base/box.dart @@ -89,9 +89,6 @@ abstract class Box extends Positionable { /// A bounding box with coordinate values as a view backed by [source]. /// - /// A double iterable of [source] may be represented by a [List] or any - /// [Iterable] with efficient `length` and `elementAt` implementations. - /// /// The [source] must contain 4, 6 or 8 coordinate values. Supported /// coordinate value combinations by coordinate [type] are: /// @@ -110,7 +107,7 @@ abstract class Box extends Positionable { /// xyz | west, south, minElev, east, north, maxElev /// xym | west, south, minM, east, north, maxM /// xyzm | west, south, minElev, minM, east, north, maxElev, maxM - factory Box.view(Iterable source, {Coords? type}) { + factory Box.view(List source, {Coords? type}) { final coordType = type ?? Coords.fromDimension(source.length ~/ 2); if (source.length != 2 * coordType.coordinateDimension) { throw invalidCoordinates; @@ -943,6 +940,9 @@ class _BoxCoords extends Box { final Coords _type; /// A bounding box with coordinate values of [type] from [source]. + /// + /// A double iterable of [source] may be represented by a [List] or any + /// [Iterable] with efficient `length` and `elementAt` implementations. const _BoxCoords.view(Iterable source, {Coords type = Coords.xy}) : _data = source, _type = type; diff --git a/dart/geobase/lib/src/coordinates/base/position.dart b/dart/geobase/lib/src/coordinates/base/position.dart index 3d486b00..a3797e39 100644 --- a/dart/geobase/lib/src/coordinates/base/position.dart +++ b/dart/geobase/lib/src/coordinates/base/position.dart @@ -99,9 +99,6 @@ abstract class Position extends Positionable { /// A position with coordinate values as a view backed by [source]. /// - /// A double iterable of [source] may be represented by a [List] or any - /// [Iterable] with efficient `length` and `elementAt` implementations. - /// /// The [source] must contain 2, 3 or 4 coordinate values. Supported /// coordinate value combinations by coordinate [type] are: /// @@ -120,7 +117,7 @@ abstract class Position extends Positionable { /// xyz | lon, lat, elev /// xym | lon, lat, m /// xyzm | lon, lat, elev, m - factory Position.view(Iterable source, {Coords? type}) { + factory Position.view(List source, {Coords? type}) { final len = source.length; final coordType = type ?? Coords.fromDimension(len); if (len != coordType.coordinateDimension) { @@ -733,6 +730,9 @@ class _PositionCoords extends Position { final Coords _type; /// A position with coordinate values of [type] from [source]. + /// + /// A double iterable of [source] may be represented by a [List] or any + /// [Iterable] with efficient `length` and `elementAt` implementations. const _PositionCoords.view(Iterable source, {required Coords type}) : _data = source, _type = type; diff --git a/dart/geobase/lib/src/coordinates/base/position_series.dart b/dart/geobase/lib/src/coordinates/base/position_series.dart index 5335d9ae..c58658a0 100644 --- a/dart/geobase/lib/src/coordinates/base/position_series.dart +++ b/dart/geobase/lib/src/coordinates/base/position_series.dart @@ -62,14 +62,9 @@ abstract class PositionSeries implements Positionable { /// 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 source, { + List source, { Coords type = Coords.xy, }) => _PositionDataCoords.view(source, type: type); @@ -81,19 +76,23 @@ abstract class PositionSeries implements Positionable { /// 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. + /// If [source] is `List` then it's used directly as a source for a + /// new `PositionSeries` object. If [source] is `Iterable` then + /// items are iterated and copied to a list that is used as a source. /// /// See [Position] for description about supported coordinate values. factory PositionSeries.from(Iterable source, {Coords? type}) { + // ensure a list data structure + final data = + source is List ? source : source.toList(growable: false); + if (type != null) { - return _PositionArray.view(source, type: type); + return _PositionArray.view(data, type: type); } else { var is3D = true; var isMeasured = true; - for (final elem in source) { + for (final elem in data) { final type = elem.type; is3D &= type.is3D; isMeasured &= type.isMeasured; @@ -101,7 +100,7 @@ abstract class PositionSeries implements Positionable { } return _PositionArray.view( - source, + data, type: Coords.select(is3D: is3D, isMeasured: isMeasured), ); } @@ -384,6 +383,10 @@ class _PositionArray extends PositionSeries { final Coords _type; /// A series of positions with positions stored in [source]. + /// + /// 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. const _PositionArray.view( Iterable source, { required Coords type, @@ -508,6 +511,11 @@ class _PositionDataCoords extends PositionSeries { final Coords _type; /// A series of positions with coordinate values of [type] from [source]. + /// + /// 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. const _PositionDataCoords.view( Iterable source, { Coords type = Coords.xy, diff --git a/dart/geobase/lib/src/utils/coord_utils.dart b/dart/geobase/lib/src/utils/coord_utils.dart index a8c46e56..f44cb746 100644 --- a/dart/geobase/lib/src/utils/coord_utils.dart +++ b/dart/geobase/lib/src/utils/coord_utils.dart @@ -11,7 +11,7 @@ import '/src/codes/coords.dart'; /// A function to create an object of [T] from [coordinates] of [type]. @internal typedef CreateAt = T Function( - Iterable coordinates, { + List coordinates, { required Coords type, }); @@ -30,7 +30,7 @@ T doCreateRange( if (coordinates is List) { // the source coordinates is a List, get range return to.call( - coordinates.getRange(start, end), + coordinates.getRange(start, end).toList(growable: false), type: type, ); } else { diff --git a/dart/geobase/lib/src/vector_data/model/geometry/linestring.dart b/dart/geobase/lib/src/vector_data/model/geometry/linestring.dart index 1799311a..192334c8 100644 --- a/dart/geobase/lib/src/vector_data/model/geometry/linestring.dart +++ b/dart/geobase/lib/src/vector_data/model/geometry/linestring.dart @@ -51,9 +51,7 @@ class LineString extends SimpleGeometry { /// The coordinate type of all positions in a chain should be the same. factory LineString.from(Iterable chain, {Box? bounds}) => LineString( - PositionSeries.from( - chain is List ? chain : chain.toList(growable: false), - ), + PositionSeries.from(chain), bounds: bounds, ); diff --git a/dart/geobase/lib/src/vector_data/model/geometry/multi_linestring.dart b/dart/geobase/lib/src/vector_data/model/geometry/multi_linestring.dart index f732aab5..4fa774b4 100644 --- a/dart/geobase/lib/src/vector_data/model/geometry/multi_linestring.dart +++ b/dart/geobase/lib/src/vector_data/model/geometry/multi_linestring.dart @@ -60,13 +60,7 @@ class MultiLineString extends SimpleGeometry { Box? bounds, }) => MultiLineString( - lineStrings - .map( - (chain) => PositionSeries.from( - chain is List ? chain : chain.toList(growable: false), - ), - ) - .toList(growable: false), + lineStrings.map(PositionSeries.from).toList(growable: false), bounds: bounds, ); diff --git a/dart/geobase/lib/src/vector_data/model/geometry/multi_polygon.dart b/dart/geobase/lib/src/vector_data/model/geometry/multi_polygon.dart index 35fa3ab8..084c0997 100644 --- a/dart/geobase/lib/src/vector_data/model/geometry/multi_polygon.dart +++ b/dart/geobase/lib/src/vector_data/model/geometry/multi_polygon.dart @@ -68,15 +68,8 @@ class MultiPolygon extends SimpleGeometry { MultiPolygon( polygons .map( - (polygon) => polygon - .map( - (ring) => PositionSeries.from( - ring is List - ? ring - : ring.toList(growable: false), - ), - ) - .toList(growable: false), + (polygon) => + polygon.map(PositionSeries.from).toList(growable: false), ) .toList(growable: false), bounds: bounds, diff --git a/dart/geobase/lib/src/vector_data/model/geometry/polygon.dart b/dart/geobase/lib/src/vector_data/model/geometry/polygon.dart index 3d765e76..3c56f509 100644 --- a/dart/geobase/lib/src/vector_data/model/geometry/polygon.dart +++ b/dart/geobase/lib/src/vector_data/model/geometry/polygon.dart @@ -72,13 +72,7 @@ class Polygon extends SimpleGeometry { Box? bounds, }) => Polygon( - rings - .map( - (ring) => PositionSeries.from( - ring is List ? ring : ring.toList(growable: false), - ), - ) - .toList(growable: false), + rings.map(PositionSeries.from).toList(growable: false), bounds: bounds, ); diff --git a/dart/geobase/test/coordinates/position_coords_test.dart b/dart/geobase/test/coordinates/position_coords_test.dart index f1605bba..87d67d27 100644 --- a/dart/geobase/test/coordinates/position_coords_test.dart +++ b/dart/geobase/test/coordinates/position_coords_test.dart @@ -257,7 +257,7 @@ void main() { void _testCoordinateOrder( String text, - Iterable coords, [ + List coords, [ Coords? type, ]) { final factories = [Position.create]; diff --git a/dart/geobase/test/coordinates/position_series_test.dart b/dart/geobase/test/coordinates/position_series_test.dart index 34605c35..7ca21bc9 100644 --- a/dart/geobase/test/coordinates/position_series_test.dart +++ b/dart/geobase/test/coordinates/position_series_test.dart @@ -171,8 +171,8 @@ void main() { test('Test equalsCoords', () { final iter = arr.map((e) => e * 10.0); - final xyIter2 = PositionSeries.view(iter); - final xyIter3 = PositionSeries.view(iter); + final xyIter2 = PositionSeries.view(iter.toList(growable: false)); + final xyIter3 = PositionSeries.view(iter.toList(growable: false)); expect(xy1.equalsCoords(xyIter2), false); expect(xyIter3.equalsCoords(xyIter2), true);