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

Implements booleanOverlap and line_overlap function #141

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 4 additions & 2 deletions lib/src/bbox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ BBox bbox(GeoJSONObject geoJson, {bool recompute = false}) {
}

var result = BBox.named(
lat1: double.infinity,
// min x & y
lng1: double.infinity,
lat2: double.negativeInfinity,
lat1: double.infinity,
// max x & y
lng2: double.negativeInfinity,
lat2: double.negativeInfinity,
);

coordEach(
Expand Down
230 changes: 112 additions & 118 deletions lib/src/line_overlap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,154 +43,148 @@ FeatureCollection<LineString> lineOverlap(
RBushBox _toRBBox(Feature<LineString> feature) {
var bb = bbox(feature);
return RBushBox(
minX: bb.lng1.toDouble(),
minY: bb.lat1.toDouble(),
maxX: bb.lng2.toDouble(),
maxY: bb.lat2.toDouble());
minX: bb.lng1.toDouble(),
minY: bb.lat1.toDouble(),
maxX: bb.lng2.toDouble(),
maxY: bb.lat2.toDouble(),
);
}

// Containers
var features = <Feature<LineString>>[];

// Create Spatial Index
var tree = RBushBase<Feature<LineString>>(
getMinY: (Feature<LineString> feature) => bbox(feature).lat1.toDouble(),
getMinX: (Feature<LineString> feature) => bbox(feature).lng1.toDouble(),
toBBox: (feature) => _toRBBox(feature));
getMinX: (Feature<LineString> feature) => bbox(feature).lng1.toDouble(),
getMinY: (Feature<LineString> feature) => bbox(feature).lat1.toDouble(),
toBBox: (feature) => _toRBBox(feature),
jsiedentop marked this conversation as resolved.
Show resolved Hide resolved
);

FeatureCollection<LineString> line = lineSegment(line1);
tree.load(line.features);
Feature<LineString>? overlapSegment;
var additionalSegments = <Feature<LineString>>[];
List<Feature<LineString>> additionalSegments = [];

// Line Intersection

// Iterate over line segments
segmentEach(
line2,
(Feature<LineString> currentSegment, int featureIndex,
int? multiFeatureIndex, int? geometryIndex, int segmentIndex) {
var doesOverlap = false;
segmentEach(line2, (Feature<LineString> currentSegment, int featureIndex,
int? multiFeatureIndex, int? geometryIndex, int segmentIndex) {
bool doesOverlap = false;

// Iterate over each segments which falls within the same bounds
featureEach(
// Iterate over each segments which falls within the same bounds
featureEach(
FeatureCollection<LineString>(
features: tree.search(_toRBBox(currentSegment))),
(match, index) {
if (!doesOverlap) {
List<Position> coordsSegment = () {
List<Position> list = getCoords(currentSegment) as List<Position>;
list.sort(((a, b) {
return a.lng < b.lng
? -1
: a.lng > b.lng
? 1
: 0;
}));
return list;
}();
List<Position> coordsMatch = () {
List<Position> list = getCoords(match) as List<Position>;
list.sort(((a, b) {
return a.lng < b.lng
? -1
: a.lng > b.lng
? 1
: 0;
}));
return list;
}();
(Feature currentFeature, int featureIndex) {
if (!doesOverlap) {
var coords = getCoords(currentSegment) as List<Position>;
var coordsMatch = getCoords(currentFeature) as List<Position>;

coords.sort(((a, b) {
return a.lng < b.lng
? -1
: a.lng > b.lng
? 1
: 0;
}));

coordsMatch.sort(((a, b) {
return a.lng < b.lng
? -1
: a.lng > b.lng
? 1
: 0;
}));

Equality eq = Equality();
// Segment overlaps feature - with dummy LineStrings just to use eq.
if (eq.compare(LineString(coordinates: coordsSegment),
LineString(coordinates: coordsMatch))) {
doesOverlap = true;
// Overlaps already exists - only append last coordinate of segment
if (overlapSegment != null) {
overlapSegment =
concatSegment(overlapSegment!, currentSegment) ??
overlapSegment;
} else {
overlapSegment = currentSegment;
}
// Match segments which don't share nodes (Issue #901)
} else if (tolerance == 0
? booleanPointOnLine(Point(coordinates: coordsSegment[0]),
match.geometry as LineString) &&
booleanPointOnLine(Point(coordinates: coordsSegment[1]),
match.geometry as LineString)
: nearestPointOnLine(match.geometry as LineString,
Point(coordinates: coordsSegment[0]))
.properties!['dist'] <=
tolerance &&
nearestPointOnLine(match.geometry as LineString,
Point(coordinates: coordsSegment[1]))
.properties!['dist'] <=
tolerance) {
doesOverlap = true;
if (overlapSegment != null) {
overlapSegment =
concatSegment(overlapSegment!, currentSegment) ??
overlapSegment;
} else {
overlapSegment = currentSegment;
}
} else if (tolerance == 0
? booleanPointOnLine(Point(coordinates: coordsMatch[0]),
currentSegment.geometry as LineString) &&
booleanPointOnLine(Point(coordinates: coordsMatch[1]),
currentSegment.geometry as LineString)
: nearestPointOnLine(currentSegment.geometry as LineString,
Point(coordinates: coordsMatch[0]))
.properties!['dist'] <=
tolerance &&
nearestPointOnLine(currentSegment.geometry as LineString,
Point(coordinates: coordsMatch[1]))
.properties!['dist'] <=
tolerance) {
// Do not define doesOverlap = true since more matches can occur
// within the same segment
// doesOverlaps = true;
if (overlapSegment != null) {
Feature<LineString>? combinedSegment = concatSegment(
overlapSegment!, match as Feature<LineString>);
if (combinedSegment != null) {
overlapSegment = combinedSegment;
} else {
additionalSegments.add(match);
}
} else {
overlapSegment = match as Feature<LineString>;
}
Equality eq = Equality();
// Segment overlaps feature - with dummy LineStrings just to use eq.
if (eq.compare(LineString(coordinates: coords),
LineString(coordinates: coordsMatch))) {
doesOverlap = true;
// Overlaps already exists - only append last coordinate of segment
if (overlapSegment != null) {
overlapSegment = concatSegment(overlapSegment!, currentSegment) ??
overlapSegment;
} else {
overlapSegment = currentSegment;
}
// Match segments which don't share nodes (Issue #901)
} else if (tolerance == 0
? booleanPointOnLine(Point(coordinates: coords[0]),
currentFeature.geometry as LineString) &&
booleanPointOnLine(Point(coordinates: coords[1]),
currentFeature.geometry as LineString)
: nearestPointOnLine(currentFeature.geometry as LineString,
Point(coordinates: coords[0]))
.properties!['dist'] <=
tolerance &&
nearestPointOnLine(currentFeature.geometry as LineString,
Point(coordinates: coords[1]))
.properties!['dist'] <=
tolerance) {
doesOverlap = true;
if (overlapSegment != null) {
overlapSegment = concatSegment(overlapSegment!, currentSegment) ??
overlapSegment;
} else {
overlapSegment = currentSegment;
}
} else if (tolerance == 0
? booleanPointOnLine(Point(coordinates: coordsMatch[0]),
currentSegment.geometry as LineString) &&
booleanPointOnLine(Point(coordinates: coordsMatch[1]),
currentSegment.geometry as LineString)
: nearestPointOnLine(currentSegment.geometry as LineString,
Point(coordinates: coordsMatch[0]))
.properties!['dist'] <=
tolerance &&
nearestPointOnLine(currentSegment.geometry as LineString,
Point(coordinates: coordsMatch[1]))
.properties!['dist'] <=
tolerance) {
// Do not define doesOverlap = true since more matches can occur
// within the same segment
// doesOverlaps = true;
if (overlapSegment != null) {
Feature<LineString>? combinedSegment = concatSegment(
overlapSegment!, currentFeature as Feature<LineString>);
if (combinedSegment != null) {
overlapSegment = combinedSegment;
} else {
additionalSegments.add(currentFeature);
}
} else {
overlapSegment = currentFeature as Feature<LineString>;
}
},
);

// Segment doesn't overlap - add overlaps to results & reset
if (doesOverlap == false && overlapSegment != null) {
features.add(overlapSegment!);
if (additionalSegments.isNotEmpty) {
features = [...features, ...additionalSegments];
additionalSegments = [];
}
overlapSegment = null;
}
},
);
});

// Segment doesn't overlap - add overlaps to results & reset
if (doesOverlap == false && overlapSegment != null) {
features.add(overlapSegment!);
if (additionalSegments.isNotEmpty) {
features.addAll(additionalSegments);
additionalSegments = [];
}
overlapSegment = null;
}
});
// Add last segment if exists
if (overlapSegment != null) features.add(overlapSegment!);

return FeatureCollection(features: features);
}

Feature<LineString>? concatSegment(
Feature<LineString> line, Feature<LineString> segment) {
var coords = getCoords(segment);
var lineCoords = getCoords(line);
var start = lineCoords[0];
var end = lineCoords[lineCoords.length - 1];
Feature<LineString> line,
Feature<LineString> segment,
) {
var coords = getCoords(segment) as List<Position>;
var lineCoords = getCoords(line) as List<Position>;
Position start = lineCoords[0];
Position end = lineCoords[lineCoords.length - 1];
List<Position> positions = (line.geometry as LineString).clone().coordinates;

if (coords[0] == start) {
Expand Down
25 changes: 12 additions & 13 deletions test/components/line_overlap_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import 'dart:io';
import 'package:test/test.dart';
import 'package:turf/helpers.dart';
import 'package:turf/meta.dart';
import 'package:turf/src/invariant.dart';
import 'package:turf/src/line_overlap.dart';
import 'package:turf/src/meta/feature.dart';
import 'package:turf_equality/turf_equality.dart';
Expand Down Expand Up @@ -52,26 +51,26 @@ void main() {
Equality eq = Equality();
FeatureCollection shared = colorize(
lineOverlap(
inGeom.features[0],
inGeom.features[1],
inGeom.features.first,
inGeom.features.last,
),
color: "#0F0");
print(shared.features.length);
shared.features.forEach(
(element) {
print(element.geometry);
(element.geometry as GeometryType)
.coordinates
.forEach((e) => print("${e.lng}-${e.lat}"));
},
);
print((outGeom as FeatureCollection).features.length);
print(shared.features);
// shared.features.forEach(
// (element) {
// print(element.geometry);
// (element.geometry as GeometryType)
// .coordinates
// .forEach((e) => print("${e.lng}-${e.lat}"));
// },
// );
FeatureCollection results = FeatureCollection(features: [
...shared.features,
inGeom.features.first,
inGeom.features.last
]);
print(results.features.length);
expect(eq.compare(results, outGeom), isTrue);
},
);
}
Expand Down