convert degrees to kilometers for near calculation

This commit is contained in:
Ken Stevens 2020-01-22 16:55:19 -05:00
parent f2e8037c90
commit e6f5af01ed
6 changed files with 97 additions and 91 deletions

View File

@ -20,15 +20,7 @@ package ca.uhn.fhir.jpa.dao;
* #L% * #L%
*/ */
import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.*;
import ca.uhn.fhir.context.BaseRuntimeDeclaredChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.api.Pointcut;
@ -51,18 +43,8 @@ import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.searchparam.util.SourceParam; import ca.uhn.fhir.jpa.searchparam.util.SourceParam;
import ca.uhn.fhir.jpa.term.VersionIndependentConcept; import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.jpa.util.BaseIterator; import ca.uhn.fhir.jpa.util.*;
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener; import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.jpa.util.ScrollableResultsIterator;
import ca.uhn.fhir.jpa.util.SqlQueryList;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
import ca.uhn.fhir.model.base.composite.BaseQuantityDt; import ca.uhn.fhir.model.base.composite.BaseQuantityDt;
@ -71,11 +53,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.api.QualifiedParamList;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IPreResourceAccessDetails; import ca.uhn.fhir.rest.api.server.IPreResourceAccessDetails;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.*; import ca.uhn.fhir.rest.param.*;
@ -99,6 +77,7 @@ import org.hibernate.ScrollableResults;
import org.hibernate.query.Query; import org.hibernate.query.Query;
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl; import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
import org.hibernate.query.criteria.internal.predicate.BooleanStaticAssertionPredicate; import org.hibernate.query.criteria.internal.predicate.BooleanStaticAssertionPredicate;
import org.hibernate.search.spatial.impl.Point;
import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
@ -121,11 +100,7 @@ import java.util.Map.Entry;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank; import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trim;
/** /**
* The SearchBuilder is responsible for actually forming the SQL query that handles * The SearchBuilder is responsible for actually forming the SQL query that handles
@ -2075,7 +2050,7 @@ public class SearchBuilder implements ISearchBuilder {
From<?, ResourceIndexedSearchParamCoords> theFrom) { From<?, ResourceIndexedSearchParamCoords> theFrom) {
String latitudeValue; String latitudeValue;
String longitudeValue; String longitudeValue;
Double distance = 0.0; Double distanceKm = 0.0;
if (theParam instanceof TokenParam) { // DSTU3 if (theParam instanceof TokenParam) { // DSTU3
TokenParam param = (TokenParam) theParam; TokenParam param = (TokenParam) theParam;
@ -2091,7 +2066,7 @@ public class SearchBuilder implements ISearchBuilder {
} }
QuantityParam distanceParam = myParams.getNearDistanceParam(); QuantityParam distanceParam = myParams.getNearDistanceParam();
if (distanceParam != null) { if (distanceParam != null) {
distance = distanceParam.getValue().doubleValue(); distanceKm = distanceParam.getValue().doubleValue();
} }
} else if (theParam instanceof SpecialParam) { // R4 } else if (theParam instanceof SpecialParam) { // R4
SpecialParam param = (SpecialParam) theParam; SpecialParam param = (SpecialParam) theParam;
@ -2108,7 +2083,7 @@ public class SearchBuilder implements ISearchBuilder {
if (parts.length >= 3) { if (parts.length >= 3) {
String distanceString = parts[2]; String distanceString = parts[2];
if (!isBlank(distanceString)) { if (!isBlank(distanceString)) {
distance = Double.valueOf(distanceString); distanceKm = Double.valueOf(distanceString);
} }
} }
} else { } else {
@ -2117,22 +2092,27 @@ public class SearchBuilder implements ISearchBuilder {
Predicate latitudePredicate; Predicate latitudePredicate;
Predicate longitudePredicate; Predicate longitudePredicate;
if (distance == 0.0) { if (distanceKm == 0.0) {
latitudePredicate = theBuilder.equal(theFrom.get("myLatitude"), latitudeValue); latitudePredicate = theBuilder.equal(theFrom.get("myLatitude"), latitudeValue);
longitudePredicate = theBuilder.equal(theFrom.get("myLongitude"), longitudeValue); longitudePredicate = theBuilder.equal(theFrom.get("myLongitude"), longitudeValue);
} else if (distance < 0.0) { } else if (distanceKm < 0.0) {
throw new IllegalArgumentException("Invalid " + Location.SP_NEAR_DISTANCE + " parameter '" + distance + "' must be >= 0.0"); throw new IllegalArgumentException("Invalid " + Location.SP_NEAR_DISTANCE + " parameter '" + distanceKm + "' must be >= 0.0");
} else { } else {
// FIXME KHS scale distance based on lat/long Double latitudeDegrees = Double.valueOf(latitudeValue);
Double latitude = Double.valueOf(latitudeValue); Double longitudeDegrees = Double.valueOf(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);
latitudePredicate = theBuilder.and( latitudePredicate = theBuilder.and(
theBuilder.greaterThanOrEqualTo(theFrom.get("myLatitude"), latitude - distance), theBuilder.greaterThanOrEqualTo(theFrom.get("myLatitude"), southPoint.getLatitude()),
theBuilder.lessThanOrEqualTo(theFrom.get("myLatitude"), latitude + distance) theBuilder.lessThanOrEqualTo(theFrom.get("myLatitude"), northPoint.getLatitude())
); );
Double longitude = Double.valueOf(longitudeValue);
longitudePredicate = theBuilder.and( longitudePredicate = theBuilder.and(
theBuilder.greaterThanOrEqualTo(theFrom.get("myLongitude"), longitude - distance), theBuilder.greaterThanOrEqualTo(theFrom.get("myLongitude"), westPoint.getLongitude()),
theBuilder.lessThanOrEqualTo(theFrom.get("myLongitude"), longitude + distance) theBuilder.lessThanOrEqualTo(theFrom.get("myLongitude"), eastPoint.getLongitude())
); );
} }
Predicate singleCode = theBuilder.and(latitudePredicate, longitudePredicate); Predicate singleCode = theBuilder.and(latitudePredicate, longitudePredicate);

View File

@ -1,13 +1,24 @@
package ca.uhn.fhir.jpa.util; package ca.uhn.fhir.jpa.util;
import org.springframework.data.geo.Point;
import org.hibernate.search.spatial.impl.Point;
public class CoordCalculator { public class CoordCalculator {
public static Point findTarget(double theLatitude, double theLongitude, double theBearing, double theDistance) { public static final double RADIUS_EARTH_KM = 6378.1;
double x;
double y; public static Point findTarget(double theLatitudeDegrees, double theLongitudeDegrees, double theBearingDegrees, double theDistanceKm) {
x = theLatitude;
y = theLatitude; double latitudeRadians = Math.toRadians(theLatitudeDegrees);
return new Point(x, y); double longitudeRadians = Math.toRadians(theLongitudeDegrees);
double bearingRadians = Math.toRadians(theBearingDegrees);
double distanceRadians = theDistanceKm / RADIUS_EARTH_KM;
double targetLatitude = Math.asin( Math.sin(latitudeRadians) * Math.cos(distanceRadians) +
Math.cos(latitudeRadians) * Math.sin(distanceRadians) * Math.cos(bearingRadians));
double targetLongitude = longitudeRadians + Math.atan2(Math.sin(bearingRadians) * Math.sin(distanceRadians) * Math.cos(latitudeRadians),
Math.cos(distanceRadians)-Math.sin(latitudeRadians) * Math.sin(targetLatitude));
return Point.fromDegrees(Math.toDegrees(targetLatitude), Math.toDegrees(targetLongitude));
} }
} }

View File

@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl; import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
import ca.uhn.fhir.jpa.util.CoordCalculatorTest;
import ca.uhn.fhir.jpa.util.TestUtil; import ca.uhn.fhir.jpa.util.TestUtil;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
@ -3476,8 +3477,8 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
@Test @Test
public void testNearSearchDistanceNoDistance() { public void testNearSearchDistanceNoDistance() {
Location loc = new Location(); Location loc = new Location();
double latitude = 1000.0; double latitude = CoordCalculatorTest.LATITUDE_CHIN;
double longitude = 2000.0; double longitude = CoordCalculatorTest.LONGITUDE_CHIN;
Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude);
loc.setPosition(position); loc.setPosition(position);
String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue();
@ -3494,8 +3495,8 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
@Test @Test
public void testNearSearchDistanceZero() { public void testNearSearchDistanceZero() {
Location loc = new Location(); Location loc = new Location();
double latitude = 1000.0; double latitude = CoordCalculatorTest.LATITUDE_CHIN;
double longitude = 2000.0; double longitude = CoordCalculatorTest.LONGITUDE_CHIN;
Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude);
loc.setPosition(position); loc.setPosition(position);
String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue();
@ -3514,30 +3515,31 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
@Test @Test
public void testNearSearchApproximate() { public void testNearSearchApproximate() {
Location loc = new Location(); Location loc = new Location();
double latitude = 1000.0; double latitude = CoordCalculatorTest.LATITUDE_UHN;
double longitude = 2000.0; double longitude = CoordCalculatorTest.LONGITUDE_UHN;
Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude);
loc.setPosition(position); loc.setPosition(position);
String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue();
{ // In the box { // In the box
double offset = 50.0; double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2;
SearchParameterMap map = myMatchUrlService.translateMatchUrl( SearchParameterMap map = myMatchUrlService.translateMatchUrl(
"Location?" + "Location?" +
Location.SP_NEAR + "=" + (latitude + offset) + ":" + (longitude - offset) + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + ":" + CoordCalculatorTest.LONGITUDE_CHIN +
"&" + "&" +
Location.SP_NEAR_DISTANCE + "=" + (offset * 2) + "|http://unitsofmeasure.org|km", myFhirCtx.getResourceDefinition("Location")); Location.SP_NEAR_DISTANCE + "=" + bigEnoughDistance + "|http://unitsofmeasure.org|km", myFhirCtx.getResourceDefinition("Location"));
List<String> ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); List<String> ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map));
assertThat(ids, contains(locId)); assertThat(ids, contains(locId));
} }
{ // Outside the box { // Outside the box
double offset = 50.0; double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2;
SearchParameterMap map = myMatchUrlService.translateMatchUrl( SearchParameterMap map = myMatchUrlService.translateMatchUrl(
"Location?" + "Location?" +
Location.SP_NEAR + "=" + (latitude + offset) + ":" + (longitude - offset) + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + ":" + CoordCalculatorTest.LONGITUDE_CHIN +
"&" + "&" +
Location.SP_NEAR_DISTANCE + "=" + (offset / 2) + "|http://unitsofmeasure.org|km", myFhirCtx.getResourceDefinition("Location")); Location.SP_NEAR_DISTANCE + "=" + tooSmallDistance + "|http://unitsofmeasure.org|km", myFhirCtx.getResourceDefinition("Location"));
List<String> ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); List<String> ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map));
assertThat(ids.size(), is(0)); assertThat(ids.size(), is(0));

View File

@ -15,6 +15,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.jpa.util.CoordCalculatorTest;
import ca.uhn.fhir.jpa.util.TestUtil; import ca.uhn.fhir.jpa.util.TestUtil;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
@ -4146,8 +4147,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test @Test
public void testNearSearchDistanceNoDistance() { public void testNearSearchDistanceNoDistance() {
Location loc = new Location(); Location loc = new Location();
double latitude = 1000.0; double latitude = CoordCalculatorTest.LATITUDE_CHIN;
double longitude = 2000.0; double longitude = CoordCalculatorTest.LATITUDE_CHIN;
Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude);
loc.setPosition(position); loc.setPosition(position);
String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue();
@ -4164,8 +4165,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test @Test
public void testNearSearchDistanceZero() { public void testNearSearchDistanceZero() {
Location loc = new Location(); Location loc = new Location();
double latitude = 1000.0; double latitude = CoordCalculatorTest.LATITUDE_CHIN;
double longitude = 2000.0; double longitude = CoordCalculatorTest.LATITUDE_CHIN;
Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude);
loc.setPosition(position); loc.setPosition(position);
String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue();
@ -4192,36 +4193,36 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test @Test
public void testNearSearchApproximate() { public void testNearSearchApproximate() {
Location loc = new Location(); Location loc = new Location();
double latitude = 1000.0; double latitude = CoordCalculatorTest.LATITUDE_UHN;
double longitude = 2000.0; double longitude = CoordCalculatorTest.LONGITUDE_UHN;
Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude);
loc.setPosition(position); loc.setPosition(position);
String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue();
{ // In the box { // In the box
double offset = 50.0; double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2;
SearchParameterMap map = myMatchUrlService.translateMatchUrl( SearchParameterMap map = myMatchUrlService.translateMatchUrl(
"Location?" + "Location?" +
Location.SP_NEAR + "=" + (latitude + offset) + "|" + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + "|"
(longitude - offset) + "|" + + CoordCalculatorTest.LONGITUDE_CHIN + "|" +
(offset * 2) + "|km", bigEnoughDistance, myFhirCtx.getResourceDefinition("Location"));
myFhirCtx.getResourceDefinition("Location"));
List<String> ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); List<String> ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map));
assertThat(ids, contains(locId)); assertThat(ids, contains(locId));
} }
{ // Outside the box { // Outside the box
double offset = 50.0; double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2;
SearchParameterMap map = myMatchUrlService.translateMatchUrl( SearchParameterMap map = myMatchUrlService.translateMatchUrl(
"Location?" + "Location?" +
Location.SP_NEAR + "=" + (latitude + offset) + "|" + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + "|"
(longitude - offset) + "|" + + CoordCalculatorTest.LONGITUDE_CHIN + "|" +
(offset / 2) + "|km", tooSmallDistance, myFhirCtx.getResourceDefinition("Location"));
myFhirCtx.getResourceDefinition("Location"));
List<String> ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); List<String> ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map));
assertThat(ids.size(), is(0)); assertThat(ids.size(), is(0));
} }
} }
private String toStringMultiline(List<?> theResults) { private String toStringMultiline(List<?> theResults) {

View File

@ -1,20 +1,28 @@
package ca.uhn.fhir.jpa.util; package ca.uhn.fhir.jpa.util;
import org.hibernate.search.spatial.impl.Point;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.geo.Point; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
public class CoordCalculatorTest { public class CoordCalculatorTest {
@Test private final Logger ourLog = LoggerFactory.getLogger(CoordCalculatorTest.class);
public void testCoordCalculator() { // CHIN and UHN coordinates from Google Maps
double latitude = 52.20472; // Distance and bearing from https://www.movable-type.co.uk/scripts/latlong.html
double longitude = 0.14056; public static final double LATITUDE_CHIN = 43.65513;
double bearing = 1.57; public static final double LONGITUDE_CHIN = -79.4170007;
double distance = 15.0; public static final double LATITUDE_UHN = 43.656765;
public static final double LONGITUDE_UHN = -79.3987645;
public static final double DISTANCE_KM_CHIN_TO_UHN = 1.478;
public static final double BEARING_CHIN_TO_UHN = 82 + (55.0 / 60) + (46.0 / 3600);
Point result = CoordCalculator.findTarget(latitude, longitude, bearing, distance); @Test
assertEquals(52.20444, result.getX(), 0.00001); public void testCHINToUHN() {
assertEquals(0.36056, result.getX(), 0.00001); 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);
} }
} }

View File

@ -315,7 +315,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
IExtractor<BaseResourceIndexedSearchParam> extractor = (params, searchParam, value, path) -> { IExtractor<BaseResourceIndexedSearchParam> extractor = (params, searchParam, value, path) -> {
if ("Location.position".equals(path)) { if ("Location.position".equals(path)) {
addCoords_Position(resourceTypeName, params, searchParam, value); addCoords_Position(resourceTypeName, params, searchParam, value);
} }
}; };
@ -741,6 +741,10 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
org.hl7.fhir.r4.model.Location.LocationPositionComponent value = (org.hl7.fhir.r4.model.Location.LocationPositionComponent) theValue; org.hl7.fhir.r4.model.Location.LocationPositionComponent value = (org.hl7.fhir.r4.model.Location.LocationPositionComponent) theValue;
latitude = value.getLatitude(); latitude = value.getLatitude();
longitude = value.getLongitude(); longitude = value.getLongitude();
} else if (theValue instanceof org.hl7.fhir.r5.model.Location.LocationPositionComponent) {
org.hl7.fhir.r5.model.Location.LocationPositionComponent value = (org.hl7.fhir.r5.model.Location.LocationPositionComponent) theValue;
latitude = value.getLatitude();
longitude = value.getLongitude();
} }
// We only accept coordinates when both are present // We only accept coordinates when both are present
if (latitude != null && longitude != null) { if (latitude != null && longitude != null) {