From d8f30f714bd61e14b752f518b3fb16c36d1fb375 Mon Sep 17 00:00:00 2001 From: amber Date: Mon, 13 Jul 2026 16:58:51 +1000 Subject: [PATCH 1/2] feat: always label bounding box selection and remove unused index param --- .../ogcapi/server/core/util/EmailUtils.java | 7 +++---- .../server/core/util/EmailUtilsTest.java | 21 ++++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/util/EmailUtils.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/util/EmailUtils.java index 4c8a47f7..2c0a90c0 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/util/EmailUtils.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/util/EmailUtils.java @@ -170,8 +170,7 @@ public static String generateBboxHtml(Object multipolygon, ObjectMapper objectMa String east = String.valueOf(envelope.getMaxX()); bboxCounter++; - int displayIndex = bboxCounter > 1 ? bboxCounter : 0; - html.append(buildBboxSection(north, south, west, east, displayIndex)); + html.append(buildBboxSection(north, south, west, east)); } } @@ -632,8 +631,8 @@ protected static String buildPolygonSection(List> vertices, int /** * Build a bounding box section showing its north, west, south and east bounds */ - protected static String buildBboxSection(String north, String south, String west, String east, int index) { - String title = index > 0 ? "Bounding Box " + index : "Bounding Box Selection"; + protected static String buildBboxSection(String north, String south, String west, String east) { + String title = "Bounding Box Selection"; // Coordinate display order groups the latitude bounds (N, S) then the longitude bounds (W, E). // Keep this list as the single source of truth for the order. diff --git a/server/src/test/java/au/org/aodn/ogcapi/server/core/util/EmailUtilsTest.java b/server/src/test/java/au/org/aodn/ogcapi/server/core/util/EmailUtilsTest.java index 61f95ebb..1103a984 100644 --- a/server/src/test/java/au/org/aodn/ogcapi/server/core/util/EmailUtilsTest.java +++ b/server/src/test/java/au/org/aodn/ogcapi/server/core/util/EmailUtilsTest.java @@ -39,7 +39,7 @@ void testPolygonImageResourceExists() throws IOException { */ @Test void testSingleBbox() { - String html = EmailUtils.buildBboxSection("-40.0", "-41.0", "145.0", "146.0", 0); + String html = EmailUtils.buildBboxSection("-40.0", "-41.0", "145.0", "146.0"); assertTrue(html.contains("Bounding Box Selection")); assertTrue(html.contains("N: -40.0")); @@ -53,7 +53,7 @@ void testSingleBbox() { */ @Test void testBboxCoordinateOrder() { - String html = EmailUtils.buildBboxSection("-40.0", "-41.0", "145.0", "146.0", 0); + String html = EmailUtils.buildBboxSection("-40.0", "-41.0", "145.0", "146.0"); int n = html.indexOf("N: -40.0"); int s = html.indexOf("S: -41.0"); @@ -64,15 +64,16 @@ void testBboxCoordinateOrder() { } /** - * Test multiple bboxes with numbers + * Test that every bbox always shows "Bounding Box Selection", never a number */ @Test void testMultipleBboxes() { - String html1 = EmailUtils.buildBboxSection("-40.0", "-41.0", "145.0", "146.0", 1); - String html2 = EmailUtils.buildBboxSection("-38.0", "-39.0", "147.0", "148.0", 2); + String html1 = EmailUtils.buildBboxSection("-40.0", "-41.0", "145.0", "146.0"); + String html2 = EmailUtils.buildBboxSection("-38.0", "-39.0", "147.0", "148.0"); - assertTrue(html1.contains("Bounding Box 1")); - assertTrue(html2.contains("Bounding Box 2")); + assertTrue(html1.contains("Bounding Box Selection")); + assertTrue(html2.contains("Bounding Box Selection")); + assertFalse(html2.contains("Bounding Box 2")); } /** @@ -80,7 +81,7 @@ void testMultipleBboxes() { */ @Test void testDecimalFormat() { - String html = EmailUtils.buildBboxSection("-35.12345", "-36.54321", "150.11111", "151.99999", 0); + String html = EmailUtils.buildBboxSection("-35.12345", "-36.54321", "150.11111", "151.99999"); assertTrue(html.contains("N: -35.12345")); assertTrue(html.contains("S: -36.54321")); @@ -93,7 +94,7 @@ void testDecimalFormat() { */ @Test void testScientificNotation() { - String html = EmailUtils.buildBboxSection("4.9E-324", "-11.919807423710694", "-45.42305428582753", "4.9E-324", 0); + String html = EmailUtils.buildBboxSection("4.9E-324", "-11.919807423710694", "-45.42305428582753", "4.9E-324"); assertTrue(html.contains("N: 4.9E-324")); assertTrue(html.contains("S: -11.919807423710694")); @@ -106,7 +107,7 @@ void testScientificNotation() { */ @Test void testImagePlaceholder() { - String html = EmailUtils.buildBboxSection("-40.0", "-41.0", "145.0", "146.0", 0); + String html = EmailUtils.buildBboxSection("-40.0", "-41.0", "145.0", "146.0"); assertTrue(html.contains("{{BBOX_IMG}}")); } From 409fbf6c769f182252d6f843271e96240cd847e3 Mon Sep 17 00:00:00 2001 From: amber Date: Mon, 13 Jul 2026 17:07:42 +1000 Subject: [PATCH 2/2] feat: format bbox/polygon coordinates to fixed 5 decimals in email template --- .../ogcapi/server/core/util/EmailUtils.java | 20 +++++-- .../server/core/util/EmailUtilsTest.java | 57 ++++++++++++------- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/util/EmailUtils.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/util/EmailUtils.java index 2c0a90c0..48d28e98 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/util/EmailUtils.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/util/EmailUtils.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -164,10 +165,10 @@ public static String generateBboxHtml(Object multipolygon, ObjectMapper objectMa Polygon normalizedPolygon = (Polygon) normalizedBbox.getGeometryN(i); Envelope envelope = normalizedPolygon.getEnvelopeInternal(); - String north = String.valueOf(envelope.getMaxY()); - String south = String.valueOf(envelope.getMinY()); - String west = String.valueOf(envelope.getMinX()); - String east = String.valueOf(envelope.getMaxX()); + String north = formatCoordinate(envelope.getMaxY()); + String south = formatCoordinate(envelope.getMinY()); + String west = formatCoordinate(envelope.getMinX()); + String east = formatCoordinate(envelope.getMaxX()); bboxCounter++; html.append(buildBboxSection(north, south, west, east)); @@ -230,6 +231,13 @@ public static String generatePolygonHtml(Object multipolygon, ObjectMapper objec } } + /** + * Format a coordinate to a fixed 5 decimal places (~1m precision), avoiding scientific notation. + */ + protected static String formatCoordinate(double value) { + return BigDecimal.valueOf(value).setScale(5, RoundingMode.HALF_UP).toPlainString(); + } + /** * Remove the GeoJSON closing point (where the last vertex repeats the first) to get the unique vertices. * @@ -582,8 +590,8 @@ protected static String buildPolygonSection(List> vertices, int for (int i = 0; i < vertices.size(); i++) { List point = vertices.get(i); // GeoJSON stores [lon, lat]; display latitude-first per geographic convention. - String lon = point.get(0).toPlainString(); - String lat = point.get(1).toPlainString(); + String lon = formatCoordinate(point.get(0).doubleValue()); + String lat = formatCoordinate(point.get(1).doubleValue()); coordinateRows.append(buildCoordinateRow("Point " + (i + 1) + ": (" + lat + ", " + lon + ")")); } diff --git a/server/src/test/java/au/org/aodn/ogcapi/server/core/util/EmailUtilsTest.java b/server/src/test/java/au/org/aodn/ogcapi/server/core/util/EmailUtilsTest.java index 1103a984..3931c81b 100644 --- a/server/src/test/java/au/org/aodn/ogcapi/server/core/util/EmailUtilsTest.java +++ b/server/src/test/java/au/org/aodn/ogcapi/server/core/util/EmailUtilsTest.java @@ -77,29 +77,48 @@ void testMultipleBboxes() { } /** - * Test decimal formatting - now tests that original string values are preserved + * Test coordinates are rounded to a fixed 5 decimal places */ @Test - void testDecimalFormat() { - String html = EmailUtils.buildBboxSection("-35.12345", "-36.54321", "150.11111", "151.99999"); + void testCoordinateRoundsToFiveDecimals() { + assertEquals("-35.12346", EmailUtils.formatCoordinate(-35.123456789)); + assertEquals("145.00000", EmailUtils.formatCoordinate(145.0)); + } - assertTrue(html.contains("N: -35.12345")); - assertTrue(html.contains("S: -36.54321")); - assertTrue(html.contains("W: 150.11111")); - assertTrue(html.contains("E: 151.99999")); + /** + * Test coordinates never use scientific notation + */ + @Test + void testCoordinateAvoidsScientificNotation() { + assertEquals("0.00000", EmailUtils.formatCoordinate(4.9E-324)); + assertEquals("-11.91981", EmailUtils.formatCoordinate(-11.919807423710694)); } /** - * Test scientific notation values are preserved + * Test that generated bbox HTML rounds high-precision input to 5 decimals with no scientific notation */ @Test - void testScientificNotation() { - String html = EmailUtils.buildBboxSection("4.9E-324", "-11.919807423710694", "-45.42305428582753", "4.9E-324"); + void testGenerateBboxHtmlRoundsCoordinates() { + Map bbox = Map.of( + "type", "MultiPolygon", + "coordinates", List.of( + List.of( + List.of( + List.of(145.123456789, -40.987654321), + List.of(145.123456789, -41.0), + List.of(146.0, -41.0), + List.of(146.0, -40.987654321), + List.of(145.123456789, -40.987654321) + ) + ) + ) + ); + + String html = EmailUtils.generateBboxHtml(bbox, new ObjectMapper()); - assertTrue(html.contains("N: 4.9E-324")); - assertTrue(html.contains("S: -11.919807423710694")); - assertTrue(html.contains("W: -45.42305428582753")); - assertTrue(html.contains("E: 4.9E-324")); + assertTrue(html.contains("W: 145.12346")); + assertTrue(html.contains("N: -40.98765")); + assertFalse(html.contains("E-")); } /** @@ -328,8 +347,8 @@ void testSinglePolygon() { String html = EmailUtils.buildPolygonSection(vertices, 0); assertTrue(html.contains("Polygon Selection")); - assertTrue(html.contains("Point 1: (-40.0, 145.0)")); - assertTrue(html.contains("Point 5: (-41.0, 144.5)")); + assertTrue(html.contains("Point 1: (-40.00000, 145.00000)")); + assertTrue(html.contains("Point 5: (-41.00000, 144.50000)")); } /** @@ -353,12 +372,12 @@ void testMultiplePolygons() { } /** - * Test polygon vertices preserve full precision (no scientific notation) + * Test polygon vertices are rounded to 5 decimals (no scientific notation) */ @Test void testPolygonDecimalFormat() { List> vertices = List.of( - List.of(new BigDecimal("150.11111"), new BigDecimal("-35.12345")), + List.of(new BigDecimal("150.123456"), new BigDecimal("-35.123454")), List.of(new BigDecimal("151.99999"), new BigDecimal("-36.54321")), List.of(new BigDecimal("152.50000"), new BigDecimal("-37.00001")), List.of(new BigDecimal("151.00000"), new BigDecimal("-38.00002")), @@ -367,7 +386,7 @@ void testPolygonDecimalFormat() { String html = EmailUtils.buildPolygonSection(vertices, 0); - assertTrue(html.contains("Point 1: (-35.12345, 150.11111)")); + assertTrue(html.contains("Point 1: (-35.12345, 150.12346)")); assertTrue(html.contains("Point 2: (-36.54321, 151.99999)")); }