Skip to content

Commit

Permalink
feat(geobase): populated and unpopulated methods for Bounded #197
Browse files Browse the repository at this point in the history
  • Loading branch information
navispatial committed Aug 28, 2023
1 parent e8c1e9e commit 96657f2
Show file tree
Hide file tree
Showing 13 changed files with 409 additions and 11 deletions.
1 change: 1 addition & 0 deletions dart/geobase/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ NOTE: Version 0.6.0 currently [under development](https://github.com/navibyte/ge

🧩 Features:
* [Add copyWith (and map) methods to coordinate, meta, feature and geometry classes #189](https://github.com/navibyte/geospatial/issues/189)
* [Populating and unpopulating bounds in geometries and feature objects #197](https://github.com/navibyte/geospatial/issues/197)

⚠️ 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
30 changes: 30 additions & 0 deletions dart/geobase/lib/src/vector_data/model/bounded/bounded.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,36 @@ abstract class Bounded {
/// cannot be calculated (for example in the case of an empty geometry).
Bounded bounded({bool recalculate = false});

/// Returns a bounded object of the same subtype as this with certain data
/// members populated.
///
/// If nothing is populated then `this` is returned.
///
/// If [onBounds] is true (as by default):
/// * The `bounds` in a returned bounded object is ensured to be populated
/// (expect when cannot be calculated, for example in the case of an empty
/// geometry).
/// * If [traverse] is true, then also bounding boxes of any child bounded
/// objects are populated.
Bounded populated({
bool traverse = false,
bool onBounds = true,
});

/// Returns a bounded object of the same subtype as this with certain data
/// members unpopulated (or cleared).
///
/// If nothing is unpopulated then `this` is returned.
///
/// If [onBounds] is true (as by default):
/// * The `bounds` in a returned bounded object is ensured to be unpopulated.
/// * If [traverse] is true, then also bounding boxes of any child bounded
/// objects are unpopulated.
Bounded unpopulated({
bool traverse = false,
bool onBounds = true,
});

/// Returns a new bounded object with all geometries projected using
/// [projection] and other properties left intact.
///
Expand Down
49 changes: 49 additions & 0 deletions dart/geobase/lib/src/vector_data/model/feature/feature.dart
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,55 @@ class Feature<T extends Geometry> extends FeatureObject {
);
}

@override
Feature populated({
bool traverse = false,
bool onBounds = true,
}) {
if (onBounds) {
// populate a geometry when traversing is asked
final geom = traverse
? geometry?.populated(traverse: traverse, onBounds: onBounds)
: geometry;

// create a new feature if geometry changed or bounds was unpopulated
if (geom != geometry || (bounds == null && geom != null)) {
return Feature(
id: id,
geometry: geom,
properties: properties,
bounds: geom != null ? _buildBoundsFrom(geom) : null,
custom: custom,
);
}
}
return this;
}

@override
Feature unpopulated({
bool traverse = false,
bool onBounds = true,
}) {
if (onBounds) {
// unpopulate a geometry when traversing is asked
final geom = traverse
? geometry?.unpopulated(traverse: traverse, onBounds: onBounds)
: geometry;

// create a new feature if geometry changed or bounds was populated
if (geom != geometry || bounds != null) {
return Feature(
id: id,
geometry: geom,
properties: properties,
custom: custom,
);
}
}
return this;
}

@override
Feature<T> project(Projection projection) {
final projectedGeom = geometry?.project(projection) as T?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,62 @@ class FeatureCollection<E extends Feature> extends FeatureObject {
);
}

@override
FeatureCollection populated({
bool traverse = false,
bool onBounds = true,
}) {
if (onBounds) {
// populate features when traversing is asked
final coll = traverse && features.isNotEmpty
? features
.map<E>(
(f) => f.populated(traverse: traverse, onBounds: onBounds) as E,
)
.toList(growable: false)
: features;

// create a new collection if features changed or bounds was unpopulated
if (coll != features || (bounds == null && coll.isNotEmpty)) {
return FeatureCollection<E>._(
coll,
coordType,
bounds: _buildBoundsFrom(coll, coordType),
custom: custom,
);
}
}
return this;
}

@override
FeatureCollection unpopulated({
bool traverse = false,
bool onBounds = true,
}) {
if (onBounds) {
// unpopulate features when traversing is asked
final coll = traverse && features.isNotEmpty
? features
.map<E>(
(f) =>
f.unpopulated(traverse: traverse, onBounds: onBounds) as E,
)
.toList(growable: false)
: features;

// create a new collection if features changed or bounds was populated
if (coll != features || bounds != null) {
return FeatureCollection<E>._(
coll,
coordType,
custom: custom,
);
}
}
return this;
}

@override
FeatureCollection<E> project(Projection projection) {
final projected = features
Expand Down
13 changes: 13 additions & 0 deletions dart/geobase/lib/src/vector_data/model/feature/feature_object.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import 'dart:typed_data';

import 'package:geobase/vector_data.dart';
import 'package:meta/meta.dart';

import '/src/coordinates/projection/projection.dart';
Expand All @@ -28,6 +29,18 @@ abstract class FeatureObject extends Bounded {
const FeatureObject({super.bounds, Map<String, dynamic>? custom})
: _custom = custom;

@override
FeatureObject populated({
bool traverse = false,
bool onBounds = true,
});

@override
FeatureObject unpopulated({
bool traverse = false,
bool onBounds = true,
});

/// Returns a new feature obect with all geometries projected using
/// [projection].
///
Expand Down
12 changes: 12 additions & 0 deletions dart/geobase/lib/src/vector_data/model/geometry/geometry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ abstract class Geometry extends Bounded {
/// * `GeometryCollection` has no geometries.
bool get isEmpty;

@override
Geometry populated({
bool traverse = false,
bool onBounds = true,
});

@override
Geometry unpopulated({
bool traverse = false,
bool onBounds = true,
});

/// Returns a new geometry projected using [projection].
///
/// The returned geometry sub type must be the same as the type of this.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,57 @@ class GeometryCollection<E extends Geometry> extends Geometry {
);
}

@override
GeometryCollection populated({
bool traverse = false,
bool onBounds = true,
}) {
if (onBounds) {
// populate geometries when traversing is asked
final coll = traverse && geometries.isNotEmpty
? geometries
.map<E>(
(f) => f.populated(traverse: traverse, onBounds: onBounds) as E,
)
.toList(growable: false)
: geometries;

// create a new collection if geometries changed or bounds was unpopulated
if (coll != geometries || (bounds == null && coll.isNotEmpty)) {
return GeometryCollection<E>._(
coll,
coordType,
bounds: _buildBoundsFrom(coll, coordType),
);
}
}
return this;
}

@override
GeometryCollection unpopulated({
bool traverse = false,
bool onBounds = true,
}) {
if (onBounds) {
// unpopulate geometries when traversing is asked
final coll = traverse && geometries.isNotEmpty
? geometries
.map<E>(
(f) =>
f.unpopulated(traverse: traverse, onBounds: onBounds) as E,
)
.toList(growable: false)
: geometries;

// create a new collection if geometries changed or bounds was populated
if (coll != geometries || bounds != null) {
return GeometryCollection<E>._(coll, coordType);
}
}
return this;
}

@override
GeometryCollection<E> project(Projection projection) {
final projected = _geometries
Expand Down
38 changes: 36 additions & 2 deletions dart/geobase/lib/src/vector_data/model/geometry/linestring.dart
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,9 @@ class LineString extends SimpleGeometry {
if (recalculate || bounds == null) {
// return a new linestring (chain kept intact) with populated bounds
return LineString(
_chain,
chain,
bounds: BoundsBuilder.calculateBounds(
array: _chain,
array: chain,
type: coordType,
),
);
Expand All @@ -191,6 +191,40 @@ class LineString extends SimpleGeometry {
}
}

@override
LineString populated({
bool traverse = false,
bool onBounds = true,
}) {
if (onBounds) {
// create a new geometry if bounds was unpopulated and geometry not empty
if (bounds == null && !isEmpty) {
return LineString(
chain,
bounds: BoundsBuilder.calculateBounds(
array: chain,
type: coordType,
),
);
}
}
return this;
}

@override
LineString unpopulated({
bool traverse = false,
bool onBounds = true,
}) {
if (onBounds) {
// create a new geometry if bounds was populated
if (bounds != null) {
return LineString(chain);
}
}
return this;
}

@override
LineString project(Projection projection) {
final projected = _chain.project(projection);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,9 @@ class MultiLineString extends SimpleGeometry {
if (recalculate || bounds == null) {
// return a new MultiLineString (chains kept intact) with populated bounds
return MultiLineString(
_lineStrings,
chains,
bounds: BoundsBuilder.calculateBounds(
arrays: _lineStrings,
arrays: chains,
type: coordType,
),
);
Expand All @@ -217,6 +217,40 @@ class MultiLineString extends SimpleGeometry {
}
}

@override
MultiLineString populated({
bool traverse = false,
bool onBounds = true,
}) {
if (onBounds) {
// create a new geometry if bounds was unpopulated and geometry not empty
if (bounds == null && !isEmpty) {
return MultiLineString(
chains,
bounds: BoundsBuilder.calculateBounds(
arrays: chains,
type: coordType,
),
);
}
}
return this;
}

@override
MultiLineString unpopulated({
bool traverse = false,
bool onBounds = true,
}) {
if (onBounds) {
// create a new geometry if bounds was populated
if (bounds != null) {
return MultiLineString(chains);
}
}
return this;
}

@override
MultiLineString project(Projection projection) {
final projected = _lineStrings
Expand Down
Loading

0 comments on commit 96657f2

Please sign in to comment.