Skip to content

Commit e3fedb4

Browse files
P72Bdellisd
andauthored
Grids - rectangle grid (#121)
* Ported turf grids squareGrid function * Apply suggestions from code review * Fixed MaxLineLength detect issue --------- Co-authored-by: Derek Ellis <[email protected]>
1 parent 4c738e1 commit e3fedb4

File tree

4 files changed

+188
-1
lines changed

4 files changed

+188
-1
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package io.github.dellisd.spatialk.turf
2+
3+
import io.github.dellisd.spatialk.geojson.BoundingBox
4+
import io.github.dellisd.spatialk.geojson.Feature
5+
import io.github.dellisd.spatialk.geojson.FeatureCollection
6+
import io.github.dellisd.spatialk.geojson.Polygon
7+
import io.github.dellisd.spatialk.geojson.Position
8+
import kotlin.math.abs
9+
import kotlin.math.floor
10+
11+
/**
12+
* Creates a rectangle grid within a [BoundingBox].
13+
*
14+
* @param [BoundingBox] bbox extent
15+
* @param double cellWidth of each cell, in units
16+
* @param double cellHeight of each cell, in units
17+
* @param units The unit of measurement of the cellSide length
18+
* @returns [FeatureCollection] a grid of polygons
19+
*/
20+
@ExperimentalTurfApi
21+
fun rectangleGrid(
22+
bbox: BoundingBox,
23+
cellWidth: Double,
24+
cellHeight: Double,
25+
units: Units = Units.Kilometers
26+
): FeatureCollection {
27+
val featureList = mutableListOf<Feature>()
28+
val west = bbox.southwest.longitude
29+
val south = bbox.southwest.latitude
30+
val east = bbox.northeast.longitude
31+
val north = bbox.northeast.latitude
32+
33+
val bboxWidth = east - west
34+
val cellWidthDeg = convertLength(cellWidth, units, Units.Degrees)
35+
36+
val bboxHeight = north - south;
37+
val cellHeightDeg = convertLength(cellHeight, units, Units.Degrees);
38+
39+
val columns = floor(abs(bboxWidth) / cellWidthDeg)
40+
val rows = floor(abs(bboxHeight) / cellHeightDeg)
41+
42+
val deltaX = (bboxWidth - columns * cellWidthDeg) / 2
43+
val deltaY = (bboxHeight - rows * cellHeightDeg) / 2
44+
45+
var currentX = west + deltaX
46+
repeat (columns.toInt()) {
47+
var currentY = south + deltaY
48+
repeat (rows.toInt()) {
49+
val positions = mutableListOf<Position>().apply {
50+
add(Position(currentX, currentY))
51+
add(Position(currentX, currentY + cellHeightDeg))
52+
add(Position(currentX + cellWidthDeg, currentY + cellHeightDeg))
53+
add(Position(currentX + cellWidthDeg, currentY))
54+
add(Position(currentX, currentY))
55+
}
56+
mutableListOf<List<Position>>().apply {
57+
add(positions)
58+
}.also {
59+
featureList.add(Feature(Polygon(it)))
60+
}
61+
currentY += cellHeightDeg;
62+
}
63+
currentX += cellWidthDeg;
64+
}
65+
return FeatureCollection(featureList)
66+
}

turf/src/commonMain/kotlin/io/github/dellisd/spatialk/turf/Measurement.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ fun bbox(feature: Feature): BoundingBox = computeBbox(feature.coordAll() ?: empt
243243
fun bbox(featureCollection: FeatureCollection): BoundingBox = computeBbox(featureCollection.coordAll())
244244

245245
@Suppress("MagicNumber")
246-
private fun computeBbox(coordinates: List<Position>): BoundingBox {
246+
fun computeBbox(coordinates: List<Position>): BoundingBox {
247247
val result = doubleArrayOf(
248248
Double.POSITIVE_INFINITY,
249249
Double.POSITIVE_INFINITY,
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package io.github.dellisd.spatialk.turf
2+
3+
import io.github.dellisd.spatialk.geojson.BoundingBox
4+
import io.github.dellisd.spatialk.geojson.FeatureCollection
5+
import io.github.dellisd.spatialk.geojson.Polygon
6+
import io.github.dellisd.spatialk.geojson.Position
7+
import io.github.dellisd.spatialk.turf.utils.readResource
8+
import kotlin.test.BeforeTest
9+
import kotlin.test.Test
10+
import kotlin.test.assertEquals
11+
12+
class GridsTest {
13+
14+
private lateinit var box: BoundingBox
15+
16+
@BeforeTest
17+
fun before() {
18+
Polygon.fromJson(readResource("grids/bbox.json")).also {
19+
box = computeBbox(it.coordinates[0])
20+
}
21+
}
22+
@OptIn(ExperimentalTurfApi::class)
23+
@Test
24+
fun testRectangleGrid() {
25+
rectangleGrid(bbox = box, cellWidth = 200.0, cellHeight = 200.0, units = Units.Meters).also {
26+
verifyValidGrid(it)
27+
}
28+
}
29+
30+
@OptIn(ExperimentalTurfApi::class)
31+
@Test
32+
fun testRectangleGridSameCellSizeButDifferentUnitWillHaveSameResult() {
33+
rectangleGrid(bbox = box, cellWidth = 0.2, cellHeight = 0.2, units = Units.Kilometers).also {
34+
verifyValidGrid(it)
35+
}
36+
}
37+
38+
@OptIn(ExperimentalTurfApi::class)
39+
@Test
40+
fun defaultUnitsValueIsKilometers() {
41+
rectangleGrid(bbox = box, cellWidth = 0.2, cellHeight = 0.2).also {
42+
verifyValidGrid(it)
43+
}
44+
}
45+
46+
@OptIn(ExperimentalTurfApi::class)
47+
private fun verifyValidGrid(grid: FeatureCollection) {
48+
assertEquals(16, grid.features.size)
49+
val expectedFistItem = mutableListOf(
50+
Position(13.170147683370761, 52.515969323342695),
51+
Position(13.170147683370761, 52.517765865),
52+
Position(13.17194422502807, 52.517765865),
53+
Position(13.17194422502807, 52.515969323342695),
54+
Position(13.170147683370761, 52.515969323342695))
55+
assertEquals(expectedFistItem, grid.features.first().geometry!!.coordAll())
56+
val expectedLastItem = mutableListOf(
57+
Position(13.18272347497193, 52.517765865),
58+
Position(13.18272347497193, 52.51956240665731),
59+
Position(13.18452001662924, 52.51956240665731),
60+
Position(13.18452001662924, 52.517765865),
61+
Position(13.18272347497193, 52.517765865))
62+
assertEquals(expectedLastItem, grid.features.last().geometry!!.coordAll())
63+
}
64+
65+
@OptIn(ExperimentalTurfApi::class)
66+
@Test
67+
fun cellSizeBiggerThanBboxExtendLeadIntoEmptyGrid() {
68+
rectangleGrid(bbox = box, cellWidth = 2000.0, cellHeight = 2000.0, units = Units.Meters).also {
69+
assertEquals(0, it.features.size)
70+
}
71+
}
72+
73+
@OptIn(ExperimentalTurfApi::class)
74+
@Test
75+
fun smallerCellSizeWillOutputMoreCellsInGrid() {
76+
rectangleGrid(bbox = box, cellWidth = 0.1, cellHeight = 0.1).also {
77+
assertEquals(85, it.features.size)
78+
}
79+
}
80+
81+
@OptIn(ExperimentalTurfApi::class)
82+
@Test
83+
fun increasedCellSizeWillOutputLessCellsInGrid() {
84+
rectangleGrid(bbox = box, cellWidth = 0.3, cellHeight = 0.3).also {
85+
assertEquals(5, it.features.size)
86+
}
87+
}
88+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"type": "Polygon",
3+
"coordinates": [
4+
[
5+
[
6+
13.16949919,
7+
52.51513922
8+
],
9+
[
10+
13.16949919,
11+
52.52039251
12+
],
13+
[
14+
13.18516851,
15+
52.52039251
16+
],
17+
[
18+
13.18516851,
19+
52.51513922
20+
],
21+
[
22+
13.16949919,
23+
52.51513922
24+
]
25+
]
26+
],
27+
"crs": {
28+
"type": "name",
29+
"properties": {
30+
"name": "EPSG:3857"
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)