convert degrees to kilometers for near calculation
This commit is contained in:
parent
f2e8037c90
commit
e6f5af01ed
|
@ -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);
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue