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

Features/6008 grid bug fix #164

Merged
merged 2 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
Expand Up @@ -20,8 +20,6 @@
@EnableRetry
@EnableAsync
public class IndexerConfig {
@Value("${app.geometry.gridLandSize:10.0}")
protected double gridSize;

@Value("${app.geometry.enableGridSpatialExtents:false}")
protected boolean girdSpatialExtents;
Expand All @@ -31,11 +29,8 @@ public class IndexerConfig {

@PostConstruct
public void init() {
GeometryUtils.setGridSize(gridSize);
GeometryUtils.setGridSpatialExtents(girdSpatialExtents);
GeometryUtils.setCoastalPrecision(coastalPrecision);
GeometryUtils.setExecutorService(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));

GeometryUtils.init();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ String mapUUID(MDMetadataType source) {
List<List<BigDecimal>> mapExtentBbox(MDMetadataType source) {
return GeometryUtils.createGeometryItems(
source,
null,
BBoxUtils::createBBoxFrom
);
}
Expand Down Expand Up @@ -343,11 +344,16 @@ private HashMap<GeoNetworkField, String> getMetadataDateInfoFrom(List<AbstractTy
});
return dateMap;
}

/**
* This is the default centroid if requester do not indicate a zoom level
* @param source
* @return
*/
@Named("mapSummaries.centroid")
List<List<BigDecimal>> mapGeometryCentroid(MDMetadataType source) {
return GeometryUtils.createGeometryItems(
source,
10,
GeometryUtils::createCentroidFrom
);
}
Expand All @@ -356,6 +362,7 @@ List<List<BigDecimal>> mapGeometryCentroid(MDMetadataType source) {
Map<?,?> mapSummariesGeometry(MDMetadataType source) {
return GeometryUtils.createGeometryItems(
source,
10, // This is useful in testing/edge only.
GeometryUtils::createGeometryFrom
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class BBoxUtils {

protected static Logger logger = LogManager.getLogger(BBoxUtils.class);

public static List<List<BigDecimal>> createBBoxFrom(List<List<AbstractEXGeographicExtentType>> rawInput) {
public static List<List<BigDecimal>> createBBoxFrom(List<List<AbstractEXGeographicExtentType>> rawInput, final Integer noUse) {
//TODO: avoid hardcode CRS, get it from document
List<List<Geometry>> polygons = GeometryBase.findPolygonsFrom(GeometryBase.COORDINATE_SYSTEM_CRS84, rawInput);
return StacUtils.createStacBBox(polygons);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.geojson.geom.GeometryJSON;
import org.locationtech.jts.algorithm.Area;
import org.locationtech.jts.algorithm.Orientation;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.operation.union.UnaryUnionOp;
import org.locationtech.jts.simplify.DouglasPeuckerSimplifier;
Expand All @@ -26,6 +25,7 @@
import java.net.URL;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.BiFunction;
import java.util.function.Function;

public class GeometryUtils {
Expand All @@ -43,7 +43,7 @@ public enum PointOrientation {
// Create an ExecutorService with a fixed thread pool size
@Getter
@Setter
protected static ExecutorService executorService;
protected static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

@Getter
@Setter
Expand All @@ -53,10 +53,6 @@ public enum PointOrientation {
@Setter
protected static int centroidScale = 3;

@Getter
@Setter
protected static double gridSize = 10.0;

@Getter
@Setter
protected static double coastalPrecision = 0.03;
Expand Down Expand Up @@ -243,28 +239,40 @@ protected static Coordinate[] reverseCoordinates(Coordinate[] coords) {
*/
protected static List<Polygon> createGridPolygons(Envelope envelope, double cellSize) {
List<Polygon> gridPolygons = new ArrayList<>();
GeometryFactory geometryFactory = new GeometryFactory();

double minX = envelope.getMinX();
double minY = envelope.getMinY();
double maxX = envelope.getMaxX();
double maxY = envelope.getMaxY();

// Loop to create grid cells
for (double x = minX; x < maxX; x += cellSize) {
for (double y = minY; y < maxY; y += cellSize) {
// Create a polygon for each grid cell
Polygon gridCell = geometryFactory.createPolygon(new Coordinate[]{
new Coordinate(x, y),
new Coordinate(x + cellSize, y),
new Coordinate(x + cellSize, y + cellSize),
new Coordinate(x, y + cellSize),
new Coordinate(x, y) // Closing the polygon
});
gridPolygons.add(gridCell);
// Check if the cellSize is too small on both side, that is we need to split even if the
// envelope have long width but short height etc
if (cellSize <= 0 || (cellSize > (maxX - minX) && cellSize > (maxY - minY))) {
Polygon itself = factory.createPolygon(new Coordinate[]{
new Coordinate(minX, minY),
new Coordinate(maxX, minY),
new Coordinate(maxX, maxY),
new Coordinate(minX, maxY),
new Coordinate(minX, minY) // Closing the polygon
});
gridPolygons.add(itself);
}
else {
// Loop to create grid cells
for (double x = minX; x < maxX; x += cellSize) {
for (double y = minY; y < maxY; y += cellSize) {
// Create a polygon for each grid cell
Polygon gridCell = factory.createPolygon(new Coordinate[]{
new Coordinate(x, y),
new Coordinate(x + cellSize, y),
new Coordinate(x + cellSize, y + cellSize),
new Coordinate(x, y + cellSize),
new Coordinate(x, y) // Closing the polygon
});
gridPolygons.add(gridCell);
}
}
}

return gridPolygons;
}
/**
Expand Down Expand Up @@ -294,14 +302,14 @@ protected static List<Geometry> convertToListGeometry(Geometry multipolygon) {
* @param large - A Polygon to break into grid
* @return - A polygon the break into grid.
*/
protected static List<Geometry> breakLargeGeometryToGrid(final Geometry large) {
protected static List<Geometry> breakLargeGeometryToGrid(final Geometry large, int gridSize) {
logger.debug("Break down large geometry to grid {}", large);
// Get the bounding box (extent) of the large polygon
Envelope envelope = large.getEnvelopeInternal();

// Hard code cell size, we can adjust the break grid size. 10.0 result in 3x3 grid
// cover Australia
List<Polygon> gridPolygons = createGridPolygons(envelope, getGridSize());
List<Polygon> gridPolygons = createGridPolygons(envelope, gridSize);

// List to store Future objects representing the results of the tasks
List<Future<Geometry>> futureResults = new ArrayList<>();
Expand Down Expand Up @@ -334,10 +342,10 @@ protected static List<Geometry> breakLargeGeometryToGrid(final Geometry large) {
return intersectedPolygons;
}

protected static List<List<Geometry>> splitAreaToGrid(List<List<Geometry>> geoList) {
protected static List<List<Geometry>> splitAreaToGrid(List<List<Geometry>> geoList, final int gridSize) {
return geoList.stream()
.flatMap(Collection::stream)
.map(GeometryUtils::breakLargeGeometryToGrid)
.map(i -> GeometryUtils.breakLargeGeometryToGrid(i, gridSize))
.toList();
}
/**
Expand Down Expand Up @@ -445,7 +453,8 @@ protected static List<List<BigDecimal>> createCentroid(List<List<Geometry>> geom
*/
public static <R> R createGeometryItems(
MDMetadataType source,
Function<List<List<AbstractEXGeographicExtentType>>, R> handler) {
Integer gridSize,
BiFunction<List<List<AbstractEXGeographicExtentType>>, Integer, R> handler) {

List<MDDataIdentificationType> items = MapperUtils.findMDDataIdentificationType(source);
if(!items.isEmpty()) {
Expand Down Expand Up @@ -492,7 +501,7 @@ public static <R> R createGeometryItems(
.toList()
)
.toList();
return handler.apply(rawInput);
return handler.apply(rawInput, gridSize);
}
return null;
}
Expand All @@ -510,8 +519,8 @@ protected static List<List<Geometry>> createGeometryWithoutLand(List<List<Abstra
* @param rawInput - The parsed XML block that contains the spatial extents area
* @return - Centroid point which will not appear on land.
*/
public static List<List<BigDecimal>> createCentroidFrom(List<List<AbstractEXGeographicExtentType>> rawInput) {
List<List<Geometry>> grid = splitAreaToGrid(createGeometryWithoutLand(rawInput));
public static List<List<BigDecimal>> createCentroidFrom(List<List<AbstractEXGeographicExtentType>> rawInput, Integer gridSize) {
List<List<Geometry>> grid = splitAreaToGrid(createGeometryWithoutLand(rawInput), gridSize);
return (grid != null && !grid.isEmpty()) ? createCentroid(grid) : null;
}
/**
Expand All @@ -521,7 +530,7 @@ public static List<List<BigDecimal>> createCentroidFrom(List<List<AbstractEXGeog
* @param rawInput - The parsed XML block that contains the spatial extents area
* @return
*/
public static Map<?, ?> createGeometryFrom(List<List<AbstractEXGeographicExtentType>> rawInput) {
public static Map<?, ?> createGeometryFrom(List<List<AbstractEXGeographicExtentType>> rawInput, Integer gridSize) {
// The return polygon is in EPSG:4326, so we can call createGeoJson directly

// This line will cause the spatial extents to break into grid, it may help to debug but will make production
Expand All @@ -530,7 +539,7 @@ public static List<List<BigDecimal>> createCentroidFrom(List<List<AbstractEXGeog
List<List<Geometry>> polygon = GeometryBase.findPolygonsFrom(GeometryBase.COORDINATE_SYSTEM_CRS84, rawInput);

// This is helpful when debug, it help to visualize the grid size on say edge env.
polygon = isGridSpatialExtents() ? splitAreaToGrid(polygon) : polygon;
polygon = isGridSpatialExtents() ? splitAreaToGrid(polygon, gridSize) : polygon;

return (polygon != null && !polygon.isEmpty()) ? createGeoJson(polygon) : null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.geojson.feature.FeatureJSON;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.locationtech.jts.geom.*;
Expand All @@ -18,7 +19,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;

import static au.org.aodn.esindexer.BaseTestClass.readResourceFile;
import static org.junit.jupiter.api.Assertions.assertEquals;
Expand All @@ -34,9 +34,7 @@ public GeometryUtilsTest() throws JAXBException {
@BeforeEach
public void init() {
GeometryUtils.setCoastalPrecision(0.03);
GeometryUtils.setGridSize(10.0);
GeometryUtils.setGridSpatialExtents(false);
GeometryUtils.setExecutorService(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));

GeometryUtils.init();
}
Expand All @@ -48,7 +46,8 @@ public void verifyLandStrippedFromSpatialExtents() throws IOException, JAXBExcep
// Whole spatial extends
List<List<Geometry>> withLand = GeometryUtils.createGeometryItems(
source,
(rawInput) -> GeometryBase.findPolygonsFrom(GeometryBase.COORDINATE_SYSTEM_CRS84, rawInput)
null,
(rawInput, size) -> GeometryBase.findPolygonsFrom(GeometryBase.COORDINATE_SYSTEM_CRS84, rawInput)
);

List<List<Geometry>> l = Objects.requireNonNull(withLand);
Expand All @@ -73,7 +72,8 @@ public void verifyLandStrippedFromSpatialExtents() throws IOException, JAXBExcep
// Strip the land away.
List<List<Geometry>> noLand = GeometryUtils.createGeometryItems(
source,
GeometryUtils::createGeometryWithoutLand
null,
(rawInput, s) -> GeometryUtils.createGeometryWithoutLand(rawInput)
);

List<List<Geometry>> nl = Objects.requireNonNull(noLand);
Expand Down Expand Up @@ -139,4 +139,44 @@ protected GeometryCollection convertToGeometryCollection(
// Create and return a GeometryCollection from the array of geometries
return geometryFactory.createGeometryCollection(geometryArray);
}

@Test
public void verifyCreateGirdPolygonWithValidCellSize() {
Envelope envelope = new Envelope(0, 10, 0, 10); // 10x10 envelope
double cellSize = 2.0; // Valid cell size

List<Polygon> gridPolygons = GeometryUtils.createGridPolygons(envelope, cellSize);

// Check that the grid has been divided into cells
Assertions.assertFalse(gridPolygons.isEmpty(), "Expected non-empty grid polygons list");

// Check the number of cells created (should be 5x5 = 25 for a 10x10 grid with cell size 2)
Assertions.assertEquals(25, gridPolygons.size(), "Expected 25 grid cells");

// Check that each cell is a valid polygon and has the expected cell size
gridPolygons.forEach(polygon -> {
Assertions.assertNotNull(polygon, "Expected each grid cell to be a valid polygon");
Assertions.assertTrue(polygon.getArea() <= (cellSize * cellSize), "Expected each cell to have an area <= cellSize^2");
});
}

@Test
public void verifyCreateGirdPolygonWithTooBigCellSize() {
Envelope envelope = new Envelope(0, 10, 0, 10); // 10x10 envelope
Polygon polygon = GeometryUtils.factory.createPolygon(new Coordinate[]{
new Coordinate(envelope.getMinX(), envelope.getMinY()),
new Coordinate(envelope.getMaxX(), envelope.getMinY()),
new Coordinate(envelope.getMaxX(), envelope.getMaxY()),
new Coordinate(envelope.getMinX(), envelope.getMaxY()),
new Coordinate(envelope.getMinX(), envelope.getMinY()) // Closing point
});
double cellSize = 20; // Too small cell size

// Verify that an exception is thrown for too small cell size
List<Polygon> gridPolygons = GeometryUtils.createGridPolygons(envelope, cellSize);

// Check that the exception message is as expected
Assertions.assertEquals(1, gridPolygons.size(), "Get 1 back");
Assertions.assertTrue(gridPolygons.get(0).equalsExact(polygon), "Get back itself");
}
}
47 changes: 46 additions & 1 deletion indexer/src/test/resources/canned/sample7_stac.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,52 @@
"end": "2022-12-31T12:59:59Z"
}
],
"centroid": []
"centroid": [
[
142.942,
-9.999
],
[
143.397,
-9.735
],
[
143.397,
-9.847
],
[
145.443,
-14.685
],
[
146.228,
-16.863
],
[
146.5,
-18.572
],
[
146.52,
-18.644
],
[
147.647,
-18.779
],
[
150.97,
-23.197
],
[
151.925,
-23.446
],
[
152.392,
-23.893
]
]
},
"contacts": [
{
Expand Down
Loading
Loading