///|
pub fn tile_bounds(tile : Tile) -> Result[TileBounds, RouteError] {
match validate_tile(tile) {
Ok(_) => ()
Err(err) => return Err(err)
}
let n = (1 << tile.z).to_double()
let west = tile.x.to_double() / n * 360.0 - 180.0
let east = (tile.x + 1).to_double() / n * 360.0 - 180.0
let north = tile_y_to_lat(tile.y, n)
let south = tile_y_to_lat(tile.y + 1, n)
Ok(TileBounds(tile, west, south, east, north))
}
///|
pub fn tile_center(tile : Tile) -> Result[Point, RouteError] {
let bounds = match tile_bounds(tile) {
Ok(value) => value
Err(err) => return Err(err)
}
Ok(
Point(
(bounds.south + bounds.north) / 2.0,
(bounds.west + bounds.east) / 2.0,
),
)
}
///|
pub fn tile_range_for_bbox(
bbox : BBox,
zoom : Int,
) -> Result[TileRange, RouteError] {
match validate_zoom(zoom) {
Ok(_) => ()
Err(err) => return Err(err)
}
let sw = match point_to_tile(Point(bbox.min_lat, bbox.min_lon), zoom) {
Ok(value) => value
Err(err) => return Err(err)
}
let ne = match point_to_tile(Point(bbox.max_lat, bbox.max_lon), zoom) {
Ok(value) => value
Err(err) => return Err(err)
}
Ok(
TileRange(
zoom,
min_int(sw.x, ne.x),
min_int(sw.y, ne.y),
max_int(sw.x, ne.x),
max_int(sw.y, ne.y),
),
)
}
///|
pub fn tiles_for_bbox(
bbox : BBox,
zoom : Int,
) -> Result[Array[Tile], RouteError] {
let range = match tile_range_for_bbox(bbox, zoom) {
Ok(value) => value
Err(err) => return Err(err)
}
tiles_for_range(range)
}
///|
pub fn tiles_for_range(range : TileRange) -> Result[Array[Tile], RouteError] {
match validate_zoom(range.z) {
Ok(_) => ()
Err(err) => return Err(err)
}
let limit = 1 << range.z
if range.min_x < 0 ||
range.min_y < 0 ||
range.max_x >= limit ||
range.max_y >= limit ||
range.max_x < range.min_x ||
range.max_y < range.min_y {
return Err(
InvalidTileCoordinate(tile=Tile(range.z, range.min_x, range.min_y)),
)
}
let out = Array::new()
let mut y = range.min_y
while y <= range.max_y {
let mut x = range.min_x
while x <= range.max_x {
out.push(Tile(range.z, x, y))
x = x + 1
}
y = y + 1
}
Ok(out)
}
///|
pub fn tile_count_for_range(range : TileRange) -> Result[Int, RouteError] {
match tiles_for_range(range) {
Ok(value) => Ok(value.length())
Err(err) => Err(err)
}
}
///|
pub fn tile_parent(tile : Tile) -> Result[Tile, RouteError] {
match validate_tile(tile) {
Ok(_) => ()
Err(err) => return Err(err)
}
if tile.z == 0 {
Ok(tile)
} else {
Ok(Tile(tile.z - 1, tile.x / 2, tile.y / 2))
}
}
///|
pub fn tile_children(tile : Tile) -> Result[Array[Tile], RouteError] {
match validate_tile(tile) {
Ok(_) => ()
Err(err) => return Err(err)
}
let out = Array::new(capacity=4)
let z = tile.z + 1
if z > 22 {
return Err(InvalidZoom(zoom=z))
}
out.push(Tile(z, tile.x * 2, tile.y * 2))
out.push(Tile(z, tile.x * 2 + 1, tile.y * 2))
out.push(Tile(z, tile.x * 2, tile.y * 2 + 1))
out.push(Tile(z, tile.x * 2 + 1, tile.y * 2 + 1))
Ok(out)
}
///|
pub fn tile_neighbors(tile : Tile) -> Result[Array[Tile], RouteError] {
match validate_tile(tile) {
Ok(_) => ()
Err(err) => return Err(err)
}
let out = Array::new()
let limit = 1 << tile.z
let mut dy = -1
while dy <= 1 {
let mut dx = -1
while dx <= 1 {
if dx == 0 && dy == 0 {
()
} else {
let x = tile.x + dx
let y = tile.y + dy
if x >= 0 && x < limit && y >= 0 && y < limit {
out.push(Tile(tile.z, x, y))
}
}
dx = dx + 1
}
dy = dy + 1
}
Ok(out)
}
///|
pub fn expand_tiles_with_neighbors(
tiles : ArrayView[Tile],
) -> Result[Array[Tile], RouteError] {
let out = Array::new()
for tile in tiles {
match validate_tile(tile) {
Ok(_) => push_unique_tile(out, tile)
Err(err) => return Err(err)
}
let neighbors = match tile_neighbors(tile) {
Ok(value) => value
Err(err) => return Err(err)
}
for neighbor in neighbors {
push_unique_tile(out, neighbor)
}
}
Ok(out)
}
///|
pub fn tile_quadkey(tile : Tile) -> Result[String, RouteError] {
match validate_tile(tile) {
Ok(_) => ()
Err(err) => return Err(err)
}
if tile.z == 0 {
return Ok("")
}
let out = StringBuilder(size_hint=tile.z)
let mut mask = 1 << (tile.z - 1)
while mask > 0 {
let mut digit = 0
if (tile.x & mask) != 0 {
digit = digit + 1
}
if (tile.y & mask) != 0 {
digit = digit + 2
}
out.write_string(digit.to_string())
mask = mask / 2
}
Ok(out.to_string())
}
///|
pub fn quadkey_to_tile(quadkey : String) -> Result[Tile, RouteError] {
let z = quadkey.length()
match validate_zoom(z) {
Ok(_) => ()
Err(err) => return Err(err)
}
if z == 0 {
return Ok(Tile(0, 0, 0))
}
let mut x = 0
let mut y = 0
let mut mask = 1 << (z - 1)
for i in 0.. ()
'1' => x = x + mask
'2' => y = y + mask
'3' => {
x = x + mask
y = y + mask
}
_ =>
return Err(
MalformedPolyline(pos=i, reason="quadkey digit must be 0, 1, 2, or 3"),
)
}
mask = mask / 2
}
Ok(Tile(z, x, y))
}
///|
pub fn route_tile_range(
points : ArrayView[Point],
zoom : Int,
) -> Result[TileRange, RouteError] {
let bbox = match bounding_box(points) {
Ok(value) => value
Err(err) => return Err(err)
}
tile_range_for_bbox(bbox, zoom)
}
///|
pub fn route_tile_bounds(
points : ArrayView[Point],
zoom : Int,
) -> Result[BBox, RouteError] {
let tiles = match tile_cover(points, zoom) {
Ok(value) => value
Err(err) => return Err(err)
}
if tiles.length() == 0 {
return Err(EmptyRoute)
}
let first = match tile_bounds(tiles[0]) {
Ok(value) => value.to_bbox()
Err(err) => return Err(err)
}
let mut out = first
for i in 1.. out = out.merge(bounds.to_bbox())
Err(err) => return Err(err)
}
}
Ok(out)
}
///|
pub fn tile_range_to_string(range : TileRange) -> String {
range.to_string()
}
///|
fn validate_tile(tile : Tile) -> Result[Unit, RouteError] {
match validate_zoom(tile.z) {
Ok(_) => ()
Err(err) => return Err(err)
}
let limit = 1 << tile.z
if tile.x >= 0 && tile.x < limit && tile.y >= 0 && tile.y < limit {
Ok(())
} else {
Err(InvalidTileCoordinate(tile~))
}
}
///|
fn tile_y_to_lat(y : Int, n : Double) -> Double {
let mercator = @math.PI * (1.0 - 2.0 * y.to_double() / n)
rad_to_deg(@math.atan(sinh_double(mercator)))
}
///|
fn sinh_double(value : Double) -> Double {
(@math.exp(value) - @math.exp(-value)) / 2.0
}