From 72ccd76ed013a61fa991d7c93f5cd947ebb30cdd Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Fri, 24 Jan 2020 14:37:28 -0500 Subject: [PATCH] prepping for antimeridian test --- .../dao/predicate/PredicateBuilderCoords.java | 24 +++++++----- .../ca/uhn/fhir/jpa/util/CoordCalculator.java | 18 ++++++++- .../java/ca/uhn/fhir/jpa/util/SearchBox.java | 21 ++++++++++ .../fhir/jpa/util/CoordCalculatorTest.java | 39 ++++++++++++++++++- .../SearchParamExtractorService.java | 4 -- 5 files changed, 90 insertions(+), 16 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SearchBox.java diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderCoords.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderCoords.java index 64744c71c37..f82ddf60c24 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderCoords.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderCoords.java @@ -4,12 +4,14 @@ import ca.uhn.fhir.jpa.dao.SearchBuilder; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.util.CoordCalculator; +import ca.uhn.fhir.jpa.util.SearchBox; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.dstu2.resource.Location; import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.SpecialParam; import ca.uhn.fhir.rest.param.TokenParam; -import org.hibernate.search.spatial.impl.Point; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -25,6 +27,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank; @Component @Scope("prototype") public class PredicateBuilderCoords extends BasePredicateBuilder implements IPredicateBuilder { + private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderCoords.class); PredicateBuilderCoords(SearchBuilder theSearchBuilder) { super(theSearchBuilder); @@ -84,22 +87,25 @@ public class PredicateBuilderCoords extends BasePredicateBuilder implements IPre longitudePredicate = theBuilder.equal(theFrom.get("myLongitude"), longitudeValue); } else if (distanceKm < 0.0) { throw new IllegalArgumentException("Invalid " + Location.SP_NEAR_DISTANCE + " parameter '" + distanceKm + "' must be >= 0.0"); + } else if (distanceKm > CoordCalculator.MAX_SUPPORTED_DISTANCE_KM) { + throw new IllegalArgumentException("Invalid " + Location.SP_NEAR_DISTANCE + " parameter '" + distanceKm + "' must be <= " + CoordCalculator.MAX_SUPPORTED_DISTANCE_KM); } else { double latitudeDegrees = Double.parseDouble(latitudeValue); double longitudeDegrees = Double.parseDouble(longitudeValue); - Point northPoint = CoordCalculator.findTarget(latitudeDegrees, longitudeDegrees, 0.0, distanceKm); - Point eastPoint = CoordCalculator.findTarget(latitudeDegrees, longitudeDegrees, 90.0, distanceKm); - Point southPoint = CoordCalculator.findTarget(latitudeDegrees, longitudeDegrees, 180.0, distanceKm); - Point westPoint = CoordCalculator.findTarget(latitudeDegrees, longitudeDegrees, 270.0, distanceKm); + SearchBox box = CoordCalculator.getBox(latitudeDegrees, longitudeDegrees, distanceKm); + // FIXME KHS + ourLog.info("Searching for {} =< latitude <= {}", box.getSouthWest().getLatitude(), box.getNorthEast().getLatitude()); latitudePredicate = theBuilder.and( - theBuilder.greaterThanOrEqualTo(theFrom.get("myLatitude"), southPoint.getLatitude()), - theBuilder.lessThanOrEqualTo(theFrom.get("myLatitude"), northPoint.getLatitude()) + theBuilder.greaterThanOrEqualTo(theFrom.get("myLatitude"), box.getSouthWest().getLatitude()), + theBuilder.lessThanOrEqualTo(theFrom.get("myLatitude"), box.getNorthEast().getLatitude()) ); + // FIXME KHS + ourLog.info("Searching for {} =< longitude <= {}", box.getSouthWest().getLongitude(), box.getNorthEast().getLongitude()); longitudePredicate = theBuilder.and( - theBuilder.greaterThanOrEqualTo(theFrom.get("myLongitude"), westPoint.getLongitude()), - theBuilder.lessThanOrEqualTo(theFrom.get("myLongitude"), eastPoint.getLongitude()) + theBuilder.greaterThanOrEqualTo(theFrom.get("myLongitude"), box.getSouthWest().getLongitude()), + theBuilder.lessThanOrEqualTo(theFrom.get("myLongitude"), box.getNorthEast().getLongitude()) ); } Predicate singleCode = theBuilder.and(latitudePredicate, longitudePredicate); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/CoordCalculator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/CoordCalculator.java index 200b0c2f249..abcbc4649b5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/CoordCalculator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/CoordCalculator.java @@ -4,9 +4,11 @@ package ca.uhn.fhir.jpa.util; import org.hibernate.search.spatial.impl.Point; public class CoordCalculator { - public static final double RADIUS_EARTH_KM = 6378.1; + public static final double MAX_SUPPORTED_DISTANCE_KM = 10000.0; // Slightly less than a quarter of the earth's circumference + private static final double RADIUS_EARTH_KM = 6378.1; - public static Point findTarget(double theLatitudeDegrees, double theLongitudeDegrees, double theBearingDegrees, double theDistanceKm) { + // Source: https://stackoverflow.com/questions/7222382/get-lat-long-given-current-point-distance-and-bearing + static Point findTarget(double theLatitudeDegrees, double theLongitudeDegrees, double theBearingDegrees, double theDistanceKm) { double latitudeRadians = Math.toRadians(theLatitudeDegrees); double longitudeRadians = Math.toRadians(theLongitudeDegrees); @@ -21,4 +23,16 @@ public class CoordCalculator { return Point.fromDegrees(Math.toDegrees(targetLatitude), Math.toDegrees(targetLongitude)); } + + /** + * Find a box around my coordinates such that the closest distance to each edge is the provided distance + */ + public static SearchBox getBox(double theLatitudeDegrees, double theLongitudeDegrees, Double theDistanceKm) { + double diagonalDistanceKm = theDistanceKm * Math.sqrt(2.0); + + Point northEast = CoordCalculator.findTarget(theLatitudeDegrees, theLongitudeDegrees, 45.0, diagonalDistanceKm); + Point southWest = CoordCalculator.findTarget(theLatitudeDegrees, theLongitudeDegrees, 225.0, diagonalDistanceKm); + + return new SearchBox(southWest, northEast); + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SearchBox.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SearchBox.java new file mode 100644 index 00000000000..e55746cfaf0 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SearchBox.java @@ -0,0 +1,21 @@ +package ca.uhn.fhir.jpa.util; + +import org.hibernate.search.spatial.impl.Point; + +public class SearchBox { + private final Point mySouthWest; + private final Point myNorthEast; + + public SearchBox(Point theSouthWest, Point theNorthEast) { + mySouthWest = theSouthWest; + myNorthEast = theNorthEast; + } + + public Point getSouthWest() { + return mySouthWest; + } + + public Point getNorthEast() { + return myNorthEast; + } +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/CoordCalculatorTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/CoordCalculatorTest.java index a2ef5694898..a284cebfd07 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/CoordCalculatorTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/util/CoordCalculatorTest.java @@ -23,6 +23,43 @@ public class CoordCalculatorTest { Point result = CoordCalculator.findTarget(LATITUDE_CHIN, LONGITUDE_CHIN, BEARING_CHIN_TO_UHN, DISTANCE_KM_CHIN_TO_UHN); assertEquals(LATITUDE_UHN, result.getLatitude(), 0.0001); - assertEquals(LONGITUDE_UHN, result.getLatitude(), 0.0001); + assertEquals(LONGITUDE_UHN, result.getLongitude(), 0.0001); } + + @Test + public void testBox() { + SearchBox box = CoordCalculator.getBox(LATITUDE_CHIN, LONGITUDE_CHIN, 1.0); + double expectedLatitudeDelta = 0.0090; + assertEquals(LATITUDE_CHIN - expectedLatitudeDelta, box.getSouthWest().getLatitude(), 0.0001); + assertEquals(LATITUDE_CHIN + expectedLatitudeDelta, box.getNorthEast().getLatitude(), 0.0001); + double expectedLongitudeDelta = 0.012414; + assertEquals(LONGITUDE_CHIN - expectedLongitudeDelta, box.getSouthWest().getLongitude(), 0.0001); + assertEquals(LONGITUDE_CHIN + expectedLongitudeDelta, box.getNorthEast().getLongitude(), 0.0001); + } + + @Test + public void testOnPrimeMeridian() { + double meridianLongitide = 0.0; + SearchBox box = CoordCalculator.getBox(LATITUDE_CHIN, meridianLongitide, 1.0); + double expectedLatitudeDelta = 0.0090; + assertEquals(LATITUDE_CHIN - expectedLatitudeDelta, box.getSouthWest().getLatitude(), 0.0001); + assertEquals(LATITUDE_CHIN + expectedLatitudeDelta, box.getNorthEast().getLatitude(), 0.0001); + double expectedLongitudeDelta = 0.012414; + assertEquals(meridianLongitide - expectedLongitudeDelta, box.getSouthWest().getLongitude(), 0.0001); + assertEquals(meridianLongitide + expectedLongitudeDelta, box.getNorthEast().getLongitude(), 0.0001); + } + + @Test + public void testOnAntiMeridian() { + double antiMeridianLongitide = 180.0; + SearchBox box = CoordCalculator.getBox(LATITUDE_CHIN, antiMeridianLongitide, 1.0); + double expectedLatitudeDelta = 0.0090; + assertEquals(LATITUDE_CHIN - expectedLatitudeDelta, box.getSouthWest().getLatitude(), 0.0001); + assertEquals(LATITUDE_CHIN + expectedLatitudeDelta, box.getNorthEast().getLatitude(), 0.0001); + double expectedLongitudeDelta = 0.012414; + assertEquals(antiMeridianLongitide - expectedLongitudeDelta, box.getSouthWest().getLongitude(), 0.0001); + // This case wraps + assertEquals(antiMeridianLongitide + expectedLongitudeDelta - 360.0, box.getNorthEast().getLongitude(), 0.0001); + } + } diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java index 6f60979c97e..95ce06c08de 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java @@ -67,10 +67,6 @@ public class SearchParamExtractorService { handleWarnings(theRequestDetails, myInterceptorBroadcaster, uris); theParams.myUriParams.addAll(uris); -// ISearchParamExtractor.SearchParamSet coords = extractSearchParamCoords(theResource); -// handleWarnings(theRequestDetails, myInterceptorBroadcaster, coords); -// theParams.myCoordsParams.addAll(coords); - ourLog.trace("Storing date indexes: {}", theParams.myDateParams); for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(theResource)) {