diff --git a/src/main/kotlin/com/terraformation/backend/tracking/model/Constants.kt b/src/main/kotlin/com/terraformation/backend/tracking/model/Constants.kt index 72171692ae4..fc1340a863c 100644 --- a/src/main/kotlin/com/terraformation/backend/tracking/model/Constants.kt +++ b/src/main/kotlin/com/terraformation/backend/tracking/model/Constants.kt @@ -9,7 +9,7 @@ const val HECTARES_SCALE = 1 val MAX_SITE_ENVELOPE_AREA_HA = BigDecimal(20000) /** Monitoring plot width and height in meters. */ -const val MONITORING_PLOT_SIZE: Double = 25.0 +const val MONITORING_PLOT_SIZE: Double = 30.0 /** Monitoring plot width and height in meters. */ const val MONITORING_PLOT_SIZE_INT = MONITORING_PLOT_SIZE.toInt() diff --git a/src/main/kotlin/com/terraformation/backend/tracking/model/PlantingZoneModel.kt b/src/main/kotlin/com/terraformation/backend/tracking/model/PlantingZoneModel.kt index d1f3abee05e..35301794635 100644 --- a/src/main/kotlin/com/terraformation/backend/tracking/model/PlantingZoneModel.kt +++ b/src/main/kotlin/com/terraformation/backend/tracking/model/PlantingZoneModel.kt @@ -298,14 +298,16 @@ data class PlantingZoneModel } /** - * Returns the monitoring plot that contains the center point of a shape, or null if the shape - * isn't in any plot. + * Returns the monitoring plot that is of the correct size and contains the center point of a + * shape, or null if the shape isn't in any plot. */ fun findMonitoringPlot(geometry: Geometry): MonitoringPlotModel? { val centroid = geometry.centroid return plantingSubzones.firstNotNullOfOrNull { subzone -> - subzone.monitoringPlots.firstOrNull { plot -> plot.boundary.contains(centroid) } + subzone.monitoringPlots.firstOrNull { plot -> + plot.boundary.contains(centroid) && plot.sizeMeters == MONITORING_PLOT_SIZE_INT + } } } diff --git a/src/test/kotlin/com/terraformation/backend/tracking/TrackingSearchTest.kt b/src/test/kotlin/com/terraformation/backend/tracking/TrackingSearchTest.kt index d67e677f084..60ec705ee0d 100644 --- a/src/test/kotlin/com/terraformation/backend/tracking/TrackingSearchTest.kt +++ b/src/test/kotlin/com/terraformation/backend/tracking/TrackingSearchTest.kt @@ -81,7 +81,8 @@ class TrackingSearchTest : DatabaseTest(), RunsAsUser { plantingCompletedTime = Instant.ofEpochSecond(1), name = "4") val monitoringPlotId7 = insertMonitoringPlot(boundary = monitoringPlotGeometry7) - val monitoringPlotId8 = insertMonitoringPlot(boundary = monitoringPlotGeometry8) + val monitoringPlotId8 = + insertMonitoringPlot(boundary = monitoringPlotGeometry8, sizeMeters = 25) val speciesId1 = insertSpecies() val speciesId2 = insertSpecies() @@ -314,7 +315,7 @@ class TrackingSearchTest : DatabaseTest(), RunsAsUser { "northeastLongitude" to "7", "northwestLatitude" to "8", "northwestLongitude" to "5", - "sizeMeters" to "25", + "sizeMeters" to "30", "southeastLatitude" to "6", "southeastLongitude" to "7", "southwestLatitude" to "6", @@ -326,7 +327,7 @@ class TrackingSearchTest : DatabaseTest(), RunsAsUser { "northeastLongitude" to "8", "northwestLatitude" to "9", "northwestLongitude" to "6", - "sizeMeters" to "25", + "sizeMeters" to "30", "southeastLatitude" to "7", "southeastLongitude" to "8", "southwestLatitude" to "7", @@ -362,7 +363,7 @@ class TrackingSearchTest : DatabaseTest(), RunsAsUser { "northeastLongitude" to "9", "northwestLatitude" to "10", "northwestLongitude" to "7", - "sizeMeters" to "25", + "sizeMeters" to "30", "southeastLatitude" to "8", "southeastLongitude" to "9", "southwestLatitude" to "8", diff --git a/src/test/kotlin/com/terraformation/backend/tracking/db/ObservationStoreTest.kt b/src/test/kotlin/com/terraformation/backend/tracking/db/ObservationStoreTest.kt index 7f15d64925f..f0bff5a2d6c 100644 --- a/src/test/kotlin/com/terraformation/backend/tracking/db/ObservationStoreTest.kt +++ b/src/test/kotlin/com/terraformation/backend/tracking/db/ObservationStoreTest.kt @@ -207,7 +207,7 @@ class ObservationStoreTest : DatabaseTest(), RunsAsUser { plantingSubzoneId = plantingSubzoneId1, plantingSubzoneName = "Z1-S1", plotName = "Z1-S1-1", - sizeMeters = 25, + sizeMeters = 30, ), AssignedPlotDetails( model = @@ -225,7 +225,7 @@ class ObservationStoreTest : DatabaseTest(), RunsAsUser { plantingSubzoneId = plantingSubzoneId1, plantingSubzoneName = "Z1-S1", plotName = "Z1-S1-2", - sizeMeters = 25, + sizeMeters = 30, ), AssignedPlotDetails( model = @@ -247,7 +247,7 @@ class ObservationStoreTest : DatabaseTest(), RunsAsUser { plantingSubzoneId = plantingSubzoneId2, plantingSubzoneName = "Z1-S2", plotName = "Z1-S2-1", - sizeMeters = 25, + sizeMeters = 30, )) val actual = store.fetchObservationPlotDetails(observationId) diff --git a/src/test/kotlin/com/terraformation/backend/tracking/db/plantingSiteStore/PlantingSiteStoreEnsurePermanentTest.kt b/src/test/kotlin/com/terraformation/backend/tracking/db/plantingSiteStore/PlantingSiteStoreEnsurePermanentTest.kt index 6c088156a61..3761064ec0e 100644 --- a/src/test/kotlin/com/terraformation/backend/tracking/db/plantingSiteStore/PlantingSiteStoreEnsurePermanentTest.kt +++ b/src/test/kotlin/com/terraformation/backend/tracking/db/plantingSiteStore/PlantingSiteStoreEnsurePermanentTest.kt @@ -76,13 +76,16 @@ internal class PlantingSiteStoreEnsurePermanentTest : PlantingSiteStoreTest() { @Test fun `can use temporary plot from previous observation as permanent plot`() { val gridOrigin = point(0) - val siteBoundary = Turtle(gridOrigin).makeMultiPolygon { rectangle(51, 26) } + val siteBoundary = + Turtle(gridOrigin).makeMultiPolygon { + rectangle(MONITORING_PLOT_SIZE * 2 + 1, MONITORING_PLOT_SIZE + 1) + } // Temporary plot is in the east half of the site. val existingPlotBoundary = Turtle(gridOrigin).makePolygon { - east(25) - square(25) + east(MONITORING_PLOT_SIZE) + square(MONITORING_PLOT_SIZE) } val plantingSiteId = insertPlantingSite(boundary = siteBoundary, gridOrigin = gridOrigin) diff --git a/src/test/kotlin/com/terraformation/backend/tracking/db/plantingSiteStore/PlantingSiteStoreReadTest.kt b/src/test/kotlin/com/terraformation/backend/tracking/db/plantingSiteStore/PlantingSiteStoreReadTest.kt index 522e31d0408..a3671ce282e 100644 --- a/src/test/kotlin/com/terraformation/backend/tracking/db/plantingSiteStore/PlantingSiteStoreReadTest.kt +++ b/src/test/kotlin/com/terraformation/backend/tracking/db/plantingSiteStore/PlantingSiteStoreReadTest.kt @@ -376,7 +376,7 @@ internal class PlantingSiteStoreReadTest : PlantingSiteStoreTest() { isAvailable = true, name = "1", fullName = "Z1-1-1", - sizeMeters = 25)), + sizeMeters = 30)), ))))) val allExpected = @@ -459,7 +459,7 @@ internal class PlantingSiteStoreReadTest : PlantingSiteStoreTest() { isAvailable = true, name = "1", fullName = "Z1-1-1", - sizeMeters = 25)), + sizeMeters = 30)), ))))) val actual = store.fetchSiteById(plantingSiteId, PlantingSiteDepth.Plot) diff --git a/src/test/kotlin/com/terraformation/backend/tracking/model/PlantingZoneModelTest.kt b/src/test/kotlin/com/terraformation/backend/tracking/model/PlantingZoneModelTest.kt index b5324092688..c31ce6ce216 100644 --- a/src/test/kotlin/com/terraformation/backend/tracking/model/PlantingZoneModelTest.kt +++ b/src/test/kotlin/com/terraformation/backend/tracking/model/PlantingZoneModelTest.kt @@ -158,24 +158,28 @@ class PlantingZoneModelTest { @Test fun `does not choose plots that lie partially outside subzone`() { - // Zone is 76 meters by 26 meters, split into two 38x26 subzones such that there are three - // plot locations but the middle one sits on the subzone boundary. Only subzone 1 is planted. - val subzone1Boundary = Turtle(siteOrigin).makeMultiPolygon { rectangle(38, 26) } + // Zone is three plots wide by one plot high, split horizontally into two equal-sized subzones + // such that there are three plot locations but the middle one sits on the subzone boundary. + // Only subzone 1 is planted. + val subzoneWidth = MONITORING_PLOT_SIZE * 1.5 + val subzoneHeight = MONITORING_PLOT_SIZE + 1 + val subzone1Boundary = + Turtle(siteOrigin).makeMultiPolygon { rectangle(subzoneWidth, subzoneHeight) } val subzone2Boundary = Turtle(siteOrigin).makeMultiPolygon { - east(38) - rectangle(38, 26) + east(subzoneWidth) + rectangle(subzoneWidth, subzoneHeight) } - val subzone1PlotBoundary = Turtle(siteOrigin).makePolygon { square(25) } + val subzone1PlotBoundary = Turtle(siteOrigin).makePolygon { square(MONITORING_PLOT_SIZE) } val bothSubzonesPlotBoundary = Turtle(siteOrigin).makePolygon { - east(25) - square(25) + east(MONITORING_PLOT_SIZE) + square(MONITORING_PLOT_SIZE) } val subzone2PlotBoundary = Turtle(siteOrigin).makePolygon { - east(50) - square(25) + east(MONITORING_PLOT_SIZE * 2) + square(MONITORING_PLOT_SIZE) } val model = @@ -567,9 +571,9 @@ class PlantingZoneModelTest { val targetArea = Turtle(siteOrigin).makePolygon { - east(Random.nextInt(edgeMeters - 51)) - north(Random.nextInt(edgeMeters - 51)) - square(51) + east(Random.nextInt(edgeMeters - 61)) + north(Random.nextInt(edgeMeters - 61)) + square(61) } val siteBoundary = geometryFactory.createMultiPolygon((triangles + targetArea).toTypedArray()) @@ -578,7 +582,7 @@ class PlantingZoneModelTest { boundary = siteBoundary, subzones = listOf(plantingSubzoneModel(boundary = siteBoundary, plots = emptyList()))) - val square = zone.findUnusedSquare(siteOrigin, 25) + val square = zone.findUnusedSquare(siteOrigin, 30) assertNotNull(square, "Unused square") if (square!!.intersection(targetArea).area < square.area * 0.99999) { @@ -591,9 +595,9 @@ class PlantingZoneModelTest { inner class FindUnusedSquares { @Test fun `excludes permanent monitoring plots`() { - // Boundary is a 51x26m square, and there is an existing plot in the southwestern 25x25m. - val siteBoundary = Turtle(siteOrigin).makeMultiPolygon { rectangle(51, 26) } - val existingPlotPolygon = Turtle(siteOrigin).makePolygon { square(25) } + // Boundary is a 61x31m square, and there is an existing plot in the southwestern 30x30m. + val siteBoundary = Turtle(siteOrigin).makeMultiPolygon { rectangle(61, 31) } + val existingPlotPolygon = Turtle(siteOrigin).makePolygon { square(30) } val zone = plantingZoneModel( @@ -609,15 +613,15 @@ class PlantingZoneModelTest { val expected = Turtle(siteOrigin).makePolygon { - east(25) - square(25) + east(30) + square(30) } repeat(20) { val actual = zone.findUnusedSquares( gridOrigin = siteOrigin, - sizeMeters = 25, + sizeMeters = 30, count = 1, excludeAllPermanentPlots = true) @@ -658,13 +662,13 @@ class PlantingZoneModelTest { return Turtle(siteOrigin).makePolygon { // Subzone corner - east(subzoneId * 50) - north(25 * (plotNumber / 2)) + east(subzoneId * MONITORING_PLOT_SIZE * 2) + north(MONITORING_PLOT_SIZE * (plotNumber / 2)) if (plotNumber.rem(2) == 1) { - east(25) + east(MONITORING_PLOT_SIZE) } - square(25) + square(MONITORING_PLOT_SIZE) } } @@ -682,7 +686,7 @@ class PlantingZoneModelTest { name = "name", permanentCluster = permanentCluster, permanentClusterSubplot = if (permanentCluster != null) 1 else null, - sizeMeters = 25, + sizeMeters = MONITORING_PLOT_SIZE_INT, ) } @@ -703,27 +707,27 @@ class PlantingZoneModelTest { */ private fun plantingSubzoneBoundary(id: Int, numPlots: Int): MultiPolygon { return Turtle(siteOrigin).makeMultiPolygon { - east(id * 50) + east(id * MONITORING_PLOT_SIZE * 2) val southwest = currentPosition // Figure out the northwest corner's location. - north(((numPlots + 1) / 2) * 25) + north(((numPlots + 1) / 2) * MONITORING_PLOT_SIZE) val northwest = currentPosition moveTo(southwest) startDrawing() - east(50) - north((numPlots / 2) * 25) + east(MONITORING_PLOT_SIZE * 2) + north((numPlots / 2) * MONITORING_PLOT_SIZE) if (numPlots.and(1) == 0) { // Even number of plots; the boundary is a plain rectangle. moveTo(northwest) } else { // Odd number of plots; space for the last plot is on the west half of the northern edge. - west(25) - north(25) + west(MONITORING_PLOT_SIZE) + north(MONITORING_PLOT_SIZE) moveTo(northwest) } }