Commit 685be9be authored by Matija Obreza's avatar Matija Obreza

Fixed rounding error in CoordUtil#tileToLon (fixes #55)

parent e012be01
......@@ -23,54 +23,120 @@ import org.genesys2.server.service.impl.FilterHandler.AppliedFilters;
public interface MappingService {
/** The default tile color. */
public Color DEFAULT_TILE_COLOR = Color.decode("#88ba41");
/**
* Clear the tiles cache.
*/
void clearCache();
/**
* Produce kml with data filtered by specified filter.
*
* @param filters
* the filters
* @return the string
*/
String filteredKml(AppliedFilters filters);
/**
* Filtered geo json.
*
* @param filters
* the filters
* @param limit
* the limit
* @return the string
* @throws IOException
* Signals that an I/O exception has occurred.
*/
String filteredGeoJson(AppliedFilters filters, Integer limit) throws IOException;
/**
* Gets the tile image for applied filters
*
* @param filters
* the filters
* @param zoom
* the zoom
* @param xtile
* the xtile
* @param ytile
* the ytile
* @return the tile
*/
byte[] getTile(AppliedFilters filters, int zoom, int xtile, int ytile);
/**
* Coordinate utilities
*/
public static class CoordUtil {
public static double tileToLon(int zoom, int xtile) {
return Math.floor((double) xtile / (1 << zoom) * 360.0 - 180.0);
/**
* Convert tile index at zoom level to longitude.
*
* @param zoom
* zoom level
* @param xtile
* tile index
* @return longitude of left side of tile
*/
public static double tileToLon(final int zoom, final int xtile) {
return 360.0 * xtile / (1 << zoom) - 180.0;
}
public static double tileToLat(int zoom, int ytile) {
/**
* Convert tile index at zoom level to latitude
*
* @param zoom
* zoom level
* @param ytile
* tile index
* @return latitude of the top side of the tile
*/
public static double tileToLat(final int zoom, final int ytile) {
final double n = Math.PI - 2.0 * Math.PI * ytile / (1 << zoom);
return Math.toDegrees(Math.atan(Math.sinh(n)));
}
public static int tileToLon1(int zoom, int xtile) {
// n = 2 ^ zoom
final double n = 1 << zoom;
// lon_deg = xtile / n * 360.0 - 180.0
final double lon = xtile / n * 360.0 - 180.0;
return (int) Math.floor(lon);
}
public static int tileToLat1(int zoom, int ytile) {
// n = 2 ^ zoom
final double n = 1 << zoom;
// lat_rad = arctan(sinh(π * (1 - 2 * ytile / n)))
final double latr = Math.atan(Math.sinh(Math.PI * (1.0d - 2.0d * ytile / n)));
// lat_deg = lat_rad * 180.0 / π
final double lat = latr * 180.0 / Math.PI;
return (int) Math.floor(lat);
}
public static int lonToTile(int zoom, double lon) {
/**
* Lon to tile.
*
* @param zoom
* the zoom
* @param lon
* the lon
* @return the int
*/
public static int lonToTile(final int zoom, final double lon) {
return (int) Math.floor((lon + 180.0) / 360.0 * (1 << zoom));
}
public static int latToTile(int zoom, double lat) {
/**
* Lat to tile.
*
* @param zoom
* the zoom
* @param lat
* the lat
* @return the int
*/
public static int latToTile(final int zoom, final double lat) {
final double latr = Math.toRadians(lat);
return (int) Math.floor((1 - Math.log(Math.tan(latr) + 1 / Math.cos(latr)) / Math.PI) / 2 * (1 << zoom));
}
public static int lonToImg(int zoom, double lon) {
/**
* Lon to img.
*
* @param zoom
* the zoom
* @param lon
* the lon
* @return the int
*/
public static int lonToImg(final int zoom, double lon) {
// n = 2 ^ zoom
final int xtile = lonToTile(zoom, lon);
final double a = tileToLon(zoom, xtile);
......@@ -86,7 +152,16 @@ public interface MappingService {
return (int) Math.floor(lon);
}
public static int latToImg(int zoom, double lat) {
/**
* Lat to img.
*
* @param zoom
* the zoom
* @param lat
* the lat
* @return the int
*/
public static int latToImg(final int zoom, double lat) {
// n = 2 ^ zoom
final int ytile = latToTile(zoom, lat);
// System.err.println("ytile=" + ytile);
......@@ -103,16 +178,42 @@ public interface MappingService {
return (int) Math.floor(lat);
}
public static int latToImg3(int zoom, int tile, double lat) {
final double latr = Math.toRadians(lat);
/**
* Mercator projection of decimal latitude to a y-pixel on tile at
* specific zoom level.
*
* @param zoom
* Zoom level
* @param tile
* Tile index
* @param lat
* Latitude
* @return the int
*/
public static int latToImg3(final int zoom, final int tile, double lat) {
lat = Math.min(85.0511287, Math.max(lat, -85.0511287));
final double pixAtZoom = (1 << zoom) * 256;
final double latr = Math.toRadians(lat);
final double y1 = Math.floor((1 - Math.log(Math.tan(latr) + 1 / Math.cos(latr)) / Math.PI) / 2 * pixAtZoom);
return (int) y1 - tile * 256;
}
public static int lonToImg3(int zoom, int tile, double lng) {
final double pixAtZoom = (1 << zoom) * 256;
return (int) (Math.floor((180.0 + lng) / 360.0 * pixAtZoom) - tile * 256);
/**
* Convert decimal longitude to an x-pixel on tile at specific zoom
* level.
*
* @param zoom
* Zoom level
* @param tile
* Tile index
* @param lng
* Longitude
* @return the int
*/
public static int lonToImg3(final int zoom, final int tile, final double lng) {
final double totalPixelsAtZoom = (1 << zoom) * 256;
final double longitudePixelAtZoom = Math.floor((180.0 + lng) / 360.0 * totalPixelsAtZoom);
return (int) (longitudePixelAtZoom - tile * 256);
}
}
}
......@@ -177,13 +177,12 @@ public class MappingServiceImpl implements MappingService {
@Override
public void processRow(ResultSet rs) throws SQLException {
try {
// System.err.println("Adding dot " +
// rs.getDouble("longitude") + " " +
// rs.getDouble("latitude"));
final int longitude = CoordUtil.lonToImg3(zoom, xtile, rs.getDouble("longitude"));
final int latitude = CoordUtil.latToImg3(zoom, ytile, rs.getDouble("latitude"));
// System.err.println("Dotting " + longitude + "," +
// latitude);
if (LOG.isTraceEnabled()) {
LOG.trace("Adding geo=" + rs.getDouble("longitude") + ", " + rs.getDouble("latitude") + " as pixe=[" + longitude + "," + latitude + "]");
}
for (int i = -pixelSize / 2; i <= pixelSize / 2; i++) {
for (int j = -pixelSize / 2; j <= pixelSize / 2; j++) {
......
......@@ -16,11 +16,15 @@
package org.genesys2.server.test;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.genesys2.server.service.MappingService.CoordUtil.latToImg;
import static org.genesys2.server.service.MappingService.CoordUtil.lonToImg;
import static org.genesys2.server.service.MappingService.CoordUtil.latToImg3;
import static org.genesys2.server.service.MappingService.CoordUtil.lonToImg3;
import static org.genesys2.server.service.MappingService.CoordUtil.tileToLat;
import static org.genesys2.server.service.MappingService.CoordUtil.tileToLon;
import static org.junit.Assert.assertEquals;
import org.genesys2.server.service.MappingService.CoordUtil;
import org.junit.Test;
......@@ -40,6 +44,15 @@ public class TilesMathTest {
assertEquals(0, tileToLon(2, 2), 0);
assertEquals(90, tileToLon(2, 3), 0);
assertEquals(180, tileToLon(2, 4), 0);
for (int zoom = 0; zoom < 20; zoom++) {
double last = -200.0;
for (int tile = 0; tile <= (1 << zoom); tile++) {
double curr = tileToLon(zoom, tile);
assertThat("Zoom=" + zoom + " tile=" + tile + " problem", curr, greaterThan(last));
last = curr;
}
}
}
@Test
......@@ -57,30 +70,83 @@ public class TilesMathTest {
assertEquals(255, lonToImg(5, 179.99999));
}
/**
* https://www.genesys-pgr.org/acn/id/4620209 doesn't show at zoom level 9
*/
@Test
public void acn4620209() {
double lat = 33.07361, lon = -16.32389;
assertEquals(200, lonToImg3(9, 232, lon));
assertEquals(145, lonToImg3(10, 465, lon));
assertThat(tileToLon(8, 116), lessThanOrEqualTo(lon));
assertThat(tileToLon(8, 116 + 1), greaterThanOrEqualTo(lon));
assertThat(tileToLon(9, 232), lessThanOrEqualTo(lon));
assertThat(tileToLon(9, 232 + 1), greaterThanOrEqualTo(lon));
assertEquals(103, latToImg3(0, 0, lat));
assertEquals(156, latToImg3(2, 1, lat));
assertEquals(56, latToImg3(3, 3, lat));
assertEquals(112, latToImg3(4, 6, lat));
assertEquals(225, latToImg3(5, 12, lat));
assertEquals(195, latToImg3(6, 25, lat));
assertEquals(134, latToImg3(7, 51, lat));
assertEquals(13, latToImg3(8, 103, lat));
assertThat(CoordUtil.tileToLat(8, 103), greaterThanOrEqualTo(lat));
assertThat(CoordUtil.tileToLat(8, 103 + 1), lessThanOrEqualTo(lat));
assertEquals(27, latToImg3(9, 206, lat));
assertThat(CoordUtil.tileToLat(9, 206), greaterThanOrEqualTo(lat));
assertThat(CoordUtil.tileToLat(9, 206 + 1), lessThanOrEqualTo(lat));
assertEquals(55, latToImg3(10, 412, lat));
assertThat(CoordUtil.tileToLat(10, 412), greaterThanOrEqualTo(lat));
assertThat(CoordUtil.tileToLat(10, 412 + 1), lessThanOrEqualTo(lat));
assertEquals(111, latToImg3(11, 824, lat));
}
@Test
public void testLatToImg() {
// assertEquals(0, latToImg(1, 0));
// assertEquals(0, latToImg(1, 85));
// assertEquals(0, latToImg(2, 85));
// assertEquals(0, latToImg(3, 85));
// assertEquals(0, latToImg(4, 85));
// assertEquals(0, latToImg(5, 85));
// assertEquals(0, latToImg(6, 85));
// System.err.println("HERE" + latToImg(0, 85));
// System.err.println("HERE" + latToImg(1, 85));
// System.err.println("HERE" + latToImg(2, 85));
System.err.println("HERE=" + latToImg(0, 85));
System.err.println("HERE=" + latToImg(0, -85.010));
System.err.println("......");
System.err.println("HERE=" + latToImg(1, 85));
System.err.println("HERE=" + latToImg(1, -85.01));
public void testLonToImg3() {
assertEquals(0, lonToImg3(1, 0, -180.0));
assertEquals(0, lonToImg3(2, 0, -180.0));
assertEquals(0, lonToImg3(3, 0, -180.0));
assertEquals(0, lonToImg3(4, 0, -180.0));
assertEquals(0, lonToImg3(9, 0, -180.0));
assertEquals(255, lonToImg3(1, 1, 179.99999));
assertEquals(255, lonToImg3(2, (1 << 2) - 1, 179.99999));
assertEquals(255, lonToImg3(3, (1 << 3) - 1, 179.99999));
assertEquals(255, lonToImg3(4, (1 << 4) - 1, 179.99999));
assertEquals(255, lonToImg3(9, (1 << 9) - 1, 179.99999));
assertEquals(255, lonToImg3(12, (1 << 12) - 1, 179.99999));
assertEquals(255, lonToImg3(20, (1 << 20) - 1, 179.9999999999));
}
@Test
public void test2LatToImg() {
System.err.println(CoordUtil.tileToLon1(1, 1));
System.err.println(CoordUtil.tileToLon(1, 1));
public void testLatToImg3() {
assertEquals(0, latToImg(1, 85.0511287));
assertEquals(0, latToImg3(1, 0, 85.0));
assertEquals(0, latToImg3(2, 0, 85.05));
assertEquals(0, latToImg3(3, 0, 85.05));
assertEquals(0, latToImg3(4, 0, 85.05));
assertEquals(0, latToImg3(9, 0, 85.051));
assertEquals(0, latToImg3(20, 0, 90));
assertEquals(128, latToImg3(0, 0, 0.0));
assertEquals(256, latToImg3(1, 0, 0.0));
assertEquals(0, latToImg3(2, (1 << 2) / 2, 0.0));
assertEquals(0, latToImg3(3, (1 << 3) / 2, 0.0));
assertEquals(0, latToImg3(4, (1 << 4) / 2, 0.0));
assertEquals(0, latToImg3(9, (1 << 9) / 2, 0.0));
assertEquals(255, latToImg3(1, 1, -85.051));
assertEquals(255, latToImg3(2, (1 << 2) - 1, -85.051));
assertEquals(255, latToImg3(3, (1 << 3) - 1, -85.051));
assertEquals(255, latToImg3(4, (1 << 4) - 1, -85.051));
assertEquals(255, latToImg3(9, (1 << 9) - 1, -85.051));
assertEquals(255, latToImg3(12, (1 << 12) - 1, -85.0511));
assertEquals(255, latToImg3(20, (1 << 20) - 1, -85.0511287));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment