Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement lineOverlap, booleanOverlap, refactoring and bug fixes #174

Merged
merged 4 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ Any new benchmarks must be named `*_benchmark.dart` and reside in the
- [ ] lineArc
- [ ] lineChunk
- [ ] [lineIntersect](https://github.com/dartclub/turf_dart/blob/main/lib/src/line_intersect.dart)
- [ ] lineOverlap
- [x] [lineOverlap](https://github.com/dartclub/turf_dart/blob/main/lib/src/line_overlap.dart)
- [x] [lineSegment](https://github.com/dartclub/turf_dart/blob/main/lib/src/line_segment.dart)
- [x] [lineSlice](https://github.com/dartclub/turf_dart/blob/main/lib/src/line_slice.dart)
- [ ] lineSliceAlong
Expand Down Expand Up @@ -233,7 +233,7 @@ Any new benchmarks must be named `*_benchmark.dart` and reside in the
- [x] [booleanDisjoint](https://github.com/dartclub/turf_dart/blob/main/lib/src/booleans/boolean_disjoint.dart)
- [x] [booleanEqual](https://github.com/dartclub/turf_dart/blob/main/lib/src/booleans/boolean_equal.dart)
- [x] [booleanIntersects](https://github.com/dartclub/turf_dart/blob/main/lib/src/booleans/boolean_intersects.dart)
- [ ] booleanOverlap
- [x] [booleanOverlap](https://github.com/dartclub/turf_dart/blob/main/lib/src/booleans/boolean_overlap.dart)
- [x] [booleanParallel](https://github.com/dartclub/turf_dart/blob/main/lib/src/booleans/boolean_parallel.dart)
- [x] [booleanPointInPolygon](https://github.com/dartclub/turf_dart/blob/main/lib/src/booleans/boolean_point_in_polygon.dart)
- [x] [booleanPointOnLine](https://github.com/dartclub/turf_dart/blob/main/lib/src/booleans/boolean_point_on_line.dart)
Expand Down
2 changes: 1 addition & 1 deletion lib/boolean.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export 'src/booleans/boolean_crosses.dart';
export 'src/booleans/boolean_disjoint.dart';
export 'src/booleans/boolean_equal.dart';
export 'src/booleans/boolean_intersects.dart';
// export 'src/booleans/boolean_overlap.dart';
export 'src/booleans/boolean_overlap.dart';
export 'src/booleans/boolean_parallel.dart';
export 'src/booleans/boolean_point_in_polygon.dart';
export 'src/booleans/boolean_point_on_line.dart';
Expand Down
3 changes: 3 additions & 0 deletions lib/line_overlap.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
library turf_line_overlap;

export "src/line_overlap.dart";
13 changes: 5 additions & 8 deletions lib/src/booleans/boolean_contains.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import 'package:turf/src/invariant.dart';
import 'package:turf/turf.dart';

import 'boolean_point_in_polygon.dart';
import 'boolean_point_on_line.dart';
import 'boolean_helper.dart';

/// [booleanContains] returns [true] if the second geometry is completely contained
Expand Down Expand Up @@ -32,15 +29,15 @@ bool booleanContains(GeoJSONObject feature1, GeoJSONObject feature2) {
if (geom2 is Point) {
return coords1 == coords2;
} else {
throw FeatureNotSupported(geom1, geom2);
throw GeometryCombinationNotSupported(geom1, geom2);
}
} else if (geom1 is MultiPoint) {
if (geom2 is Point) {
return isPointInMultiPoint(geom2, geom1);
} else if (geom2 is MultiPoint) {
return isMultiPointInMultiPoint(geom2, geom1);
} else {
throw FeatureNotSupported(geom1, geom2);
throw GeometryCombinationNotSupported(geom1, geom2);
}
} else if (geom1 is LineString) {
if (geom2 is Point) {
Expand All @@ -50,7 +47,7 @@ bool booleanContains(GeoJSONObject feature1, GeoJSONObject feature2) {
} else if (geom2 is MultiPoint) {
return isMultiPointOnLine(geom2, geom1);
} else {
throw FeatureNotSupported(geom1, geom2);
throw GeometryCombinationNotSupported(geom1, geom2);
}
} else if (geom1 is Polygon) {
if (geom2 is Point) {
Expand All @@ -63,10 +60,10 @@ bool booleanContains(GeoJSONObject feature1, GeoJSONObject feature2) {
} else if (geom2 is MultiPoint) {
return isMultiPointInPolygon(geom2, geom1);
} else {
throw FeatureNotSupported(geom1, geom2);
throw GeometryCombinationNotSupported(geom1, geom2);
}
} else {
throw FeatureNotSupported(geom1, geom2);
throw GeometryCombinationNotSupported(geom1, geom2);
}
}

Expand Down
15 changes: 11 additions & 4 deletions lib/src/booleans/boolean_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,22 @@ import 'package:turf/src/bbox.dart';
import 'boolean_point_on_line.dart';
import 'boolean_point_in_polygon.dart';

class FeatureNotSupported implements Exception {
class GeometryNotSupported implements Exception {
final GeometryObject geometry;
GeometryNotSupported(this.geometry);

@override
String toString() => "geometry not supported ($geometry).";
}

class GeometryCombinationNotSupported implements Exception {
final GeometryObject geometry1;
final GeometryObject geometry2;

FeatureNotSupported(this.geometry1, this.geometry2);
GeometryCombinationNotSupported(this.geometry1, this.geometry2);

@override
String toString() =>
"feature geometry not supported ($geometry1, $geometry2).";
String toString() => "geometry not supported ($geometry1, $geometry2).";
}

bool isPointInMultiPoint(Point point, MultiPoint multipoint) {
Expand Down
179 changes: 179 additions & 0 deletions lib/src/booleans/boolean_overlap.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import 'package:turf/helpers.dart';
import 'package:turf/line_overlap.dart';
import 'package:turf/line_segment.dart';
import 'package:turf/src/invariant.dart';
import 'package:turf/src/line_intersect.dart';
import 'package:turf_equality/turf_equality.dart';
import 'boolean_helper.dart';

/// Takes two geometries [firstFeature] and [secondFeature] and checks if they
/// share an common area but are not completely contained by each other.
///
/// Supported Geometries are `Feature<MultiPoint>`, `Feature<LineString>`,
/// `Feature<MultiLineString>`, `Feature<Polygon>`, `Feature<MultiPolygon>`.
/// Features must be of the same type. LineString/MultiLineString and
/// Polygon/MultiPolygon combinations are supported. If the Geometries are not
/// supported an [GeometryNotSupported] or [GeometryCombinationNotSupported]
/// error is thrown.
///
/// Returns false if [firstFeature] and [secondFeature] are equal.
/// - MultiPoint: returns Returns true if the two MultiPoints share any point.
/// - LineString: returns true if the two Lines share any line segment.
/// - Polygon: returns true if the two Polygons intersect.
///
/// Example:
/// ```dart
/// final first = Polygon(coordinates: [
/// [
/// Position(0, 0),
/// Position(0, 5),
/// Position(5, 5),
/// Position(5, 0),
/// Position(0, 0)
/// ]
/// ]);
/// final second = Polygon(coordinates: [
/// [
/// Position(1, 1),
/// Position(1, 6),
/// Position(6, 6),
/// Position(6, 1),
/// Position(1, 1)
/// ]
/// ]);
/// final third = Polygon(coordinates: [
/// [
/// Position(10, 10),
/// Position(10, 15),
/// Position(15, 15),
/// Position(15, 10),
/// Position(10, 10)
/// ]
/// ]);
///
/// final isOverlapping = booleanOverlap(first, second);
/// final isNotOverlapping = booleanOverlap(second, third);
/// ```
bool booleanOverlap(
Feature firstFeature,
Feature secondFeature,
) {
final first = getGeom(firstFeature);
final second = getGeom(secondFeature);

_checkIfGeometryCombinationIsSupported(first, second);

final eq = Equality(
reversedGeometries: true,
shiftedPolygons: true,
);
if (eq.compare(first, second)) {
return false;
}

switch (first.runtimeType) {
case MultiPoint:
switch (second.runtimeType) {
case MultiPoint:
return _isMultiPointOverlapping(
first as MultiPoint,
second as MultiPoint,
);
default:
throw GeometryCombinationNotSupported(first, second);
}
case MultiLineString:
case LineString:
switch (second.runtimeType) {
case LineString:
case MultiLineString:
return _isLineOverlapping(first, second);
default:
throw GeometryCombinationNotSupported(first, second);
}
case MultiPolygon:
case Polygon:
switch (second.runtimeType) {
case Polygon:
case MultiPolygon:
return _isPolygonOverlapping(first, second);
default:
throw GeometryCombinationNotSupported(first, second);
}
default:
throw GeometryCombinationNotSupported(first, second);
}
}

bool _isGeometrySupported(GeometryObject geometry) =>
geometry is MultiPoint ||
geometry is LineString ||
geometry is MultiLineString ||
geometry is Polygon ||
geometry is MultiPolygon;

void _checkIfGeometryCombinationIsSupported(
GeometryObject first,
GeometryObject second,
) {
if (!_isGeometrySupported(first) || !_isGeometrySupported(second)) {
throw GeometryCombinationNotSupported(first, second);
}
}

void _checkIfGeometryIsSupported(GeometryObject geometry) {
if (!_isGeometrySupported(geometry)) {
throw GeometryNotSupported(geometry);
}
}

List<Feature<LineString>> _segmentsOfGeometry(GeometryObject geometry) {
_checkIfGeometryIsSupported(geometry);
List<Feature<LineString>> segments = [];
segmentEach(
geometry,
(Feature<LineString> segment, _, __, ___, ____) {
segments.add(segment);
},
);
return segments;
}

bool _isLineOverlapping(GeometryObject firstLine, GeometryObject secondLine) {
for (final firstSegment in _segmentsOfGeometry(firstLine)) {
for (final secondSegment in _segmentsOfGeometry(secondLine)) {
if (lineOverlap(firstSegment, secondSegment).features.isNotEmpty) {
return true;
}
}
}
return false;
}

bool _isPolygonOverlapping(
GeometryObject firstPolygon,
GeometryObject secondPolygon,
) {
for (final firstSegment in _segmentsOfGeometry(firstPolygon)) {
for (final secondSegment in _segmentsOfGeometry(secondPolygon)) {
if (lineIntersect(firstSegment, secondSegment).features.isNotEmpty) {
return true;
}
}
}
return false;
}

bool _isMultiPointOverlapping(
MultiPoint first,
MultiPoint second,
) {
for (final firstPoint in first.coordinates) {
for (final secondPoint in second.coordinates) {
if (firstPoint == secondPoint) {
return true;
}
}
}
return false;
}
13 changes: 6 additions & 7 deletions lib/src/booleans/boolean_within.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ import 'boolean_helper.dart';
/// Position.of([1, 4])
/// ],
/// );
/// booleanWithin(point, line);
/// //=true
/// final isWithin = booleanWithin(point, line); // true
/// ```
bool booleanWithin(
GeoJSONObject feature1,
Expand All @@ -43,7 +42,7 @@ bool booleanWithin(
case MultiPolygon:
return isPointInMultiPolygon(point, geom2 as MultiPolygon);
default:
throw FeatureNotSupported(geom1, geom2);
throw GeometryCombinationNotSupported(geom1, geom2);
}
case MultiPoint:
final multipoint = geom1 as MultiPoint;
Expand All @@ -57,7 +56,7 @@ bool booleanWithin(
case MultiPolygon:
return isMultiPointInMultiPolygon(multipoint, geom2 as MultiPolygon);
default:
throw FeatureNotSupported(geom1, geom2);
throw GeometryCombinationNotSupported(geom1, geom2);
}
case LineString:
final line = geom1 as LineString;
Expand All @@ -69,7 +68,7 @@ bool booleanWithin(
case MultiPolygon:
return isLineInMultiPolygon(line, geom2 as MultiPolygon);
default:
throw FeatureNotSupported(geom1, geom2);
throw GeometryCombinationNotSupported(geom1, geom2);
}
case Polygon:
final polygon = geom1 as Polygon;
Expand All @@ -79,9 +78,9 @@ bool booleanWithin(
case MultiPolygon:
return isPolygonInMultiPolygon(polygon, geom2 as MultiPolygon);
default:
throw FeatureNotSupported(geom1, geom2);
throw GeometryCombinationNotSupported(geom1, geom2);
}
default:
throw FeatureNotSupported(geom1, geom2);
throw GeometryCombinationNotSupported(geom1, geom2);
}
}
Loading
Loading