From 8b1fdce4f503cb6d173373bdae26208f45af0017 Mon Sep 17 00:00:00 2001 From: Andrew Byrd Date: Tue, 17 Oct 2023 00:18:13 +0200 Subject: [PATCH] Combine WGS84 to web Mercator functions WebMercatorGridPointSet methods now call static Grid functions with zoom. All functions are now using standard Java Math (not FastMath). --- .../java/com/conveyal/r5/analyst/Grid.java | 32 ++++++++-------- .../r5/analyst/WebMercatorGridPointSet.java | 37 ++++--------------- 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/conveyal/r5/analyst/Grid.java b/src/main/java/com/conveyal/r5/analyst/Grid.java index dc6559330..788fd647a 100644 --- a/src/main/java/com/conveyal/r5/analyst/Grid.java +++ b/src/main/java/com/conveyal/r5/analyst/Grid.java @@ -8,7 +8,6 @@ import com.csvreader.CsvReader; import com.google.common.io.LittleEndianDataInputStream; import com.google.common.io.LittleEndianDataOutputStream; -import org.apache.commons.math3.util.FastMath; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.GridCoverageFactory; import org.geotools.coverage.grid.io.AbstractGridFormat; @@ -64,14 +63,9 @@ import static com.conveyal.gtfs.util.Util.human; import static com.conveyal.r5.common.GeometryUtils.checkWgsEnvelopeSize; -import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static java.lang.Double.parseDouble; -import static org.apache.commons.math3.util.FastMath.atan; -import static org.apache.commons.math3.util.FastMath.cos; -import static org.apache.commons.math3.util.FastMath.log; -import static org.apache.commons.math3.util.FastMath.sinh; -import static org.apache.commons.math3.util.FastMath.tan; +import static java.lang.Math.*; /** * Class that represents a grid of opportunity counts in the spherical Mercator "projection" at a given zoom level. @@ -462,6 +456,7 @@ public int featureCount() { /** Like lonToPixel but returns fractional values for positions within a pixel instead of integer pixel numbers. */ public static double lonToFractionalPixel (double lon, int zoom) { + // Factor of 256 yields a pixel value rather than the tile number. return (lon + 180) / 360 * Math.pow(2, zoom) * 256; } @@ -474,12 +469,13 @@ public static int lonToPixel (double lon, int zoom) { } /** - * Return the longitude of the west edge of any pixel at the given zoom level and x pixel number measured from the - * west edge of the world (assuming an integer x pixel number). Noninteger x pixel coordinates will return - * WGS84 locations within that pixel. + * Given an integer web Mercator pixel number, return the longitude in degrees of the west edge of that pixel at + * the given zoom level measured from the west edge of the world (not relative to this grid or to any particular + * tile). Given a non-integer web Mercator pixel number, return WGS84 locations within that pixel. + * Naming should somehow be revised to clarify that it doesn't return the center of the pixel. */ public static double pixelToLon (double xPixel, int zoom) { - return xPixel / (Math.pow(2, zoom) * 256) * 360 - 180; + return xPixel / (pow(2, zoom) * 256) * 360 - 180; } /** @@ -492,7 +488,7 @@ public static double pixelToCenterLon (int xPixel, int zoom) { /** Like latToPixel but returns fractional values for positions within a pixel instead of integer pixel numbers. */ public static double latToFractionalPixel (double lat, int zoom) { - double latRad = FastMath.toRadians(lat); + final double latRad = toRadians(lat); return (1 - log(tan(latRad) + 1 / cos(latRad)) / Math.PI) * Math.pow(2, zoom - 1) * 256; } @@ -502,12 +498,16 @@ public static int latToPixel (double lat, int zoom) { } /** - * Return the latitude of the north edge of any pixel at the given zoom level and y coordinate relative to the top - * edge of the world (assuming an integer pixel). Noninteger pixels will return locations within the pixel. - * We're using FastMath here, because the built-in math functions were taking a large amount of time in profiling. + * Given an integer web Mercator pixel number, return the latitude in degrees of the north edge of that pixel at the + * given zoom level relative to the top (north) edge of the world (not relative to this grid or to any particular + * tile). Given a non-integer web Mercator pixel number, return WGS84 locations within that pixel. + * + * TODO Profile this some time because we had versions using both FastMath and built-in Math functions. + * The difference should be less significant these days. Java now has a StrictMath and the normal Math is optimized. */ public static double pixelToLat (double yPixel, int zoom) { - return FastMath.toDegrees(atan(sinh(Math.PI - (yPixel / 256d) / Math.pow(2, zoom) * 2 * Math.PI))); + final double tile = yPixel / 256d; + return toDegrees(atan(sinh(PI - tile * PI * 2 / pow(2, zoom)))); } /** diff --git a/src/main/java/com/conveyal/r5/analyst/WebMercatorGridPointSet.java b/src/main/java/com/conveyal/r5/analyst/WebMercatorGridPointSet.java index 46607a470..f8665421f 100644 --- a/src/main/java/com/conveyal/r5/analyst/WebMercatorGridPointSet.java +++ b/src/main/java/com/conveyal/r5/analyst/WebMercatorGridPointSet.java @@ -150,54 +150,33 @@ public TIntList getPointsInEnvelope (Envelope envelopeFixedDegrees) { } // http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Mathematics - // FIXME Grid.java has seemingly identical definitions of these same mercator-to-wgs methods. - // These methods should just be wrappers that pass in this WebMercatorGridPointSet's zoom level. - // Grid also has methods that distinguish between pixel corner and center. Those corner methods should be renamed. - // WebMercatorGridPointSet, Grid, and WebMercatorExtents are in the same package and should share methods. /** - * Return the x pixel number containing the given longitude at this grid's zoom level, relative to the left edge - * of the world (not relative to this grid or to any particular tile). - * TODO This method could be reusable and static if zoom level was a parameter. And moved to WebMercatorExtents. + * See com.conveyal.r5.analyst.Grid#lonToPixel(double, int) */ public int lonToPixel (double lon) { - // factor of 256 is to get a pixel value not a tile number - return (int) ((lon + 180) / 360 * Math.pow(2, zoom) * 256); + return Grid.lonToPixel(lon, zoom); } /** - * Return the y pixel number containing the given latitude at this grid's zoom level, relative to the top edge of - * the world (not relative to this grid or to any particular tile). - * TODO This method could be reusable and static if zoom level was a parameter. And moved to WebMercatorExtents. + * See com.conveyal.r5.analyst.Grid#latToPixel(double, int) */ public int latToPixel (double lat) { - double invCos = 1 / Math.cos(Math.toRadians(lat)); - double tan = Math.tan(Math.toRadians(lat)); - double ln = Math.log(tan + invCos); - return (int) ((1 - ln / Math.PI) * Math.pow(2, zoom - 1) * 256); + return Grid.latToPixel(lat, zoom); } /** - * Return the longitude in degrees of the west edge of any pixel at the specified x coordinate relative to the left - * edge of the world (not relative to this grid or to any particular tile), at this grid's zoom level. - * TODO This method could be reusable and static if zoom level was a parameter. - * It should probably also be renamed to clarify that it doesn't return the center of the pixel. - * Another equivalent method is found in Grid, and should probably be merged with this one and WebMercatorExtents. + * See com.conveyal.r5.analyst.Grid#pixelToLon(double, int) */ public double pixelToLon (double x) { - return x / (Math.pow(2, zoom) * 256) * 360 - 180; + return Grid.pixelToLon(x, zoom); } /** - * Return the latitude in degrees of the north edge of any pixel at the specified y coordinate relative to the top - * edge of the world (not relative to this grid or to any particular tile), at this grid's zoom level. - * TODO This method could be reusable and static if zoom level was a parameter. - * It should probably also be renamed to clarify that it doesn't return the center of the pixel. - * Another equivalent method is found in Grid, and should probably be merged with this one and WebMercatorExtents. + * See com.conveyal.r5.analyst.Grid#pixelToLat(double, int) */ public double pixelToLat (double y) { - double tile = y / 256d; - return Math.toDegrees(Math.atan(Math.sinh(Math.PI - tile * Math.PI * 2 / Math.pow(2, zoom)))); + return Grid.pixelToLat(y, zoom); } @Override