/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.lucene.util; import org.elasticsearch.common.geo.GeoDistance; import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.test.ESTestCase; import org.junit.Test; import static org.hamcrest.number.IsCloseTo.closeTo; public class SloppyMathTests extends ESTestCase { @Test public void testAccuracy() { for (double lat1 = -89; lat1 <= 89; lat1+=1) { final double lon1 = randomLongitude(); for (double i = -180; i <= 180; i+=1) { final double lon2 = i; final double lat2 = randomLatitude(); assertAccurate(lat1, lon1, lat2, lon2); } } } @Test public void testSloppyMath() { testSloppyMath(DistanceUnit.METERS, 0.01, 5, 45, 90); testSloppyMath(DistanceUnit.KILOMETERS, 0.01, 5, 45, 90); testSloppyMath(DistanceUnit.INCH, 0.01, 5, 45, 90); testSloppyMath(DistanceUnit.MILES, 0.01, 5, 45, 90); } private static double maxError(double distance) { return distance / 1000.0; } private void testSloppyMath(DistanceUnit unit, double...deltaDeg) { final double lat1 = randomLatitude(); final double lon1 = randomLongitude(); logger.info("testing SloppyMath with {} at \"{}, {}\"", unit, lat1, lon1); for (int test = 0; test < deltaDeg.length; test++) { for (int i = 0; i < 100; i++) { // crop pole areas, sine we now there the function // is not accurate around lat(89°, 90°) and lat(-90°, -89°) final double lat2 = Math.max(-89.0, Math.min(+89.0, lat1 + (random().nextDouble() - 0.5) * 2 * deltaDeg[test])); final double lon2 = lon1 + (random().nextDouble() - 0.5) * 2 * deltaDeg[test]; final double accurate = GeoDistance.ARC.calculate(lat1, lon1, lat2, lon2, unit); final double dist = GeoDistance.SLOPPY_ARC.calculate(lat1, lon1, lat2, lon2, unit); assertThat("distance between("+lat1+", "+lon1+") and ("+lat2+", "+lon2+"))", dist, closeTo(accurate, maxError(accurate))); } } } private static void assertAccurate(double lat1, double lon1, double lat2, double lon2) { double accurate = GeoDistance.ARC.calculate(lat1, lon1, lat2, lon2, DistanceUnit.METERS); double sloppy = GeoDistance.SLOPPY_ARC.calculate(lat1, lon1, lat2, lon2, DistanceUnit.METERS); assertThat("distance between("+lat1+", "+lon1+") and ("+lat2+", "+lon2+"))", sloppy, closeTo(accurate, maxError(accurate))); } private static final double randomLatitude() { // crop pole areas, sine we now there the function // is not accurate around lat(89°, 90°) and lat(-90°, -89°) return (random().nextDouble() - 0.5) * 178.0; } private static final double randomLongitude() { return (random().nextDouble() - 0.5) * 360.0; } }