Skip to content

Commit dc49a29

Browse files
committed
feat(geobase): equalsCoords in PositionArray, geometries and features #146
1 parent 3201653 commit dc49a29

File tree

13 files changed

+321
-4
lines changed

13 files changed

+321
-4
lines changed

dart/geobase/lib/src/vector_data/array/position_array.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ abstract class PositionArray with _CoordinatesMixin {
107107
projection.projectCoords(_data, type: _type),
108108
type: _type,
109109
);
110+
111+
/// Returns true if this and [other] contain exactly same coordinate values
112+
/// (or both are empty) in the same order and with the same coordinate type.
113+
bool equalsCoords(PositionArray other);
110114
}
111115

112116
@immutable
@@ -139,6 +143,33 @@ class _PositionArrayImpl extends PositionArray {
139143
//
140144
// Anyway, a position array might be really large, so calculating equality and
141145
// hash might then have performance issues too.
146+
147+
@override
148+
bool equalsCoords(PositionArray other) {
149+
if (_type != other.type) return false;
150+
151+
final coords1 = _data;
152+
final coords2 = other is _PositionArrayImpl ? other._data : other;
153+
final len = coords1.length;
154+
if (len != coords2.length) return false;
155+
156+
if (identical(coords1, coords2)) return true;
157+
158+
if (coords1 is List<double> && coords2 is List<double>) {
159+
for (var i = 0; i < len; i++) {
160+
if (coords1[i] != coords2[i]) return false;
161+
}
162+
} else {
163+
final iter1 = coords1.iterator;
164+
final iter2 = coords2.iterator;
165+
while (iter1.moveNext()) {
166+
if (!iter2.moveNext()) return false;
167+
if (iter1.current != iter2.current) return false;
168+
}
169+
}
170+
171+
return true;
172+
}
142173
}
143174

144175
class _PositionArrayData<E extends Position> with PositionData<E> {

dart/geobase/lib/src/vector_data/model/feature/feature.dart

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,51 @@ class Feature<T extends Geometry> extends FeatureObject {
296296
);
297297
}
298298

299+
/// Returns true if this and [other] contain exactly same coordinate values
300+
/// (or both are empty) in the same order and with the same coordinate type.
301+
///
302+
/// If [ignoreCustomGeometries] is true, then [customGeometries] are ignored
303+
/// in testing.
304+
bool equalsCoords(
305+
Feature other, {
306+
bool ignoreCustomGeometries = false,
307+
}) {
308+
if (identical(this, other)) return true;
309+
310+
if (bounds != null && other.bounds != null && !(bounds! == other.bounds!)) {
311+
// both feature collections has bound boxes and boxes do not equal
312+
return false;
313+
}
314+
315+
// test main geometry
316+
final mg1 = geometry;
317+
final mg2 = other.geometry;
318+
if (mg1 != null) {
319+
if (mg2 == null) return false;
320+
if (!mg1.equalsCoords(mg2)) return false;
321+
} else {
322+
if (mg2 != null) return false;
323+
}
324+
325+
// test custom geometries unless they should be ignored
326+
if (!ignoreCustomGeometries) {
327+
final cg1 = customGeometries;
328+
final cg2 = other.customGeometries;
329+
if (cg1 != null) {
330+
if (cg2 == null || cg1.length != cg2.length) return false;
331+
for (final cg1entry in cg1.entries) {
332+
final cg2value = cg2[cg1entry.key];
333+
if (cg2value == null) return false;
334+
if (!cg1entry.value.equalsCoords(cg2value)) return false;
335+
}
336+
} else {
337+
if (cg2 != null) return false;
338+
}
339+
}
340+
341+
return true;
342+
}
343+
299344
/// True if this feature equals with [other] by testing 2D coordinates of the
300345
/// [geometry] object (and any [customGeometries] possibly contained).
301346
///
@@ -323,7 +368,7 @@ class Feature<T extends Geometry> extends FeatureObject {
323368
other.bounds!,
324369
toleranceHoriz: toleranceHoriz,
325370
)) {
326-
// both geometries has bound boxes and boxes do not equal in 2D
371+
// both features has bound boxes and boxes do not equal in 2D
327372
return false;
328373
}
329374

@@ -396,7 +441,7 @@ class Feature<T extends Geometry> extends FeatureObject {
396441
toleranceHoriz: toleranceHoriz,
397442
toleranceVert: toleranceVert,
398443
)) {
399-
// both geometries has bound boxes and boxes do not equal in 3D
444+
// both features has bound boxes and boxes do not equal in 3D
400445
return false;
401446
}
402447

dart/geobase/lib/src/vector_data/model/feature/feature_collection.dart

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,36 @@ class FeatureCollection<E extends Feature> extends FeatureObject {
237237
);
238238
}
239239

240+
/// Returns true if this and [other] contain exactly same coordinate values
241+
/// (or both are empty) in the same order and with the same coordinate type.
242+
///
243+
/// If [ignoreCustomGeometries] is true, then `customGeometries` of features
244+
/// are ignored in testing.
245+
bool equalsCoords(
246+
FeatureCollection other, {
247+
bool ignoreCustomGeometries = false,
248+
}) {
249+
if (identical(this, other)) return true;
250+
251+
if (bounds != null && other.bounds != null && !(bounds! == other.bounds!)) {
252+
// both feature collections has bound boxes and boxes do not equal
253+
return false;
254+
}
255+
256+
final fc1 = features;
257+
final fc2 = other.features;
258+
if (fc1.length != fc2.length) return false;
259+
for (var i = 0; i < fc1.length; i++) {
260+
if (!fc1[i].equalsCoords(
261+
fc2[i],
262+
ignoreCustomGeometries: ignoreCustomGeometries,
263+
)) {
264+
return false;
265+
}
266+
}
267+
return true;
268+
}
269+
240270
/// True if this feature collection equals with [other] by testing 2D
241271
/// coordinates of geometries of [features] (that must be in same order in
242272
/// both collections) contained.
@@ -262,7 +292,7 @@ class FeatureCollection<E extends Feature> extends FeatureObject {
262292
other.bounds!,
263293
toleranceHoriz: toleranceHoriz,
264294
)) {
265-
// both geometries has bound boxes and boxes do not equal in 2D
295+
// both feature collections has bound boxes and boxes do not equal in 2D
266296
return false;
267297
}
268298

@@ -315,7 +345,7 @@ class FeatureCollection<E extends Feature> extends FeatureObject {
315345
toleranceHoriz: toleranceHoriz,
316346
toleranceVert: toleranceVert,
317347
)) {
318-
// both geometries has bound boxes and boxes do not equal in 3D
348+
// both feature collections has bound boxes and boxes do not equal in 3D
319349
return false;
320350
}
321351

dart/geobase/lib/src/vector_data/model/geometry/geometry.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ abstract class Geometry extends Bounded {
103103
return encoder.toBytes();
104104
}
105105

106+
/// Returns true if this and [other] contain exactly same coordinate values
107+
/// (or both are empty) in the same order and with the same coordinate type.
108+
bool equalsCoords(Geometry other);
109+
106110
/// True if this geometry equals with [other] by testing 2D coordinates of all
107111
/// positions (that must be in same order in both geometries).
108112
///

dart/geobase/lib/src/vector_data/model/geometry/geometry_collection.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,24 @@ class GeometryCollection<E extends Geometry> extends Geometry {
201201
bounds: bounds,
202202
);
203203

204+
@override
205+
bool equalsCoords(Geometry other) {
206+
if (other is! GeometryCollection) return false;
207+
if (identical(this, other)) return true;
208+
if (bounds != null && other.bounds != null && !(bounds! == other.bounds!)) {
209+
// both geometries has bound boxes and boxes do not equal
210+
return false;
211+
}
212+
213+
final g1 = geometries;
214+
final g2 = other.geometries;
215+
if (g1.length != g2.length) return false;
216+
for (var i = 0; i < g1.length; i++) {
217+
if (!g1[i].equalsCoords(g2[i])) return false;
218+
}
219+
return true;
220+
}
221+
204222
@override
205223
bool equals2D(
206224
Geometry other, {

dart/geobase/lib/src/vector_data/model/geometry/linestring.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,18 @@ class LineString extends SimpleGeometry {
213213

214214
// NOTE: coordinates as raw data
215215

216+
@override
217+
bool equalsCoords(Geometry other) {
218+
if (other is! LineString) return false;
219+
if (identical(this, other)) return true;
220+
if (bounds != null && other.bounds != null && !(bounds! == other.bounds!)) {
221+
// both geometries has bound boxes and boxes do not equal
222+
return false;
223+
}
224+
225+
return chain.equalsCoords(other.chain);
226+
}
227+
216228
@override
217229
bool equals2D(
218230
Geometry other, {

dart/geobase/lib/src/vector_data/model/geometry/multi_linestring.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,24 @@ class MultiLineString extends SimpleGeometry {
254254

255255
// NOTE: coordinates as raw data
256256

257+
@override
258+
bool equalsCoords(Geometry other) {
259+
if (other is! MultiLineString) return false;
260+
if (identical(this, other)) return true;
261+
if (bounds != null && other.bounds != null && !(bounds! == other.bounds!)) {
262+
// both geometries has bound boxes and boxes do not equal
263+
return false;
264+
}
265+
266+
final c1 = chains;
267+
final c2 = other.chains;
268+
if (c1.length != c2.length) return false;
269+
for (var i = 0; i < c1.length; i++) {
270+
if (!c1[i].equalsCoords(c2[i])) return false;
271+
}
272+
return true;
273+
}
274+
257275
@override
258276
bool equals2D(
259277
Geometry other, {

dart/geobase/lib/src/vector_data/model/geometry/multi_point.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,24 @@ class MultiPoint extends SimpleGeometry {
225225

226226
// NOTE: coordinates as raw data
227227

228+
@override
229+
bool equalsCoords(Geometry other) {
230+
if (other is! MultiPoint) return false;
231+
if (identical(this, other)) return true;
232+
if (bounds != null && other.bounds != null && !(bounds! == other.bounds!)) {
233+
// both geometries has bound boxes and boxes do not equal
234+
return false;
235+
}
236+
237+
final p1 = positions;
238+
final p2 = other.positions;
239+
if (p1.length != p2.length) return false;
240+
for (var i = 0; i < p1.length; i++) {
241+
if (p1[i] != p2[i]) return false;
242+
}
243+
return true;
244+
}
245+
228246
@override
229247
bool equals2D(
230248
Geometry other, {

dart/geobase/lib/src/vector_data/model/geometry/multi_polygon.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,33 @@ class MultiPolygon extends SimpleGeometry {
293293

294294
// NOTE: coordinates as raw data
295295

296+
@override
297+
bool equalsCoords(Geometry other) {
298+
if (other is! MultiPolygon) return false;
299+
if (identical(this, other)) return true;
300+
if (bounds != null && other.bounds != null && !(bounds! == other.bounds!)) {
301+
// both geometries has bound boxes and boxes do not equal
302+
return false;
303+
}
304+
305+
final arr1 = ringArrays;
306+
final arr2 = other.ringArrays;
307+
if (arr1.length != arr2.length) return false;
308+
// loop all arrays of ring data
309+
for (var j = 0; j < arr1.length; j++) {
310+
// get linear ring lists from arrays by index j
311+
final r1 = arr1[j];
312+
final r2 = arr2[j];
313+
// ensure r1 and r2 has same amount of linear rings
314+
if (r1.length != r2.length) return false;
315+
// loop all linear rings and test coordinates
316+
for (var i = 0; i < r1.length; i++) {
317+
if (!r1[i].equalsCoords(r2[i])) return false;
318+
}
319+
}
320+
return true;
321+
}
322+
296323
@override
297324
bool equals2D(
298325
Geometry other, {

dart/geobase/lib/src/vector_data/model/geometry/point.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ class Point implements SimpleGeometry {
212212
return encoder.toBytes();
213213
}
214214

215+
@override
216+
bool equalsCoords(Geometry other) =>
217+
other is Point && position == other.position;
218+
215219
@override
216220
bool equals2D(
217221
Geometry other, {

0 commit comments

Comments
 (0)