Skip to content

Commit

Permalink
feat(geobase): create box functions, also related to (#96)
Browse files Browse the repository at this point in the history
  • Loading branch information
navispatial committed Feb 23, 2022
1 parent 0a5a325 commit c1f19c5
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 47 deletions.
83 changes: 79 additions & 4 deletions dart/geobase/lib/src/base/coordinates/box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,35 @@
//
// Docs: https://github.com/navibyte/geospatial

import 'dart:math' as math;

import '/src/utils/tolerance.dart';

import 'position.dart';
import 'positionable.dart';

/// Creates a new bounding box from [minX], [minY], [maxX] and [maxY] values.
///
/// Optional [minZ] and [maxZ] for 3D boxes, and [minM] and [maxM] for
/// measured boxes can be provided too.
///
/// For projected or cartesian bounding boxes (`ProjBox`), coordinates axis are
/// applied as is.
///
/// For geographic bounding boxes (`GeoBox`), coordinates are applied as:
/// `minX` => `west`, `minY` => `south`, `minZ` => `minElev`, `minM` => `minM`,
/// `maxX` => `east`, `maxY` => `north`, `maxZ` => `maxElev`, `maxM` => `maxM`
typedef CreateBox<T extends Box> = T Function({
required num minX,
required num minY,
num? minZ,
num? minM,
required num maxX,
required num maxY,
num? maxZ,
num? maxM,
});

/// A base interface for axis-aligned bounding boxes with min & max coordinates.
///
/// This interface defines min and max coordinate values only for the m axis.
Expand Down Expand Up @@ -121,8 +145,8 @@ abstract class Box extends Positionable {
/// Returns true if this bounding box intesects with [other] box.
///
/// X ja y (or lon and lat) are always compared on intersection calculation.
///
/// This and [other] must equal on [is3D] and [isMeasured] properties. Z (or
///
/// This and [other] must equal on [is3D] and [isMeasured] properties. Z (or
/// elev) is further compared when both has z coordinates, and m is compared
/// when both has m coordinates.
bool intersects(Box other) => Box.testIntersects(this, other);
Expand All @@ -136,8 +160,8 @@ abstract class Box extends Positionable {
/// Returns true if this bounding box intesects with [point].
///
/// X ja y (or lon and lat) are always compared on intersection calculation.
///
/// This and [point] must equal on [is3D] and [isMeasured] properties. Z (or
///
/// This and [point] must equal on [is3D] and [isMeasured] properties. Z (or
/// elev) is further compared when both has z coordinates, and m is compared
/// when both has m coordinates.
bool intersectsPoint(Position point) => Box.testIntersectsPoint(this, point);
Expand Down Expand Up @@ -173,6 +197,57 @@ abstract class Box extends Positionable {
}
}

/// A minimum bounding box created by [factory], calculated from [positions].
///
/// Throws FormatException if cannot create (ie. [positions] is empty).
static R createBoxFrom<R extends Box>(
Iterable<Position> positions,
CreateBox<R> factory,
) {
// calculate mininum and maximum coordinates
num? minX, minY, minZ, minM;
num? maxX, maxY, maxZ, maxM;
var isFirst = true;
for (final cp in positions) {
if (isFirst) {
minX = cp.x;
minY = cp.y;
minZ = cp.optZ;
minM = cp.optM;
maxX = cp.x;
maxY = cp.y;
maxZ = cp.optZ;
maxM = cp.optM;
} else {
minX = math.min(minX!, cp.x);
minY = math.min(minY!, cp.y);
minZ = cp.is3D && minZ != null ? math.min(minZ, cp.z) : null;
minM = cp.isMeasured && minM != null ? math.min(minM, cp.m) : null;
maxX = math.max(maxX!, cp.x);
maxY = math.max(maxY!, cp.y);
maxZ = cp.is3D && maxZ != null ? math.max(maxZ, cp.z) : null;
maxM = cp.isMeasured && maxM != null ? math.max(maxM, cp.m) : null;
}
isFirst = false;
}

if(isFirst) {
throw const FormatException('Positions should not be empty.');
}

// create a new bounding box
return factory.call(
minX: minX!,
minY: minY!,
minZ: minZ,
minM: minM,
maxX: maxX!,
maxY: maxY!,
maxZ: maxZ,
maxM: maxM,
);
}

/// True if [box1] and [box2] equals by testing all coordinate values.
static bool testEquals(Box box1, Box box2) =>
box1.minX == box2.minX &&
Expand Down
25 changes: 25 additions & 0 deletions dart/geobase/lib/src/base/coordinates/geobox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,31 @@ class GeoBox extends Box {
_maxElev = maxElev,
_maxM = maxM;

/// A bounding box from parameters compatible with `CreateBox` function type.
GeoBox.create({
required num minX,
required num minY,
num? minZ,
num? minM,
required num maxX,
required num maxY,
num? maxZ,
num? maxM,
}) : _west = minX.toDouble(),
_south = minY.toDouble(),
_minElev = minZ?.toDouble(),
_minM = minM?.toDouble(),
_east = maxX.toDouble(),
_north = maxY.toDouble(),
_maxElev = maxZ?.toDouble(),
_maxM = maxM?.toDouble();

/// A minimum bounding box calculated from [positions].
///
/// Throws FormatException if cannot create (ie. [positions] is empty).
factory GeoBox.from(Iterable<Geographic> positions) =>
Box.createBoxFrom(positions, GeoBox.create);

/// The west coordinate as geographic longitude.
double get west => _west;

Expand Down
66 changes: 23 additions & 43 deletions dart/geobase/lib/src/base/coordinates/projbox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@
//
// Docs: https://github.com/navibyte/geospatial

import 'dart:math' as math;

import 'package:meta/meta.dart';

import '/src/base/codes.dart';

import 'box.dart';
import 'position.dart';
import 'projected.dart';

/// A bounding box with [minX], [minY], [maxX] and [maxY] coordinates.
Expand Down Expand Up @@ -51,47 +48,30 @@ class ProjBox extends Box {
_maxZ = maxZ,
_maxM = maxM;

/// A minimum bounding box calculated from [positions].
factory ProjBox.from(Iterable<Position> positions) {
// calculate mininum and maximum coordinates
num? minX, minY, minZ, minM;
num? maxX, maxY, maxZ, maxM;
var isFirst = true;
for (final cp in positions) {
if (isFirst) {
minX = cp.x;
minY = cp.y;
minZ = cp.optZ;
minM = cp.optM;
maxX = cp.x;
maxY = cp.y;
maxZ = cp.optZ;
maxM = cp.optM;
} else {
minX = math.min(minX!, cp.x);
minY = math.min(minY!, cp.y);
minZ = cp.is3D && minZ != null ? math.min(minZ, cp.z) : null;
minM = cp.isMeasured && minM != null ? math.min(minM, cp.m) : null;
maxX = math.max(maxX!, cp.x);
maxY = math.max(maxY!, cp.y);
maxZ = cp.is3D && maxZ != null ? math.max(maxZ, cp.z) : null;
maxM = cp.isMeasured && maxM != null ? math.max(maxM, cp.m) : null;
}
isFirst = false;
}
/// A bounding box from parameters compatible with `CreateBox` function type.
const ProjBox.create({
required num minX,
required num minY,
num? minZ,
num? minM,
required num maxX,
required num maxY,
num? maxZ,
num? maxM,
}) : _minX = minX,
_minY = minY,
_minZ = minZ,
_minM = minM,
_maxX = maxX,
_maxY = maxY,
_maxZ = maxZ,
_maxM = maxM;

// create a new bounding box
return ProjBox(
minX: minX!,
minY: minY!,
minZ: minZ,
minM: minM,
maxX: maxX!,
maxY: maxY!,
maxZ: maxZ,
maxM: maxM,
);
}
/// A minimum bounding box calculated from [positions].
///
/// Throws FormatException if cannot create (ie. [positions] is empty).
factory ProjBox.from(Iterable<Projected> positions) =>
Box.createBoxFrom(positions, ProjBox.create);

@override
num get minX => _minX;
Expand Down

0 comments on commit c1f19c5

Please sign in to comment.