Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grids - rectangle grid #121

Merged
merged 3 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<Feature>()
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<Position>().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<List<Position>>().apply {
add(positions)
}.also {
featureList.add(Feature(Polygon(it)))
}
currentY += cellHeightDeg;
}
currentX += cellWidthDeg;
}
return FeatureCollection(featureList)
}
dellisd marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -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<Position>): BoundingBox {
fun computeBbox(coordinates: List<Position>): BoundingBox {
val result = doubleArrayOf(
Double.POSITIVE_INFINITY,
Double.POSITIVE_INFINITY,
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
dellisd marked this conversation as resolved.
Show resolved Hide resolved
33 changes: 33 additions & 0 deletions turf/src/commonTest/resources/grids/bbox.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
dellisd marked this conversation as resolved.
Show resolved Hide resolved