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

Added support for the area function #123

Merged
merged 8 commits into from
Jul 28, 2022
Merged
Show file tree
Hide file tree
Changes from 7 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Any new benchmarks must be named `*_benchmark.dart` and reside in the

### Measurement
- [ ] along
- [ ] area
- [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)
- [x] [bboxPolygon](https://github.com/dartclub/turf_dart/blob/main/lib/src/bbox_polygon.dart)
- [x] [bearing](https://github.com/dartclub/turf_dart/blob/main/lib/src/bearing.dart)
Expand Down
25 changes: 25 additions & 0 deletions benchmark/area_benchmark.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:benchmark/benchmark.dart';
import 'package:turf/turf.dart';

Feature<Polygon> poly = Feature<Polygon>(
geometry: Polygon(coordinates: [
[
Position(125, -15),
Position(113, -22),
Position(117, -37),
Position(130, -33),
Position(148, -39),
Position(154, -27),
Position(144, -15),
Position(125, -15)
]
]),
);

main() {
group('area', () {
benchmark('simple', () {
area(poly);
});
});
}
3 changes: 3 additions & 0 deletions lib/area.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
library turf_area;

export "src/area.dart";
129 changes: 129 additions & 0 deletions lib/src/area.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import 'dart:math';

import '../helpers.dart';
import '../meta.dart';

/// Takes a [GeoJSONObject] and returns their area in square meters.
///
/// ```dart
/// Feature<Polygon> poly = Feature<Polygon>(
/// geometry: Polygon(coordinates: [
/// [
/// Position(125, -15),
/// Position(113, -22),
/// Position(117, -37),
/// Position(130, -33),
/// Position(148, -39),
/// Position(154, -27),
/// Position(144, -15),
/// Position(125, -15)
/// ]
/// ]),
/// );
///
/// var area = turf.area(polygon);
/// ```
num? area(GeoJSONObject geojson) {
return geomReduce<num>(geojson, (value, geom, _, __, ___, ____) {
return value! + _calculateArea(geom!);
}, 0);
}

/// Calculate Area
num _calculateArea(GeometryType geom) {
num total = 0;
switch (geom.type) {
case GeoJSONObjectType.polygon:
return _polygonArea((geom as Polygon).coordinates);
case GeoJSONObjectType.multiPolygon:
geom as MultiPolygon;
for (var i = 0; i < geom.coordinates.length; i++) {
total += _polygonArea(geom.coordinates[i]);
}
return total;
case GeoJSONObjectType.point:
case GeoJSONObjectType.multiPoint:
case GeoJSONObjectType.lineString:
case GeoJSONObjectType.multiLineString:
return 0;
case GeoJSONObjectType.geometryCollection:
final geometryCollection = geom as GeometryCollection;
for (var i = 0; i < geometryCollection.geometries.length; i++) {
total += _calculateArea(geometryCollection.geometries[i]);
}
return total;
case GeoJSONObjectType.feature:
final feature = geom as Feature;
return _calculateArea(feature.geometry as GeometryType);
case GeoJSONObjectType.featureCollection:
final featureCollection = geom as FeatureCollection;
for (var i = 0; i < featureCollection.features.length; i++) {
total += _calculateArea(featureCollection.features[i].geometry as GeometryType);
}
return total;
}
}

num _polygonArea(List<List<Position>> coords) {
num total = 0;
if (coords.isNotEmpty) {
total += _ringArea(coords[0]).abs();
for (var i = 1; i < coords.length; i++) {
total -= _ringArea(coords[i]).abs();
}
}
return total;
}

///
/// Calculate the approximate area of the [Polygon] were it projected onto the earth in square meters.
///
/// Note that the area will be positive if ring is oriented clockwise, otherwise it will be negative.
///
/// Reference:
/// Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for Polygons on a Sphere",
/// JPL Publication 07-03, Jet Propulsion
/// Laboratory, Pasadena, CA, June 2007 https://trs.jpl.nasa.gov/handle/2014/40409
num _ringArea(List<Position> coords) {
Position p1;
Position p2;
Position p3;
int lowerIndex;
int middleIndex;
int upperIndex;
int i;
num total = 0;
final coordsLength = coords.length;

if (coordsLength > 2) {
for (i = 0; i < coordsLength; i++) {
if (i == coordsLength - 2) {
// i = N-2
lowerIndex = coordsLength - 2;
middleIndex = coordsLength - 1;
upperIndex = 0;
} else if (i == coordsLength - 1) {
// i = N-1
lowerIndex = coordsLength - 1;
middleIndex = 0;
upperIndex = 1;
} else {
// i = 0 to N-3
lowerIndex = i;
middleIndex = i + 1;
upperIndex = i + 2;
}
p1 = coords[lowerIndex];
p2 = coords[middleIndex];
p3 = coords[upperIndex];
total += (_rad(p3[0]!) - _rad(p1[0]!)) * sin(_rad(p2[1]!));
}

total = (total * earthRadius * earthRadius) / 2;
}
return total;
}

num _rad(num number) {
return (number * pi) / 180;
}
1 change: 1 addition & 0 deletions lib/turf.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
library turf;

export 'src/area.dart';
export 'src/bbox.dart';
export 'src/bearing.dart';
export 'src/center.dart';
Expand Down
68 changes: 68 additions & 0 deletions test/components/area_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import 'package:test/test.dart';
import 'package:turf/src/geojson.dart';
import 'package:turf/turf.dart';

main() {
yardenfi marked this conversation as resolved.
Show resolved Hide resolved
group('area', () {
final position1 = Position(0, 0);
final position2 = Position(0, 1);
final positions = [position1, position2];
final point = Point(coordinates: position1);
final multiPoint = MultiPoint(coordinates: positions);
final lineString = LineString(coordinates: positions);
final multiLineString = LineString(coordinates: positions);
List<GeometryType> zeroAreaGeometries = [point, multiPoint, lineString, multiLineString];
final geometryCollection = GeometryCollection(geometries: zeroAreaGeometries);


Polygon polygon = Polygon(coordinates: [
[
Position(125, -15),
Position(113, -22),
Position(117, -37),
Position(130, -33),
Position(148, -39),
Position(154, -27),
Position(144, -15),
Position(125, -15)
]
]);

Feature<Polygon> polygonFeature = Feature<Polygon>(
geometry: polygon
);

test('test area of polygon', () {
var areaResult = area(polygon);
expect(areaResult, isNot(equals(null)));
final roundedResult = round(areaResult!);
expect(roundedResult, equals(7748891609977));
});

test('test area of polygon equals to the area of a feature and a feature collection that includes it', () {
var polygonAreaResult = round(area(polygon)!);
var featureAreaResult = round(area(Feature(geometry: polygon))!);
var featureCollectionAreaResult = round(area(FeatureCollection(features: [Feature(geometry: polygon)]))!);
expect(polygonAreaResult, equals(featureAreaResult));
expect(featureCollectionAreaResult, equals(featureAreaResult));
});

test('test area of polygon feature', () {
var areaResult = area(polygon);
expect(areaResult, isNot(equals(null)));
final roundedResult = round(areaResult!);
expect(roundedResult, equals(7748891609977));
});

test('area of point, multiPoint, lineString and multiLineString are 0', () {
expect(area(point), equals(0));
expect(area(multiPoint), equals(0));
expect(area(lineString), equals(0));
expect(area(multiLineString), equals(0));
});

test('area of geomrtry collection of (point, multiPoint, lineString and multiLineString) is 0', () {
expect(area(geometryCollection), equals(0));
});
});
}