diff --git a/Runtime/Mapbox/BaseModule/Data/Interfaces/ITerrainLayerModule.cs b/Runtime/Mapbox/BaseModule/Data/Interfaces/ITerrainLayerModule.cs index ef892eb4c..032a06ae1 100644 --- a/Runtime/Mapbox/BaseModule/Data/Interfaces/ITerrainLayerModule.cs +++ b/Runtime/Mapbox/BaseModule/Data/Interfaces/ITerrainLayerModule.cs @@ -1,3 +1,6 @@ +using System; +using System.Collections; +using Mapbox.BaseModule.Data.DataFetchers; using Mapbox.BaseModule.Data.Tiles; namespace Mapbox.BaseModule.Data.Interfaces @@ -5,5 +8,6 @@ namespace Mapbox.BaseModule.Data.Interfaces public interface ITerrainLayerModule : ILayerModule { float QueryElevation(CanonicalTileId tileId, float x, float y); + IEnumerator LoadTileData(CanonicalTileId tileId, Action callback = null); } } \ No newline at end of file diff --git a/Runtime/Mapbox/BaseModule/Map/MapInformationStaticMethods.cs b/Runtime/Mapbox/BaseModule/Map/MapInformationStaticMethods.cs index e3a6a2884..1f6f28e78 100644 --- a/Runtime/Mapbox/BaseModule/Map/MapInformationStaticMethods.cs +++ b/Runtime/Mapbox/BaseModule/Map/MapInformationStaticMethods.cs @@ -30,12 +30,21 @@ public static Vector3 ConvertLatLngToPositionForScale(this IMapInformation mapIn return scaledDeltaMercatorVector3; } - public static Vector3 ConvertLatLngToPosition(this IMapInformation mapInfo, LatitudeLongitude latlng) + public static Vector3 ConvertLatLngToPosition(this IMapInformation mapInfo, LatitudeLongitude latlng, bool queryElevation = false) { var mercator = Conversions.LatitudeLongitudeToWebMercator(latlng); var deltaMercator = mercator - mapInfo.CenterMercator; var scaledDeltaMercator = deltaMercator / mapInfo.Scale; - var scaledDeltaMercatorVector3 = scaledDeltaMercator.ToVector3xz(); + var elevation = 0f; + + if (queryElevation) + { + var tileId = Conversions.LatitudeLongitudeToTileId(latlng, 14).Canonical; + var pointPosition = Conversions.LatitudeLongitudeToInTile01(latlng, tileId); + elevation = mapInfo.QueryElevation(tileId, pointPosition.x, pointPosition.y); + } + + var scaledDeltaMercatorVector3 = new Vector3((float)scaledDeltaMercator.x, elevation, (float)scaledDeltaMercator.y); return scaledDeltaMercatorVector3; } diff --git a/Runtime/Mapbox/BaseModule/Map/MapboxMapExtensions.cs b/Runtime/Mapbox/BaseModule/Map/MapboxMapExtensions.cs new file mode 100644 index 000000000..2930981bb --- /dev/null +++ b/Runtime/Mapbox/BaseModule/Map/MapboxMapExtensions.cs @@ -0,0 +1,79 @@ +using Mapbox.BaseModule.Data.Tiles; +using Mapbox.BaseModule.Data.Vector2d; +using Mapbox.BaseModule.Unity; +using Mapbox.BaseModule.Utilities; + +namespace Mapbox.BaseModule.Map +{ + public static class MapboxMapExtensions + { + public static bool TryGetMapTile(this MapboxMap map, UnwrappedTileId tileId, out UnityMapTile tile) + { + if (map.MapVisualizer.ActiveTiles.TryGetValue(tileId, out tile)) + { + return true; + } + + tile = null; + return false; + } + + public static bool TryGetMapTile(this MapboxMap map, LatitudeLongitude coordinates, out UnityMapTile tile, int maxZoomLevel = 20) + { + var maxTileId = Conversions.LatitudeLongitudeToTileId(coordinates, maxZoomLevel); + for (int i = maxZoomLevel; i > 1; i--) + { + if (map.MapVisualizer.ActiveTiles.TryGetValue(maxTileId, out tile)) + { + return true; + } + else + { + maxTileId = maxTileId.Parent; + } + } + + tile = null; + return false; + } + + /// + /// Only queries active tiles / visible world + /// + /// + /// + /// + /// + /// + /// + public static bool TryGetElevation(this MapboxMap map, LatitudeLongitude location, out float elevation, int maxZoomLevel = 20, int minZoomLevel = 2) + { + var maxTileId = Conversions.LatitudeLongitudeToTileId(location, maxZoomLevel); + UnityMapTile tile = null; + for (int i = maxZoomLevel; i >= minZoomLevel; i--) + { + if (map.MapVisualizer.ActiveTiles.TryGetValue(maxTileId, out tile)) + { + break; + } + else + { + maxTileId = maxTileId.Parent; + } + } + + // Check tile and nested properties for null to prevent NullReferenceException + if (tile != null && + tile.TerrainContainer != null && + tile.TerrainContainer.TerrainData != null) + { + var tilePos = Conversions.LatitudeLongitudeToInTile01(location, tile.TerrainContainer.TerrainData.TileId); + elevation = tile.TerrainContainer.QueryHeightData(tilePos.x, tilePos.y); + return true; + } + + elevation = float.MinValue; + return false; + } + } +} \ No newline at end of file diff --git a/Runtime/Mapbox/BaseModule/Map/MapboxMapExtensions.cs.meta b/Runtime/Mapbox/BaseModule/Map/MapboxMapExtensions.cs.meta new file mode 100644 index 000000000..8ca4b59f3 --- /dev/null +++ b/Runtime/Mapbox/BaseModule/Map/MapboxMapExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3d874d1186584faf95af2fa2761ddcbb +timeCreated: 1767092724 \ No newline at end of file diff --git a/Runtime/Mapbox/BaseModule/Map/MapboxMapVisualizer.cs b/Runtime/Mapbox/BaseModule/Map/MapboxMapVisualizer.cs index 681dc1f4e..3c04f2164 100644 --- a/Runtime/Mapbox/BaseModule/Map/MapboxMapVisualizer.cs +++ b/Runtime/Mapbox/BaseModule/Map/MapboxMapVisualizer.cs @@ -194,6 +194,14 @@ public void OnDestroy() public bool TryGetLayerModule(out T module) where T : ILayerModule { module = (T)LayerModules.FirstOrDefault(x => x is T); + if (module == null) + { + var composite = LayerModules.FirstOrDefault(x => x is CompositeLayerModule) as CompositeLayerModule; + if (composite != null) + { + module = (T)composite.LayerModules.FirstOrDefault(x => x is T); + } + } return module != null; } diff --git a/Runtime/Mapbox/CustomImageryModule.meta b/Runtime/Mapbox/CustomImageryModule.meta new file mode 100644 index 000000000..ee0e1a460 --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9957d55bdc494407c87f742bb99a0cc8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Mapbox/CustomImageryModule/CustomApiLayerModuleScript.cs b/Runtime/Mapbox/CustomImageryModule/CustomApiLayerModuleScript.cs new file mode 100644 index 000000000..007c55f86 --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/CustomApiLayerModuleScript.cs @@ -0,0 +1,55 @@ +using Mapbox.BaseModule.Data.Interfaces; +using Mapbox.BaseModule.Map; +using Mapbox.BaseModule.Unity; +using Mapbox.BaseModule.Utilities; +using Mapbox.ImageModule; +using Mapbox.UnityMapService; +using UnityEngine; + +namespace Mapbox.CustomImageryModule +{ + public class CustomApiLayerModuleScript : ModuleConstructorScript + { + public CustomSourceSettings CustomSourceSettings; + public StaticLayerModuleSettings Settings = new StaticLayerModuleSettings() + { + RejectTilesOutsideZoom = new Vector2(2, 25), + DataSettings = new ImageSourceSettings() + { + ClampDataLevelToMax = 25 + } + }; + public override ILayerModule ModuleImplementation { get; protected set; } + + private void Start() + { + + } + + public override ILayerModule ConstructModule(MapService service, IMapInformation mapInformation, + UnityContext unityContext) + { + if (Settings.SourceType == ImagerySourceType.None) + { + + } + else if (Settings.SourceType == ImagerySourceType.Custom) + { + Settings.DataSettings.TilesetId = Settings.CustomSourceId; + } + else + { + var imageryTileset = MapboxDefaultImagery.GetParameters(Settings.SourceType); + Settings.DataSettings.TilesetId = imageryTileset.Id; + } + + var unityService = service as MapUnityService; + var source = new CustomSource(CustomSourceSettings, unityService.FetchingManager, unityService.CacheManager, new ImageSourceSettings() + { + TilesetId = "CustomImagery" + }); + ModuleImplementation = new StaticApiLayerModule(source, Settings); + return ModuleImplementation; + } + } +} \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/CustomApiLayerModuleScript.cs.meta b/Runtime/Mapbox/CustomImageryModule/CustomApiLayerModuleScript.cs.meta new file mode 100644 index 000000000..64db1dc66 --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/CustomApiLayerModuleScript.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6aed0ab09c9749069b3359d261d35ae4 +timeCreated: 1767099820 \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/CustomSource.cs b/Runtime/Mapbox/CustomImageryModule/CustomSource.cs new file mode 100644 index 000000000..ee4c1efc8 --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/CustomSource.cs @@ -0,0 +1,58 @@ +using Mapbox.BaseModule.Data.DataFetchers; +using Mapbox.BaseModule.Data.Platform.Cache; +using Mapbox.BaseModule.Data.Tiles; +using Mapbox.BaseModule.Map; +using Mapbox.UnityMapService.DataSources; + +namespace Mapbox.CustomImageryModule +{ + public class CustomSource : ImageSource + { + protected CustomSourceSettings _customSourceSettings; + protected ImageSourceSettings _settings; + + public CustomSource(CustomSourceSettings customSourceSettings, DataFetchingManager dataFetchingManager, + MapboxCacheManager mapboxCacheManager, ImageSourceSettings settings) + : base(dataFetchingManager, mapboxCacheManager, settings) + { + _settings = settings; + _customSourceSettings = customSourceSettings; + } + + protected override RasterTile CreateTile(CanonicalTileId tileId, string tilesetId) + { + if (_customSourceSettings.InvertY) + { + return new CustomTMSTile(_customSourceSettings.UrlFormat, tileId, tilesetId, true); + } + else + { + return new RasterTile(tileId, tilesetId, true); + } + } + + protected override RasterData CreateRasterDataWrapper(RasterTile tile) + { + RasterData rasterData; + rasterData = new RasterData() + { + TileId = tile.Id, + TilesetId = tile.TilesetId, + Texture = tile.Texture2D, + CacheType = tile.FromCache, + Data = tile.Data, + ETag = tile.ETag, + ExpirationDate = tile.ExpirationDate + }; + + return rasterData; + } + + protected override void CheckExpiration(RasterData cacheItem) + { + //not checking for expiration and updating for custom sources + //just using whatever is in cache + return; + } + } +} \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/CustomSource.cs.meta b/Runtime/Mapbox/CustomImageryModule/CustomSource.cs.meta new file mode 100644 index 000000000..5e19cd35a --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/CustomSource.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: db00866e0fd14890ad560a64229aae45 +timeCreated: 1767707026 \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/CustomSourceSettings.cs b/Runtime/Mapbox/CustomImageryModule/CustomSourceSettings.cs new file mode 100644 index 000000000..f2ac463b9 --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/CustomSourceSettings.cs @@ -0,0 +1,14 @@ +using System; +using UnityEngine; + +namespace Mapbox.CustomImageryModule +{ + [Serializable] + public class CustomSourceSettings + { + [Tooltip("Url format string, structured as C# string format with '{}' fields for X/Y/Z coordinates. {0}=Z, {1}=X, {2}=Y")] + public string UrlFormat; + [Tooltip("Invert Y axis coordinates for TMS coordinate system, which starts from bottom left and grows to top-right")] + public bool InvertY; + } +} \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/CustomSourceSettings.cs.meta b/Runtime/Mapbox/CustomImageryModule/CustomSourceSettings.cs.meta new file mode 100644 index 000000000..cd1886295 --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/CustomSourceSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 83855a3135724ccd938059a88012027d +timeCreated: 1767707253 \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/CustomTMSTile.cs b/Runtime/Mapbox/CustomImageryModule/CustomTMSTile.cs new file mode 100644 index 000000000..8f3b789f0 --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/CustomTMSTile.cs @@ -0,0 +1,35 @@ +using System; +using Mapbox.BaseModule.Data.DataFetchers; +using Mapbox.BaseModule.Data.Platform; +using Mapbox.BaseModule.Data.Tiles; +using UnityEngine; + +namespace Mapbox.CustomImageryModule +{ + /// + /// TMS tile in the name means Y axis is inverted in the tile ids + /// + public class CustomTMSTile : RasterTile + { + private string _urlFormat; + public CustomTMSTile(string urlFormat, CanonicalTileId tileId, string tilesetId, bool useNonReadableTexture) : base(tileId, tilesetId, useNonReadableTexture) + { + _urlFormat = urlFormat; + } + + public override void Initialize(IFileSource fileSource, Action p) + { + TileState = TileState.Loading; + _callback = p; + + var invertY = (Mathf.Pow(2, Id.Z))- Id.Y - 1; + _generatedUrl = string.Format(_urlFormat, Id.Z, Id.X, (int)invertY); + DoTheRequest(fileSource); + } + + protected override void DoTheRequest(IFileSource fileSource) + { + _webRequest = fileSource.CustomImageRequest(_generatedUrl, HandleTileResponse, ETag, 10, IsTextureNonreadable); + } + } +} \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/CustomTMSTile.cs.meta b/Runtime/Mapbox/CustomImageryModule/CustomTMSTile.cs.meta new file mode 100644 index 000000000..25a8c0e9f --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/CustomTMSTile.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f1f46f6d333e4d25a4ccd278b71cecdc +timeCreated: 1767706943 \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/CustomTerrainLayerModuleScript.cs b/Runtime/Mapbox/CustomImageryModule/CustomTerrainLayerModuleScript.cs new file mode 100644 index 000000000..8087393b9 --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/CustomTerrainLayerModuleScript.cs @@ -0,0 +1,42 @@ +using Mapbox.BaseModule.Data.Interfaces; +using Mapbox.BaseModule.Map; +using Mapbox.BaseModule.Unity; +using Mapbox.ImageModule.Terrain; +using Mapbox.UnityMapService; +using UnityEngine; + +namespace Mapbox.CustomImageryModule +{ + public class CustomTerrainLayerModuleScript : ModuleConstructorScript + { + public CustomSourceSettings CustomSourceSettings; + public TerrainLayerModuleSettings Settings = new TerrainLayerModuleSettings() + { + RejectTilesOutsideZoom = new Vector2(2, 25), + DataSettings = new ImageSourceSettings() + { + ClampDataLevelToMax = 25 + } + }; + public override ILayerModule ModuleImplementation { get; protected set; } + + private void Start() + { + + } + + public override ILayerModule ConstructModule(MapService service, IMapInformation mapInformation, + UnityContext unityContext) + { + Settings.DataSettings.TilesetId = "CustomTerrain"; + + var unityService = service as MapUnityService; + var source = new CustomTerrainSource(CustomSourceSettings, unityService.FetchingManager, unityService.CacheManager, new ImageSourceSettings() + { + TilesetId = "CustomTerrain" + }); + ModuleImplementation = new TerrainLayerModule(source, Settings); + return ModuleImplementation; + } + } +} \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/CustomTerrainLayerModuleScript.cs.meta b/Runtime/Mapbox/CustomImageryModule/CustomTerrainLayerModuleScript.cs.meta new file mode 100644 index 000000000..ccdbd7c1a --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/CustomTerrainLayerModuleScript.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 83ddcee6d2844b7e9f15508beb22c72b +timeCreated: 1767632785 \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/CustomTerrainSource.cs b/Runtime/Mapbox/CustomImageryModule/CustomTerrainSource.cs new file mode 100644 index 000000000..eb45d275f --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/CustomTerrainSource.cs @@ -0,0 +1,52 @@ +using Mapbox.BaseModule.Data.DataFetchers; +using Mapbox.BaseModule.Data.Platform.Cache; +using Mapbox.BaseModule.Data.Tiles; +using Mapbox.BaseModule.Map; +using Mapbox.UnityMapService.DataSources; + +namespace Mapbox.CustomImageryModule +{ + public class CustomTerrainSource : TerrainSource + { + protected CustomSourceSettings _customSourceSettings; + protected ImageSourceSettings _settings; + + public CustomTerrainSource(CustomSourceSettings customSettings, DataFetchingManager dataFetchingManager, MapboxCacheManager mapboxCacheManager, ImageSourceSettings settings) + : base(dataFetchingManager, mapboxCacheManager, settings) + { + _customSourceSettings = customSettings; + _settings = settings; + } + + protected override RasterTile CreateTile(CanonicalTileId tileId, string tilesetId) + { + if (_customSourceSettings.InvertY) + { + return new CustomTMSTile( + _customSourceSettings.UrlFormat, + tileId, tilesetId, true); + } + else + { + return new RasterTile(tileId, tilesetId, true); + } + } + + protected override TerrainData CreateRasterDataWrapper(RasterTile tile) + { + TerrainData rasterData; + rasterData = new TerrainData() + { + TileId = tile.Id, + TilesetId = tile.TilesetId, + Texture = tile.Texture2D, + CacheType = tile.FromCache, + Data = tile.Data, + ETag = tile.ETag, + ExpirationDate = tile.ExpirationDate + }; + + return rasterData; + } + } +} \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/CustomTerrainSource.cs.meta b/Runtime/Mapbox/CustomImageryModule/CustomTerrainSource.cs.meta new file mode 100644 index 000000000..29c8d3b8c --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/CustomTerrainSource.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cfc3281b14f542aa8d2164cabb8967c7 +timeCreated: 1767707245 \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/Editor.meta b/Runtime/Mapbox/CustomImageryModule/Editor.meta new file mode 100644 index 000000000..68a67e75e --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0b6b51283dd7d417a962d32657e3e2f5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Mapbox/CustomImageryModule/Editor/CustomApiLayerModuleScriptEditor.cs b/Runtime/Mapbox/CustomImageryModule/Editor/CustomApiLayerModuleScriptEditor.cs new file mode 100644 index 000000000..5b05645c1 --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/Editor/CustomApiLayerModuleScriptEditor.cs @@ -0,0 +1,46 @@ +using UnityEditor; + +namespace Mapbox.CustomImageryModule.Editor +{ + [CustomEditor(typeof(CustomApiLayerModuleScript))] + public class CustomApiLayerModuleScriptEditor : UnityEditor.Editor + { + SerializedProperty customSourceSettingsProp; + + SerializedProperty rejectTilesOutsideZoomProp; + SerializedProperty clampDataLevelToMaxProp; + + void OnEnable() + { + // Root properties + customSourceSettingsProp = serializedObject.FindProperty("CustomSourceSettings"); + + // Settings.RejectTilesOutsideZoom + rejectTilesOutsideZoomProp = + serializedObject.FindProperty("Settings") + .FindPropertyRelative("RejectTilesOutsideZoom"); + + // Settings.DataSettings.ClampDataLevelToMax + clampDataLevelToMaxProp = + serializedObject.FindProperty("Settings") + .FindPropertyRelative("DataSettings") + .FindPropertyRelative("ClampDataLevelToMax"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.LabelField("Custom Source Settings", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(customSourceSettingsProp, true); + + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Tile Settings", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(rejectTilesOutsideZoomProp); + EditorGUILayout.PropertyField(clampDataLevelToMaxProp); + + serializedObject.ApplyModifiedProperties(); + } + } +} \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/Editor/CustomApiLayerModuleScriptEditor.cs.meta b/Runtime/Mapbox/CustomImageryModule/Editor/CustomApiLayerModuleScriptEditor.cs.meta new file mode 100644 index 000000000..e758ce0e4 --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/Editor/CustomApiLayerModuleScriptEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c31619c32fa2549edbbc6761da29703b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Mapbox/CustomImageryModule/Editor/MapboxCustomImageryModule.Editor.asmdef b/Runtime/Mapbox/CustomImageryModule/Editor/MapboxCustomImageryModule.Editor.asmdef new file mode 100644 index 000000000..bc263dd76 --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/Editor/MapboxCustomImageryModule.Editor.asmdef @@ -0,0 +1,18 @@ +{ + "name": "MapboxCustomImageryModule.Editor", + "rootNamespace": "", + "references": [ + "GUID:60dcad938ceef450e9b8d773e5c61b99" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/Editor/MapboxCustomImageryModule.Editor.asmdef.meta b/Runtime/Mapbox/CustomImageryModule/Editor/MapboxCustomImageryModule.Editor.asmdef.meta new file mode 100644 index 000000000..76a5b8ec5 --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/Editor/MapboxCustomImageryModule.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4c80974ccb411472e8e926051beeec6f +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Mapbox/CustomImageryModule/MapboxCustomImageryModule.asmdef b/Runtime/Mapbox/CustomImageryModule/MapboxCustomImageryModule.asmdef new file mode 100644 index 000000000..34eca74fd --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/MapboxCustomImageryModule.asmdef @@ -0,0 +1,18 @@ +{ + "name": "MapboxCustomImageryModule", + "rootNamespace": "", + "references": [ + "GUID:093b9fb2cd4794a8c94472d55c8bb0a9", + "GUID:36ca6af6e2b304d4090888554d6ce199", + "GUID:5ff00896142b94bc7a58a7c72b2faf57" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Runtime/Mapbox/CustomImageryModule/MapboxCustomImageryModule.asmdef.meta b/Runtime/Mapbox/CustomImageryModule/MapboxCustomImageryModule.asmdef.meta new file mode 100644 index 000000000..3e2d8598f --- /dev/null +++ b/Runtime/Mapbox/CustomImageryModule/MapboxCustomImageryModule.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 60dcad938ceef450e9b8d773e5c61b99 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Mapbox/ImageModule/Terrain/TerrainLayerModule.cs b/Runtime/Mapbox/ImageModule/Terrain/TerrainLayerModule.cs index 310f1f4c5..a018bbd23 100644 --- a/Runtime/Mapbox/ImageModule/Terrain/TerrainLayerModule.cs +++ b/Runtime/Mapbox/ImageModule/Terrain/TerrainLayerModule.cs @@ -5,8 +5,10 @@ using Mapbox.BaseModule.Data.DataFetchers; using Mapbox.BaseModule.Data.Interfaces; using Mapbox.BaseModule.Data.Tiles; +using Mapbox.BaseModule.Data.Vector2d; using Mapbox.BaseModule.Map; using Mapbox.BaseModule.Unity; +using Mapbox.BaseModule.Utilities; using Mapbox.ImageModule.Terrain.TerrainStrategies; using UnityEngine; using TerrainData = Mapbox.BaseModule.Data.DataFetchers.TerrainData; @@ -91,22 +93,6 @@ public virtual bool RetainTiles(HashSet retainedTiles) isReady = _rasterSource.RetainTiles(_retainedTerrainTiles); return isReady; } - - public float QueryElevation(CanonicalTileId tileId, float x, float y) - { - var originalTileId = tileId; - var targetTileId = tileId; - for (int i = 0; i < 5; i++) - { - if (_rasterSource.GetInstantData(targetTileId, out var instantData)) - { - return instantData.QueryHeightData(originalTileId, x, y); - } - targetTileId.MoveToParent(); - } - - return 0; - } public void UpdatePositioning(IMapInformation mapInfo) { @@ -120,7 +106,7 @@ public void OnDestroy() //COROUTINE METHODS only used in initialization so far #region coroutine methods - public virtual IEnumerator LoadTileData(CanonicalTileId tileId, Action callback = null) + public virtual IEnumerator LoadTileData(CanonicalTileId tileId, Action callback = null) { return _rasterSource.LoadTileCoroutine(tileId, callback); } @@ -133,13 +119,11 @@ public virtual IEnumerator LoadTiles(IEnumerable tiles) public IEnumerable GetTileCoverCoroutines(IEnumerable tiles) { var targetTiles = GetDataId(tiles).Distinct(); - return targetTiles.Select(x => _rasterSource.LoadTileCoroutine(x)).Where(x => x != null); + return targetTiles.Select(x => LoadTileData(x)).Where(x => x != null); } #endregion - - //PRIVATE METHODS private bool IsZinSupportedRange(int targetZ) { @@ -150,7 +134,7 @@ private CanonicalTileId GetDataId(CanonicalTileId tileId) { var maxZoom = _settings.DataSettings.ClampDataLevelToMax; var currentZ = tileId.Z; - var targetZ = currentZ - 2; + var targetZ = (int)Mathf.Max(currentZ - 2, _settings.RejectTilesOutsideZoom.x); if (targetZ >= maxZoom) { return tileId.ParentAt(maxZoom); @@ -161,10 +145,73 @@ private CanonicalTileId GetDataId(CanonicalTileId tileId) } } + + //API + + /// + /// This method will only search the memory cache for available data. + /// + /// TileId of the elevation data + /// Point in texture coordinate space, origin is bottom left. + /// Point in texture coordinate space, origin is bottom left. + /// + public float QueryElevation(CanonicalTileId tileId, float x, float y) + { + var originalTileId = tileId; + var targetTileId = tileId; + for (int i = 0; i < 5; i++) + { + if (_rasterSource.GetInstantData(targetTileId, out var instantData)) + { + return instantData.QueryHeightData(originalTileId, x, y); + } + targetTileId.MoveToParent(); + } + + return 0; + } + public IEnumerable GetDataId(IEnumerable tileIdList) { return tileIdList.Where(x => IsZinSupportedRange(x.Z)).Select(GetDataId).Distinct(); } + /// + /// This method will cause tile requests if data isn't already available on memory cache. Use with caution. + /// + /// Latitude longitude of the location you want to query + /// Callback method returning the elevation in float + /// + public IEnumerator GetElevationData(LatitudeLongitude latLng, Action callback = null) + { + var tileId = Conversions.LatitudeLongitudeToTileId(latLng, 14).Canonical; + var dataFound = false; + for (int i = 14; i >= 2; i--) + { + if (_rasterSource.GetInstantData(tileId, out var instantData)) + { + var tilePos = Conversions.LatitudeLongitudeToInTile01(latLng, instantData.TileId); + var elevation = instantData.QueryHeightData(tilePos); + callback?.Invoke(elevation); + dataFound = true; + break; + } + else + { + tileId = tileId.GetParentTileId; + } + } + + if (!dataFound) + { + tileId = Conversions.LatitudeLongitudeToTileId(latLng, 14).Canonical; + yield return LoadTileData(tileId, terrainData => + { + var tilePos = Conversions.LatitudeLongitudeToInTile01(latLng, terrainData.TileId); + var elevation = terrainData.QueryHeightData(tilePos); + callback?.Invoke(elevation); + }); + } + } } } \ No newline at end of file diff --git a/Runtime/Mapbox/UnityMapService/DataSources/ImageSource.cs b/Runtime/Mapbox/UnityMapService/DataSources/ImageSource.cs index 9068febbd..f3a10d8d2 100644 --- a/Runtime/Mapbox/UnityMapService/DataSources/ImageSource.cs +++ b/Runtime/Mapbox/UnityMapService/DataSources/ImageSource.cs @@ -362,7 +362,7 @@ protected void BackgroundLoad(CanonicalTileId tileId, string tilesetId) }); } - private void CheckExpiration(T cacheItem) + protected virtual void CheckExpiration(T cacheItem) { var dataTask = ReadEtagExpiration(cacheItem, 4); if (dataTask != null) //can be null if sqlite cache isn't available diff --git a/Runtime/Mapbox/UnityMapService/DataSources/TerrainSource.cs b/Runtime/Mapbox/UnityMapService/DataSources/TerrainSource.cs index 2e275d0f0..885caaa60 100644 --- a/Runtime/Mapbox/UnityMapService/DataSources/TerrainSource.cs +++ b/Runtime/Mapbox/UnityMapService/DataSources/TerrainSource.cs @@ -112,7 +112,7 @@ public override IEnumerator LoadTileCoroutine(CanonicalTileId requestedDataTileI { terrainData = data; })); - if (terrainData != null) + if (terrainData != null && terrainData.Texture != null) { yield return Runnable.Instance.StartCoroutine(ExtractElevationValues(terrainData)); } diff --git a/Runtime/Mapbox/UnityMapService/MapUnityService.cs b/Runtime/Mapbox/UnityMapService/MapUnityService.cs index a7327c719..12d884f2d 100644 --- a/Runtime/Mapbox/UnityMapService/MapUnityService.cs +++ b/Runtime/Mapbox/UnityMapService/MapUnityService.cs @@ -24,7 +24,10 @@ public sealed class MapUnityService : MapService, IUnityMapService private MapboxCacheManager _cacheManager; private DataFetchingManager _fetchingManager; - public override IFileSource FileSource => _fetchingManager; + public override IFileSource FileSource => FetchingManager; + + public MapboxCacheManager CacheManager => _cacheManager; + public DataFetchingManager FetchingManager => _fetchingManager; public MapUnityService( UnityContext unityContext, @@ -63,40 +66,40 @@ public override Source GetNewRasterSource(string name, string tilese public override Source GetTerrainRasterSource(ImageSourceSettings settings) { - var terrainSource = new TerrainSource(_fetchingManager, _cacheManager, settings); + var terrainSource = new TerrainSource(FetchingManager, CacheManager, settings); _dataSources.Add(terrainSource); return terrainSource; } public override Source GetStaticRasterSource(ImageSourceSettings settings) { - var staticRasterSource = new StaticSource(_fetchingManager, _cacheManager, settings); + var staticRasterSource = new StaticSource(FetchingManager, CacheManager, settings); _dataSources.Add(staticRasterSource); return staticRasterSource; } public override Source GetVectorSource(VectorSourceSettings settings) { - var vectorSource = new VectorSource(_fetchingManager, _cacheManager, settings); + var vectorSource = new VectorSource(FetchingManager, CacheManager, settings); _dataSources.Add(vectorSource); return vectorSource; } public override Source GetBuildingSource(VectorSourceSettings settings) { - var vectorSource = new BuildingSource(_fetchingManager, _cacheManager, settings); + var vectorSource = new BuildingSource(FetchingManager, CacheManager, settings); _dataSources.Add(vectorSource); return vectorSource; } - public MapboxCacheManager GetCacheManager() => _cacheManager; - public DataFetchingManager GetFetchingManager() => _fetchingManager; + public MapboxCacheManager GetCacheManager() => CacheManager; + public DataFetchingManager GetFetchingManager() => FetchingManager; public override void OnDestroy() { base.OnDestroy(); - _fetchingManager.OnDestroy(); - _cacheManager.OnDestroy(); + FetchingManager.OnDestroy(); + CacheManager.OnDestroy(); } } } diff --git a/Tests/Runtime/BaseModule/MapboxMapTests.cs b/Tests/Runtime/BaseModule/MapboxMapTests.cs index 95cd90dd5..cddecd05e 100644 --- a/Tests/Runtime/BaseModule/MapboxMapTests.cs +++ b/Tests/Runtime/BaseModule/MapboxMapTests.cs @@ -9,6 +9,7 @@ using Mapbox.BaseModule.Map; using Mapbox.BaseModule.Unity; using Mapbox.BaseModule.Utilities; +using Mapbox.ImageModule.Terrain; using Mapbox.ImageModule.Terrain.TerrainStrategies; using Mapbox.MapDebug.Scripts.Logging; using Mapbox.UnityMapService; @@ -62,6 +63,12 @@ public void OneTimeSetUp() _map = new MapboxMap(mapInfo, unityContext, mapService); var mapVisualizer = new MapboxMapVisualizer(mapInfo, unityContext, new TileCreator(unityContext)); + mapVisualizer.LayerModules.Add( + new TerrainLayerModule(mapService.GetTerrainRasterSource( + new ImageSourceSettings() + { + TilesetId = MapboxDefaultElevation.GetParameters(ElevationSourceType.MapboxTerrain).Id + }), new TerrainLayerModuleSettings())); _map.MapVisualizer = mapVisualizer; } @@ -148,6 +155,47 @@ public void ChangeViewToSF() Assert.AreEqual(_map.MapInformation.Pitch, 45); Assert.AreEqual(_map.MapInformation.Bearing, 30); } + + + private static IEnumerable LatLngElevationsource + { + get + { + yield return new TestCaseData(new LatitudeLongitude(27.9878, 86.9250), 8848f).Returns(null); // Mount Everest summit, Nepal/China + yield return new TestCaseData(new LatitudeLongitude(35.3606, 138.7274), 3776f).Returns(null); // Mount Fuji, Japan + yield return new TestCaseData(new LatitudeLongitude(36.5786, -118.2923), 4421f).Returns(null); // Mount Whitney, USA + yield return new TestCaseData(new LatitudeLongitude(51.1789, -1.8262), 102f).Returns(null); // Stonehenge, UK + + yield return new TestCaseData(new LatitudeLongitude(46.8523, -121.7603), 4372f).Returns(null); // Mount Rainier, USA + yield return new TestCaseData(new LatitudeLongitude(-13.1631, -72.5450), 2430f).Returns(null); // Machu Picchu, Peru + yield return new TestCaseData(new LatitudeLongitude(19.8207, -155.4681), 4207f).Returns(null); // Mauna Kea, USA + + yield return new TestCaseData(new LatitudeLongitude(-25.3444, 131.0369), 863f).Returns(null); // Uluru, Australia + yield return new TestCaseData(new LatitudeLongitude(43.6532, -79.3832), 92f).Returns(null); // Toronto, Canada + yield return new TestCaseData(new LatitudeLongitude(55.7558, 37.6176), 156f).Returns(null); // Moscow, Russia + yield return new TestCaseData(new LatitudeLongitude(35.6895, 139.6917), 38f).Returns(null); // Tokyo, Japan + yield return new TestCaseData(new LatitudeLongitude(-34.6037, -58.3816), 25f).Returns(null); // Buenos Aires, Argentina + + yield return new TestCaseData(new LatitudeLongitude(51.5074, -0.1278), 18f).Returns(null); // London, UK + yield return new TestCaseData(new LatitudeLongitude(37.7749, -122.4194), 16f).Returns(null); // San Francisco, USA + yield return new TestCaseData(new LatitudeLongitude(28.6139, 77.2090), 216f).Returns(null); // New Delhi, India + yield return new TestCaseData(new LatitudeLongitude(41.9028, 12.4964), 52f).Returns(null); // Rome, Italy + yield return new TestCaseData(new LatitudeLongitude(19.4326, -99.1332), 2240f).Returns(null); // Mexico City, Mexico + } + } + + [UnityTest] + [TestCaseSource(nameof(LatLngElevationsource))] + public IEnumerator TestElevations(LatitudeLongitude latLng, float expectedElevation) + { + if (_map.MapVisualizer.TryGetLayerModule(out var terrainModule)) + { + yield return terrainModule.GetElevationData(latLng, (elevation) => + { + Assert.AreEqual(elevation, expectedElevation, expectedElevation * 0.1f, $"{latLng} : {elevation} - {expectedElevation}"); + }); + } + } } public class DataFetcherTests