-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#369 monitor vector tiles poc - improve tile coordinate calculation
- Loading branch information
Showing
7 changed files
with
156 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
server/src/main/scala/kpn/server/analyzer/engine/tiles/domain/CoordinateTransform.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package kpn.server.analyzer.engine.tiles.domain | ||
|
||
import org.locationtech.jts.geom.Coordinate | ||
import org.locationtech.jts.geom.LineString | ||
|
||
import scala.math.Pi | ||
import scala.math.atan | ||
import scala.math.exp | ||
import scala.math.log | ||
import scala.math.sin | ||
|
||
/* | ||
EPSG:4326 | ||
WGS84 reference system used by OSM | ||
See: https://en.wikipedia.org/wiki/Mercator_projection | ||
coordinates | ||
longitude (x) | ||
latitude (y) | ||
EPSG:3857 | ||
reference system WGS84 Web Mercator used in tiles | ||
See: https://en.wikipedia.org/wiki/Web_Mercator_projection | ||
world coordinates | ||
worldX | ||
The longitude for a web mercator coordinate where 0 is the international | ||
date line on the west side, 1 is the international date line on the east | ||
side, and 0.5 is the prime meridian. | ||
worldY | ||
The latitude for a web mercator coordinate where 0 is the north edge of | ||
the map, 0.5 is the equator, and 1 is the south edge of the map. | ||
*/ | ||
|
||
object CoordinateTransform { | ||
|
||
private val DEGREES_PER_RADIAN = 180 / Pi | ||
private val RADIANS_PER_DEGREE = Pi / 180 | ||
private val MAX_LAT = worldYtoLat(-0.1) | ||
private val MIN_LAT = worldYtoLat(1.1) | ||
|
||
def worldXtoLon(worldX: Double): Double = { | ||
worldX * 360 - 180 | ||
} | ||
|
||
def worldYtoLat(worldY: Double): Double = { | ||
val n = Pi - 2 * Pi * worldY | ||
DEGREES_PER_RADIAN * atan(0.5 * (exp(n) - exp(-n))) | ||
} | ||
|
||
def lonToWorldX(lon: Double): Double = { | ||
(lon + 180) / 360 | ||
} | ||
|
||
def latToWorldY(lat: Double): Double = { | ||
if (lat <= MIN_LAT) return 1.1 | ||
if (lat >= MAX_LAT) return -0.1 | ||
val sinus = sin(lat * RADIANS_PER_DEGREE) | ||
0.5 - 0.25 * log((1 + sinus) / (1 - sinus)) / Pi | ||
} | ||
|
||
def toWorldCoordinates(lineString: LineString): Array[Coordinate] = { | ||
(0 until lineString.getNumPoints).toArray.map { index => | ||
val point = lineString.getPointN(index) | ||
val x = lonToWorldX(point.getX) | ||
val y = latToWorldY(point.getY) | ||
new Coordinate(x, y) | ||
} | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
server/src/main/scala/kpn/server/analyzer/engine/tiles/domain/NewTile.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package kpn.server.analyzer.engine.tiles.domain | ||
|
||
import org.locationtech.jts.geom.Coordinate | ||
|
||
class NewTile(val z: Int, val x: Int, val y: Int) { | ||
|
||
val name: String = s"$z-$x-$y" | ||
|
||
private val zoomFactor = 1 << z // the number of tiles across the map in each direction | ||
|
||
private val worldXMin = x.toDouble / zoomFactor | ||
private val worldXMax = (x.toDouble + 1) / zoomFactor | ||
private val worldYMin = y.toDouble / zoomFactor | ||
private val worldYMax = (y.toDouble + 1) / zoomFactor | ||
private val worldWidth = worldXMax - worldXMin | ||
private val worldHeight = worldYMax - worldYMin | ||
|
||
|
||
override def equals(obj: Any): Boolean = { | ||
obj.isInstanceOf[Tile] && obj.asInstanceOf[Tile].name == name | ||
} | ||
|
||
override def hashCode(): Int = name.hashCode() | ||
|
||
def scale(worldCoordinates: Seq[Coordinate]): Seq[Coordinate] = { | ||
worldCoordinates.map(scale) | ||
} | ||
|
||
def scale(coordinate: Coordinate): Coordinate = { | ||
val scaledX = ((coordinate.x - worldXMin) * 256 / worldWidth) | ||
val scaledY = ((coordinate.y - worldYMin) * 256 / (worldHeight)) | ||
new Coordinate(scaledX, scaledY) | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
server/src/test/scala/kpn/server/analyzer/engine/tiles/domain/CoordinateTransformTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package kpn.server.analyzer.engine.tiles.domain | ||
|
||
import kpn.core.util.UnitTest | ||
import kpn.server.analyzer.engine.tiles.domain.CoordinateTransform.worldYtoLat | ||
|
||
class CoordinateTransformTest extends UnitTest { | ||
|
||
test("coordinate transform") { | ||
assertTransform(0, 0, 0.5, 0.5) | ||
assertTransform(0, -180, 0, 0.5) | ||
assertTransform(0, 180, 1, 0.5) | ||
assertTransform(0, 180 - 1e-7, 1, 0.5) | ||
assertTransform(45, 0, 0.5, 0.3597) | ||
|
||
worldYtoLat(-0.1) should equal(87.35 +- 0.01) | ||
worldYtoLat(1.1) should equal(-87.35 +- 0.01) | ||
} | ||
|
||
private def assertTransform(lat: Double, lon: Double, worldX: Double, worldY: Double): Unit = { | ||
CoordinateTransform.latToWorldY(lat) should equal(worldY +- 0.0001) | ||
CoordinateTransform.lonToWorldX(lon) should equal(worldX +- 0.0001) | ||
CoordinateTransform.worldYtoLat(worldY) should equal(lat +- 0.01) | ||
CoordinateTransform.worldXtoLon(worldX) should equal(lon +- 0.01) | ||
} | ||
} |