Skip to content

Commit

Permalink
perf(geobase): double arrays as List<double> instead of Iterable<doub…
Browse files Browse the repository at this point in the history
…le> in Position, Box and PositionSeries #200, #203
  • Loading branch information
navispatial committed Sep 13, 2023
1 parent 5272c87 commit 6c2d1fa
Show file tree
Hide file tree
Showing 11 changed files with 39 additions and 51 deletions.
1 change: 1 addition & 0 deletions dart/geobase/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions dart/geobase/lib/src/coordinates/base/box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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:
///
Expand All @@ -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<double> source, {Coords? type}) {
factory Box.view(List<double> source, {Coords? type}) {
final coordType = type ?? Coords.fromDimension(source.length ~/ 2);
if (source.length != 2 * coordType.coordinateDimension) {
throw invalidCoordinates;
Expand Down Expand Up @@ -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<double> source, {Coords type = Coords.xy})
: _data = source,
_type = type;
Expand Down
8 changes: 4 additions & 4 deletions dart/geobase/lib/src/coordinates/base/position.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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:
///
Expand All @@ -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<double> source, {Coords? type}) {
factory Position.view(List<double> source, {Coords? type}) {
final len = source.length;
final coordType = type ?? Coords.fromDimension(len);
if (len != coordType.coordinateDimension) {
Expand Down Expand Up @@ -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<double> source, {required Coords type})
: _data = source,
_type = type;
Expand Down
32 changes: 20 additions & 12 deletions dart/geobase/lib/src/coordinates/base/position_series.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<double> source, {
List<double> source, {
Coords type = Coords.xy,
}) =>
_PositionDataCoords.view(source, type: type);
Expand All @@ -81,27 +76,31 @@ 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<Position>` then it's used directly as a source for a
/// new `PositionSeries` object. If [source] is `Iterable<Position>` 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<Position> source, {Coords? type}) {
// ensure a list data structure
final data =
source is List<Position> ? 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;
if (!is3D && !isMeasured) break;
}

return _PositionArray.view(
source,
data,
type: Coords.select(is3D: is3D, isMeasured: isMeasured),
);
}
Expand Down Expand Up @@ -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<Position> source, {
required Coords type,
Expand Down Expand Up @@ -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<double> source, {
Coords type = Coords.xy,
Expand Down
4 changes: 2 additions & 2 deletions dart/geobase/lib/src/utils/coord_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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> = T Function(
Iterable<double> coordinates, {
List<double> coordinates, {
required Coords type,
});

Expand All @@ -30,7 +30,7 @@ T doCreateRange<T>(
if (coordinates is List<double>) {
// 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Position> chain, {Box? bounds}) =>
LineString(
PositionSeries.from(
chain is List<Position> ? chain : chain.toList(growable: false),
),
PositionSeries.from(chain),
bounds: bounds,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,7 @@ class MultiLineString extends SimpleGeometry {
Box? bounds,
}) =>
MultiLineString(
lineStrings
.map(
(chain) => PositionSeries.from(
chain is List<Position> ? chain : chain.toList(growable: false),
),
)
.toList(growable: false),
lineStrings.map(PositionSeries.from).toList(growable: false),
bounds: bounds,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,8 @@ class MultiPolygon extends SimpleGeometry {
MultiPolygon(
polygons
.map(
(polygon) => polygon
.map(
(ring) => PositionSeries.from(
ring is List<Position>
? ring
: ring.toList(growable: false),
),
)
.toList(growable: false),
(polygon) =>
polygon.map(PositionSeries.from).toList(growable: false),
)
.toList(growable: false),
bounds: bounds,
Expand Down
8 changes: 1 addition & 7 deletions dart/geobase/lib/src/vector_data/model/geometry/polygon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,7 @@ class Polygon extends SimpleGeometry {
Box? bounds,
}) =>
Polygon(
rings
.map(
(ring) => PositionSeries.from(
ring is List<Position> ? ring : ring.toList(growable: false),
),
)
.toList(growable: false),
rings.map(PositionSeries.from).toList(growable: false),
bounds: bounds,
);

Expand Down
2 changes: 1 addition & 1 deletion dart/geobase/test/coordinates/position_coords_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ void main() {

void _testCoordinateOrder(
String text,
Iterable<double> coords, [
List<double> coords, [
Coords? type,
]) {
final factories = [Position.create];
Expand Down
4 changes: 2 additions & 2 deletions dart/geobase/test/coordinates/position_series_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 6c2d1fa

Please sign in to comment.