diff --git a/src/main/java/mil/nga/gars/GARS.java b/src/main/java/mil/nga/gars/GARS.java index 4d9e076..38b05d1 100644 --- a/src/main/java/mil/nga/gars/GARS.java +++ b/src/main/java/mil/nga/gars/GARS.java @@ -315,31 +315,34 @@ public static GARS from(Point point) { // Normalize the longitude if needed GeometryUtils.normalizeWGS84(point); + double longitude = point.getLongitude(); + if (longitude >= GARSConstants.MAX_LON) { + // 180 is the same meridian as -180, in band 001 + longitude -= 360.0; + } - double lon = GARSUtils.getLongitudeDecimalBand(point.getLongitude()); - double lat = GARSUtils.getLatitudeDecimalBandValue(point.getLatitude()); + // Index of the 5 minute cell, counted from 180 W and 90 S. Multiply + // before adding the offset so that values on a cell boundary land in + // the cell they start (scaling the band fractions in steps let + // rounding put them in the cell below or to the left). The top row + // includes latitude 90. + int lonIndex = (int) Math.floor(longitude * 12.0) + 180 * 12; + int latIndex = (int) Math.floor(point.getLatitude() * 12.0) + 90 * 12; + lonIndex = Math.max(0, Math.min(lonIndex, 360 * 12 - 1)); + latIndex = Math.max(0, Math.min(latIndex, 180 * 12 - 1)); - int lonInt = (int) lon; - int latInt = (int) lat; + int lonInt = lonIndex / 6 + 1; + int latInt = latIndex / 6 + 1; String latBand = GARSUtils.bandLetters(latInt); - double lonDecimal = lon - lonInt; - double latDecimal = lat - latInt; - - double quadrantColumn = lonDecimal * 2.0; - double quadrantRow = latDecimal * 2.0; - - int quadrantColumnInt = (int) quadrantColumn; - int quadrantRowInt = (int) quadrantRow; + int quadrantColumnInt = (lonIndex % 6) / 3; + int quadrantRowInt = (latIndex % 6) / 3; int quadrant = GARSUtils.quadrant(quadrantColumnInt, quadrantRowInt); - lonDecimal = quadrantColumn - quadrantColumnInt; - latDecimal = quadrantRow - quadrantRowInt; - - int keypadColumn = (int) (lonDecimal * 3.0); - int keypadRow = (int) (latDecimal * 3.0); + int keypadColumn = lonIndex % 3; + int keypadRow = latIndex % 3; int keypad = GARSUtils.keypad(keypadColumn, keypadRow); diff --git a/src/test/java/mil/nga/gars/GARSTest.java b/src/test/java/mil/nga/gars/GARSTest.java index e4fb015..7256041 100644 --- a/src/test/java/mil/nga/gars/GARSTest.java +++ b/src/test/java/mil/nga/gars/GARSTest.java @@ -290,7 +290,9 @@ public void testPointBounds() { // Max latitude tests - String gars = "462RA49"; + // Latitude 90 is in the top band, QZ (there is no band RA, and + // parse() rejects it) + String gars = "462QZ23"; String gars2 = "462QZ26"; double longitude = 50.920338; double latitudeBelow = 89.9; @@ -497,4 +499,45 @@ private void testCoordinate(Point point, String value) } + /** + * Test points on the antimeridian, at the north pole, and on whole + * minute cell boundaries + * + * @throws ParseException + * upon failure to parse + */ + @Test + public void testCellBoundaries() throws ParseException { + + // 180 is the same meridian as -180; latitude 90 is in the top row. + // These used to give 721HN37 and 361RA37, which parse() rejects. + assertEquals("001HN37", GARS.from(180, 0).coordinate()); + assertEquals("361QZ11", GARS.from(0, 90).coordinate()); + assertEquals("001QZ11", GARS.from(180, 90).coordinate()); + assertEquals("720QZ23", GARS.from(179.9999999, 89.9999999).coordinate()); + GARS.parse(GARS.from(180, 0).coordinate()); + GARS.parse(GARS.from(0, 90).coordinate()); + + // A point on a 5 minute boundary is in the cell it starts + // (10 05' E 10 05' N used to give 381JJ38, the cell to the south) + assertEquals("381JJ35", GARS.from(10 + 5 / 60.0, 10 + 5 / 60.0).coordinate()); + assertEquals("340GR23", + GARS.from(-(10 + 5 / 60.0), -(10 + 5 / 60.0)).coordinate()); + + for (int i = 0; i < 360 * 12; i += 7) { + for (int j = 0; j < 180 * 12; j += 5) { + int lonMinutes = i * 5 - 180 * 60; + int latMinutes = j * 5 - 90 * 60; + double lon = Math.signum(lonMinutes) * (Math.abs(lonMinutes) / 60 + + (Math.abs(lonMinutes) % 60) / 60.0); + double lat = Math.signum(latMinutes) * (Math.abs(latMinutes) / 60 + + (Math.abs(latMinutes) % 60) / 60.0); + Point southwest = GARS.from(lon, lat).toPoint(); + assertEquals(lon, southwest.getLongitude(), 1e-9); + assertEquals(lat, southwest.getLatitude(), 1e-9); + } + } + + } + }