Skip to content

Implement Sample #234

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

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
4 changes: 4 additions & 0 deletions lib/combine.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
library turf_combine;

export 'package:geotypes/geotypes.dart';
export 'src/combine.dart';
3 changes: 3 additions & 0 deletions lib/flatten.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
library turf_flatten;

export 'package:turf/src/flatten.dart';
4 changes: 4 additions & 0 deletions lib/points_within_polygon.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
library turf_point_to_line_distance;

export 'package:geotypes/geotypes.dart';
export 'src/points_within_polygon.dart';
4 changes: 4 additions & 0 deletions lib/polygon_tangents.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
library turf_polygon_tangents;

export 'package:geotypes/geotypes.dart';
export 'src/polygon_tangents.dart';
9 changes: 9 additions & 0 deletions lib/polygonize.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/// Implementation of the polygonize algorithm that converts a collection of
/// LineString features to a collection of Polygon features.
///
/// This module follows RFC 7946 (GeoJSON) standards and provides a robust
/// implementation for converting line segments into closed polygons.

library polygonize;

export 'src/polygonize.dart';
4 changes: 4 additions & 0 deletions lib/sample.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
library turf_sample;

export 'package:geotypes/geotypes.dart';
export 'src/sample.dart';
111 changes: 111 additions & 0 deletions lib/src/combine.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import 'package:turf/meta.dart';

/// Combines a [FeatureCollection] of Point, LineString or Polygon features
/// into a single MultiPoint, MultiLineString or MultiPolygon feature.
///
/// The [collection] must be a FeatureCollection of the same geometry type.
/// Supported types are Point, LineString, and Polygon.
///
/// Returns a [Feature] with a Multi* geometry containing all coordinates from the input collection.
/// Throws [ArgumentError] if features have inconsistent geometry types or unsupported types.
///
/// If [mergeProperties] is true, properties from the first feature will be preserved.
/// Otherwise, properties will be empty by default.
///
/// See: https://turfjs.org/docs/#combine
Feature combine(
FeatureCollection collection, {
bool mergeProperties = false,
}) {
// Validate that the collection is not empty
if (collection.features.isEmpty) {
throw ArgumentError('FeatureCollection must contain at least one feature');
}

// Get the geometry type of the first feature to validate consistency
final firstFeature = collection.features.first;
final geometryType = firstFeature.geometry?.runtimeType;
if (geometryType == null) {
throw ArgumentError('Feature must have a geometry');
}

final firstGeometry = firstFeature.geometry!;

// Ensure all features have the same geometry type
for (final feature in collection.features) {
final geometry = feature.geometry;
if (geometry == null) {
throw ArgumentError('All features must have a geometry');
}

if (geometry.runtimeType != firstGeometry.runtimeType) {
throw ArgumentError(
'All features must have the same geometry type. '
'Found: ${geometry.type}, expected: ${firstGeometry.type}',
);
}
}

// Set of properties to include in result if mergeProperties is true
final properties = mergeProperties && firstFeature.properties != null
? Map<String, dynamic>.from(firstFeature.properties!)
: <String, dynamic>{};

// Create the appropriate geometry based on type
GeometryObject resultGeometry;

if (firstGeometry is Point) {
// Combine all Point coordinates into a single MultiPoint
final coordinates = <Position>[];
for (final feature in collection.features) {
final point = feature.geometry as Point;
coordinates.add(point.coordinates);
}

resultGeometry = MultiPoint(coordinates: coordinates);
} else if (firstGeometry is LineString) {
// Combine all LineString coordinate arrays into a MultiLineString
final coordinates = <List<Position>>[];
for (final feature in collection.features) {
final line = feature.geometry as LineString;
coordinates.add(line.coordinates);
}

resultGeometry = MultiLineString(coordinates: coordinates);
} else if (firstGeometry is Polygon) {
// Combine all Polygon coordinate arrays into a MultiPolygon
final coordinates = <List<List<Position>>>[];
for (final feature in collection.features) {
final polygon = feature.geometry as Polygon;
coordinates.add(polygon.coordinates);
}

resultGeometry = MultiPolygon(coordinates: coordinates);
} else {
// Throw if unsupported geometry type is encountered
throw ArgumentError(
'Unsupported geometry type: ${firstGeometry.type}. '
'Only Point, LineString, and Polygon are supported.',
);
}

// Create the Feature result
final result = Feature(
geometry: resultGeometry,
properties: properties,
);

// Apply otherMembers from the first feature to preserve GeoJSON compliance
final resultJson = result.toJson();
final firstFeatureJson = firstFeature.toJson();

// Copy any non-standard GeoJSON fields (otherMembers)
firstFeatureJson.forEach((key, value) {
if (key != 'type' && key != 'geometry' && key != 'properties' && key != 'id') {
resultJson[key] = value;
}
});

// Return the result with otherMembers preserved
return Feature.fromJson(resultJson);
}
73 changes: 73 additions & 0 deletions lib/src/flatten.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import 'package:turf/helpers.dart';
import 'package:turf/src/meta/flatten.dart';

/// Takes any [GeoJSONObject] and returns a [FeatureCollection] of simple features.
/// The function flattens all Multi* geometries and GeometryCollections into single-geometry Features.
///
/// This function is useful when handling complex shapes with multiple parts, making it easier to process
/// each part as a distinct feature.
///
/// * [geojson] - any valid [GeoJSONObject] (Feature, FeatureCollection, Geometry)
/// * Returns a [FeatureCollection] of Features where each feature has a single geometry type
///
/// Altitude values (z coordinates) are preserved in all coordinate positions.
/// Properties and other metadata in the input Feature are preserved in each output Feature.
///
/// Replicates behavior from: https://turfjs.org/docs/#flatten
///
/// Example:
/// ```dart
/// var multiLineString = MultiLineString(coordinates: [
/// [Position(0, 0), Position(1, 1)],
/// [Position(2, 2), Position(3, 3)]
/// ]);
///
/// var flattened = flatten(multiLineString);
/// // Returns FeatureCollection with 2 LineString features
/// ```
///
/// Throws [ArgumentError] if:
/// - A null [geojson] is provided
/// - A [GeometryCollection] is provided (explicitly not supported)
/// - A Feature with null geometry is provided
/// - An unsupported geometry type is encountered
FeatureCollection<GeometryObject> flatten(GeoJSONObject geojson) {
if (geojson == null) {
throw ArgumentError('Cannot flatten null geojson');
}

// Reject GeometryCollection inputs - not supported per the requirements
if (geojson is GeometryCollection) {
throw ArgumentError('flatten does not support GeometryCollection input.');
}

// Use a list to collect all flattened features
final List<Feature<GeometryObject>> features = [];

// Use flattenEach from meta to iterate through each flattened feature
flattenEach(geojson, (currentFeature, featureIndex, multiFeatureIndex) {
// If the geometry is null, skip this feature (implementation choice)
if (currentFeature.geometry == null) {
return;
}

// We know this is a Feature with a GeometryType, but we want to ensure
// it's treated as a Feature<GeometryObject> to match return type
final feature = Feature<GeometryObject>(
geometry: currentFeature.geometry,
properties: currentFeature.properties,
id: currentFeature.id,
bbox: currentFeature.bbox,
);

// Add to our features list - this maintains original geometry order
features.add(feature);
});

// Create and return a FeatureCollection containing all the flattened features
return FeatureCollection<GeometryObject>(
features: features,
// If the original object was a Feature, preserve its bbox
bbox: (geojson is Feature) ? geojson.bbox : null,
);
}
62 changes: 62 additions & 0 deletions lib/src/points_within_polygon.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

import 'package:turf/meta.dart';
import 'package:turf/src/booleans/boolean_point_in_polygon.dart';

/// Returns every Point (or the subset of coordinates of
/// a MultiPoint) that falls inside at least one Polygon/MultiPolygon.
///
/// The geometry type of each returned feature matches
/// its input type: Point ➜ Point, MultiPoint ➜ trimmed MultiPoint.
FeatureCollection<GeometryObject> pointsWithinPolygon(
GeoJSONObject points,
GeoJSONObject polygons,
) {
final List<Feature<GeometryObject>> results = [];

// Iterate over each Point or MultiPoint feature
featureEach(points, (Feature current, int? _) {
bool contained = false;

final geom = current.geometry;
if (geom is Point) {
// Check a single Point against every polygon
geomEach(polygons, (poly, __, ___, ____, _____) {
if (booleanPointInPolygon(geom.coordinates, poly as GeoJSONObject)) {
contained = true;
}
});
if (contained) results.add(current);
}

else if (geom is MultiPoint) {
final inside = <Position>[];

// Test every coordinate of the MultiPoint
geomEach(polygons, (poly, __, ___, ____, _____) {
for (final pos in geom.coordinates) {
if (booleanPointInPolygon(pos, poly as GeoJSONObject)) {
contained = true;
inside.add(pos);
}
}
});

if (contained) {
results.add(
Feature<MultiPoint>(
geometry: MultiPoint(coordinates: inside),
properties: current.properties,
id: current.id,
bbox: current.bbox,
) as Feature<GeometryObject>,
);
}
}

else {
throw ArgumentError('Input geometry must be Point or MultiPoint');
}
});

return FeatureCollection<GeometryObject>(features: results);
}
Loading
Loading