diff --git a/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Transformation.kt b/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Transformation.kt index 7d39438..c80b2d4 100644 --- a/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Transformation.kt +++ b/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Transformation.kt @@ -1,6 +1,8 @@ package io.github.dellisd.spatialk.turf import io.github.dellisd.spatialk.geojson.LineString +import io.github.dellisd.spatialk.geojson.Point +import io.github.dellisd.spatialk.geojson.Polygon import io.github.dellisd.spatialk.geojson.Position /** @@ -14,7 +16,7 @@ import io.github.dellisd.spatialk.geojson.Position * @param sharpness a measure of how curvy the path should be between splines * @return A [LineString] containing a curved line around the positions of the input line */ -@OptIn(ExperimentalTurfApi::class) +@ExperimentalTurfApi public fun bezierSpline(line: LineString, duration: Int = 10_000, sharpness: Double = 0.85): LineString = LineString(bezierSpline(line.coordAll(), duration, sharpness)) @@ -134,3 +136,26 @@ public fun bezierSpline(coords: List, duration: Int = 10_000, sharpnes return positions } + +/** + * Takes a [Point] and calculates the circle polygon given a radius in degrees, radians, miles, or kilometers; and steps + * for precision. + * + * @param center center point of circle + * @param radius radius of the circle defined in [units] + * @param steps number of steps, must be at least four. Default is 64 + * @param units unit of [radius], default is [Units.Kilometers] + */ +@ExperimentalTurfApi +public fun circle(center: Point, radius: Double, steps: Int = 64, units: Units = Units.Kilometers): Polygon { + require(steps >= 4) { "circle needs to have four or more coordinates." } + require(radius > 0) { "radius must be a positive value" } + val coordinates = (0..steps).map { step -> + destination(center.coordinates, radius, (step * -360) / steps.toDouble(), units) + } + val ring = coordinates.plus(coordinates.first()) + return Polygon( + coordinates = listOf(ring), + bbox = computeBbox(ring) + ) +} \ No newline at end of file diff --git a/turf/src/commonTest/kotlin/io/github/dellisd/spatialk/turf/TransformationTest.kt b/turf/src/commonTest/kotlin/io/github/dellisd/spatialk/turf/TransformationTest.kt index 2843f1e..9e09c25 100644 --- a/turf/src/commonTest/kotlin/io/github/dellisd/spatialk/turf/TransformationTest.kt +++ b/turf/src/commonTest/kotlin/io/github/dellisd/spatialk/turf/TransformationTest.kt @@ -1,8 +1,12 @@ package io.github.dellisd.spatialk.turf import io.github.dellisd.spatialk.geojson.Feature +import io.github.dellisd.spatialk.geojson.FeatureCollection import io.github.dellisd.spatialk.geojson.LineString +import io.github.dellisd.spatialk.geojson.Point import io.github.dellisd.spatialk.turf.utils.readResource +import kotlinx.serialization.json.double +import kotlinx.serialization.json.jsonPrimitive import kotlin.test.Test import kotlin.test.assertEquals @@ -37,4 +41,19 @@ class TransformationTest { assertEquals(expectedOut.geometry, bezierSpline(feature.geometry as LineString)) } + + @Test + fun testCircle() { + val point = Feature.fromJson(readResource("transformation/circle/in/circle1.json")) + val expectedOut = FeatureCollection.fromJson(readResource("transformation/circle/out/circle1.json")) + + val (_, expectedCircle) = expectedOut.features + + val circle = circle( + center = point.geometry as Point, + radius = point.properties["radius"]?.jsonPrimitive?.double ?: 0.0, + ) + + assertEquals(expectedCircle.geometry, circle) + } } diff --git a/turf/src/commonTest/resources/transformation/circle/in/circle1.json b/turf/src/commonTest/resources/transformation/circle/in/circle1.json new file mode 100644 index 0000000..119ffc0 --- /dev/null +++ b/turf/src/commonTest/resources/transformation/circle/in/circle1.json @@ -0,0 +1,10 @@ +{ + "type": "Feature", + "properties": { + "radius": 5 + }, + "geometry": { + "type": "Point", + "coordinates": [-75.343, 39.984] + } +} diff --git a/turf/src/commonTest/resources/transformation/circle/out/circle1.json b/turf/src/commonTest/resources/transformation/circle/out/circle1.json new file mode 100644 index 0000000..755f544 --- /dev/null +++ b/turf/src/commonTest/resources/transformation/circle/out/circle1.json @@ -0,0 +1,299 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + -75.343, + 39.984 + ] + }, + "properties": { + "radius": 5 + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "bbox": [ + -75.40168521055865, + 39.93903398181377, + -75.28431478944134, + 40.02896601818623 + ], + "coordinates": [ + [ + [ + -75.343, + 40.02896601818623 + ], + [ + -75.34875592857028, + 40.02874935221753 + ], + [ + -75.35445631564411, + 40.02810144502913 + ], + [ + -75.36004615881171, + 40.02702854848104 + ], + [ + -75.36547152848289, + 40.0255410148978 + ], + [ + -75.37068009096993, + 40.02365319660857 + ], + [ + -75.37562161573996, + 40.02138330670212 + ], + [ + -75.38024846182591, + 40.01875324237723 + ], + [ + -75.38451603860885, + 40.01578837263805 + ], + [ + -75.38838323645888, + 40.01251729243424 + ], + [ + -75.39181282304071, + 40.00897154567282 + ], + [ + -75.39477180145282, + 40.00518531983158 + ], + [ + -75.39723172676842, + 40.00119511517658 + ], + [ + -75.3991689779784, + 39.99703939182863 + ], + [ + -75.40056498279498, + 39.99275819813223 + ], + [ + -75.40140639325509, + 39.98839278395333 + ], + [ + -75.40168521055865, + 39.983985202669075 + ], + [ + -75.40139885808249, + 39.9795779057111 + ], + [ + -75.40055020202162, + 39.97521333358458 + ], + [ + -75.39914751961999, + 39.970933507307144 + ], + [ + -75.3972044154561, + 39.966779624195745 + ], + [ + -75.39473968674338, + 39.96279166187577 + ], + [ + -75.39177713908293, + 39.95900799429653 + ], + [ + -75.38834535456607, + 39.95546502341165 + ], + [ + -75.38447741456031, + 39.9521968300236 + ], + [ + -75.38021057992215, + 39.94923484710077 + ], + [ + -75.37558593176193, + 39.94660755865401 + ], + [ + -75.37064797623403, + 39.94434022701197 + ], + [ + -75.36544421714193, + 39.942454651060615 + ], + [ + -75.36002470042686, + 39.94096895771676 + ], + [ + -75.35444153485052, + 39.939897428590044 + ], + [ + -75.34874839338673, + 39.93925036345563 + ], + [ + -75.343, + 39.93903398181377 + ], + [ + -75.33725160661326, + 39.93925036345563 + ], + [ + -75.33155846514948, + 39.939897428590044 + ], + [ + -75.32597529957314, + 39.94096895771676 + ], + [ + -75.32055578285807, + 39.942454651060615 + ], + [ + -75.31535202376598, + 39.94434022701197 + ], + [ + -75.31041406823807, + 39.94660755865401 + ], + [ + -75.30578942007786, + 39.94923484710077 + ], + [ + -75.3015225854397, + 39.9521968300236 + ], + [ + -75.29765464543392, + 39.95546502341165 + ], + [ + -75.29422286091707, + 39.95900799429653 + ], + [ + -75.29126031325661, + 39.96279166187577 + ], + [ + -75.2887955845439, + 39.966779624195745 + ], + [ + -75.28685248038002, + 39.970933507307144 + ], + [ + -75.28544979797837, + 39.97521333358458 + ], + [ + -75.2846011419175, + 39.9795779057111 + ], + [ + -75.28431478944134, + 39.983985202669075 + ], + [ + -75.28459360674492, + 39.98839278395333 + ], + [ + -75.28543501720503, + 39.99275819813223 + ], + [ + -75.2868310220216, + 39.99703939182863 + ], + [ + -75.28876827323158, + 40.00119511517658 + ], + [ + -75.29122819854717, + 40.00518531983158 + ], + [ + -75.29418717695928, + 40.00897154567282 + ], + [ + -75.29761676354111, + 40.01251729243424 + ], + [ + -75.30148396139116, + 40.01578837263805 + ], + [ + -75.30575153817409, + 40.01875324237723 + ], + [ + -75.31037838426005, + 40.02138330670212 + ], + [ + -75.31531990903008, + 40.02365319660857 + ], + [ + -75.32052847151711, + 40.0255410148978 + ], + [ + -75.32595384118828, + 40.02702854848104 + ], + [ + -75.33154368435589, + 40.02810144502913 + ], + [ + -75.33724407142972, + 40.02874935221753 + ], + [ + -75.343, + 40.02896601818623 + ], + [ + -75.343, + 40.02896601818623 + ] + ] + ] + }, + "properties": {} + } + ] +}