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 polygon-smooth and its tests #127

Merged
merged 3 commits into from
Aug 22, 2022
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
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This includes a fully [RFC 7946](https://tools.ietf.org/html/rfc7946)-compliant
Most of the implementation is a direct translation from [turf.js](https://github.com/Turfjs/turf).

## Get started

- Get the [Dart tools](https://dart.dev/tools)
- Install the library with `dart pub add turf`
- Import the library in your code and use it. For example:
Expand Down Expand Up @@ -58,12 +59,14 @@ main() {
![polymorphism](https://user-images.githubusercontent.com/10634693/159876354-f9da2f37-02b3-4546-b32a-c0f82c372272.png)

## Notable Design Decisions

- Nested `GeometryCollections` (as described in
[RFC 7946 section 3.1.8](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.8))
are _not supported_ which takes a slightly firmer stance than the "should
avoid" language in the specification

## Tests and Benchmarks

Tests are run with `dart test` and benchmarks can be run with
`dart run benchmark`

Expand All @@ -73,6 +76,7 @@ Any new benchmarks must be named `*_benchmark.dart` and reside in the
## Components

### Measurement

- [ ] along
- [x] [area](https://github.com/dartclub/turf_dart/blob/main/lib/src/area.dart)
- [x] [bbox](https://github.com/dartclub/turf_dart/blob/main/lib/src/bbox.dart)
Expand All @@ -96,13 +100,15 @@ Any new benchmarks must be named `*_benchmark.dart` and reside in the
- [ ] greatCircle

### Coordinate Mutation

- [x] [cleanCoords](https://github.com/dartclub/turf_dart/blob/main/lib/src/clean_coords.dart)
- [ ] flip
- [ ] rewind
- [ ] round
- [x] [truncate](https://github.com/dartclub/turf_dart/blob/main/lib/src/truncate.dart)

### Transformation

- [ ] bboxClip
- [ ] bezierSpline
- [ ] buffer
Expand All @@ -114,17 +120,18 @@ Any new benchmarks must be named `*_benchmark.dart` and reside in the
- [ ] dissolve
- [ ] intersect
- [ ] lineOffset
- [ ] polygonSmooth
- [x] [polygonSmooth](ttps://github.com/dartclub/turf_dart/blob/main/lib/src/polygon_smooth.dart)
- [ ] simplify
- [ ] tesselate
- [ ] transformRotate
- [ ] transformTranslate
- [ ] transformScale
- [ ] union
- [ ] voronoi
- [x] [polyLineDecode](https://github.com/Dennis-Mwea/turf_dart/blob/main/lib/src/polyline.dart)
- [x] [polyLineDecode](https://github.com/dartclub/turf_dart/blob/main/lib/src/polyline.dart)

### Feature Conversion

- [ ] combine
- [x] [explode](https://github.com/dartclub/turf_dart/blob/main/lib/src/explode.dart)
- [ ] flatten
Expand All @@ -133,6 +140,7 @@ Any new benchmarks must be named `*_benchmark.dart` and reside in the
- [x] [polygonToLine](https://github.com/dartclub/turf_dart/blob/main/lib/src/polygon_to_line.dart)

### MISC

- [ ] ellipse
- [ ] kinks
- [ ] lineArc
Expand All @@ -150,40 +158,48 @@ Any new benchmarks must be named `*_benchmark.dart` and reside in the
- [ ] unkinkPolygon

### Random

- [ ] randomPosition
- [ ] randomPoint
- [ ] randomLineString
- [ ] randomPolygon

### Data

- [ ] sample

### Interpolation

- [ ] interpolate
- [ ] isobands
- [ ] isolines
- [ ] planepoint
- [ ] tin

### Joins

- [ ] pointsWithinPolygon
- [ ] tag

### Grids

- [ ] hexGrid
- [ ] pointGrid
- [ ] squareGrid
- [ ] triangleGrid

### Classification

- [x] [nearestPoint](https://github.com/dartclub/turf_dart/blob/main/lib/src/nearest_point.dart)

### Aggregation

- [ ] collect
- [ ] clustersDbscan
- [ ] clustersKmeans

### META

- [x] [coordAll](https://github.com/dartclub/turf_dart/blob/main/lib/src/meta/coord.dart)
- [x] [coordEach](https://github.com/dartclub/turf_dart/blob/main/lib/src/meta/coord.dart)
- [x] [coordReduce](https://github.com/dartclub/turf_dart/blob/main/lib/src/meta/coord.dart)
Expand All @@ -204,6 +220,7 @@ Any new benchmarks must be named `*_benchmark.dart` and reside in the
- [x] [clusterReduce](https://github.com/dartclub/turf_dart/blob/main/lib/src/meta/cluster.dart)

### Booleans

- [ ] booleanClockwise
- [ ] booleanConcave
- [ ] booleanContains
Expand All @@ -218,6 +235,7 @@ Any new benchmarks must be named `*_benchmark.dart` and reside in the
- [ ] booleanWithin

### Unit Conversion

- [x] [bearingToAzimuth](https://github.com/dartclub/turf_dart/blob/main/lib/src/helpers.dart)
- [x] [convertArea](https://github.com/dartclub/turf_dart/blob/main/lib/src/helpers.dart)
- [x] [convertLength](https://github.com/dartclub/turf_dart/blob/main/lib/src/helpers.dart)
Expand Down
21 changes: 21 additions & 0 deletions benchmark/polygon_smooth_benchmark.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'dart:convert';
import 'dart:io';

import 'package:benchmark/benchmark.dart';
import 'package:turf/polygon_smooth.dart';
import 'package:turf/turf.dart';

main() {
group("turf-polygon-smooth", () {
var inDir = Directory('./test/examples/polygonSmooth/in');
for (var file in inDir.listSync(recursive: true)) {
if (file is File && file.path.endsWith('.geojson')) {
benchmark(file.path, () {
var inSource = file.readAsStringSync();
var inGeom = GeoJSONObject.fromJson(jsonDecode(inSource));
polygonSmooth(inGeom, iterations: 3);
});
}
}
});
}
3 changes: 3 additions & 0 deletions lib/polygon_smooth.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
library turf_polygon_smooth;

export 'src/polygon_smooth.dart';
30 changes: 14 additions & 16 deletions lib/src/line_to_polygon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,26 +143,24 @@ Feature<Polygon> lineStringToPolygon(
List<List<Position>> multiCoords = [];
num largestArea = 0;

line.coordinates.forEach(
(coord) {
if (autoComplete) {
coord = _autoCompleteCoords(coord);
}
for (var coord in line.coordinates) {
if (autoComplete) {
coord = _autoCompleteCoords(coord);
}

// Largest LineString to be placed in the first position of the coordinates array
if (orderCoords) {
var area = _calculateArea(bbox(LineString(coordinates: coord)));
if (area > largestArea) {
multiCoords.insert(0, coord);
largestArea = area;
} else {
multiCoords.add(coord);
}
// Largest LineString to be placed in the first position of the coordinates array
if (orderCoords) {
var area = _calculateArea(bbox(LineString(coordinates: coord)));
if (area > largestArea) {
multiCoords.insert(0, coord);
largestArea = area;
} else {
multiCoords.add(coord);
}
},
);
} else {
multiCoords.add(coord);
}
}
return Feature(
geometry: Polygon(coordinates: multiCoords), properties: properties);
} else {
Expand Down
152 changes: 152 additions & 0 deletions lib/src/polygon_smooth.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import 'package:turf/meta.dart';
import 'package:turf/turf.dart';

///
/// Smooths a [Polygon], [MultiPolygon], also inside [Feature]s, [FeatureCollection]s, or [GeometryCollection]. Based on [Chaikin's algorithm](http://graphics.cs.ucdavis.edu/education/CAGDNotes/Chaikins-Algorithm/Chaikins-Algorithm.html).
/// Warning: may create degenerate polygons.
/// The optional parameter [iterations] is the number of times to smooth the polygon. A higher value means a smoother polygon.
/// The functions returns a [FeatureCollection] of [Polygon]s and [MultiPolygon]s.
///
/// ```dart
/// var polygon = Polygon(coordinates: [
/// [
/// Position(11, 0),
/// Position(22, 4),
/// Position(31, 0),
/// Position(31, 11),
/// Position(21, 15),
/// Position(11, 11),
/// Position(11, 0),
/// ]
/// ]);
///
/// var smoothed = polygonSmooth(polygon, iterations: 3);
/// ```
FeatureCollection polygonSmooth(GeoJSONObject inputPolys,
{int iterations = 1}) {
var outPolys = <Feature>[];

geomEach(inputPolys, (
GeometryType? geom,
int? geomIndex,
Map<String, dynamic>? featureProperties,
BBox? featureBBox,
dynamic featureId,
) {
var outCoords;
var poly;
var tempOutput;

switch (geom?.type) {
case GeoJSONObjectType.polygon:
outCoords = <List<Position>>[[]];
for (var i = 0; i < iterations; i++) {
tempOutput = <List<Position>>[[]];
poly = geom;
if (i > 0) poly = Polygon(coordinates: outCoords);
_processPolygon(poly, tempOutput);
outCoords = List<List<Position>>.of(tempOutput);
}
outPolys.add(Feature(
geometry: Polygon(coordinates: outCoords),
properties: featureProperties));
break;
case GeoJSONObjectType.multiPolygon:
outCoords = [
[<Position>[]]
];
for (var y = 0; y < iterations; y++) {
tempOutput = <List<List<Position>>>[
[[]]
];
poly = geom;
if (y > 0) poly = MultiPolygon(coordinates: outCoords);
_processMultiPolygon(poly, tempOutput);
outCoords = List<List<List<Position>>>.of(tempOutput);
}
outPolys.add(Feature(
geometry: MultiPolygon(coordinates: outCoords),
properties: featureProperties));
break;
default:
throw Exception("geometry is invalid, must be Polygon or MultiPolygon");
}
});
return FeatureCollection(features: outPolys);
}

_processPolygon(Polygon poly, List<List<Position>> tempOutput) {
var prevGeomIndex = 0;
var subtractCoordIndex = 0;

coordEach(poly, (currentCoord, coordIndex, featureIndex, multiFeatureIndex,
geometryIndex) {
if (geometryIndex! > prevGeomIndex) {
prevGeomIndex = geometryIndex;
subtractCoordIndex = coordIndex!;
tempOutput.add([]);
}
var realCoordIndex = coordIndex! - subtractCoordIndex;
var p1 = poly.coordinates[geometryIndex][realCoordIndex + 1];
var p0x = currentCoord!.lng;
var p0y = currentCoord.lat;
var p1x = p1.lng;
var p1y = p1.lat;
tempOutput[geometryIndex].add(Position(
0.75 * p0x + 0.25 * p1x,
0.75 * p0y + 0.25 * p1y,
));
tempOutput[geometryIndex].add(Position(
0.25 * p0x + 0.75 * p1x,
0.25 * p0y + 0.75 * p1y,
));
}, true);
for (var ring in tempOutput) {
ring.add(ring[0]);
}
}

_processMultiPolygon(poly, List<List<List<Position>>> tempOutput) {
var prevGeomIndex = 0;
var subtractCoordIndex = 0;
var prevMultiIndex = 0;

coordEach(poly, (currentCoord, coordIndex, featureIndex, multiFeatureIndex,
geometryIndex) {
if (multiFeatureIndex! > prevMultiIndex) {
prevMultiIndex = multiFeatureIndex;
subtractCoordIndex = coordIndex!;
tempOutput.add([[]]);
}
if (geometryIndex! > prevGeomIndex) {
prevGeomIndex = geometryIndex;
subtractCoordIndex = coordIndex!;
tempOutput[multiFeatureIndex].add([]);
}
var realCoordIndex = coordIndex! - subtractCoordIndex;
if (realCoordIndex + 1 ==
poly.coordinates[multiFeatureIndex][geometryIndex].length) {
return;
}
var p1 =
poly.coordinates[multiFeatureIndex][geometryIndex][realCoordIndex + 1];
var p0x = currentCoord!.lng;
var p0y = currentCoord.lat;
var p1x = p1.lng;
var p1y = p1.lat;
tempOutput[multiFeatureIndex][geometryIndex].add(Position(
0.75 * p0x + 0.25 * p1x,
0.75 * p0y + 0.25 * p1y,
));
tempOutput[multiFeatureIndex][geometryIndex].add(Position(
0.25 * p0x + 0.75 * p1x,
0.25 * p0y + 0.75 * p1y,
));
}, true);

for (var poly in tempOutput) {
for (var ring in poly) {
ring.add(ring[0]);
}
}
}
4 changes: 3 additions & 1 deletion lib/src/polygon_to_line.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ FeatureCollection _multiPolygonToLine(MultiPolygon geom,
properties = properties ?? <String, dynamic>{};

var lines = <Feature>[];
coords.forEach((coord) => {lines.add(_coordsToLine(coord, properties))});
for (var coord in coords) {
lines.add(_coordsToLine(coord, properties));
}
return FeatureCollection(features: lines);
}

Expand Down
Loading