/* INFINITY CODE */ /* https://infinity-code.com */ using System; using System.Linq; using System.Text; using System.Text.RegularExpressions; using UnityEngine; using Random = UnityEngine.Random; namespace InfinityCode.RealWorldTerrain { public static class RealWorldTerrainTextureProviderManager { private const string SATELLITE = "Satellite"; private const string RELIEF = "Relief"; private const string TERRAIN = "Terrain"; private const string MAP = "Map"; private static Provider[] providers; public static MapType FindMapType(string mapTypeID) { if (providers == null) InitProviders(); if (string.IsNullOrEmpty(mapTypeID)) return providers[0].types[0]; string[] parts = mapTypeID.Split('.'); foreach (Provider provider in providers) { if (provider.id == parts[0]) { if (parts.Length == 1) return provider.types[0]; foreach (MapType type in provider.types) { if (type.id == parts[1]) return type; } return provider.types[0]; } } return providers[0].types[0]; } public static Provider[] GetProviders() { if (providers == null) InitProviders(); return providers; } public static string[] GetProvidersTitle() { if (providers == null) InitProviders(); return providers.Select(p => p.title).ToArray(); } private static void InitProviders() { providers = new[] { new Provider("arcgis", "ArcGIS (Esri)") { url = "https://server.arcgisonline.com/ArcGIS/rest/services/{variant}/MapServer/tile/{zoom}/{y}/{x}", types = new [] { new MapType("WorldImagery", "ag") { variant = "World_Imagery" }, } }, new Provider("DigitalGlobe") { url = "https://a.tiles.mapbox.com/v4/digitalglobe.{variant}/{zoom}/{x}/{y}.jpg?access_token={accesstoken}", types = new [] { new MapType("Satellite", "dg") { variant = "nal0g75k" }, }, extraFields = new [] { new ExtraField("Access Token", "accesstoken"), }, help = new [] { "1 map view = 15 tiles" } }, new Provider("Mapbox") { types = new [] { new MapType(SATELLITE, "mbs") { url = "https://a.tiles.mapbox.com/v4/mapbox.satellite/{zoom}/{x}/{y}.png?events=true&access_token={accesstoken}" }, new MapType("Map", "mbm") { url = "https://api.mapbox.com/styles/v1/{userid}/{mapid}/tiles/256/{z}/{x}/{y}?access_token={accesstoken}", extraFields = new [] { new ExtraField("User ID", "userid"), new ExtraField("Map ID", "mapid"), } }, new MapType("Classic", "mbc") { url = "https://a.tiles.mapbox.com/v4/{mapid}/{zoom}/{x}/{y}.png?&events=true&access_token={accesstoken}", extraFields = new [] { new ExtraField("Map ID", "mapid"), }, help = new [] { "Only raster tiles are supported." } } }, extraFields = new [] { new ExtraField("Access Token", "accesstoken"), }, help = new [] { "1 map view = 15 tiles" } }, new Provider("MapQuest") { url = "https://a.tiles.mapbox.com/v4/{variant}/{zoom}/{x}/{y}.png?access_token={accesstoken}", types = new [] { new MapType(SATELLITE, "mq") { variant = "mapquest.satellite" }, }, extraFields = new [] { new ToggleExtraGroup("Anonymous", true, new [] { new ExtraField("Access Token", "accesstoken", "pk.eyJ1IjoibWFwcXVlc3QiLCJhIjoiY2Q2N2RlMmNhY2NiZTRkMzlmZjJmZDk0NWU0ZGJlNTMifQ.mPRiEubbajc6a5y9ISgydg") }) }, }, new Provider("mapy", "Mapy.CZ") { url = "https://m[0-4].mapserver.mapy.cz/{variant}/{zoom}-{x}-{y}", types = new [] { new MapType(SATELLITE, "mcz") { variant = "ophoto-m" }, } }, new Provider("nokia", "Nokia Maps (here.com)") { url = "https://[1-4].{prop2}.maps.cit.api.here.com/maptile/2.1/{prop}/newest/{variant}/{zoom}/{x}/{y}/256/png8?lg={lng}&app_id={appid}&app_code={appcode}", prop = "maptile", prop2 = "base", types = new [] { new MapType(SATELLITE, "n") { variant = "satellite.day", prop2 = "aerial", }, }, extraFields = new [] { new ToggleExtraGroup("Anonymous", true, new [] { new ExtraField("App ID", "appid", "xWVIueSv6JL0aJ5xqTxb"), new ExtraField("App Code", "appcode", "djPZyynKsbTjIUDOBcHZ2g"), }) } }, new Provider("osm", "OpenStreetMap") { types = new [] { new MapType("Mapnik", "osmm") { url = "https://a.tile.openstreetmap.org/{zoom}/{x}/{y}.png" }, new MapType("BlackAndWhite", "osmbw") { url = "http://a.tiles.wmflabs.org/bw-mapnik/{zoom}/{x}/{y}.png" }, new MapType("DE", "osmde") { url = "http://a.tile.openstreetmap.de/tiles/osmde/{zoom}/{x}/{y}.png" }, new MapType("France", "osmfr") { url = "https://a.tile.openstreetmap.fr/osmfr/{zoom}/{x}/{y}.png" }, new MapType("HOT", "osmhot") { url = "https://a.tile.openstreetmap.fr/hot/{zoom}/{x}/{y}.png" }, } }, new Provider("sentinel2", "Sentinel-2") { types = new [] { new MapType("satellite", "s2s") { url = "https://services.sentinel-hub.com/ogc/wmts/cd280189-7c51-45a6-ab05-f96a76067710?REQUEST=GetTile&TILEMATRIXSET=PopularWebMercator256&LAYER=1_TRUE_COLOR&TILEMATRIX={zoom}&TILEROW={y}&TILECOL={x}&showlogo=false" }, } }, new Provider("virtualearth", "Virtual Earth (Bing Maps)") { types = new [] { new MapType("Aerial", "ve") { url = "https://t[0-4].ssl.ak.tiles.virtualearth.net/tiles/a{quad}.jpeg?mkt={lng}&g=1457&n=z", }, } }, new Provider("Custom") { types = new [] { new MapType("Custom", "custom") { isCustom = true } } } }; for (int i = 0; i < providers.Length; i++) { Provider provider = providers[i]; provider.index = i; for (int j = 0; j < provider.types.Length; j++) { MapType type = provider.types[j]; type.provider = provider; type.fullID = provider.id + "." + type.id; type.index = j; } } } public static string Upgrade(RealWorldTerrainTextureProvider providerID) { StringBuilder builder = new StringBuilder(); if (providerID == RealWorldTerrainTextureProvider.arcGIS) builder.Append("arcgis"); else if (providerID == RealWorldTerrainTextureProvider.google) { Debug.LogWarning("Support for Google Maps is removed, please use another provider.\nIf you really want to continue using Google Maps, you can do it using Provider - Custom."); return "arcgis"; } else if (providerID == RealWorldTerrainTextureProvider.nokia) builder.Append("nokia"); else if (providerID == RealWorldTerrainTextureProvider.mapQuest) builder.Append("mapquest"); else if (providerID == RealWorldTerrainTextureProvider.virtualEarth) builder.Append("virtualearth"); else if (providerID == RealWorldTerrainTextureProvider.openStreetMap) builder.Append("osm"); else if (providerID == RealWorldTerrainTextureProvider.custom) builder.Append("custom").Append(".").Append("custom"); else { Debug.LogWarning("Trying to upgrade provider failed. Please select the provider manually."); return "arcgis"; } return builder.ToString(); } public class Provider { /// /// ID of provider /// public readonly string id; /// /// Human-readable provider title. /// public readonly string title; /// /// Index of current provider. /// public int index; /// /// Extension. Token {ext}, that is being replaced in the URL. /// public string ext; /// /// Property. Token {prop}, that is being replaced in the URL. /// public string prop; /// /// Property. Token {prop2}, that is being replaced in the URL. /// public string prop2; public bool logUrl = false; public IExtraField[] extraFields; public string[] help; private string _url; private MapType[] _types; /// /// Array of map types available for the current provider. /// public MapType[] types { get { return _types; } set { _types = value; } } /// /// Gets / sets the URL pattern of tiles. /// public string url { get { return _url; } set { _url = value; } } public Provider(string title) : this(title.ToLower(), title) { } public Provider(string id, string title) { this.id = id.ToLower(); this.title = title; } /// /// Gets map type by index. /// /// Index of map type. /// TRUE - Repeat index value, FALSE - Clamp index value. /// Instance of map type. public MapType GetByIndex(int index, bool repeat = false) { if (repeat) index = Mathf.RoundToInt(Mathf.Repeat(index, _types.Length - 1)); else index = Mathf.Clamp(index, 0, _types.Length); return _types[index]; } } /// /// Class of map type /// public class MapType { /// /// ID of map type /// public readonly string id; public string filePrefix; public string fullID; /// /// Human-readable map type title. /// public readonly string title; /// /// Reference to provider instance. /// public Provider provider; /// /// Index of map type /// public int index; public IExtraField[] extraFields; /// /// Indicates that this is an custom provider. /// public bool isCustom; private string _ext; private string _url; private string _variant; private string _prop; private string _prop2; private bool? _logUrl; public string[] help; /// /// Extension. Token {ext}, that is being replaced in the URL. /// public string ext { get { if (!string.IsNullOrEmpty(_ext)) return _ext; if (!string.IsNullOrEmpty(provider.ext)) return provider.ext; return string.Empty; } set { _ext = value; } } public bool logUrl { get { if (_logUrl.HasValue) return _logUrl.Value; return provider.logUrl; } set { _logUrl = value; } } /// /// Property. Token {prop}, that is being replaced in the URL. /// public string prop { get { if (!string.IsNullOrEmpty(_prop)) return _prop; return provider.prop; } set { _prop = value; } } /// /// Property. Token {prop2}, that is being replaced in the URL. /// public string prop2 { get { return string.IsNullOrEmpty(_prop2) ? provider.prop2 : _prop2; } set { _prop2 = value; } } /// /// Variant. Token {variant}, that is being replaced in the URL. /// public string variant { get { return _variant; } set { _variant = value; } } /// /// Gets / sets the URL pattern of tiles. /// public string url { get { if (!string.IsNullOrEmpty(_url)) return _url; return provider.url; } set { _url = value; } } /// /// Constructor /// /// Human-readable map type title. public MapType(string title, string filePrefix) : this(title.ToLower(), title, filePrefix) { } /// /// Constructor /// /// ID of map type. /// Human-readable map type title. public MapType(string id, string title, string filePrefix) { this.filePrefix = filePrefix; this.id = id; this.title = title; } public string GetSettings() { if (provider.extraFields == null) return null; StringBuilder builder = new StringBuilder(); foreach (IExtraField field in provider.extraFields) field.SaveSettings(builder); return builder.ToString(); } /// /// Gets the URL to download the tile texture. /// /// Tile zoom /// Tile X /// Tile Y /// URL to tile texture. public string GetURL(int zoom, int x, int y) { return GetURL(zoom, x, y, url); } public string GetURL(int zoom, int x, int y, string url) { url = Regex.Replace(url, @"{\w+}", delegate (Match match) { string v = match.Value.ToLower().Trim('{', '}'); if (v == "zoom") return zoom.ToString(); if (v == "z") return zoom.ToString(); if (v == "x") return x.ToString(); if (v == "y") return y.ToString(); if (v == "quad") return RealWorldTerrainUtils.TileToQuadKey(x, y, zoom); if (v == "ext") return ext; if (v == "prop") return prop; if (v == "prop2") return prop2; if (v == "variant") return variant; if (TryUseExtraFields(ref v)) return v; return v; }); url = Regex.Replace(url, @"\[(\d+)-(\d+)\]", delegate (Match match) { int v1 = int.Parse(match.Groups[1].Value); int v2 = int.Parse(match.Groups[2].Value); return Random.Range(v1, v2 + 1).ToString(); }); if (logUrl) Debug.Log(url); return url; } public void LoadSettings(string settings) { IExtraField[] fields = provider.extraFields; if (fields == null || string.IsNullOrEmpty(settings)) return; int i = 0; while (i < settings.Length) { int titleLength = int.Parse(settings.Substring(i, 2)); i += 2; string title = settings.Substring(i, titleLength); i += titleLength; int contentLengthSize = int.Parse(settings.Substring(i, 1)); i++; int contentSize = int.Parse(settings.Substring(i, contentLengthSize)); i += contentLengthSize; foreach (IExtraField field in fields) if (field.TryLoadSettings(title, settings, i, contentSize)) break; i += contentSize; } } private bool TryUseExtraFields(ref string token) { if (provider.extraFields != null) { foreach (IExtraField field in provider.extraFields) { string value; if (field.GetTokenValue(token, false, out value)) { token = value; return true; } } } if (extraFields != null) { foreach (IExtraField field in extraFields) { string value; if (field.GetTokenValue(token, false, out value)) { token = value; return true; } } } return false; } public override string ToString() { return fullID; } } /// /// Group of toggle extra fields /// public class ToggleExtraGroup : IExtraField { /// /// Array of extra fields /// public IExtraField[] fields; /// /// Group title /// public string title; /// /// Group value /// public bool value = false; /// /// Group ID /// public string id; public ToggleExtraGroup(string title, bool value = false) { this.title = title; this.value = value; } public ToggleExtraGroup(string title, bool value, IExtraField[] fields) : this(title, value) { this.fields = fields; } public bool GetTokenValue(string token, bool useDefaultValue, out string value) { value = null; if (fields == null) return false; foreach (IExtraField field in fields) { if (field.GetTokenValue(token, this.value || useDefaultValue, out value)) return true; } return false; } public void SaveSettings(StringBuilder builder) { int titleLength = title.Length; if (titleLength < 10) builder.Append("0"); builder.Append(titleLength); builder.Append(title); StringBuilder dataBuilder = new StringBuilder(); dataBuilder.Append(value ? 1 : 0); if (fields != null) foreach (IExtraField field in fields) field.SaveSettings(dataBuilder); builder.Append(dataBuilder.Length.ToString().Length); builder.Append(dataBuilder.Length); builder.Append(dataBuilder); } public bool TryLoadSettings(string title, string settings, int index, int contentSize) { if (this.title != title) return false; value = settings.Substring(index, 1) == "1"; int i = index + 1; while (i < index + contentSize) { int titleLength = int.Parse(settings.Substring(i, 2)); i += 2; string fieldTitle = settings.Substring(i, titleLength); i += titleLength; int contentLengthSize = int.Parse(settings.Substring(i, 1)); i++; int contentLength = int.Parse(settings.Substring(i, contentLengthSize)); i += contentLengthSize; foreach (IExtraField field in fields) if (field.TryLoadSettings(fieldTitle, settings, i, contentLength)) break; i += contentLength; } return true; } } /// /// Interface for extra fields tile provider /// public interface IExtraField { bool GetTokenValue(string token, bool useDefaultValue, out string value); void SaveSettings(StringBuilder builder); bool TryLoadSettings(string title, string settings, int index, int contentSize); } /// /// Class for extra field /// public class ExtraField : IExtraField { /// /// Title /// public string title; /// /// Value /// public string value; /// /// Default value /// public string defaultValue; /// /// Token (ID) /// public string token; public ExtraField(string title, string token) { this.title = title; this.token = token; } public ExtraField(string title, string token, string defaultValue) : this(title, token) { this.defaultValue = defaultValue; } public bool GetTokenValue(string token, bool useDefaultValue, out string value) { value = null; if (this.token == token) { value = useDefaultValue ? defaultValue : this.value; return true; } return false; } public void SaveSettings(StringBuilder builder) { int titleLength = title.Length; if (titleLength < 10) builder.Append("0"); builder.Append(titleLength); builder.Append(title); if (string.IsNullOrEmpty(value)) builder.Append(1).Append(1).Append(0); else { StringBuilder dataBuilder = new StringBuilder(); int valueLength = value.Length; dataBuilder.Append(valueLength.ToString().Length); dataBuilder.Append(valueLength); dataBuilder.Append(value); builder.Append(dataBuilder.Length.ToString().Length); builder.Append(dataBuilder.Length); builder.Append(dataBuilder); } } public bool TryLoadSettings(string title, string settings, int index, int contentSize) { if (this.title != title) return false; int lengthSize = int.Parse(settings.Substring(index, 1)); if (lengthSize == 0) value = ""; else { index++; int length = int.Parse(settings.Substring(index, lengthSize)); index += lengthSize; value = settings.Substring(index, length); } return true; } } } }