Преглед изворни кода

Use faster maths to project WGS84 to mercator (#88231)

Ignacio Vera пре 3 година
родитељ
комит
7930d98bc0

+ 5 - 0
docs/changelog/88231.yaml

@@ -0,0 +1,5 @@
+pr: 88231
+summary: Use faster maths to project WGS84 to mercator
+area: Geo
+type: enhancement
+issues: []

+ 7 - 3
server/src/main/java/org/elasticsearch/common/geo/SphericalMercatorUtils.java

@@ -8,6 +8,8 @@
 
 package org.elasticsearch.common.geo;
 
+import org.apache.lucene.util.SloppyMath;
+import org.elasticsearch.core.ESSloppyMath;
 import org.elasticsearch.geometry.Rectangle;
 
 /**
@@ -17,8 +19,8 @@ public class SphericalMercatorUtils {
 
     public static final double MERCATOR_BOUNDS = 20037508.34;
     private static final double MERCATOR_FACTOR = MERCATOR_BOUNDS / 180.0;
-    // latitude lower limit. Below this limit the transformation might result in -Infinity
-    private static final double LAT_LOWER_LIMIT = Math.nextUp(-90.0);
+    // tangent lower limit. Below this limit the transformation result is -Infinity
+    private static final double TAN_LOWER_LIMIT = Math.tan(Math.nextUp(0.0));
 
     /**
      * Transforms WGS84 longitude to a Spherical mercator longitude
@@ -31,7 +33,9 @@ public class SphericalMercatorUtils {
      * Transforms WGS84 latitude to a Spherical mercator latitude
      */
     public static double latToSphericalMercator(double lat) {
-        double y = Math.log(Math.tan((90 + Math.max(lat, LAT_LOWER_LIMIT)) * Math.PI / 360)) / (Math.PI / 180);
+        final double cos = SloppyMath.cos((90 + lat) * Math.PI / 360);
+        final double tan = Math.max(TAN_LOWER_LIMIT, Math.sqrt(1 - cos * cos) / cos);
+        final double y = ESSloppyMath.log(tan) / (Math.PI / 180);
         return y * MERCATOR_FACTOR;
     }
 

+ 13 - 0
server/src/test/java/org/elasticsearch/common/geo/SphericalMercatorUtilTests.java

@@ -66,4 +66,17 @@ public class SphericalMercatorUtilTests extends ESTestCase {
         assertThat(mercatorRect.getMinY(), Matchers.equalTo(latToSphericalMercator(rect.getMinY())));
         assertThat(mercatorRect.getMaxY(), Matchers.equalTo(latToSphericalMercator(rect.getMaxY())));
     }
+
+    public void testLatitudeExactMath() {
+        // check that the maths we are using are precise to 1mm from the strict maths
+        for (int i = 0; i < 10000; i++) {
+            double lat = randomDoubleBetween(-GeoTileUtils.LATITUDE_MASK, GeoTileUtils.LATITUDE_MASK, true);
+            assertEquals(lat + "", strictLatToSphericalMercator(lat), latToSphericalMercator(lat), 1e-3);
+        }
+    }
+
+    private static double strictLatToSphericalMercator(double lat) {
+        double y = Math.log(Math.tan((90 + Math.max(lat, Math.nextUp(-90.0))) * Math.PI / 360)) / (Math.PI / 180);
+        return y * SphericalMercatorUtils.MERCATOR_BOUNDS / 180;
+    }
 }

+ 1 - 0
x-pack/plugin/build.gradle

@@ -143,6 +143,7 @@ tasks.named("yamlRestTestV7CompatTransform").configure { task ->
   task.skipTest("indices.freeze/10_basic/Test index options", "#70192 -- the freeze index API is removed from 8.0")
   task.skipTest("sql/sql/Paging through results", "scrolling through search hit queries no longer produces empty last page in 8.2")
   task.skipTest("service_accounts/10_basic/Test get service accounts", "new service accounts are added")
+  task.skipTest("spatial/70_script_doc_values/diagonal length", "precision changed in 8.4.0")
 
   task.replaceValueInMatch("_type", "_doc")
   task.addAllowedWarningRegex("\\[types removal\\].*")

+ 1 - 1
x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/spatial/70_script_doc_values.yml

@@ -143,4 +143,4 @@ setup:
               script:
                 source: "doc['geo_shape'].getMercatorHeight()"
   - match: { hits.hits.0.fields.width.0: 389.62170283915475 }
-  - match: { hits.hits.0.fields.height.0: 333.37976840604097 }
+  - match: { hits.hits.0.fields.height.0: 333.37976841442287 }