Skip to content
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';
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