You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

160 lines
5.4 KiB

6 months ago
//-----------------------------------------------------------------------
// <copyright file="TileCover.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Map
{
using System;
using System.Collections.Generic;
using Mapbox.Utils;
using UnityEngine;
/// <summary>
/// Helper funtions to get a tile cover, i.e. a set of tiles needed for
/// covering a bounding box.
/// </summary>
public static class TileCover
{
/// <summary> Get a tile cover for the specified bounds and zoom. </summary>
/// <param name="bounds"> Geographic bounding box.</param>
/// <param name="zoom"> Zoom level. </param>
/// <returns> The tile cover set. </returns>
/// <example>
/// Build a map of Colorado using TileCover:
/// <code>
/// var sw = new Vector2d(36.997749, -109.0524961);
/// var ne = new Vector2d(41.0002612, -102.0609668);
/// var coloradoBounds = new Vector2dBounds(sw, ne);
/// var tileCover = TileCover.Get(coloradoBounds, 8);
/// Console.Write("Tiles Needed: " + tileCover.Count);
/// foreach (var id in tileCover)
/// {
/// var tile = new RasterTile();
/// var parameters = new Tile.Parameters();
/// parameters.Id = id;
/// parameters.Fs = MapboxAccess.Instance;
/// parameters.TilesetId = "mapbox://styles/mapbox/outdoors-v10";
/// tile.Initialize(parameters, (Action)(() =&gt;
/// {
/// // Place tiles and load textures.
/// }));
/// }
/// </code>
/// </example>
public static HashSet<CanonicalTileId> Get(Vector2dBounds bounds, int zoom)
{
var tiles = new HashSet<CanonicalTileId>();
if (bounds.IsEmpty() ||
bounds.South > Constants.LatitudeMax ||
bounds.North < -Constants.LatitudeMax)
{
return tiles;
}
var hull = Vector2dBounds.FromCoordinates(
new Vector2d(Math.Max(bounds.South, -Constants.LatitudeMax), bounds.West),
new Vector2d(Math.Min(bounds.North, Constants.LatitudeMax), bounds.East));
var sw = CoordinateToTileId(hull.SouthWest, zoom);
var ne = CoordinateToTileId(hull.NorthEast, zoom);
// Scanlines.
for (var x = sw.X; x <= ne.X; ++x)
{
for (var y = ne.Y; y <= sw.Y; ++y)
{
tiles.Add(new UnwrappedTileId(zoom, x, y).Canonical);
}
}
return tiles;
}
public static HashSet<UnwrappedTileId> GetWithWebMerc(Vector2dBounds bounds, int zoom)
{
HashSet<UnwrappedTileId> tiles = new HashSet<UnwrappedTileId>();
HashSet<CanonicalTileId> canonicalTiles = new HashSet<CanonicalTileId>();
if (bounds.IsEmpty()) { return tiles; }
//stay within WebMerc bounds
Vector2d swWebMerc = new Vector2d(Math.Max(bounds.SouthWest.x, -Constants.WebMercMax), Math.Max(bounds.SouthWest.y, -Constants.WebMercMax));
Vector2d neWebMerc = new Vector2d(Math.Min(bounds.NorthEast.x, Constants.WebMercMax), Math.Min(bounds.NorthEast.y, Constants.WebMercMax));
UnwrappedTileId swTile = WebMercatorToTileId(swWebMerc, zoom);
UnwrappedTileId neTile = WebMercatorToTileId(neWebMerc, zoom);
for (int x = swTile.X; x <= neTile.X; x++)
{
for (int y = neTile.Y; y <= swTile.Y; y++)
{
UnwrappedTileId uwtid = new UnwrappedTileId(zoom, x, y);
//hack: currently too many tiles are created at lower zoom levels
//investigate formulas, this worked before
if (!canonicalTiles.Contains(uwtid.Canonical))
{
tiles.Add(uwtid);
canonicalTiles.Add(uwtid.Canonical);
}
}
}
return tiles;
}
/// <summary> Converts a coordinate to a tile identifier. </summary>
/// <param name="coord"> Geographic coordinate. </param>
/// <param name="zoom"> Zoom level. </param>
/// <returns>The to tile identifier.</returns>
/// <example>
/// Convert a geocoordinate to a TileId:
/// <code>
/// var unwrappedTileId = TileCover.CoordinateToTileId(new Vector2d(40.015, -105.2705), 18);
/// Console.Write("UnwrappedTileId: " + unwrappedTileId.ToString());
/// </code>
/// </example>
public static UnwrappedTileId CoordinateToTileId(Vector2d coord, int zoom)
{
var lat = coord.x;
var lng = coord.y;
// See: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
var x = (int)Math.Floor((lng + 180.0) / 360.0 * Math.Pow(2.0, zoom));
var y = (int)Math.Floor((1.0 - Math.Log(Math.Tan(lat * Math.PI / 180.0)
+ 1.0 / Math.Cos(lat * Math.PI / 180.0)) / Math.PI) / 2.0 * Math.Pow(2.0, zoom));
return new UnwrappedTileId(zoom, x, y);
}
/// <summary>
/// Converts a Web Mercator coordinate to a tile identifier. https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Derivation_of_tile_names
/// </summary>
/// <param name="webMerc">Web Mercator coordinate</param>
/// <param name="zoom">Zoom level</param>
/// <returns>The to tile identifier.</returns>
public static UnwrappedTileId WebMercatorToTileId(Vector2d webMerc, int zoom)
{
// See: https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Derivation_of_tile_names
double tileCount = Math.Pow(2, zoom);
//this SDK defines Vector2d.x as latitude and Vector2d.y as longitude
//same for WebMerc, so we have to flip x/y to make this formula work
double dblX = webMerc.x / Constants.WebMercMax;
double dblY = webMerc.y / Constants.WebMercMax;
int x = (int)Math.Floor((1 + dblX) / 2 * tileCount);
int y = (int)Math.Floor((1 - dblY) / 2 * tileCount);
return new UnwrappedTileId(zoom, x, y);
}
}
}