From 20065d968b1f264a13110b457eff43a7146fbf7c Mon Sep 17 00:00:00 2001 From: Willy Marten Date: Thu, 5 Oct 2023 19:11:28 +0200 Subject: [PATCH 1/3] Ported turf grids squareGrid function --- .../io/github/dellisd/spatialk/turf/Grids.kt | 61 +++++++++++++ .../dellisd/spatialk/turf/Measurement.kt | 2 +- .../github/dellisd/spatialk/turf/GridsTest.kt | 88 +++++++++++++++++++ turf/src/commonTest/resources/grids/bbox.json | 33 +++++++ 4 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Grids.kt create mode 100644 turf/src/commonTest/kotlin/io/github/dellisd/spatialk/turf/GridsTest.kt create mode 100644 turf/src/commonTest/resources/grids/bbox.json diff --git a/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Grids.kt b/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Grids.kt new file mode 100644 index 00000000..5b26e25f --- /dev/null +++ b/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Grids.kt @@ -0,0 +1,61 @@ +package io.github.dellisd.spatialk.turf + +import io.github.dellisd.spatialk.geojson.BoundingBox +import io.github.dellisd.spatialk.geojson.Feature +import io.github.dellisd.spatialk.geojson.FeatureCollection +import io.github.dellisd.spatialk.geojson.Polygon +import io.github.dellisd.spatialk.geojson.Position +import kotlin.math.abs +import kotlin.math.floor + +/** + * Creates a rectangle grid within a [BoundingBox]. + * + * @param [BoundingBox] bbox extent + * @param double cellWidth of each cell, in units + * @param double cellHeight of each cell, in units + * @param units The unit of measurement of the cellSide length + * @returns [FeatureCollection] a grid of polygons + */ +@ExperimentalTurfApi +fun rectangleGrid(bbox: BoundingBox, cellWidth: Double, cellHeight: Double, units: Units = Units.Kilometers): FeatureCollection { + val featureList = mutableListOf() + val west = bbox.southwest.longitude + val south = bbox.southwest.latitude + val east = bbox.northeast.longitude + val north = bbox.northeast.latitude + + val bboxWidth = east - west + val cellWidthDeg = convertLength(cellWidth, units, Units.Degrees) + + val bboxHeight = north - south; + val cellHeightDeg = convertLength(cellHeight, units, Units.Degrees); + + val columns = floor(abs(bboxWidth) / cellWidthDeg) + val rows = floor(abs(bboxHeight) / cellHeightDeg) + + val deltaX = (bboxWidth - columns * cellWidthDeg) / 2 + val deltaY = (bboxHeight - rows * cellHeightDeg) / 2 + + var currentX = west + deltaX + repeat (columns.toInt()) { + var currentY = south + deltaY + repeat (rows.toInt()) { + val positions = mutableListOf().apply { + add(Position(currentX, currentY)) + add(Position(currentX, currentY + cellHeightDeg)) + add(Position(currentX + cellWidthDeg, currentY + cellHeightDeg)) + add(Position(currentX + cellWidthDeg, currentY)) + add(Position(currentX, currentY)) + } + mutableListOf>().apply { + add(positions) + }.also { + featureList.add(Feature(Polygon(it))) + } + currentY += cellHeightDeg; + } + currentX += cellWidthDeg; + } + return FeatureCollection(featureList) +} \ No newline at end of file diff --git a/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Measurement.kt b/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Measurement.kt index 7b59e5ab..9621c1d2 100644 --- a/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Measurement.kt +++ b/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Measurement.kt @@ -243,7 +243,7 @@ fun bbox(feature: Feature): BoundingBox? = computeBbox(feature.coordAll() ?: emp fun bbox(featureCollection: FeatureCollection): BoundingBox = computeBbox(featureCollection.coordAll()) @Suppress("MagicNumber") -private fun computeBbox(coordinates: List): BoundingBox { +fun computeBbox(coordinates: List): BoundingBox { val result = doubleArrayOf( Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, diff --git a/turf/src/commonTest/kotlin/io/github/dellisd/spatialk/turf/GridsTest.kt b/turf/src/commonTest/kotlin/io/github/dellisd/spatialk/turf/GridsTest.kt new file mode 100644 index 00000000..22325ed8 --- /dev/null +++ b/turf/src/commonTest/kotlin/io/github/dellisd/spatialk/turf/GridsTest.kt @@ -0,0 +1,88 @@ +package io.github.dellisd.spatialk.turf + +import io.github.dellisd.spatialk.geojson.BoundingBox +import io.github.dellisd.spatialk.geojson.FeatureCollection +import io.github.dellisd.spatialk.geojson.Polygon +import io.github.dellisd.spatialk.geojson.Position +import io.github.dellisd.spatialk.turf.utils.readResource +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class GridsTest { + + private lateinit var box: BoundingBox + + @BeforeTest + fun before() { + Polygon.fromJson(readResource("grids/bbox.json")).also { + box = computeBbox(it.coordinates[0]) + } + } + @OptIn(ExperimentalTurfApi::class) + @Test + fun testRectangleGrid() { + rectangleGrid(bbox = box, cellWidth = 200.0, cellHeight = 200.0, units = Units.Meters).also { + verifyValidGrid(it) + } + } + + @OptIn(ExperimentalTurfApi::class) + @Test + fun testRectangleGridSameCellSizeButDifferentUnitWillHaveSameResult() { + rectangleGrid(bbox = box, cellWidth = 0.2, cellHeight = 0.2, units = Units.Kilometers).also { + verifyValidGrid(it) + } + } + + @OptIn(ExperimentalTurfApi::class) + @Test + fun defaultUnitsValueIsKilometers() { + rectangleGrid(bbox = box, cellWidth = 0.2, cellHeight = 0.2).also { + verifyValidGrid(it) + } + } + + @OptIn(ExperimentalTurfApi::class) + private fun verifyValidGrid(grid: FeatureCollection) { + assertEquals(16, grid.features.size) + val expectedFistItem = mutableListOf( + Position(13.170147683370761, 52.515969323342695), + Position(13.170147683370761, 52.517765865), + Position(13.17194422502807, 52.517765865), + Position(13.17194422502807, 52.515969323342695), + Position(13.170147683370761, 52.515969323342695)) + assertEquals(expectedFistItem, grid.features.first().geometry!!.coordAll()) + val expectedLastItem = mutableListOf( + Position(13.18272347497193, 52.517765865), + Position(13.18272347497193, 52.51956240665731), + Position(13.18452001662924, 52.51956240665731), + Position(13.18452001662924, 52.517765865), + Position(13.18272347497193, 52.517765865)) + assertEquals(expectedLastItem, grid.features.last().geometry!!.coordAll()) + } + + @OptIn(ExperimentalTurfApi::class) + @Test + fun cellSizeBiggerThanBboxExtendLeadIntoEmptyGrid() { + rectangleGrid(bbox = box, cellWidth = 2000.0, cellHeight = 2000.0, units = Units.Meters).also { + assertEquals(0, it.features.size) + } + } + + @OptIn(ExperimentalTurfApi::class) + @Test + fun smallerCellSizeWillOutputMoreCellsInGrid() { + rectangleGrid(bbox = box, cellWidth = 0.1, cellHeight = 0.1).also { + assertEquals(85, it.features.size) + } + } + + @OptIn(ExperimentalTurfApi::class) + @Test + fun increasedCellSizeWillOutputLessCellsInGrid() { + rectangleGrid(bbox = box, cellWidth = 0.3, cellHeight = 0.3).also { + assertEquals(5, it.features.size) + } + } +} \ No newline at end of file diff --git a/turf/src/commonTest/resources/grids/bbox.json b/turf/src/commonTest/resources/grids/bbox.json new file mode 100644 index 00000000..029dcf06 --- /dev/null +++ b/turf/src/commonTest/resources/grids/bbox.json @@ -0,0 +1,33 @@ +{ + "type": "Polygon", + "coordinates": [ + [ + [ + 13.16949919, + 52.51513922 + ], + [ + 13.16949919, + 52.52039251 + ], + [ + 13.18516851, + 52.52039251 + ], + [ + 13.18516851, + 52.51513922 + ], + [ + 13.16949919, + 52.51513922 + ] + ] + ], + "crs": { + "type": "name", + "properties": { + "name": "EPSG:3857" + } + } +} \ No newline at end of file From 6f129185b4d5fb187780e9b9988593e3b8b45902 Mon Sep 17 00:00:00 2001 From: Derek Ellis Date: Mon, 12 Feb 2024 15:16:13 -0800 Subject: [PATCH 2/3] Apply suggestions from code review --- .../commonMain/kotlin/io/github/dellisd/spatialk/turf/Grids.kt | 2 +- .../kotlin/io/github/dellisd/spatialk/turf/GridsTest.kt | 2 +- turf/src/commonTest/resources/grids/bbox.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Grids.kt b/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Grids.kt index 5b26e25f..21206fcd 100644 --- a/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Grids.kt +++ b/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Grids.kt @@ -58,4 +58,4 @@ fun rectangleGrid(bbox: BoundingBox, cellWidth: Double, cellHeight: Double, unit currentX += cellWidthDeg; } return FeatureCollection(featureList) -} \ No newline at end of file +} diff --git a/turf/src/commonTest/kotlin/io/github/dellisd/spatialk/turf/GridsTest.kt b/turf/src/commonTest/kotlin/io/github/dellisd/spatialk/turf/GridsTest.kt index 22325ed8..f8cabbd7 100644 --- a/turf/src/commonTest/kotlin/io/github/dellisd/spatialk/turf/GridsTest.kt +++ b/turf/src/commonTest/kotlin/io/github/dellisd/spatialk/turf/GridsTest.kt @@ -85,4 +85,4 @@ class GridsTest { assertEquals(5, it.features.size) } } -} \ No newline at end of file +} diff --git a/turf/src/commonTest/resources/grids/bbox.json b/turf/src/commonTest/resources/grids/bbox.json index 029dcf06..02ce1801 100644 --- a/turf/src/commonTest/resources/grids/bbox.json +++ b/turf/src/commonTest/resources/grids/bbox.json @@ -30,4 +30,4 @@ "name": "EPSG:3857" } } -} \ No newline at end of file +} From 4290ae89ac289477dfdfb047d2ef2af625d87feb Mon Sep 17 00:00:00 2001 From: Willy Marten Date: Thu, 15 Feb 2024 22:01:22 +0100 Subject: [PATCH 3/3] Fixed MaxLineLength detect issue --- .../kotlin/io/github/dellisd/spatialk/turf/Grids.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Grids.kt b/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Grids.kt index 21206fcd..63b76b85 100644 --- a/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Grids.kt +++ b/turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Grids.kt @@ -18,7 +18,12 @@ import kotlin.math.floor * @returns [FeatureCollection] a grid of polygons */ @ExperimentalTurfApi -fun rectangleGrid(bbox: BoundingBox, cellWidth: Double, cellHeight: Double, units: Units = Units.Kilometers): FeatureCollection { +fun rectangleGrid( + bbox: BoundingBox, + cellWidth: Double, + cellHeight: Double, + units: Units = Units.Kilometers +): FeatureCollection { val featureList = mutableListOf() val west = bbox.southwest.longitude val south = bbox.southwest.latitude