-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add: simplify for Feature<LineString>, ported from turf.js (#183)
* Add: simplify for Feature<LineString>, ported from turf.js * Doc: update Progress.md
- Loading branch information
1 parent
b44f20c
commit 9e50c04
Showing
6 changed files
with
337 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
library turf_simplify; | ||
|
||
export 'package:geotypes/geotypes.dart'; | ||
export 'src/simplify.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import 'package:turf/helpers.dart'; | ||
import 'package:turf/turf.dart'; | ||
|
||
/* | ||
(c) 2013, Vladimir Agafonkin | ||
Simplify.js, a high-performance JS polyline simplification library | ||
mourner.github.io/simplify-js | ||
*/ | ||
|
||
// to suit your point format, run search/replace for '.x' and '.y'; | ||
// for 3D version, see 3d branch (configurability would draw significant performance overhead) | ||
|
||
/// square distance between 2 points | ||
num _getSqDist(Position p1, Position p2) { | ||
var dx = p1.lng - p2.lng, dy = p1.lat - p2.lat; | ||
|
||
return dx * dx + dy * dy; | ||
} | ||
|
||
/// square distance from a point to a segment | ||
num _getSqSegDist(Position p, Position p1, Position p2) { | ||
var x = p1.lng, y = p1.lat, dx = p2.lng - x, dy = p2.lat - y; | ||
|
||
if (dx != 0 || dy != 0) { | ||
var t = ((p.lng - x) * dx + (p.lat - y) * dy) / (dx * dx + dy * dy); | ||
|
||
if (t > 1) { | ||
x = p2.lng; | ||
y = p2.lat; | ||
} else if (t > 0) { | ||
x += dx * t; | ||
y += dy * t; | ||
} | ||
} | ||
|
||
dx = p.lng - x; | ||
dy = p.lat - y; | ||
|
||
return dx * dx + dy * dy; | ||
} | ||
// rest of the code doesn't care about point format | ||
|
||
/// basic distance-based simplification | ||
List<Position> _simplifyRadialDist(List<Position> points, double sqTolerance) { | ||
var prevPoint = points[0], newPoints = [prevPoint]; | ||
late Position point; | ||
|
||
for (var i = 1, len = points.length; i < len; i++) { | ||
point = points[i]; | ||
|
||
if (_getSqDist(point, prevPoint) > sqTolerance) { | ||
newPoints.add(point); | ||
prevPoint = point; | ||
} | ||
} | ||
|
||
if (prevPoint != point) newPoints.add(point); | ||
|
||
return newPoints; | ||
} | ||
|
||
List<Position> _simplifyDPStep(List<Position> points, int first, int last, | ||
double sqTolerance, List<Position> simplified) { | ||
num maxSqDist = sqTolerance; | ||
late int index; | ||
|
||
for (var i = first + 1; i < last; i++) { | ||
var sqDist = _getSqSegDist(points[i], points[first], points[last]); | ||
|
||
if (sqDist > maxSqDist) { | ||
index = i; | ||
maxSqDist = sqDist; | ||
} | ||
} | ||
|
||
if (maxSqDist > sqTolerance) { | ||
if (index - first > 1) { | ||
simplified = | ||
_simplifyDPStep(points, first, index, sqTolerance, simplified); | ||
} | ||
simplified.add(points[index]); | ||
if (last - index > 1) { | ||
simplified = | ||
_simplifyDPStep(points, index, last, sqTolerance, simplified); | ||
} | ||
} | ||
|
||
return simplified; | ||
} | ||
|
||
/// simplification using Ramer-Douglas-Peucker algorithm | ||
List<Position> _simplifyDouglasPeucker(List<Position> points, sqTolerance) { | ||
final last = points.length - 1; | ||
|
||
var simplified = [points[0]]; | ||
simplified = _simplifyDPStep(points, 0, last, sqTolerance, simplified); | ||
simplified.add(points[last]); | ||
|
||
return simplified; | ||
} | ||
|
||
/// Simplify a LineString feature using dart port of simplify.js high-performance JS polyline simplification library. | ||
/// | ||
/// both algorithms combined for awesome performance | ||
Feature<LineString> simplify( | ||
Feature<LineString> points, { | ||
double tolerance = 1, | ||
bool highestQuality = false, | ||
}) { | ||
var coords = getCoords(points); | ||
if (coords.length <= 2) return points; | ||
if (coords is! List<Position>) return points; | ||
|
||
final sqTolerance = tolerance * tolerance; | ||
|
||
coords = highestQuality ? coords : _simplifyRadialDist(coords, sqTolerance); | ||
coords = _simplifyDouglasPeucker(coords, sqTolerance); | ||
|
||
return Feature<LineString>( | ||
id: points.id, | ||
geometry: LineString(coordinates: coords), | ||
properties: points.properties, | ||
bbox: points.bbox, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import 'dart:convert'; | ||
import 'dart:io'; | ||
|
||
import 'package:test/test.dart'; | ||
import 'package:turf/along.dart'; | ||
import 'package:turf/area.dart'; | ||
import 'package:turf/simplify.dart'; | ||
|
||
main() { | ||
group( | ||
'simplify in == out', | ||
() { | ||
var inDir = Directory('./test/examples/simplify/in'); | ||
for (var file in inDir.listSync(recursive: true)) { | ||
if (file is File && file.path.endsWith('.geojson')) { | ||
test( | ||
file.path, | ||
() { | ||
var inSource = file.readAsStringSync(); | ||
var inGeom = Feature<LineString>.fromJson(jsonDecode(inSource)); | ||
|
||
var inSimplified = simplify( | ||
inGeom, | ||
tolerance: inGeom.properties?['tolerance'] ?? 0.01, | ||
highestQuality: inGeom.properties?['highQuality'] ?? false, | ||
); | ||
|
||
// ignore: prefer_interpolation_to_compose_strings | ||
var outPath = './' + | ||
file.uri.pathSegments | ||
.sublist(0, file.uri.pathSegments.length - 2) | ||
.join('/') + | ||
'/out/${file.uri.pathSegments.last}'; | ||
|
||
var outSource = File(outPath).readAsStringSync(); | ||
var outGeom = Feature<LineString>.fromJson(jsonDecode(outSource)); | ||
|
||
final precision = 0.0001; | ||
expect(inSimplified.id, outGeom.id); | ||
expect(inSimplified.properties, equals(outGeom.properties)); | ||
expect(inSimplified.geometry, isNotNull); | ||
expect( | ||
_roundCoords(inSimplified.geometry!.coordinates, precision), | ||
_roundCoords(outGeom.geometry!.coordinates, precision)); | ||
}, | ||
); | ||
} | ||
} | ||
}, | ||
); | ||
test( | ||
'simplify retains id, properties and bbox', | ||
() { | ||
const properties = {"foo": "bar"}; | ||
const id = 12345; | ||
final bbox = BBox(0, 0, 2, 2); | ||
final poly = Feature<LineString>( | ||
geometry: LineString(coordinates: [ | ||
Position(0, 0), | ||
Position(2, 2), | ||
Position(2, 0), | ||
Position(0, 0), | ||
]), | ||
properties: properties, | ||
bbox: bbox, | ||
id: id, | ||
); | ||
final simple = simplify(poly, tolerance: 0.1); | ||
|
||
expect(simple.id, equals(id)); | ||
expect(simple.bbox, equals(bbox)); | ||
expect(simple.properties, equals(properties)); | ||
}, | ||
); | ||
} | ||
|
||
List<Position> _roundCoords(List<Position> coords, num precision) { | ||
return coords | ||
.map((p) => Position(_round(p.lng, precision), _round(p.lat, precision))) | ||
.toList(); | ||
} | ||
|
||
num _round(num value, num precision) { | ||
return (value / precision).roundToDouble() * precision; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
{ | ||
"type": "Feature", | ||
"properties": {}, | ||
"geometry": { | ||
"type": "LineString", | ||
"coordinates": [ | ||
[-80.51399230957031, 28.069556808283608], | ||
[-80.51193237304688, 28.057438520876673], | ||
[-80.49819946289062, 28.05622661698537], | ||
[-80.5023193359375, 28.04471284867091], | ||
[-80.48583984375, 28.042288740362853], | ||
[-80.50575256347656, 28.028349057505775], | ||
[-80.50163269042969, 28.02168161433489], | ||
[-80.49476623535156, 28.021075462659883], | ||
[-80.48652648925781, 28.021075462659883], | ||
[-80.47691345214844, 28.021075462659883], | ||
[-80.46936035156249, 28.015619944017807], | ||
[-80.47760009765624, 28.007133032319448], | ||
[-80.49201965332031, 27.998039170620494], | ||
[-80.46730041503906, 27.962262536875905], | ||
[-80.46524047851562, 27.91980029694533], | ||
[-80.40550231933594, 27.930114089618602], | ||
[-80.39657592773438, 27.980455528671527], | ||
[-80.41305541992188, 27.982274659104082], | ||
[-80.42953491210938, 27.990763528690582], | ||
[-80.4144287109375, 28.00955793247135], | ||
[-80.3594970703125, 27.972572275562527], | ||
[-80.36224365234375, 27.948919060105453], | ||
[-80.38215637207031, 27.913732900444284], | ||
[-80.41786193847656, 27.881570017022806], | ||
[-80.40550231933594, 27.860932192608534], | ||
[-80.39382934570312, 27.85425440786446], | ||
[-80.37803649902344, 27.86336037597851], | ||
[-80.38215637207031, 27.880963078302393], | ||
[-80.36842346191405, 27.888246118437756], | ||
[-80.35743713378906, 27.882176952341734], | ||
[-80.35469055175781, 27.86882358965466], | ||
[-80.3594970703125, 27.8421119273228], | ||
[-80.37940979003906, 27.83300417483936], | ||
[-80.39932250976561, 27.82511017099003], | ||
[-80.40069580078125, 27.79352841586229], | ||
[-80.36155700683594, 27.786846483587688], | ||
[-80.35537719726562, 27.794743268514615], | ||
[-80.36705017089844, 27.800209937418252], | ||
[-80.36889553070068, 27.801918215058347], | ||
[-80.3690242767334, 27.803930152059845], | ||
[-80.36713600158691, 27.805942051806845], | ||
[-80.36584854125977, 27.805524490772143], | ||
[-80.36563396453857, 27.80465140342285], | ||
[-80.36619186401367, 27.803095012921272], | ||
[-80.36623477935791, 27.801842292177923], | ||
[-80.36524772644043, 27.80127286888392], | ||
[-80.36224365234375, 27.801158983867033], | ||
[-80.36065578460693, 27.802639479776524], | ||
[-80.36138534545898, 27.803740348273823], | ||
[-80.36220073699951, 27.804803245204976], | ||
[-80.36190032958984, 27.806625330038287], | ||
[-80.3609561920166, 27.80742248254359], | ||
[-80.35932540893555, 27.806853088493792], | ||
[-80.35889625549315, 27.806321651354835], | ||
[-80.35902500152588, 27.805448570411585], | ||
[-80.35863876342773, 27.804461600896783], | ||
[-80.35739421844482, 27.804461600896783], | ||
[-80.35700798034668, 27.805334689771293], | ||
[-80.35696506500244, 27.80673920932572], | ||
[-80.35726547241211, 27.80772615814989], | ||
[-80.35808086395264, 27.808295547623707], | ||
[-80.3585958480835, 27.80928248230861], | ||
[-80.35653591156006, 27.80943431761813], | ||
[-80.35572052001953, 27.808637179875486], | ||
[-80.3555917739868, 27.80772615814989], | ||
[-80.3555917739868, 27.806055931810487], | ||
[-80.35572052001953, 27.803778309057556], | ||
[-80.35537719726562, 27.801804330717825], | ||
[-80.3554630279541, 27.799564581098746], | ||
[-80.35670757293701, 27.799564581098746], | ||
[-80.35499095916748, 27.796831264786892], | ||
[-80.34610748291016, 27.79478123244122], | ||
[-80.34404754638672, 27.802070060660014], | ||
[-80.34748077392578, 27.804955086774896], | ||
[-80.3433609008789, 27.805790211616266], | ||
[-80.34353256225586, 27.8101555324401], | ||
[-80.33499240875244, 27.810079615315917], | ||
[-80.33383369445801, 27.805676331334084], | ||
[-80.33022880554199, 27.801652484744796], | ||
[-80.32872676849365, 27.80848534345178] | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"type": "Feature", | ||
"properties": {}, | ||
"geometry": { | ||
"type": "LineString", | ||
"coordinates": [ | ||
[-80.513992, 28.069557], | ||
[-80.48584, 28.042289], | ||
[-80.505753, 28.028349], | ||
[-80.476913, 28.021075], | ||
[-80.49202, 27.998039], | ||
[-80.4673, 27.962263], | ||
[-80.46524, 27.9198], | ||
[-80.405502, 27.930114], | ||
[-80.396576, 27.980456], | ||
[-80.429535, 27.990764], | ||
[-80.414429, 28.009558], | ||
[-80.359497, 27.972572], | ||
[-80.382156, 27.913733], | ||
[-80.417862, 27.88157], | ||
[-80.393829, 27.854254], | ||
[-80.368423, 27.888246], | ||
[-80.354691, 27.868824], | ||
[-80.359497, 27.842112], | ||
[-80.399323, 27.82511], | ||
[-80.400696, 27.793528], | ||
[-80.361557, 27.786846], | ||
[-80.359325, 27.806853], | ||
[-80.354991, 27.796831], | ||
[-80.328727, 27.808485] | ||
] | ||
} | ||
} |