From 676f6345ab0d3485c1f33b4db5682654aecf00b4 Mon Sep 17 00:00:00 2001 From: Thomas Rasch Date: Mon, 27 Nov 2023 16:55:56 +0100 Subject: [PATCH 1/8] Renamed Turf -> Algorithms --- README.md | 4 +- .../GISTools/{Turf => Algorithms}/Along.swift | 0 .../GISTools/{Turf => Algorithms}/Area.swift | 0 .../{Turf => Algorithms}/Bearing.swift | 0 .../BooleanClockwise.swift | 0 .../{Turf => Algorithms}/BooleanCrosses.swift | 0 .../BooleanIntersects.swift | 0 .../{Turf => Algorithms}/BooleanOverlap.swift | 0 .../BooleanParallel.swift | 0 .../BooleanPointInPolygon.swift | 0 .../BooleanPointOnLine.swift | 0 .../BoundingBoxClip.swift | 0 .../{Turf => Algorithms}/Buffer.swift | 0 .../{Turf => Algorithms}/Center.swift | 0 .../{Turf => Algorithms}/Circle.swift | 0 .../{Turf => Algorithms}/Conversions.swift | 0 .../{Turf => Algorithms}/Destination.swift | 0 .../{Turf => Algorithms}/Distance.swift | 0 .../EnumerateCoordinates.swift | 0 .../{Turf => Algorithms}/Flatten.swift | 0 .../GISTools/Algorithms/FrechetDistance.swift | 39 +++++++++++++++++++ .../{Turf => Algorithms}/Length.swift | 0 .../{Turf => Algorithms}/LineArc.swift | 0 .../{Turf => Algorithms}/LineChunk.swift | 0 .../{Turf => Algorithms}/LineIntersect.swift | 0 .../{Turf => Algorithms}/LineOverlap.swift | 0 .../{Turf => Algorithms}/LineSegments.swift | 0 .../{Turf => Algorithms}/LineSlice.swift | 0 .../{Turf => Algorithms}/LineSliceAlong.swift | 0 .../{Turf => Algorithms}/MidPoint.swift | 0 .../{Turf => Algorithms}/NearestPoint.swift | 0 .../NearestPointOnFeature.swift | 0 .../NearestPointOnLine.swift | 0 .../NearestPointToLine.swift | 0 .../{Turf => Algorithms}/PointOnFeature.swift | 0 .../PointToLineDistance.swift | 0 .../PointsWithinPolygon.swift | 0 .../PoleOfInaccessibility.swift | 0 .../{Turf => Algorithms}/Reverse.swift | 0 .../{Turf => Algorithms}/RhumbBearing.swift | 0 .../RhumbDestination.swift | 0 .../{Turf => Algorithms}/RhumbDistance.swift | 0 .../{Turf => Algorithms}/Simplify.swift | 0 .../TransformCoordinates.swift | 0 .../TransformRotate.swift | 0 .../{Turf => Algorithms}/TransformScale.swift | 0 .../TransformTranslate.swift | 0 .../{Turf => Algorithms}/Truncate.swift | 0 .../GISTools/{Turf => Algorithms}/Union.swift | 0 .../{Turf => Algorithms}/Validatable.swift | 0 .../{Turf => Algorithms}/AlongTests.swift | 0 .../{Turf => Algorithms}/BearingTests.swift | 0 .../BooleanClockwiseTests.swift | 0 .../BooleanOverlapTests.swift | 0 .../BooleanParallelTests.swift | 0 .../BoundingBoxClipTests.swift | 0 .../{Turf => Algorithms}/CircleTests.swift | 0 .../DestinationTests.swift | 0 .../{Turf => Algorithms}/DistanceTests.swift | 0 .../{Turf => Algorithms}/FlattenTests.swift | 0 .../{Turf => Algorithms}/LengthTests.swift | 0 .../{Turf => Algorithms}/LineArcTests.swift | 0 .../{Turf => Algorithms}/LineChunkTests.swift | 0 .../LineIntersectionTests.swift | 0 .../LineOverlapTests.swift | 0 .../LineSegmentsTests.swift | 0 .../LineSliceAlongTests.swift | 0 .../{Turf => Algorithms}/LineSliceTests.swift | 0 .../{Turf => Algorithms}/MidPointTests.swift | 0 .../NearestCoordinateOnLineTests.swift | 0 .../PointToLineDistanceTests.swift | 0 .../ProjectionTests.swift | 0 .../{Turf => Algorithms}/ReverseTests.swift | 0 .../RhumbBearingTests.swift | 0 .../RhumbDestinationTests.swift | 0 .../RhumbDistanceTests.swift | 0 .../{Turf => Algorithms}/SimplifyTests.swift | 0 .../TransformCoordinatesTests.swift | 0 .../TransformRotateTests.swift | 0 .../TransformScaleTests.swift | 0 .../TransformTranslateTests.swift | 0 .../{Turf => Algorithms}/TruncateTests.swift | 0 82 files changed, 41 insertions(+), 2 deletions(-) rename Sources/GISTools/{Turf => Algorithms}/Along.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Area.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Bearing.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/BooleanClockwise.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/BooleanCrosses.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/BooleanIntersects.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/BooleanOverlap.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/BooleanParallel.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/BooleanPointInPolygon.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/BooleanPointOnLine.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/BoundingBoxClip.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Buffer.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Center.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Circle.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Conversions.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Destination.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Distance.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/EnumerateCoordinates.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Flatten.swift (100%) create mode 100644 Sources/GISTools/Algorithms/FrechetDistance.swift rename Sources/GISTools/{Turf => Algorithms}/Length.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/LineArc.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/LineChunk.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/LineIntersect.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/LineOverlap.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/LineSegments.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/LineSlice.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/LineSliceAlong.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/MidPoint.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/NearestPoint.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/NearestPointOnFeature.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/NearestPointOnLine.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/NearestPointToLine.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/PointOnFeature.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/PointToLineDistance.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/PointsWithinPolygon.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/PoleOfInaccessibility.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Reverse.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/RhumbBearing.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/RhumbDestination.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/RhumbDistance.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Simplify.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/TransformCoordinates.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/TransformRotate.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/TransformScale.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/TransformTranslate.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Truncate.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Union.swift (100%) rename Sources/GISTools/{Turf => Algorithms}/Validatable.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/AlongTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/BearingTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/BooleanClockwiseTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/BooleanOverlapTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/BooleanParallelTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/BoundingBoxClipTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/CircleTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/DestinationTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/DistanceTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/FlattenTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/LengthTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/LineArcTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/LineChunkTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/LineIntersectionTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/LineOverlapTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/LineSegmentsTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/LineSliceAlongTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/LineSliceTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/MidPointTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/NearestCoordinateOnLineTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/PointToLineDistanceTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/ProjectionTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/ReverseTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/RhumbBearingTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/RhumbDestinationTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/RhumbDistanceTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/SimplifyTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/TransformCoordinatesTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/TransformRotateTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/TransformScaleTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/TransformTranslateTests.swift (100%) rename Tests/GISToolsTests/{Turf => Algorithms}/TruncateTests.swift (100%) diff --git a/README.md b/README.md index e065156..57be6f9 100644 --- a/README.md +++ b/README.md @@ -249,7 +249,7 @@ let geoJson = GeoJsonReader.geoJsonFrom(json: json)! print("Type is \(geoJson.type.rawValue)") print("Foreign members: \(geoJson.foreignMembers)") -case geoJson { +switch geoJson { case let point as Point: print("It's a Point!") case let multiPoint as MultiPoint: @@ -966,4 +966,4 @@ Thomas Rasch, Outdooractive [121]: https://github.com/Outdooractive/mvt-tools [image-1]: https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FOutdooractive%2Fgis-tools%2Fbadge%3Ftype%3Dswift-versions -[image-2]: https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FOutdooractive%2Fgis-tools%2Fbadge%3Ftype%3Dplatforms \ No newline at end of file +[image-2]: https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FOutdooractive%2Fgis-tools%2Fbadge%3Ftype%3Dplatforms diff --git a/Sources/GISTools/Turf/Along.swift b/Sources/GISTools/Algorithms/Along.swift similarity index 100% rename from Sources/GISTools/Turf/Along.swift rename to Sources/GISTools/Algorithms/Along.swift diff --git a/Sources/GISTools/Turf/Area.swift b/Sources/GISTools/Algorithms/Area.swift similarity index 100% rename from Sources/GISTools/Turf/Area.swift rename to Sources/GISTools/Algorithms/Area.swift diff --git a/Sources/GISTools/Turf/Bearing.swift b/Sources/GISTools/Algorithms/Bearing.swift similarity index 100% rename from Sources/GISTools/Turf/Bearing.swift rename to Sources/GISTools/Algorithms/Bearing.swift diff --git a/Sources/GISTools/Turf/BooleanClockwise.swift b/Sources/GISTools/Algorithms/BooleanClockwise.swift similarity index 100% rename from Sources/GISTools/Turf/BooleanClockwise.swift rename to Sources/GISTools/Algorithms/BooleanClockwise.swift diff --git a/Sources/GISTools/Turf/BooleanCrosses.swift b/Sources/GISTools/Algorithms/BooleanCrosses.swift similarity index 100% rename from Sources/GISTools/Turf/BooleanCrosses.swift rename to Sources/GISTools/Algorithms/BooleanCrosses.swift diff --git a/Sources/GISTools/Turf/BooleanIntersects.swift b/Sources/GISTools/Algorithms/BooleanIntersects.swift similarity index 100% rename from Sources/GISTools/Turf/BooleanIntersects.swift rename to Sources/GISTools/Algorithms/BooleanIntersects.swift diff --git a/Sources/GISTools/Turf/BooleanOverlap.swift b/Sources/GISTools/Algorithms/BooleanOverlap.swift similarity index 100% rename from Sources/GISTools/Turf/BooleanOverlap.swift rename to Sources/GISTools/Algorithms/BooleanOverlap.swift diff --git a/Sources/GISTools/Turf/BooleanParallel.swift b/Sources/GISTools/Algorithms/BooleanParallel.swift similarity index 100% rename from Sources/GISTools/Turf/BooleanParallel.swift rename to Sources/GISTools/Algorithms/BooleanParallel.swift diff --git a/Sources/GISTools/Turf/BooleanPointInPolygon.swift b/Sources/GISTools/Algorithms/BooleanPointInPolygon.swift similarity index 100% rename from Sources/GISTools/Turf/BooleanPointInPolygon.swift rename to Sources/GISTools/Algorithms/BooleanPointInPolygon.swift diff --git a/Sources/GISTools/Turf/BooleanPointOnLine.swift b/Sources/GISTools/Algorithms/BooleanPointOnLine.swift similarity index 100% rename from Sources/GISTools/Turf/BooleanPointOnLine.swift rename to Sources/GISTools/Algorithms/BooleanPointOnLine.swift diff --git a/Sources/GISTools/Turf/BoundingBoxClip.swift b/Sources/GISTools/Algorithms/BoundingBoxClip.swift similarity index 100% rename from Sources/GISTools/Turf/BoundingBoxClip.swift rename to Sources/GISTools/Algorithms/BoundingBoxClip.swift diff --git a/Sources/GISTools/Turf/Buffer.swift b/Sources/GISTools/Algorithms/Buffer.swift similarity index 100% rename from Sources/GISTools/Turf/Buffer.swift rename to Sources/GISTools/Algorithms/Buffer.swift diff --git a/Sources/GISTools/Turf/Center.swift b/Sources/GISTools/Algorithms/Center.swift similarity index 100% rename from Sources/GISTools/Turf/Center.swift rename to Sources/GISTools/Algorithms/Center.swift diff --git a/Sources/GISTools/Turf/Circle.swift b/Sources/GISTools/Algorithms/Circle.swift similarity index 100% rename from Sources/GISTools/Turf/Circle.swift rename to Sources/GISTools/Algorithms/Circle.swift diff --git a/Sources/GISTools/Turf/Conversions.swift b/Sources/GISTools/Algorithms/Conversions.swift similarity index 100% rename from Sources/GISTools/Turf/Conversions.swift rename to Sources/GISTools/Algorithms/Conversions.swift diff --git a/Sources/GISTools/Turf/Destination.swift b/Sources/GISTools/Algorithms/Destination.swift similarity index 100% rename from Sources/GISTools/Turf/Destination.swift rename to Sources/GISTools/Algorithms/Destination.swift diff --git a/Sources/GISTools/Turf/Distance.swift b/Sources/GISTools/Algorithms/Distance.swift similarity index 100% rename from Sources/GISTools/Turf/Distance.swift rename to Sources/GISTools/Algorithms/Distance.swift diff --git a/Sources/GISTools/Turf/EnumerateCoordinates.swift b/Sources/GISTools/Algorithms/EnumerateCoordinates.swift similarity index 100% rename from Sources/GISTools/Turf/EnumerateCoordinates.swift rename to Sources/GISTools/Algorithms/EnumerateCoordinates.swift diff --git a/Sources/GISTools/Turf/Flatten.swift b/Sources/GISTools/Algorithms/Flatten.swift similarity index 100% rename from Sources/GISTools/Turf/Flatten.swift rename to Sources/GISTools/Algorithms/Flatten.swift diff --git a/Sources/GISTools/Algorithms/FrechetDistance.swift b/Sources/GISTools/Algorithms/FrechetDistance.swift new file mode 100644 index 0000000..f2ca98c --- /dev/null +++ b/Sources/GISTools/Algorithms/FrechetDistance.swift @@ -0,0 +1,39 @@ +#if !os(Linux) +import CoreLocation +#endif +import Foundation + +/// Distance functions for ```LineString.frechetDistance```. +public enum FrechetDistanceFunction { + /// Use the eudlidean distance (very fast, not very accurate). + case euclidean + /// Use the Haversine formula to account for global curvature. + case haversine + /// Use a rhumb line. + case rhumbLine + /// Use a custom distance function. + case other((Coordinate3D, Coordinate3D) -> Double) +} + +extension LineString { + + /// Fréchet distance between to geometries. + /// + /// - Parameters: + /// - from: The other geometry of equal type + /// - distanceFunction: The algorithm to use for distance calculations + /// - tolerance: Affects the amount of simplification (in meters) + /// + /// - Returns: The frechet distance between the to geometries + public func frechetDistance( + from other: LineString, + distanceFunction: FrechetDistanceFunction = .haversine, + tolerance: CLLocationDistance? = nil) + -> Double + { + let other = other.projected(to: projection) + + return 0.0 + } + +} diff --git a/Sources/GISTools/Turf/Length.swift b/Sources/GISTools/Algorithms/Length.swift similarity index 100% rename from Sources/GISTools/Turf/Length.swift rename to Sources/GISTools/Algorithms/Length.swift diff --git a/Sources/GISTools/Turf/LineArc.swift b/Sources/GISTools/Algorithms/LineArc.swift similarity index 100% rename from Sources/GISTools/Turf/LineArc.swift rename to Sources/GISTools/Algorithms/LineArc.swift diff --git a/Sources/GISTools/Turf/LineChunk.swift b/Sources/GISTools/Algorithms/LineChunk.swift similarity index 100% rename from Sources/GISTools/Turf/LineChunk.swift rename to Sources/GISTools/Algorithms/LineChunk.swift diff --git a/Sources/GISTools/Turf/LineIntersect.swift b/Sources/GISTools/Algorithms/LineIntersect.swift similarity index 100% rename from Sources/GISTools/Turf/LineIntersect.swift rename to Sources/GISTools/Algorithms/LineIntersect.swift diff --git a/Sources/GISTools/Turf/LineOverlap.swift b/Sources/GISTools/Algorithms/LineOverlap.swift similarity index 100% rename from Sources/GISTools/Turf/LineOverlap.swift rename to Sources/GISTools/Algorithms/LineOverlap.swift diff --git a/Sources/GISTools/Turf/LineSegments.swift b/Sources/GISTools/Algorithms/LineSegments.swift similarity index 100% rename from Sources/GISTools/Turf/LineSegments.swift rename to Sources/GISTools/Algorithms/LineSegments.swift diff --git a/Sources/GISTools/Turf/LineSlice.swift b/Sources/GISTools/Algorithms/LineSlice.swift similarity index 100% rename from Sources/GISTools/Turf/LineSlice.swift rename to Sources/GISTools/Algorithms/LineSlice.swift diff --git a/Sources/GISTools/Turf/LineSliceAlong.swift b/Sources/GISTools/Algorithms/LineSliceAlong.swift similarity index 100% rename from Sources/GISTools/Turf/LineSliceAlong.swift rename to Sources/GISTools/Algorithms/LineSliceAlong.swift diff --git a/Sources/GISTools/Turf/MidPoint.swift b/Sources/GISTools/Algorithms/MidPoint.swift similarity index 100% rename from Sources/GISTools/Turf/MidPoint.swift rename to Sources/GISTools/Algorithms/MidPoint.swift diff --git a/Sources/GISTools/Turf/NearestPoint.swift b/Sources/GISTools/Algorithms/NearestPoint.swift similarity index 100% rename from Sources/GISTools/Turf/NearestPoint.swift rename to Sources/GISTools/Algorithms/NearestPoint.swift diff --git a/Sources/GISTools/Turf/NearestPointOnFeature.swift b/Sources/GISTools/Algorithms/NearestPointOnFeature.swift similarity index 100% rename from Sources/GISTools/Turf/NearestPointOnFeature.swift rename to Sources/GISTools/Algorithms/NearestPointOnFeature.swift diff --git a/Sources/GISTools/Turf/NearestPointOnLine.swift b/Sources/GISTools/Algorithms/NearestPointOnLine.swift similarity index 100% rename from Sources/GISTools/Turf/NearestPointOnLine.swift rename to Sources/GISTools/Algorithms/NearestPointOnLine.swift diff --git a/Sources/GISTools/Turf/NearestPointToLine.swift b/Sources/GISTools/Algorithms/NearestPointToLine.swift similarity index 100% rename from Sources/GISTools/Turf/NearestPointToLine.swift rename to Sources/GISTools/Algorithms/NearestPointToLine.swift diff --git a/Sources/GISTools/Turf/PointOnFeature.swift b/Sources/GISTools/Algorithms/PointOnFeature.swift similarity index 100% rename from Sources/GISTools/Turf/PointOnFeature.swift rename to Sources/GISTools/Algorithms/PointOnFeature.swift diff --git a/Sources/GISTools/Turf/PointToLineDistance.swift b/Sources/GISTools/Algorithms/PointToLineDistance.swift similarity index 100% rename from Sources/GISTools/Turf/PointToLineDistance.swift rename to Sources/GISTools/Algorithms/PointToLineDistance.swift diff --git a/Sources/GISTools/Turf/PointsWithinPolygon.swift b/Sources/GISTools/Algorithms/PointsWithinPolygon.swift similarity index 100% rename from Sources/GISTools/Turf/PointsWithinPolygon.swift rename to Sources/GISTools/Algorithms/PointsWithinPolygon.swift diff --git a/Sources/GISTools/Turf/PoleOfInaccessibility.swift b/Sources/GISTools/Algorithms/PoleOfInaccessibility.swift similarity index 100% rename from Sources/GISTools/Turf/PoleOfInaccessibility.swift rename to Sources/GISTools/Algorithms/PoleOfInaccessibility.swift diff --git a/Sources/GISTools/Turf/Reverse.swift b/Sources/GISTools/Algorithms/Reverse.swift similarity index 100% rename from Sources/GISTools/Turf/Reverse.swift rename to Sources/GISTools/Algorithms/Reverse.swift diff --git a/Sources/GISTools/Turf/RhumbBearing.swift b/Sources/GISTools/Algorithms/RhumbBearing.swift similarity index 100% rename from Sources/GISTools/Turf/RhumbBearing.swift rename to Sources/GISTools/Algorithms/RhumbBearing.swift diff --git a/Sources/GISTools/Turf/RhumbDestination.swift b/Sources/GISTools/Algorithms/RhumbDestination.swift similarity index 100% rename from Sources/GISTools/Turf/RhumbDestination.swift rename to Sources/GISTools/Algorithms/RhumbDestination.swift diff --git a/Sources/GISTools/Turf/RhumbDistance.swift b/Sources/GISTools/Algorithms/RhumbDistance.swift similarity index 100% rename from Sources/GISTools/Turf/RhumbDistance.swift rename to Sources/GISTools/Algorithms/RhumbDistance.swift diff --git a/Sources/GISTools/Turf/Simplify.swift b/Sources/GISTools/Algorithms/Simplify.swift similarity index 100% rename from Sources/GISTools/Turf/Simplify.swift rename to Sources/GISTools/Algorithms/Simplify.swift diff --git a/Sources/GISTools/Turf/TransformCoordinates.swift b/Sources/GISTools/Algorithms/TransformCoordinates.swift similarity index 100% rename from Sources/GISTools/Turf/TransformCoordinates.swift rename to Sources/GISTools/Algorithms/TransformCoordinates.swift diff --git a/Sources/GISTools/Turf/TransformRotate.swift b/Sources/GISTools/Algorithms/TransformRotate.swift similarity index 100% rename from Sources/GISTools/Turf/TransformRotate.swift rename to Sources/GISTools/Algorithms/TransformRotate.swift diff --git a/Sources/GISTools/Turf/TransformScale.swift b/Sources/GISTools/Algorithms/TransformScale.swift similarity index 100% rename from Sources/GISTools/Turf/TransformScale.swift rename to Sources/GISTools/Algorithms/TransformScale.swift diff --git a/Sources/GISTools/Turf/TransformTranslate.swift b/Sources/GISTools/Algorithms/TransformTranslate.swift similarity index 100% rename from Sources/GISTools/Turf/TransformTranslate.swift rename to Sources/GISTools/Algorithms/TransformTranslate.swift diff --git a/Sources/GISTools/Turf/Truncate.swift b/Sources/GISTools/Algorithms/Truncate.swift similarity index 100% rename from Sources/GISTools/Turf/Truncate.swift rename to Sources/GISTools/Algorithms/Truncate.swift diff --git a/Sources/GISTools/Turf/Union.swift b/Sources/GISTools/Algorithms/Union.swift similarity index 100% rename from Sources/GISTools/Turf/Union.swift rename to Sources/GISTools/Algorithms/Union.swift diff --git a/Sources/GISTools/Turf/Validatable.swift b/Sources/GISTools/Algorithms/Validatable.swift similarity index 100% rename from Sources/GISTools/Turf/Validatable.swift rename to Sources/GISTools/Algorithms/Validatable.swift diff --git a/Tests/GISToolsTests/Turf/AlongTests.swift b/Tests/GISToolsTests/Algorithms/AlongTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/AlongTests.swift rename to Tests/GISToolsTests/Algorithms/AlongTests.swift diff --git a/Tests/GISToolsTests/Turf/BearingTests.swift b/Tests/GISToolsTests/Algorithms/BearingTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/BearingTests.swift rename to Tests/GISToolsTests/Algorithms/BearingTests.swift diff --git a/Tests/GISToolsTests/Turf/BooleanClockwiseTests.swift b/Tests/GISToolsTests/Algorithms/BooleanClockwiseTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/BooleanClockwiseTests.swift rename to Tests/GISToolsTests/Algorithms/BooleanClockwiseTests.swift diff --git a/Tests/GISToolsTests/Turf/BooleanOverlapTests.swift b/Tests/GISToolsTests/Algorithms/BooleanOverlapTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/BooleanOverlapTests.swift rename to Tests/GISToolsTests/Algorithms/BooleanOverlapTests.swift diff --git a/Tests/GISToolsTests/Turf/BooleanParallelTests.swift b/Tests/GISToolsTests/Algorithms/BooleanParallelTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/BooleanParallelTests.swift rename to Tests/GISToolsTests/Algorithms/BooleanParallelTests.swift diff --git a/Tests/GISToolsTests/Turf/BoundingBoxClipTests.swift b/Tests/GISToolsTests/Algorithms/BoundingBoxClipTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/BoundingBoxClipTests.swift rename to Tests/GISToolsTests/Algorithms/BoundingBoxClipTests.swift diff --git a/Tests/GISToolsTests/Turf/CircleTests.swift b/Tests/GISToolsTests/Algorithms/CircleTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/CircleTests.swift rename to Tests/GISToolsTests/Algorithms/CircleTests.swift diff --git a/Tests/GISToolsTests/Turf/DestinationTests.swift b/Tests/GISToolsTests/Algorithms/DestinationTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/DestinationTests.swift rename to Tests/GISToolsTests/Algorithms/DestinationTests.swift diff --git a/Tests/GISToolsTests/Turf/DistanceTests.swift b/Tests/GISToolsTests/Algorithms/DistanceTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/DistanceTests.swift rename to Tests/GISToolsTests/Algorithms/DistanceTests.swift diff --git a/Tests/GISToolsTests/Turf/FlattenTests.swift b/Tests/GISToolsTests/Algorithms/FlattenTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/FlattenTests.swift rename to Tests/GISToolsTests/Algorithms/FlattenTests.swift diff --git a/Tests/GISToolsTests/Turf/LengthTests.swift b/Tests/GISToolsTests/Algorithms/LengthTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/LengthTests.swift rename to Tests/GISToolsTests/Algorithms/LengthTests.swift diff --git a/Tests/GISToolsTests/Turf/LineArcTests.swift b/Tests/GISToolsTests/Algorithms/LineArcTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/LineArcTests.swift rename to Tests/GISToolsTests/Algorithms/LineArcTests.swift diff --git a/Tests/GISToolsTests/Turf/LineChunkTests.swift b/Tests/GISToolsTests/Algorithms/LineChunkTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/LineChunkTests.swift rename to Tests/GISToolsTests/Algorithms/LineChunkTests.swift diff --git a/Tests/GISToolsTests/Turf/LineIntersectionTests.swift b/Tests/GISToolsTests/Algorithms/LineIntersectionTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/LineIntersectionTests.swift rename to Tests/GISToolsTests/Algorithms/LineIntersectionTests.swift diff --git a/Tests/GISToolsTests/Turf/LineOverlapTests.swift b/Tests/GISToolsTests/Algorithms/LineOverlapTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/LineOverlapTests.swift rename to Tests/GISToolsTests/Algorithms/LineOverlapTests.swift diff --git a/Tests/GISToolsTests/Turf/LineSegmentsTests.swift b/Tests/GISToolsTests/Algorithms/LineSegmentsTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/LineSegmentsTests.swift rename to Tests/GISToolsTests/Algorithms/LineSegmentsTests.swift diff --git a/Tests/GISToolsTests/Turf/LineSliceAlongTests.swift b/Tests/GISToolsTests/Algorithms/LineSliceAlongTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/LineSliceAlongTests.swift rename to Tests/GISToolsTests/Algorithms/LineSliceAlongTests.swift diff --git a/Tests/GISToolsTests/Turf/LineSliceTests.swift b/Tests/GISToolsTests/Algorithms/LineSliceTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/LineSliceTests.swift rename to Tests/GISToolsTests/Algorithms/LineSliceTests.swift diff --git a/Tests/GISToolsTests/Turf/MidPointTests.swift b/Tests/GISToolsTests/Algorithms/MidPointTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/MidPointTests.swift rename to Tests/GISToolsTests/Algorithms/MidPointTests.swift diff --git a/Tests/GISToolsTests/Turf/NearestCoordinateOnLineTests.swift b/Tests/GISToolsTests/Algorithms/NearestCoordinateOnLineTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/NearestCoordinateOnLineTests.swift rename to Tests/GISToolsTests/Algorithms/NearestCoordinateOnLineTests.swift diff --git a/Tests/GISToolsTests/Turf/PointToLineDistanceTests.swift b/Tests/GISToolsTests/Algorithms/PointToLineDistanceTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/PointToLineDistanceTests.swift rename to Tests/GISToolsTests/Algorithms/PointToLineDistanceTests.swift diff --git a/Tests/GISToolsTests/Turf/ProjectionTests.swift b/Tests/GISToolsTests/Algorithms/ProjectionTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/ProjectionTests.swift rename to Tests/GISToolsTests/Algorithms/ProjectionTests.swift diff --git a/Tests/GISToolsTests/Turf/ReverseTests.swift b/Tests/GISToolsTests/Algorithms/ReverseTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/ReverseTests.swift rename to Tests/GISToolsTests/Algorithms/ReverseTests.swift diff --git a/Tests/GISToolsTests/Turf/RhumbBearingTests.swift b/Tests/GISToolsTests/Algorithms/RhumbBearingTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/RhumbBearingTests.swift rename to Tests/GISToolsTests/Algorithms/RhumbBearingTests.swift diff --git a/Tests/GISToolsTests/Turf/RhumbDestinationTests.swift b/Tests/GISToolsTests/Algorithms/RhumbDestinationTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/RhumbDestinationTests.swift rename to Tests/GISToolsTests/Algorithms/RhumbDestinationTests.swift diff --git a/Tests/GISToolsTests/Turf/RhumbDistanceTests.swift b/Tests/GISToolsTests/Algorithms/RhumbDistanceTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/RhumbDistanceTests.swift rename to Tests/GISToolsTests/Algorithms/RhumbDistanceTests.swift diff --git a/Tests/GISToolsTests/Turf/SimplifyTests.swift b/Tests/GISToolsTests/Algorithms/SimplifyTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/SimplifyTests.swift rename to Tests/GISToolsTests/Algorithms/SimplifyTests.swift diff --git a/Tests/GISToolsTests/Turf/TransformCoordinatesTests.swift b/Tests/GISToolsTests/Algorithms/TransformCoordinatesTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/TransformCoordinatesTests.swift rename to Tests/GISToolsTests/Algorithms/TransformCoordinatesTests.swift diff --git a/Tests/GISToolsTests/Turf/TransformRotateTests.swift b/Tests/GISToolsTests/Algorithms/TransformRotateTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/TransformRotateTests.swift rename to Tests/GISToolsTests/Algorithms/TransformRotateTests.swift diff --git a/Tests/GISToolsTests/Turf/TransformScaleTests.swift b/Tests/GISToolsTests/Algorithms/TransformScaleTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/TransformScaleTests.swift rename to Tests/GISToolsTests/Algorithms/TransformScaleTests.swift diff --git a/Tests/GISToolsTests/Turf/TransformTranslateTests.swift b/Tests/GISToolsTests/Algorithms/TransformTranslateTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/TransformTranslateTests.swift rename to Tests/GISToolsTests/Algorithms/TransformTranslateTests.swift diff --git a/Tests/GISToolsTests/Turf/TruncateTests.swift b/Tests/GISToolsTests/Algorithms/TruncateTests.swift similarity index 100% rename from Tests/GISToolsTests/Turf/TruncateTests.swift rename to Tests/GISToolsTests/Algorithms/TruncateTests.swift From 057430e5997e2f73ffca7cb671703c930ffb4a70 Mon Sep 17 00:00:00 2001 From: Thomas Rasch Date: Tue, 28 Nov 2023 15:15:06 +0100 Subject: [PATCH 2/8] Frechet distance (for LineStrings) --- .../GISTools/Algorithms/FrechetDistance.swift | 52 +++++++++++++++++-- .../Algorithms/FrechetDistanceTests.swift | 30 +++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 Tests/GISToolsTests/Algorithms/FrechetDistanceTests.swift diff --git a/Sources/GISTools/Algorithms/FrechetDistance.swift b/Sources/GISTools/Algorithms/FrechetDistance.swift index f2ca98c..7a49fe5 100644 --- a/Sources/GISTools/Algorithms/FrechetDistance.swift +++ b/Sources/GISTools/Algorithms/FrechetDistance.swift @@ -5,14 +5,29 @@ import Foundation /// Distance functions for ```LineString.frechetDistance```. public enum FrechetDistanceFunction { - /// Use the eudlidean distance (very fast, not very accurate). + + /// Use the eudlidean distance. case euclidean /// Use the Haversine formula to account for global curvature. case haversine /// Use a rhumb line. case rhumbLine /// Use a custom distance function. - case other((Coordinate3D, Coordinate3D) -> Double) + case other((Coordinate3D, Coordinate3D) -> CLLocationDistance) + + func distance(between first: Coordinate3D, and second: Coordinate3D) -> CLLocationDistance { + switch self { + case .euclidean: + sqrt(pow(first.longitude - second.longitude, 2.0) + pow(first.latitude - second.latitude, 2.0)) + case .haversine: + first.distance(from: second) + case .rhumbLine: + first.rhumbDistance(from: second) + case let .other(distanceFuntion): + distanceFuntion(first, second) + } + } + } extension LineString { @@ -33,7 +48,38 @@ extension LineString { { let other = other.projected(to: projection) - return 0.0 + let p = allCoordinates + let q = other.allCoordinates + + var ca: [Double] = Array(repeating: -1.0, count: p.count * q.count) + + func index(_ pI: Int, _ qI: Int) -> Int { + (pI * p.count) + qI + } + + for i in 0 ..< p.count { + for j in 0 ..< q.count { + let distance = distanceFunction.distance(between: p[i], and: q[j]) + + ca[index(i, j)] = if i > 0, j > 0 { + max([ca[index(i - 1, j)], ca[index(i - 1, j - 1)], ca[index(i, j - 1)]].min() ?? -1.0, distance) + } + else if i > 0, j == 0 { + max(ca[index(i - 1, 0)], distance) + } + else if i == 0, j > 0 { + max(ca[index(0, j - 1)], distance) + } + else if i == 0, j == 0 { + distance + } + else { + .infinity + } + } + } + + return ca[index(p.count - 1, q.count - 1)] } } diff --git a/Tests/GISToolsTests/Algorithms/FrechetDistanceTests.swift b/Tests/GISToolsTests/Algorithms/FrechetDistanceTests.swift new file mode 100644 index 0000000..57794d0 --- /dev/null +++ b/Tests/GISToolsTests/Algorithms/FrechetDistanceTests.swift @@ -0,0 +1,30 @@ +#if !os(Linux) +import CoreLocation +#endif +@testable import GISTools +import XCTest + +final class FrechetDistanceTests: XCTestCase { + + func testFrechetDistance4326() throws { + let point = Point(Coordinate3D(latitude: 0.0, longitude: 0.0)) + let lineArc1 = try XCTUnwrap(point.lineArc(radius: 5000.0, bearing1: 20.0, bearing2: 60.0)) + let lineArc2 = try XCTUnwrap(point.lineArc(radius: 6000.0, bearing1: 20.0, bearing2: 60.0)) + + let distanceHaversine = lineArc1.frechetDistance(from: lineArc2, distanceFunction: .haversine) + let distanceRhumbLine = lineArc1.frechetDistance(from: lineArc2, distanceFunction: .rhumbLine) + + XCTAssertEqual(distanceHaversine, 1000.0, accuracy: 0.0001) + XCTAssertEqual(distanceRhumbLine, 1000.0, accuracy: 0.0001) + } + + func testFrechetDistance3857() throws { + let point = Point(Coordinate3D(latitude: 0.0, longitude: 0.0)).projected(to: .epsg3857) + let lineArc1 = try XCTUnwrap(point.lineArc(radius: 5000.0, bearing1: 20.0, bearing2: 60.0)) + let lineArc2 = try XCTUnwrap(point.lineArc(radius: 6000.0, bearing1: 20.0, bearing2: 60.0)) + + let distanceEucliden = lineArc1.frechetDistance(from: lineArc2, distanceFunction: .euclidean) + XCTAssertEqual(distanceEucliden, 1000.0, accuracy: 2.0) + } + +} From 190031646748f80c15da0014d4406ee758d8cd5f Mon Sep 17 00:00:00 2001 From: Thomas Rasch Date: Tue, 28 Nov 2023 15:22:13 +0100 Subject: [PATCH 3/8] Use Swift 5.9 --- Package.swift | 2 +- Sources/GISTools/Algorithms/FrechetDistance.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 08988f2..1d0879a 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.7 +// swift-tools-version:5.9 import PackageDescription diff --git a/Sources/GISTools/Algorithms/FrechetDistance.swift b/Sources/GISTools/Algorithms/FrechetDistance.swift index 7a49fe5..e648b1d 100644 --- a/Sources/GISTools/Algorithms/FrechetDistance.swift +++ b/Sources/GISTools/Algorithms/FrechetDistance.swift @@ -74,7 +74,7 @@ extension LineString { distance } else { - .infinity + Double.infinity } } } From 9ede0c5248d3d1c42b5a14a175aa442848b4d744 Mon Sep 17 00:00:00 2001 From: Thomas Rasch Date: Tue, 28 Nov 2023 15:28:15 +0100 Subject: [PATCH 4/8] Workflow update --- .github/workflows/swift.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 185d95d..4a508eb 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -15,6 +15,7 @@ jobs: runs-on: macos-latest steps: + - uses: swift-actions/setup-swift@v1 - uses: actions/checkout@v3 - name: Clean Package run: swift package clean From 1c3fd0691e6a3cfaa4d080c98874cbcfe2dc6aa1 Mon Sep 17 00:00:00 2001 From: Thomas Rasch Date: Wed, 29 Nov 2023 09:20:14 +0100 Subject: [PATCH 5/8] README update --- README.md | 221 +++++++++++++++++++++++++++--------------------------- 1 file changed, 112 insertions(+), 109 deletions(-) diff --git a/README.md b/README.md index 57be6f9..7ebc7b9 100644 --- a/README.md +++ b/README.md @@ -801,38 +801,39 @@ Hint: Most algorithms are optimized for EPSG:4326. Using other projections will | destination | `let destination = coordinate.destination(distance: 1000.0, bearing: 173.0)` | | [Source][66] / [Tests][67] | | distance | `let distance = coordinate1.distance(from: coordinate2)` | | [Source][68] / [Tests][69] | | flatten | `let featureCollection = anyGeometry.flattened` | | [Source][70] / [Tests][71] | -| length | `let length = lineString.length` | | [Source][72] / [Tests][73] | -| line-arc | `let lineArc = point.lineArc(radius: 5000.0, bearing1: 20.0, bearing2: 60.0)` | | [Source][74] / [Tests][75] | -| line-chunk | `let chunks = lineString.chunked(segmentLength: 1000.0).lineStrings` | | [Source][76] / [Tests][77] | -| line-intersect | `let intersections = feature1.intersections(other: feature2)` | | [Source][78] / [Tests][79] | -| line-overlap | `let overlappingSegments = lineString1.overlappingSegments(with: lineString2)` | | [Source][80] / [Tests][81] | -| line-segments | `let segments = anyGeometry.lineSegments` | | [Source][82] / [Tests][83] | -| line-slice | `let slice = lineString.slice(start: Coordinate3D(…), end: Coordinate3D(…))` | | [Source][84] / [Tests][85] | -| line-slice-along | `let sliced = lineString.sliceAlong(startDistance: 50.0, stopDistance: 2000.0)` | | [Source][86] / [Tests][87] | -| midpoint | `let middle = coordinate1.midpoint(to: coordinate2)` | | [Source][88] / [Tests][89] | -| nearest-point | `let nearest = anyGeometry.nearestCoordinate(from: Coordinate3D(…))` | | [Source][90] | -| nearest-point-on-feature | `let nearest = anyGeometry. nearestCoordinateOnFeature(from: Coordinate3D(…))` | | [Source][91] | -| nearest-point-on-line | `let nearest = lineString.nearestCoordinateOnLine(from: Coordinate3D(…))?.coordinate` | | [Source][92] / [Tests][93] | -| nearest-point-to-line | `let nearest = lineString. nearestCoordinate(outOf: coordinates)` | | [Source][94] | -| point-on-feature | `let coordinate = anyGeometry.coordinateOnFeature` | | [Source][95] | -| points-within-polygon | `let within = polygon.coordinatesWithin(coordinates)` | | [Source][96] | -| point-to-line-distance | `let distance = lineString.distanceFrom(coordinate: Coordinate3D(…))` | | [Source][97] / [Tests][98] | -| pole-of-inaccessibility | TODO | | [Source][99] | -| reverse | `let lineStringReversed = lineString.reversed` | | [Source][100] / [Tests][101] | -| rhumb-bearing | `let bearing = start.rhumbBearing(to: end)` | | [Source][102] / [Tests][103] | -| rhumb-destination | `let destination = coordinate.rhumbDestination(distance: 1000.0, bearing: 0.0)` | | [Source][104] / [Tests][105] | -| rhumb-distance | `let distance = coordinate1.rhumbDistance(from: coordinate2)` | | [Source][106] / [Tests][107] | -| simplify | `let simplified = lineString. simplified(tolerance: 5.0, highQuality: false)` | | [Source][108] / [Tests][109] | -| transform-coordinates | `let transformed = anyGeometry.transformCoordinates({ $0 })` | | [Source][110] / [Tests][111] | -| transform-rotate | `let transformed = anyGeometry. transformedRotate(angle: 25.0, pivot: Coordinate3D(…))` | | [Source][112] / [Tests][113] | -| transform-scale | `let transformed = anyGeometry. transformedScale(factor: 2.5, anchor: .center)` | | [Source][114] / [Tests][115] | -| transform-translate | `let transformed = anyGeometry. transformedTranslate(distance: 1000.0, direction: 25.0)` | | [Source][116] / [Tests][117] | -| truncate | `let truncated = lineString.truncated(precision: 2, removeAltitude: true)` | | [Source][118] / [Tests][119] | -| union | TODO | | [Source][120] | +| frechetDistance | `let distance = lineString.frechetDistance(from: other)` | | [Source][72] / [Tests][73] | +| length | `let length = lineString.length` | | [Source][74] / [Tests][75] | +| line-arc | `let lineArc = point.lineArc(radius: 5000.0, bearing1: 20.0, bearing2: 60.0)` | | [Source][76] / [Tests][77] | +| line-chunk | `let chunks = lineString.chunked(segmentLength: 1000.0).lineStrings` | | [Source][78] / [Tests][79] | +| line-intersect | `let intersections = feature1.intersections(other: feature2)` | | [Source][80] / [Tests][81] | +| line-overlap | `let overlappingSegments = lineString1.overlappingSegments(with: lineString2)` | | [Source][82] / [Tests][83] | +| line-segments | `let segments = anyGeometry.lineSegments` | | [Source][84] / [Tests][85] | +| line-slice | `let slice = lineString.slice(start: Coordinate3D(…), end: Coordinate3D(…))` | | [Source][86] / [Tests][87] | +| line-slice-along | `let sliced = lineString.sliceAlong(startDistance: 50.0, stopDistance: 2000.0)` | | [Source][88] / [Tests][89] | +| midpoint | `let middle = coordinate1.midpoint(to: coordinate2)` | | [Source][90] / [Tests][91] | +| nearest-point | `let nearest = anyGeometry.nearestCoordinate(from: Coordinate3D(…))` | | [Source][92] | +| nearest-point-on-feature | `let nearest = anyGeometry. nearestCoordinateOnFeature(from: Coordinate3D(…))` | | [Source][93] | +| nearest-point-on-line | `let nearest = lineString.nearestCoordinateOnLine(from: Coordinate3D(…))?.coordinate` | | [Source][94] / [Tests][95] | +| nearest-point-to-line | `let nearest = lineString. nearestCoordinate(outOf: coordinates)` | | [Source][96] | +| point-on-feature | `let coordinate = anyGeometry.coordinateOnFeature` | | [Source][97] | +| points-within-polygon | `let within = polygon.coordinatesWithin(coordinates)` | | [Source][98] | +| point-to-line-distance | `let distance = lineString.distanceFrom(coordinate: Coordinate3D(…))` | | [Source][99] / [Tests][100] | +| pole-of-inaccessibility | TODO | | [Source][101] | +| reverse | `let lineStringReversed = lineString.reversed` | | [Source][102] / [Tests][103] | +| rhumb-bearing | `let bearing = start.rhumbBearing(to: end)` | | [Source][104] / [Tests][105] | +| rhumb-destination | `let destination = coordinate.rhumbDestination(distance: 1000.0, bearing: 0.0)` | | [Source][106] / [Tests][107] | +| rhumb-distance | `let distance = coordinate1.rhumbDistance(from: coordinate2)` | | [Source][108] / [Tests][109] | +| simplify | `let simplified = lineString. simplified(tolerance: 5.0, highQuality: false)` | | [Source][110] / [Tests][111] | +| transform-coordinates | `let transformed = anyGeometry.transformCoordinates({ $0 })` | | [Source][112] / [Tests][113] | +| transform-rotate | `let transformed = anyGeometry. transformedRotate(angle: 25.0, pivot: Coordinate3D(…))` | | [Source][114] / [Tests][115] | +| transform-scale | `let transformed = anyGeometry. transformedScale(factor: 2.5, anchor: .center)` | | [Source][116] / [Tests][117] | +| transform-translate | `let transformed = anyGeometry. transformedTranslate(distance: 1000.0, direction: 25.0)` | | [Source][118] / [Tests][119] | +| truncate | `let truncated = lineString.truncated(precision: 2, removeAltitude: true)` | | [Source][120] / [Tests][121] | +| union | TODO | | [Source][122] | # Related packages Currently only one: -- [mvt-tools][121]: Vector tiles reader/writer for Swift +- [mvt-tools][123]: Vector tiles reader/writer for Swift # Contributing Please create an issue or open a pull request with a fix or enhancement. @@ -885,85 +886,87 @@ Thomas Rasch, Outdooractive [40]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/GeoJson/WKBTests.swift [41]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/GeoJson/WKTTests.swift [42]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/GeoJson/RTreeTests.swift -[43]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Along.swift -[44]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/AlongTests.swift "AlongTests.swift" -[45]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Area.swift -[46]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Bearing.swift -[47]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/BearingTests.swift "BearingTests.swift" -[48]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanClockwise.swift -[49]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/BooleanClockwiseTests.swift "BooleanClockwiseTests.swift" -[50]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanCrosses.swift -[51]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanIntersects.swift -[52]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanOverlap.swift -[53]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/BooleanOverlapTests.swift "BooleanOverlapTests.swift" -[54]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanParallel.swift -[55]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/BooleanParallelTests.swift "BooleanParallelTests.swift" -[56]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanPointInPolygon.swift -[57]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanPointOnLine.swift -[58]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Validatable.swift "Validatable.swift" -[59]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BoundingBoxClip.swift -[60]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/BoundingBoxClipTests.swift "BoundingBoxClipTests.swift" -[61]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Buffer.swift -[62]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Center.swift -[63]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Circle.swift -[64]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/CircleTests.swift "CircleTests.swift" -[65]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Conversions.swift -[66]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Destination.swift -[67]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/DestinationTests.swift "DestinationTests.swift" -[68]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Distance.swift -[69]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/DistanceTests.swift "DistanceTests.swift" -[70]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Flatten.swift -[71]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/FlattenTests.swift "FlattenTests.swift" -[72]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Length.swift -[73]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LengthTests.swift "LengthTests.swift" -[74]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineArc.swift -[75]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineArcTests.swift "LineArcTests.swift" -[76]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineChunk.swift -[77]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineChunkTests.swift "LineChunkTests.swift" -[78]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineIntersect.swift -[79]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineIntersectionTests.swift "LineIntersectionTests.swift" -[80]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineOverlap.swift -[81]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineOverlapTests.swift "LineOverlapTests.swift" -[82]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineSegments.swift -[83]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineSegmentsTests.swift "LineSegmentsTests.swift" -[84]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineSlice.swift -[85]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineSliceTests.swift "LineSliceTests.swift" -[86]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineSliceAlong.swift -[87]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineSliceAlongTests.swift "LineSliceAlongTests.swift" -[88]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/MidPoint.swift -[89]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/MidPointTests.swift "MidPointTests.swift" -[90]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/NearestPoint.swift -[91]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/NearestPointOnFeature.swift -[92]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/NearestPointOnLine.swift -[93]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/NearestCoordinateOnLineTests.swift "NearestCoordinateOnLineTests.swift" -[94]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/NearestPointToLine.swift "NearestPointToLine.swift" -[95]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/PointOnFeature.swift "PointOnFeature.swift" -[96]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/PointsWithinPolygon.swift "PointsWithinPolygon.swift" -[97]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/PointToLineDistance.swift "PointToLineDistance.swift" -[98]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/PointToLineDistanceTests.swift "PointToLineDistanceTests.swift" -[99]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/PoleOfInaccessibility.swift "PoleOfInaccessibility.swift" -[100]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Reverse.swift "Reverse.swift" -[101]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/ReverseTests.swift "ReverseTests.swift" -[102]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/RhumbBearing.swift "RhumbBearing.swift" -[103]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/RhumbBearingTests.swift "RhumbBearingTests.swift" -[104]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/RhumbDestination.swift "RhumbDestination.swift" -[105]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/RhumbDestinationTests.swift "RhumbDestinationTests.swift" -[106]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/RhumbDistance.swift "RhumbDistance.swift" -[107]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/RhumbDistanceTests.swift "RhumbDistanceTests.swift" -[108]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Simplify.swift "Simplify.swift" -[109]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/SimplifyTests.swift "SimplifyTests.swift" -[110]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/TransformCoordinates.swift "TransformCoordinates.swift" -[111]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/TransformCoordinatesTests.swift "TransformCoordinatesTests.swift" -[112]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/TransformRotate.swift "TransformRotate.swift" -[113]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/TransformRotateTests.swift "TransformRotateTests.swift" -[114]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/TransformScale.swift "TransformScale.swift" -[115]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/TransformScaleTests.swift "TransformScaleTests.swift" -[116]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/TransformTranslate.swift "TransformTranslate.swift" -[117]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/TransformTranslateTests.swift "TransformTranslateTests.swift" -[118]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Truncate.swift "Truncate.swift" -[119]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/TruncateTests.swift "TruncateTests.swift" -[120]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Union.swift -[121]: https://github.com/Outdooractive/mvt-tools +[43]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Along.swift "Along" +[44]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/AlongTests.swift "AlongTests" +[45]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Area.swift "Area" +[46]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Bearing.swift "Bearing" +[47]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/BearingTests.swift "BearingTests" +[48]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanClockwise.swift "BooleanClockwise" +[49]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/BooleanClockwiseTests.swift "BooleanClockwiseTests" +[50]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanCrosses.swift "BooleanCrosses" +[51]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanIntersects.swift "BooleanIntersects" +[52]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanOverlap.swift "BooleanOverlap" +[53]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/BooleanOverlapTests.swift "BooleanOverlapTests" +[54]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanParallel.swift "BooleanParallel" +[55]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/BooleanParallelTests.swift "BooleanParallelTests" +[56]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanPointInPolygon.swift "BooleanPointInPolygon" +[57]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BooleanPointOnLine.swift "BooleanPointOnLine" +[58]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Validatable.swift "Validatable" +[59]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/BoundingBoxClip.swift "BoundingBoxClip" +[60]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/BoundingBoxClipTests.swift "BoundingBoxClipTests" +[61]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Buffer.swift "Buffer" +[62]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Center.swift "Center" +[63]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Circle.swift "Circle" +[64]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/CircleTests.swift "CircleTests" +[65]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Conversions.swift "Conversions" +[66]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Destination.swift "Destination" +[67]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/DestinationTests.swift "DestinationTests" +[68]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Distance.swift "Distance" +[69]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/DistanceTests.swift "DistanceTests" +[70]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Flatten.swift "Flatten" +[71]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/FlattenTests.swift "FlattenTests" +[72]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Algorithms/FrechetDistance.swift "FrechetDistance" +[73]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Algorithms/FrechetDistanceTests.swift "FrechetDistanceTests" +[74]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Length.swift "Length" +[75]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LengthTests.swift "LengthTests" +[76]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineArc.swift "LineArc" +[77]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineArcTests.swift "LineArcTests" +[78]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineChunk.swift "LineChunk" +[79]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineChunkTests.swift "LineChunkTests" +[80]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineIntersect.swift "LineIntersect" +[81]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineIntersectionTests.swift "LineIntersectionTests" +[82]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineOverlap.swift "LineOverlap" +[83]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineOverlapTests.swift "LineOverlapTests" +[84]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineSegments.swift "LineSegments" +[85]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineSegmentsTests.swift "LineSegmentsTests" +[86]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineSlice.swift "LineSlice" +[87]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineSliceTests.swift "LineSliceTests" +[88]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/LineSliceAlong.swift "LineSliceAlong" +[89]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/LineSliceAlongTests.swift "LineSliceAlongTests" +[90]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/MidPoint.swift "MidPoint" +[91]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/MidPointTests.swift "MidPointTests" +[92]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/NearestPoint.swift "NearestPoint" +[93]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/NearestPointOnFeature.swift "NearestPointOnFeature" +[94]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/NearestPointOnLine.swift "NearestPointOnLine" +[95]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/NearestCoordinateOnLineTests.swift "NearestCoordinateOnLineTests" +[96]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/NearestPointToLine.swift "NearestPointToLine" +[97]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/PointOnFeature.swift "PointOnFeature" +[98]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/PointsWithinPolygon.swift "PointsWithinPolygon" +[99]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/PointToLineDistance.swift "PointToLineDistance" +[100]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/PointToLineDistanceTests.swift "PointToLineDistanceTests" +[101]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/PoleOfInaccessibility.swift "PoleOfInaccessibility" +[102]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Reverse.swift "Reverse" +[103]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/ReverseTests.swift "ReverseTests" +[104]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/RhumbBearing.swift "RhumbBearing" +[105]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/RhumbBearingTests.swift "RhumbBearingTests" +[106]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/RhumbDestination.swift "RhumbDestination" +[107]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/RhumbDestinationTests.swift "RhumbDestinationTests" +[108]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/RhumbDistance.swift "RhumbDistance" +[109]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/RhumbDistanceTests.swift "RhumbDistanceTests" +[110]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Simplify.swift "Simplify" +[111]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/SimplifyTests.swift "SimplifyTests" +[112]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/TransformCoordinates.swift "TransformCoordinates" +[113]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/TransformCoordinatesTests.swift "TransformCoordinatesTests" +[114]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/TransformRotate.swift "TransformRotate" +[115]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/TransformRotateTests.swift "TransformRotateTests" +[116]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/TransformScale.swift "TransformScale" +[117]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/TransformScaleTests.swift "TransformScaleTests" +[118]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/TransformTranslate.swift "TransformTranslate" +[119]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/TransformTranslateTests.swift "TransformTranslateTests" +[120]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Truncate.swift "Truncate" +[121]: https://github.com/Outdooractive/gis-tools/blob/main/Tests/GISToolsTests/Turf/TruncateTests.swift "TruncateTests" +[122]: https://github.com/Outdooractive/gis-tools/blob/main/Sources/GISTools/Turf/Union.swift "Union" +[123]: https://github.com/Outdooractive/mvt-tools [image-1]: https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FOutdooractive%2Fgis-tools%2Fbadge%3Ftype%3Dswift-versions -[image-2]: https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FOutdooractive%2Fgis-tools%2Fbadge%3Ftype%3Dplatforms +[image-2]: https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FOutdooractive%2Fgis-tools%2Fbadge%3Ftype%3Dplatforms \ No newline at end of file From 6c2c53271cc10eaa0d2d48045eb19cd7fb694e54 Mon Sep 17 00:00:00 2001 From: Thomas Rasch Date: Wed, 29 Nov 2023 10:14:22 +0100 Subject: [PATCH 6/8] Added LineString.evenlyDivided(segmentLength:) --- README.md | 100 +++++++++--------- Sources/GISTools/Algorithms/LineChunk.swift | 12 +++ .../Algorithms/LineChunkTests.swift | 15 +++ 3 files changed, 77 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 7ebc7b9..ba3c549 100644 --- a/README.md +++ b/README.md @@ -780,56 +780,56 @@ let mpp = MapTile.metersPerPixel(at: 15.0, latitude: 45.0) # Algorithms Hint: Most algorithms are optimized for EPSG:4326. Using other projections will have a performance penalty due to added projections. -| Name | Example | | Source/Tests | -| --------------------------- | ---------------------------------------------------------------------------------------- | --- | ---------------------------- | -| along | `let coordinate = lineString.coordinateAlong(distance: 100.0)` | | [Source][43] / [Tests][44] | -| area | `Polygon(…).area` | | [Source][45] | -| bearing | `Coordinate3D(…).bearing(to: Coordinate3D(…))` | | [Source][46] / [Tests][47] | -| boolean-clockwise | `Polygon(…).outerRing?.isClockwise` | | [Source][48] / [Tests][49] | -| boolean-crosses | TODO | | [Source][50] | -| boolean-intersects | TODO | | [Source][51] | -| boolean-overlap | `lineString1.isOverlapping(with: lineString2)` | | [Source][52] / [Tests][53] | -| boolean-parallel | `lineString1.isParallel(to: lineString2)` | | [Source][54] / [Tests][55] | -| boolean-point-in-polygon | `polygon.contains(Coordinate3D(…))` | | [Source][56] | -| boolean-point-on-line | `lineString.checkIsOnLine(Coordinate3D(…))` | | [Source][57] | -| boolean-valid | `anyGeometry.isValid` | | [Source][58] | -| bbox-clip | `let clipped = lineString.clipped(to: boundingBox)` | | [Source][59] / [Tests][60] | -| buffer | TODO | | [Source][61] | -| center/centroid/center-mean | `let center = polygon.center` | | [Source][62] | -| circle | `let circle = point.circle(radius: 5000.0)` | | [Source][63] / [Tests][64] | -| conversions/helpers | `let distance = GISTool.convert(length: 1.0, from: .miles, to: .meters)` | | [Source][65] | -| destination | `let destination = coordinate.destination(distance: 1000.0, bearing: 173.0)` | | [Source][66] / [Tests][67] | -| distance | `let distance = coordinate1.distance(from: coordinate2)` | | [Source][68] / [Tests][69] | -| flatten | `let featureCollection = anyGeometry.flattened` | | [Source][70] / [Tests][71] | -| frechetDistance | `let distance = lineString.frechetDistance(from: other)` | | [Source][72] / [Tests][73] | -| length | `let length = lineString.length` | | [Source][74] / [Tests][75] | -| line-arc | `let lineArc = point.lineArc(radius: 5000.0, bearing1: 20.0, bearing2: 60.0)` | | [Source][76] / [Tests][77] | -| line-chunk | `let chunks = lineString.chunked(segmentLength: 1000.0).lineStrings` | | [Source][78] / [Tests][79] | -| line-intersect | `let intersections = feature1.intersections(other: feature2)` | | [Source][80] / [Tests][81] | -| line-overlap | `let overlappingSegments = lineString1.overlappingSegments(with: lineString2)` | | [Source][82] / [Tests][83] | -| line-segments | `let segments = anyGeometry.lineSegments` | | [Source][84] / [Tests][85] | -| line-slice | `let slice = lineString.slice(start: Coordinate3D(…), end: Coordinate3D(…))` | | [Source][86] / [Tests][87] | -| line-slice-along | `let sliced = lineString.sliceAlong(startDistance: 50.0, stopDistance: 2000.0)` | | [Source][88] / [Tests][89] | -| midpoint | `let middle = coordinate1.midpoint(to: coordinate2)` | | [Source][90] / [Tests][91] | -| nearest-point | `let nearest = anyGeometry.nearestCoordinate(from: Coordinate3D(…))` | | [Source][92] | -| nearest-point-on-feature | `let nearest = anyGeometry. nearestCoordinateOnFeature(from: Coordinate3D(…))` | | [Source][93] | -| nearest-point-on-line | `let nearest = lineString.nearestCoordinateOnLine(from: Coordinate3D(…))?.coordinate` | | [Source][94] / [Tests][95] | -| nearest-point-to-line | `let nearest = lineString. nearestCoordinate(outOf: coordinates)` | | [Source][96] | -| point-on-feature | `let coordinate = anyGeometry.coordinateOnFeature` | | [Source][97] | -| points-within-polygon | `let within = polygon.coordinatesWithin(coordinates)` | | [Source][98] | -| point-to-line-distance | `let distance = lineString.distanceFrom(coordinate: Coordinate3D(…))` | | [Source][99] / [Tests][100] | -| pole-of-inaccessibility | TODO | | [Source][101] | -| reverse | `let lineStringReversed = lineString.reversed` | | [Source][102] / [Tests][103] | -| rhumb-bearing | `let bearing = start.rhumbBearing(to: end)` | | [Source][104] / [Tests][105] | -| rhumb-destination | `let destination = coordinate.rhumbDestination(distance: 1000.0, bearing: 0.0)` | | [Source][106] / [Tests][107] | -| rhumb-distance | `let distance = coordinate1.rhumbDistance(from: coordinate2)` | | [Source][108] / [Tests][109] | -| simplify | `let simplified = lineString. simplified(tolerance: 5.0, highQuality: false)` | | [Source][110] / [Tests][111] | -| transform-coordinates | `let transformed = anyGeometry.transformCoordinates({ $0 })` | | [Source][112] / [Tests][113] | -| transform-rotate | `let transformed = anyGeometry. transformedRotate(angle: 25.0, pivot: Coordinate3D(…))` | | [Source][114] / [Tests][115] | -| transform-scale | `let transformed = anyGeometry. transformedScale(factor: 2.5, anchor: .center)` | | [Source][116] / [Tests][117] | -| transform-translate | `let transformed = anyGeometry. transformedTranslate(distance: 1000.0, direction: 25.0)` | | [Source][118] / [Tests][119] | -| truncate | `let truncated = lineString.truncated(precision: 2, removeAltitude: true)` | | [Source][120] / [Tests][121] | -| union | TODO | | [Source][122] | +| Name | Example | | Source/Tests | +| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --- | ---------------------------- | +| along | `let coordinate = lineString.coordinateAlong(distance: 100.0)` | | [Source][43] / [Tests][44] | +| area | `Polygon(…).area` | | [Source][45] | +| bearing | `Coordinate3D(…).bearing(to: Coordinate3D(…))` | | [Source][46] / [Tests][47] | +| boolean-clockwise | `Polygon(…).outerRing?.isClockwise` | | [Source][48] / [Tests][49] | +| boolean-crosses | TODO | | [Source][50] | +| boolean-intersects | TODO | | [Source][51] | +| boolean-overlap | `lineString1.isOverlapping(with: lineString2)` | | [Source][52] / [Tests][53] | +| boolean-parallel | `lineString1.isParallel(to: lineString2)` | | [Source][54] / [Tests][55] | +| boolean-point-in-polygon | `polygon.contains(Coordinate3D(…))` | | [Source][56] | +| boolean-point-on-line | `lineString.checkIsOnLine(Coordinate3D(…))` | | [Source][57] | +| boolean-valid | `anyGeometry.isValid` | | [Source][58] | +| bbox-clip | `let clipped = lineString.clipped(to: boundingBox)` | | [Source][59] / [Tests][60] | +| buffer | TODO | | [Source][61] | +| center/centroid/center-mean | `let center = polygon.center` | | [Source][62] | +| circle | `let circle = point.circle(radius: 5000.0)` | | [Source][63] / [Tests][64] | +| conversions/helpers | `let distance = GISTool.convert(length: 1.0, from: .miles, to: .meters)` | | [Source][65] | +| destination | `let destination = coordinate.destination(distance: 1000.0, bearing: 173.0)` | | [Source][66] / [Tests][67] | +| distance | `let distance = coordinate1.distance(from: coordinate2)` | | [Source][68] / [Tests][69] | +| flatten | `let featureCollection = anyGeometry.flattened` | | [Source][70] / [Tests][71] | +| frechetDistance | `let distance = lineString.frechetDistance(from: other)` | | [Source][72] / [Tests][73] | +| length | `let length = lineString.length` | | [Source][74] / [Tests][75] | +| line-arc | `let lineArc = point.lineArc(radius: 5000.0, bearing1: 20.0, bearing2: 60.0)` | | [Source][76] / [Tests][77] | +| line-chunk | `let chunks = lineString.chunked(segmentLength: 1000.0).lineStrings` `let dividedLine = lineString.evenlyDivided(segmentLength: 1.0)` | | [Source][78] / [Tests][79] | +| line-intersect | `let intersections = feature1.intersections(other: feature2)` | | [Source][80] / [Tests][81] | +| line-overlap | `let overlappingSegments = lineString1.overlappingSegments(with: lineString2)` | | [Source][82] / [Tests][83] | +| line-segments | `let segments = anyGeometry.lineSegments` | | [Source][84] / [Tests][85] | +| line-slice | `let slice = lineString.slice(start: Coordinate3D(…), end: Coordinate3D(…))` | | [Source][86] / [Tests][87] | +| line-slice-along | `let sliced = lineString.sliceAlong(startDistance: 50.0, stopDistance: 2000.0)` | | [Source][88] / [Tests][89] | +| midpoint | `let middle = coordinate1.midpoint(to: coordinate2)` | | [Source][90] / [Tests][91] | +| nearest-point | `let nearest = anyGeometry.nearestCoordinate(from: Coordinate3D(…))` | | [Source][92] | +| nearest-point-on-feature | `let nearest = anyGeometry. nearestCoordinateOnFeature(from: Coordinate3D(…))` | | [Source][93] | +| nearest-point-on-line | `let nearest = lineString.nearestCoordinateOnLine(from: Coordinate3D(…))?.coordinate` | | [Source][94] / [Tests][95] | +| nearest-point-to-line | `let nearest = lineString. nearestCoordinate(outOf: coordinates)` | | [Source][96] | +| point-on-feature | `let coordinate = anyGeometry.coordinateOnFeature` | | [Source][97] | +| points-within-polygon | `let within = polygon.coordinatesWithin(coordinates)` | | [Source][98] | +| point-to-line-distance | `let distance = lineString.distanceFrom(coordinate: Coordinate3D(…))` | | [Source][99] / [Tests][100] | +| pole-of-inaccessibility | TODO | | [Source][101] | +| reverse | `let lineStringReversed = lineString.reversed` | | [Source][102] / [Tests][103] | +| rhumb-bearing | `let bearing = start.rhumbBearing(to: end)` | | [Source][104] / [Tests][105] | +| rhumb-destination | `let destination = coordinate.rhumbDestination(distance: 1000.0, bearing: 0.0)` | | [Source][106] / [Tests][107] | +| rhumb-distance | `let distance = coordinate1.rhumbDistance(from: coordinate2)` | | [Source][108] / [Tests][109] | +| simplify | `let simplified = lineString. simplified(tolerance: 5.0, highQuality: false)` | | [Source][110] / [Tests][111] | +| transform-coordinates | `let transformed = anyGeometry.transformCoordinates({ $0 })` | | [Source][112] / [Tests][113] | +| transform-rotate | `let transformed = anyGeometry. transformedRotate(angle: 25.0, pivot: Coordinate3D(…))` | | [Source][114] / [Tests][115] | +| transform-scale | `let transformed = anyGeometry. transformedScale(factor: 2.5, anchor: .center)` | | [Source][116] / [Tests][117] | +| transform-translate | `let transformed = anyGeometry. transformedTranslate(distance: 1000.0, direction: 25.0)` | | [Source][118] / [Tests][119] | +| truncate | `let truncated = lineString.truncated(precision: 2, removeAltitude: true)` | | [Source][120] / [Tests][121] | +| union | TODO | | [Source][122] | # Related packages Currently only one: diff --git a/Sources/GISTools/Algorithms/LineChunk.swift b/Sources/GISTools/Algorithms/LineChunk.swift index ee7eab3..88cd7bc 100644 --- a/Sources/GISTools/Algorithms/LineChunk.swift +++ b/Sources/GISTools/Algorithms/LineChunk.swift @@ -32,6 +32,18 @@ extension LineString { return MultiLineString(lineStrings) ?? MultiLineString() } + /// Divides a *LineString* into evenly spaced segments of a specified length. + /// If the line is shorter than the segment length then the original line is returned. + /// + /// - Parameter segmentLength: How long to make each segment, in meters + public func evenlyDivided(segmentLength: CLLocationDistance) -> LineString { + guard self.isValid, segmentLength > 0.0 else { + return LineString() + } + + return LineString(chunked(segmentLength: segmentLength).lineSegments) ?? LineString() + } + } extension MultiLineString { diff --git a/Tests/GISToolsTests/Algorithms/LineChunkTests.swift b/Tests/GISToolsTests/Algorithms/LineChunkTests.swift index b023524..ee335ac 100644 --- a/Tests/GISToolsTests/Algorithms/LineChunkTests.swift +++ b/Tests/GISToolsTests/Algorithms/LineChunkTests.swift @@ -39,4 +39,19 @@ final class LineChunkTests: XCTestCase { XCTAssertEqual(chunks[0], lineString) } + func testEvenlyDivided() { + let a = Coordinate3D.zero + let b = a.destination(distance: 100.0, bearing: 90.0) + let line = LineString(unchecked: [a, b]) + let dividedLine = line.evenlyDivided(segmentLength: 1.0) + + XCTAssertEqual(line.allCoordinates.count, 2) + XCTAssertEqual(dividedLine.allCoordinates.count, 101) + + for (first, second) in dividedLine.allCoordinates.overlappingPairs() { + guard let second else { break } + XCTAssertEqual(first.distance(from: second), 1.0, accuracy: 0.0001) + } + } + } From 09ef507fbaf785689b4433d9f27e123d8aa3d409 Mon Sep 17 00:00:00 2001 From: Thomas Rasch Date: Wed, 29 Nov 2023 10:21:25 +0100 Subject: [PATCH 7/8] Added segmentation --- .../GISTools/Algorithms/FrechetDistance.swift | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Sources/GISTools/Algorithms/FrechetDistance.swift b/Sources/GISTools/Algorithms/FrechetDistance.swift index e648b1d..5450e79 100644 --- a/Sources/GISTools/Algorithms/FrechetDistance.swift +++ b/Sources/GISTools/Algorithms/FrechetDistance.swift @@ -37,19 +37,25 @@ extension LineString { /// - Parameters: /// - from: The other geometry of equal type /// - distanceFunction: The algorithm to use for distance calculations - /// - tolerance: Affects the amount of simplification (in meters) + /// - segmentLength: Adds coordinates to the lines for improved matching (in meters) /// /// - Returns: The frechet distance between the to geometries public func frechetDistance( from other: LineString, distanceFunction: FrechetDistanceFunction = .haversine, - tolerance: CLLocationDistance? = nil) + segmentLength: CLLocationDistance? = nil) -> Double { - let other = other.projected(to: projection) + var firstLine = self + var secondLine = other.projected(to: projection) - let p = allCoordinates - let q = other.allCoordinates + if let segmentLength { + firstLine = firstLine.evenlyDivided(segmentLength: segmentLength) + secondLine = secondLine.evenlyDivided(segmentLength: segmentLength) + } + + let p = firstLine.allCoordinates + let q = secondLine.allCoordinates var ca: [Double] = Array(repeating: -1.0, count: p.count * q.count) From 199a6b0bcbf62097ae299c1544c70f34d892d935 Mon Sep 17 00:00:00 2001 From: Thomas Rasch Date: Mon, 4 Dec 2023 14:20:56 +0100 Subject: [PATCH 8/8] Cleanup --- Sources/GISTools/Algorithms/FrechetDistance.swift | 8 ++++---- Sources/GISTools/Algorithms/LineChunk.swift | 12 +++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Sources/GISTools/Algorithms/FrechetDistance.swift b/Sources/GISTools/Algorithms/FrechetDistance.swift index 5450e79..26db8ac 100644 --- a/Sources/GISTools/Algorithms/FrechetDistance.swift +++ b/Sources/GISTools/Algorithms/FrechetDistance.swift @@ -35,11 +35,11 @@ extension LineString { /// Fréchet distance between to geometries. /// /// - Parameters: - /// - from: The other geometry of equal type - /// - distanceFunction: The algorithm to use for distance calculations - /// - segmentLength: Adds coordinates to the lines for improved matching (in meters) + /// - from: The other geometry of equal type. + /// - distanceFunction: The algorithm to use for distance calculations. + /// - segmentLength: Adds coordinates to the lines for improved matching (in meters). /// - /// - Returns: The frechet distance between the to geometries + /// - Returns: The frechet distance between the two geometries. public func frechetDistance( from other: LineString, distanceFunction: FrechetDistanceFunction = .haversine, diff --git a/Sources/GISTools/Algorithms/LineChunk.swift b/Sources/GISTools/Algorithms/LineChunk.swift index 88cd7bc..6fb4d36 100644 --- a/Sources/GISTools/Algorithms/LineChunk.swift +++ b/Sources/GISTools/Algorithms/LineChunk.swift @@ -41,7 +41,7 @@ extension LineString { return LineString() } - return LineString(chunked(segmentLength: segmentLength).lineSegments) ?? LineString() + return LineString(chunked(segmentLength: segmentLength).lineSegments) ?? self } } @@ -56,6 +56,16 @@ extension MultiLineString { MultiLineString(lineStrings.flatMap({ $0.chunked(segmentLength: segmentLength).lineStrings })) ?? self } + /// Divides a *MultiLineString* into evenly spaced segments of a specified length. + /// If the line is shorter than the segment length then the original line is returned. + /// + /// - Parameter segmentLength: How long to make each segment, in meters + public func evenlyDivided(segmentLength: CLLocationDistance) -> MultiLineString { + MultiLineString(lineStrings.map({ + LineString($0.chunked(segmentLength: segmentLength).lineSegments) ?? $0 + })) ?? self + } + } extension Feature {