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.
 
 
 
mapbox-sdk/Unity/Map/AbstractMap.cs

1261 lines
32 KiB

using Mapbox.Platform.Cache;
using Mapbox.Unity.Map.Interfaces;
using Mapbox.Unity.Map.Strategies;
using Mapbox.Unity.Map.TileProviders;
namespace Mapbox.Unity.Map
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Mapbox.Unity.Utilities;
using Utils;
using UnityEngine;
using Mapbox.Map;
using Mapbox.Unity.MeshGeneration.Factories;
using Mapbox.Unity.MeshGeneration.Data;
using System.Globalization;
/// <summary>
/// Abstract map.
/// This is the main monobehavior which controls the map. It controls the visualization of map data.
/// Abstract map encapsulates the image, terrain and vector sources and provides a centralized interface to control the visualization of the map.
/// </summary>
[ExecuteInEditMode]
public class AbstractMap : MonoBehaviour, IMap
{
#region Private Fields
[SerializeField] private MapOptions _options = new MapOptions();
[SerializeField] private bool _initializeOnStart = true;
[SerializeField] protected ImageryLayer _imagery = new ImageryLayer();
[SerializeField] protected TerrainLayer _terrain = new TerrainLayer();
[SerializeField] protected VectorLayer _vectorData = new VectorLayer();
[SerializeField] protected AbstractTileProvider _tileProvider;
[SerializeField] protected HashSet<UnwrappedTileId> _currentExtent;
[SerializeField] protected EditorPreviewOptions _previewOptions = new EditorPreviewOptions();
private List<UnwrappedTileId> tilesToProcess;
protected AbstractMapVisualizer _mapVisualizer;
protected float _unityTileSize = 1;
protected bool _worldHeightFixed = false;
protected MapboxAccess _fileSource;
protected int _initialZoom;
protected Vector2d _centerLatitudeLongitude;
protected Vector2d _centerMercator;
protected float _worldRelativeScale;
protected Vector3 _mapScaleFactor;
protected Vector3 _cachedPosition;
protected Quaternion _cachedRotation;
protected Vector3 _cachedScale = Vector3.one;
#endregion
#region Properties
public bool IsEditorPreviewEnabled
{
get
{
return _previewOptions.isPreviewEnabled;
}
set
{
_previewOptions.isPreviewEnabled = value;
}
}
public AbstractMapVisualizer MapVisualizer
{
get
{
if(_mapVisualizer == null)
{
_mapVisualizer = ScriptableObject.CreateInstance<MapVisualizer>();
}
return _mapVisualizer;
}
set
{
_mapVisualizer = value;
}
}
public AbstractTileProvider TileProvider
{
get
{
return _tileProvider;
}
set
{
if (_tileProvider != null)
{
_tileProvider.ExtentChanged -= OnMapExtentChanged;
}
_tileProvider = value;
if (_tileProvider != null)
{
_tileProvider.ExtentChanged += OnMapExtentChanged;
}
}
}
/// <summary>
/// The map options.
/// Options to control the behaviour of the map like location,extent, scale and placement.
/// </summary>
public MapOptions Options
{
get
{
return _options;
}
set
{
_options = value;
}
}
/// <summary>
/// Options to control the imagery component of the map.
/// </summary>
[NodeEditorElement("Layers")]
public IImageryLayer ImageLayer
{
get
{
return _imagery;
}
}
/// <summary>
/// Options to control the terrain/ elevation component of the map.
/// </summary>
[NodeEditorElement("Layers")]
public ITerrainLayer Terrain
{
get
{
return _terrain;
}
}
/// <summary>
/// The vector data.
/// Options to control the vector data component of the map.
/// Adds a vector source and visualizers to define the rendering behaviour of vector data layers.
/// </summary>
[NodeEditorElement("Layers")]
public IVectorDataLayer VectorData
{
get
{
return _vectorData;
}
}
public Vector2d CenterLatitudeLongitude
{
get
{
return _centerLatitudeLongitude;
}
}
public Vector2d CenterMercator
{
get
{
return _centerMercator;
}
}
public float WorldRelativeScale
{
get
{
return _worldRelativeScale;
}
}
public float UnityTileSize
{
get
{
return _unityTileSize;
}
}
/// <summary>
/// Gets the absolute zoom of the tiles being currently rendered.
/// <seealso cref="Zoom"/>
/// </summary>
/// <value>The absolute zoom.</value>
public int AbsoluteZoom
{
get
{
return (int)Math.Floor(Options.locationOptions.zoom);
}
}
/// <summary>
/// Gets the current zoom value of the map.
/// Use <c>AbsoluteZoom</c> to get the zoom level of the tileset.
/// <seealso cref="AbsoluteZoom"/>
/// </summary>
/// <value>The zoom.</value>
public float Zoom
{
get
{
return Options.locationOptions.zoom;
}
}
public void SetZoom(float zoom)
{
Options.locationOptions.zoom = zoom;
}
/// <summary>
/// Gets the initial zoom at which the map was initialized.
/// This parameter is useful in calculating the scale of the tiles and the map.
/// </summary>
/// <value>The initial zoom.</value>
public int InitialZoom
{
get
{
return _initialZoom;
}
}
public Transform Root
{
get
{
return transform;
}
}
/// <summary>
/// Setting to trigger map initialization in Unity's Start method.
/// if set to false, Initialize method should be called explicitly to initialize the map.
/// </summary>
public bool InitializeOnStart
{
get
{
return _initializeOnStart;
}
set
{
_initializeOnStart = value;
}
}
public HashSet<UnwrappedTileId> CurrentExtent
{
get
{
return _currentExtent;
}
}
/// <summary>
/// Gets the loading texture used as a placeholder while the image tile is loading.
/// </summary>
/// <value>The loading texture.</value>
public Texture2D LoadingTexture
{
get
{
return _options.loadingTexture;
}
}
/// <summary>
/// Gets the tile material used for map tiles.
/// </summary>
/// <value>The tile material.</value>
public Material TileMaterial
{
get
{
return _options.tileMaterial;
}
}
public Type ExtentCalculatorType
{
get
{
return TileProvider.GetType();
}
}
#endregion
#region Public Methods
/// <summary>
/// Initialize the map using the specified latLon and zoom.
/// Map will automatically get initialized in the <c>Start</c> method.
/// Use this method to explicitly initialize the map and disable intialize on <c>Start</c>
/// </summary>
/// <returns>The initialize.</returns>
/// <param name="latLon">Lat lon.</param>
/// <param name="zoom">Zoom.</param>
public virtual void Initialize(Vector2d latLon, int zoom)
{
_initializeOnStart = false;
if (_options == null)
{
_options = new MapOptions();
}
_options.locationOptions.latitudeLongitude = String.Format(CultureInfo.InvariantCulture, "{0},{1}", latLon.x, latLon.y);
_options.locationOptions.zoom = zoom;
SetUpMap();
}
protected virtual void Update()
{
if (Application.isEditor && !Application.isPlaying && IsEditorPreviewEnabled == false)
{
return;
}
if (TileProvider != null)
{
TileProvider.UpdateTileProvider();
}
}
public virtual void UpdateMap()
{
UpdateMap(Conversions.StringToLatLon(_options.locationOptions.latitudeLongitude), Zoom);
}
public virtual void UpdateMap(Vector2d latLon)
{
UpdateMap(latLon, Zoom);
}
public virtual void UpdateMap(float zoom)
{
UpdateMap(Conversions.StringToLatLon(_options.locationOptions.latitudeLongitude), zoom);
}
/// <summary>
/// Updates the map.
/// Use this method to update the location of the map.
/// Update method should be used when panning, zooming or changing location of the map.
/// This method avoid startup delays that might occur on re-initializing the map.
/// </summary>
/// <param name="latLon">LatitudeLongitude.</param>
/// <param name="zoom">Zoom level.</param>
public virtual void UpdateMap(Vector2d latLon, float zoom)
{
if (Application.isEditor && !Application.isPlaying && !IsEditorPreviewEnabled)
{
return;
}
//so map will be snapped to zero using next new tile loaded
_worldHeightFixed = false;
float differenceInZoom = 0.0f;
bool isAtInitialZoom = false;
// Update map zoom, if it has changed.
if (Math.Abs(Zoom - zoom) > Constants.EpsilonFloatingPoint)
{
SetZoom(zoom);
}
// Compute difference in zoom. Will be used to calculate correct scale of the map.
differenceInZoom = Zoom - InitialZoom;
isAtInitialZoom = (differenceInZoom - 0.0 < Constants.EpsilonFloatingPoint);
//Update center latitude longitude
var centerLatitudeLongitude = latLon;
double xDelta = centerLatitudeLongitude.x;
double zDelta = centerLatitudeLongitude.y;
xDelta = xDelta > 0 ? Mathd.Min(xDelta, Mapbox.Utils.Constants.LatitudeMax) : Mathd.Max(xDelta, -Mapbox.Utils.Constants.LatitudeMax);
zDelta = zDelta > 0 ? Mathd.Min(zDelta, Mapbox.Utils.Constants.LongitudeMax) : Mathd.Max(zDelta, -Mapbox.Utils.Constants.LongitudeMax);
//Set Center in Latitude Longitude and Mercator.
SetCenterLatitudeLongitude(new Vector2d(xDelta, zDelta));
Options.scalingOptions.scalingStrategy.SetUpScaling(this);
Options.placementOptions.placementStrategy.SetUpPlacement(this);
//Scale the map accordingly.
if (Math.Abs(differenceInZoom) > Constants.EpsilonFloatingPoint || isAtInitialZoom)
{
_mapScaleFactor = Vector3.one * Mathf.Pow(2, differenceInZoom);
Root.localScale = _mapScaleFactor;
}
//Update Tile extent.
if (TileProvider != null)
{
TileProvider.UpdateTileExtent();
}
if (OnUpdated != null)
{
OnUpdated();
}
}
private void Reset()
{
DisableEditorPreview();
}
/// <summary>
/// Resets the map.
/// Use this method to reset the map.
/// </summary>
[ContextMenu("ResetMap")]
public void ResetMap()
{
if(_previewOptions.isPreviewEnabled)
{
DisableEditorPreview();
EnableEditorPreview();
}
else
{
MapOnAwakeRoutine();
MapOnStartRoutine(false);
}
}
public bool IsAccessTokenValid
{
get
{
bool isAccessTokenValid = false;
try
{
var accessTokenCheck = Unity.MapboxAccess.Instance;
if (Unity.MapboxAccess.Instance.Configuration == null || string.IsNullOrEmpty(Unity.MapboxAccess.Instance.Configuration.AccessToken))
{
return false;
}
isAccessTokenValid = true;
}
catch (System.Exception)
{
isAccessTokenValid = false;
}
return isAccessTokenValid;
}
}
#endregion
#region Private/Protected Methods
private void OnEnable()
{
tilesToProcess = new List<UnwrappedTileId>();
if (_options.tileMaterial == null)
{
_options.tileMaterial = new Material(Shader.Find("Standard"));
}
if (_options.loadingTexture == null)
{
_options.loadingTexture = new Texture2D(1, 1);
}
}
// TODO: implement IDisposable, instead?
protected virtual void OnDestroy()
{
if (TileProvider != null)
{
TileProvider.ExtentChanged -= OnMapExtentChanged;
}
_mapVisualizer.ClearMap();
_mapVisualizer.Destroy();
}
protected virtual void Awake()
{
if (_previewOptions.isPreviewEnabled == true)
{
DisableEditorPreview();
_previewOptions.isPreviewEnabled = false;
}
MapOnAwakeRoutine();
}
protected virtual void Start()
{
MapOnStartRoutine();
}
private void MapOnAwakeRoutine()
{
// Destroy any ghost game objects.
DestroyChildObjects();
// Setup a visualizer to get a "Starter" map.
if(_mapVisualizer == null)
{
_mapVisualizer = ScriptableObject.CreateInstance<MapVisualizer>();
}
_mapVisualizer.OnTileFinished += (s) =>
{
OnTileFinished(s);
};
}
public void DestroyChildObjects()
{
int destroyChildStartIndex = transform.childCount - 1;
for (int i = destroyChildStartIndex; i >= 0; i--)
{
transform.GetChild(i).gameObject.Destroy();
}
}
private void MapOnStartRoutine(bool coroutine = true)
{
if (Application.isPlaying)
{
if(coroutine)
{
StartCoroutine("SetupAccess");
}
if (_initializeOnStart)
{
SetUpMap();
}
}
}
private void EnableDisablePreview(object sender, EventArgs e)
{
if (!Application.isPlaying)
{
if (_previewOptions.isPreviewEnabled)
{
EnableEditorPreview();
}
else
{
DisableEditorPreview();
}
}
}
public void EnableEditorPreview()
{
_cachedPosition = transform.position;
_cachedRotation = transform.rotation;
_cachedScale = transform.localScale;
SetUpMap();
if (OnEditorPreviewEnabled != null)
{
OnEditorPreviewEnabled();
}
}
public void DisableEditorPreview()
{
_imagery.UpdateLayer -= OnImageOrTerrainUpdateLayer;
_terrain.UpdateLayer -= OnImageOrTerrainUpdateLayer;
_vectorData.SubLayerRemoved -= OnVectorDataSubLayerRemoved;
_vectorData.SubLayerAdded -= OnVectorDataSubLayerAdded;
_vectorData.UpdateLayer -= OnVectorDataUpdateLayer;
_vectorData.UnbindAllEvents();
if(_mapVisualizer != null)
{
_mapVisualizer.ClearMap();
}
DestroyTileProvider();
if (OnEditorPreviewDisabled != null)
{
OnEditorPreviewDisabled();
}
transform.position = _cachedPosition;
transform.rotation = _cachedRotation;
transform.localScale = _cachedScale;
}
public void DestroyTileProvider()
{
var tileProvider = TileProvider ?? gameObject.GetComponent<AbstractTileProvider>();
if (_options.extentOptions.extentType != MapExtentType.Custom && tileProvider != null)
{
tileProvider.gameObject.Destroy();
_tileProvider = null;
}
}
protected IEnumerator SetupAccess()
{
_fileSource = MapboxAccess.Instance;
yield return new WaitUntil(() => MapboxAccess.Configured);
}
/// <summary>
/// Sets up map.
/// This method uses the mapOptions and layer properties to setup the map to be rendered.
/// Override <c>SetUpMap</c> to write custom behavior to map setup.
/// </summary>
protected virtual void SetUpMap()
{
SetPlacementStrategy();
SetScalingStrategy();
SetTileProvider();
if (_imagery == null)
{
_imagery = new ImageryLayer();
}
_imagery.Initialize();
if (_terrain == null)
{
_terrain = new TerrainLayer();
}
_terrain.Initialize();
if (_vectorData == null)
{
_vectorData = new VectorLayer();
}
_vectorData.Initialize();
_mapVisualizer.Factories = new List<AbstractTileFactory>
{
_terrain.Factory,
_imagery.Factory,
_vectorData.Factory
};
InitializeMap(_options);
}
protected virtual void TileProvider_OnTileAdded(UnwrappedTileId tileId)
{
var tile = _mapVisualizer.LoadTile(tileId);
if (Options.placementOptions.snapMapToZero && !_worldHeightFixed)
{
_worldHeightFixed = true;
if (tile.HeightDataState == MeshGeneration.Enums.TilePropertyState.Loaded)
{
ApplySnapWorldToZero(tile);
}
else
{
tile.OnHeightDataChanged += (s) => { ApplySnapWorldToZero(tile); };
}
}
}
protected virtual void TileProvider_OnTileRemoved(UnwrappedTileId tileId)
{
_mapVisualizer.DisposeTile(tileId);
}
protected virtual void TileProvider_OnTileRepositioned(UnwrappedTileId tileId)
{
_mapVisualizer.RepositionTile(tileId);
}
protected void SendInitialized()
{
OnInitialized();
}
/// <summary>
/// Apply Snap World to Zero setting by moving map in Y Axis such that
/// center of the given tile will be at y=0.
/// </summary>
/// <param name="referenceTile">Tile to use for Y axis correction.</param>
private void ApplySnapWorldToZero(UnityTile referenceTile)
{
if (_options.placementOptions.snapMapToZero)
{
var h = referenceTile.QueryHeightData(.5f, .5f);
Root.transform.localPosition = new Vector3(
Root.transform.position.x,
-h,
Root.transform.position.z);
}
else
{
Root.transform.localPosition = new Vector3(
Root.transform.position.x,
0,
Root.transform.position.z);
}
}
/// <summary>
/// Initializes the map using the mapOptions.
/// </summary>
/// <param name="options">Options.</param>
protected virtual void InitializeMap(MapOptions options)
{
Options = options;
_worldHeightFixed = false;
_fileSource = MapboxAccess.Instance;
_centerLatitudeLongitude = Conversions.StringToLatLon(options.locationOptions.latitudeLongitude);
_initialZoom = (int)options.locationOptions.zoom;
options.scalingOptions.scalingStrategy.SetUpScaling(this);
options.placementOptions.placementStrategy.SetUpPlacement(this);
//Set up events for changes.
_imagery.UpdateLayer += OnImageOrTerrainUpdateLayer;
_terrain.UpdateLayer += OnImageOrTerrainUpdateLayer;
_vectorData.SubLayerRemoved += OnVectorDataSubLayerRemoved;
_vectorData.SubLayerAdded += OnVectorDataSubLayerAdded;
_vectorData.UpdateLayer += OnVectorDataUpdateLayer;
_options.locationOptions.PropertyHasChanged += (object sender, System.EventArgs eventArgs) =>
{
UpdateMap();
};
_options.extentOptions.PropertyHasChanged += (object sender, System.EventArgs eventArgs) =>
{
OnTileProviderChanged();
};
_options.extentOptions.defaultExtents.PropertyHasChanged += (object sender, System.EventArgs eventArgs) =>
{
if (Application.isEditor && !Application.isPlaying && IsEditorPreviewEnabled == false)
{
Debug.Log("defaultExtents");
return;
}
if (TileProvider != null)
{
TileProvider.UpdateTileExtent();
}
};
_options.placementOptions.PropertyHasChanged += (object sender, System.EventArgs eventArgs) =>
{
SetPlacementStrategy();
UpdateMap();
};
_options.scalingOptions.PropertyHasChanged += (object sender, System.EventArgs eventArgs) =>
{
SetScalingStrategy();
UpdateMap();
};
_mapVisualizer.Initialize(this, _fileSource);
TileProvider.Initialize(this);
SendInitialized();
TileProvider.UpdateTileExtent();
}
private void SetScalingStrategy()
{
switch (_options.scalingOptions.scalingType)
{
case MapScalingType.WorldScale:
_options.scalingOptions.scalingStrategy = new MapScalingAtWorldScaleStrategy();
break;
case MapScalingType.Custom:
_options.scalingOptions.scalingStrategy = new MapScalingAtUnityScaleStrategy();
break;
}
}
private void SetPlacementStrategy()
{
switch (_options.placementOptions.placementType)
{
case MapPlacementType.AtTileCenter:
_options.placementOptions.placementStrategy = new MapPlacementAtTileCenterStrategy();
break;
case MapPlacementType.AtLocationCenter:
_options.placementOptions.placementStrategy = new MapPlacementAtLocationCenterStrategy();
break;
default:
_options.placementOptions.placementStrategy = new MapPlacementAtTileCenterStrategy();
break;
}
}
private void SetTileProvider()
{
if (_options.extentOptions.extentType != MapExtentType.Custom)
{
ITileProviderOptions tileProviderOptions = _options.extentOptions.GetTileProviderOptions();
string tileProviderName = "TileProvider";
// Setup tileprovider based on type.
switch (_options.extentOptions.extentType)
{
case MapExtentType.CameraBounds:
{
if (TileProvider != null)
{
if (!(TileProvider is QuadTreeTileProvider))
{
TileProvider.gameObject.Destroy();
var go = new GameObject(tileProviderName);
go.transform.parent = transform;
TileProvider = go.AddComponent<QuadTreeTileProvider>();
}
}
else
{
var go = new GameObject(tileProviderName);
go.transform.parent = transform;
TileProvider = go.AddComponent<QuadTreeTileProvider>();
}
break;
}
case MapExtentType.RangeAroundCenter:
{
if (TileProvider != null)
{
TileProvider.gameObject.Destroy();
var go = new GameObject(tileProviderName);
go.transform.parent = transform;
TileProvider = go.AddComponent<RangeTileProvider>();
}
else
{
var go = new GameObject(tileProviderName);
go.transform.parent = transform;
TileProvider = go.AddComponent<RangeTileProvider>();
}
break;
}
case MapExtentType.RangeAroundTransform:
{
if (TileProvider != null)
{
if (!(TileProvider is RangeAroundTransformTileProvider))
{
TileProvider.gameObject.Destroy();
var go = new GameObject(tileProviderName);
go.transform.parent = transform;
TileProvider = go.AddComponent<RangeAroundTransformTileProvider>();
}
}
else
{
var go = new GameObject(tileProviderName);
go.transform.parent = transform;
TileProvider = go.AddComponent<RangeAroundTransformTileProvider>();
}
break;
}
default:
break;
}
TileProvider.SetOptions(tileProviderOptions);
}
else
{
TileProvider = _tileProvider;
}
}
private void TriggerTileRedrawForExtent(ExtentArgs currentExtent)
{
var _activeTiles = _mapVisualizer.ActiveTiles;
_currentExtent = new HashSet<UnwrappedTileId>(currentExtent.activeTiles);
if (tilesToProcess == null)
{
tilesToProcess = new List<UnwrappedTileId>();
}
else
{
tilesToProcess.Clear();
}
foreach (var item in _activeTiles)
{
if (TileProvider.Cleanup(item.Key))
{
tilesToProcess.Add(item.Key);
}
}
if (tilesToProcess.Count > 0)
{
OnTilesDisposing(tilesToProcess);
foreach (var t2r in tilesToProcess)
{
TileProvider_OnTileRemoved(t2r);
}
}
foreach (var tile in _activeTiles)
{
// Reposition tiles in case we panned.
TileProvider_OnTileRepositioned(tile.Key);
}
tilesToProcess.Clear();
foreach (var tile in _currentExtent)
{
if (!_activeTiles.ContainsKey(tile))
{
tilesToProcess.Add(tile);
}
}
if (tilesToProcess.Count > 0)
{
OnTilesStarting(tilesToProcess);
foreach (var tileId in tilesToProcess)
{
_mapVisualizer.State = ModuleState.Working;
TileProvider_OnTileAdded(tileId);
}
}
}
private void OnMapExtentChanged(object sender, ExtentArgs currentExtent)
{
TriggerTileRedrawForExtent(currentExtent);
}
private void OnImageOrTerrainUpdateLayer(object sender, System.EventArgs eventArgs)
{
LayerUpdateArgs layerUpdateArgs = eventArgs as LayerUpdateArgs;
if (layerUpdateArgs != null)
{
_mapVisualizer.UpdateTileForProperty(layerUpdateArgs.factory, layerUpdateArgs);
if (layerUpdateArgs.effectsVectorLayer)
{
RedrawVectorDataLayer();
}
OnMapRedrawn();
}
}
private void RedrawVectorDataLayer()
{
_mapVisualizer.UnregisterTilesFrom(_vectorData.Factory);
_vectorData.UnbindAllEvents();
_vectorData.UpdateFactorySettings();
_mapVisualizer.ReregisterTilesTo(_vectorData.Factory);
}
private void OnVectorDataSubLayerRemoved(object sender, EventArgs eventArgs)
{
VectorLayerUpdateArgs layerUpdateArgs = eventArgs as VectorLayerUpdateArgs;
if (layerUpdateArgs.visualizer != null)
{
_mapVisualizer.RemoveTilesFromLayer((VectorTileFactory)layerUpdateArgs.factory, layerUpdateArgs.visualizer);
}
OnMapRedrawn();
}
private void OnVectorDataSubLayerAdded(object sender, EventArgs eventArgs)
{
RedrawVectorDataLayer();
OnMapRedrawn();
}
private void OnVectorDataUpdateLayer(object sender, System.EventArgs eventArgs)
{
VectorLayerUpdateArgs layerUpdateArgs = eventArgs as VectorLayerUpdateArgs;
if (layerUpdateArgs.visualizer != null)
{
//We have a visualizer. Update only the visualizer.
//No need to unload the entire factory to apply changes.
_mapVisualizer.UnregisterAndRedrawTilesFromLayer((VectorTileFactory)layerUpdateArgs.factory, layerUpdateArgs.visualizer);
}
else
{
//We are updating a core property of vector section.
//All vector features need to get unloaded and re-created.
RedrawVectorDataLayer();
}
OnMapRedrawn();
}
private void OnTileProviderChanged()
{
if (Application.isEditor && !Application.isPlaying && IsEditorPreviewEnabled == false)
{
Debug.Log("extentOptions");
return;
}
SetTileProvider();
TileProvider.Initialize(this);
if (IsEditorPreviewEnabled)
{
TileProvider.UpdateTileExtent();
}
}
#endregion
#region Conversion and Height Query Methods
private Vector3 GeoToWorldPositionXZ(Vector2d latitudeLongitude)
{
// For quadtree implementation of the map, the map scale needs to be compensated for.
var scaleFactor = Mathf.Pow(2, (InitialZoom - AbsoluteZoom));
var worldPos = Conversions.GeoToWorldPosition(latitudeLongitude, CenterMercator, WorldRelativeScale * scaleFactor).ToVector3xz();
return Root.TransformPoint(worldPos);
}
protected virtual float QueryElevationAtInternal(Vector2d latlong, out float tileScale)
{
var _meters = Conversions.LatLonToMeters(latlong.x, latlong.y);
UnityTile tile;
bool foundTile = MapVisualizer.ActiveTiles.TryGetValue(Conversions.LatitudeLongitudeToTileId(latlong.x, latlong.y, (int)Zoom), out tile);
if (foundTile)
{
tileScale = tile.TileScale;
var _rect = tile.Rect;
return tile.QueryHeightData((float)((_meters - _rect.Min).x / _rect.Size.x), (float)((_meters.y - _rect.Max.y) / _rect.Size.y));
}
else
{
tileScale = 1f;
return 0f;
}
}
/// <summary>
/// Converts a latitude longitude into map space position.
/// </summary>
/// <returns>Position in map space.</returns>
/// <param name="latitudeLongitude">Latitude longitude.</param>
/// <param name="queryHeight">If set to <c>true</c> will return the terrain height(in Unity units) at that point.</param>
public virtual Vector3 GeoToWorldPosition(Vector2d latitudeLongitude, bool queryHeight = true)
{
Vector3 worldPos = GeoToWorldPositionXZ(latitudeLongitude);
if (queryHeight)
{
//Query Height.
float tileScale = 1f;
float height = QueryElevationAtInternal(latitudeLongitude, out tileScale);
// Apply height inside the unity tile space
UnityTile tile;
if (MapVisualizer.ActiveTiles.TryGetValue(Conversions.LatitudeLongitudeToTileId(latitudeLongitude.x, latitudeLongitude.y, (int)Zoom), out tile))
{
if (tile != null)
{
// Calculate height in the local space of the tile gameObject.
// Height is aligned with the y axis in local space.
// This also helps us avoid scale values when setting the height.
var localPos = tile.gameObject.transform.InverseTransformPoint(worldPos);
localPos.y = height;
worldPos = tile.gameObject.transform.TransformPoint(localPos);
}
}
}
return worldPos;
}
/// <summary>
/// Converts a position in map space into a laitude longitude.
/// </summary>
/// <returns>Position in Latitude longitude.</returns>
/// <param name="realworldPoint">Realworld point.</param>
public virtual Vector2d WorldToGeoPosition(Vector3 realworldPoint)
{
// For quadtree implementation of the map, the map scale needs to be compensated for.
var scaleFactor = Mathf.Pow(2, (InitialZoom - AbsoluteZoom));
return (Root.InverseTransformPoint(realworldPoint)).GetGeoPosition(CenterMercator, WorldRelativeScale * scaleFactor);
}
/// <summary>
/// Queries the real world elevation data in Unity units at a given latitude longitude.
/// </summary>
/// <returns>The height data.</returns>
/// <param name="latlong">Latlong.</param>
public virtual float QueryElevationInUnityUnitsAt(Vector2d latlong)
{
float tileScale = 1f;
return QueryElevationAtInternal(latlong, out tileScale);
}
/// <summary>
/// Queries the real world elevation data in Meters at a given latitude longitude.
/// </summary>
/// <returns>The height data.</returns>
/// <param name="latlong">Latlong.</param>
public virtual float QueryElevationInMetersAt(Vector2d latlong)
{
float tileScale = 1f;
float height = QueryElevationAtInternal(latlong, out tileScale);
return (height / tileScale);
}
#endregion
#region Map Property Related Changes Methods
public virtual void SetCenterMercator(Vector2d centerMercator)
{
_centerMercator = centerMercator;
}
public virtual void SetCenterLatitudeLongitude(Vector2d centerLatitudeLongitude)
{
_options.locationOptions.latitudeLongitude = string.Format("{0}, {1}", centerLatitudeLongitude.x.ToString(CultureInfo.InvariantCulture), centerLatitudeLongitude.y.ToString(CultureInfo.InvariantCulture));
_centerLatitudeLongitude = centerLatitudeLongitude;
}
public virtual void SetWorldRelativeScale(float scale)
{
_worldRelativeScale = scale;
}
public virtual void SetLoadingTexture(Texture2D loadingTexture)
{
Options.loadingTexture = loadingTexture;
}
public virtual void SetTileMaterial(Material tileMaterial)
{
Options.tileMaterial = tileMaterial;
}
/// <summary>
/// Sets the extent type and parameters to control the maps extent.
/// </summary>
/// <param name="extentType">Extent type.</param>
/// <param name="extentOptions">Extent options.</param>
public virtual void SetExtent(MapExtentType extentType, ExtentOptions extentOptions = null)
{
_options.extentOptions.extentType = extentType;
if (extentOptions != null)
{
var currentOptions = _options.extentOptions.GetTileProviderOptions();
if (currentOptions.GetType() == extentOptions.GetType())
{
currentOptions = extentOptions;
}
}
OnTileProviderChanged();
}
/// <summary>
/// Set parameters for current extent calculator strategy.
/// </summary>
/// <param name="extentOptions">Parameters to control the map extent.</param>
public virtual void SetExtentOptions(ExtentOptions extentOptions)
{
_options.extentOptions.GetTileProviderOptions().SetOptions(extentOptions);
_options.extentOptions.defaultExtents.HasChanged = true;
}
/// <summary>
/// Sets the positions of the map's root transform.
/// Use <paramref name="placementType"/> = <c> MapPlacementType.AtTileCenter</c> to place map root at the center of tile containing the latitude,longitude.
/// Use <paramref name="placementType"/> = <c> MapPlacementType.AtLocationCenter</c> to place map root at the latitude,longitude.
/// </summary>
/// <param name="placementType">Placement type.</param>
public virtual void SetPlacementType(MapPlacementType placementType)
{
_options.placementOptions.placementType = placementType;
_options.placementOptions.HasChanged = true;
}
/// <summary>
/// Translates map root by the terrain elevation at the center geo location.
/// Use this method with <c>TerrainWithElevation</c>
/// </summary>
/// <param name="active">If set to <c>true</c> active.</param>
public virtual void SnapMapToZero(bool active)
{
_options.placementOptions.snapMapToZero = active;
_options.placementOptions.HasChanged = true;
}
/// <summary>
/// Sets the map to use real world scale for map tile.
/// Use world scale for AR use cases or applications that need true world scale.
/// </summary>
public virtual void UseWorldScale()
{
_options.scalingOptions.scalingType = MapScalingType.WorldScale;
_options.scalingOptions.HasChanged = true;
}
/// <summary>
/// Sets the map to use custom scale for map tiles.
/// </summary>
/// <param name="tileSizeInUnityUnits">Tile size in unity units to scale each Web Mercator tile.</param>
public virtual void UseCustomScale(float tileSizeInUnityUnits)
{
_options.scalingOptions.scalingType = MapScalingType.Custom;
_options.scalingOptions.unityTileSize = tileSizeInUnityUnits;
_options.scalingOptions.HasChanged = true;
}
#endregion
#region Events
/// <summary>
/// Event delegate, gets called after map is initialized
/// <seealso cref="OnUpdated"/>
/// </summary>
public event Action OnInitialized = delegate { };
/// <summary>
/// Event delegate, gets called after map is updated.
/// <c>UpdateMap</c> will trigger this event.
/// <seealso cref="OnInitialized"/>
/// </summary>
public event Action OnUpdated = delegate { };
public event Action OnMapRedrawn = delegate { };
/// <summary>
/// Event delegate, gets called when map preview is enabled
/// </summary>
public event Action OnEditorPreviewEnabled = delegate { };
/// <summary>
/// Event delegate, gets called when map preview is disabled
/// </summary>
public event Action OnEditorPreviewDisabled = delegate { };
/// <summary>
/// Event delegate, gets called when a tile is completed.
/// </summary>
public event Action<UnityTile> OnTileFinished = delegate { };
/// <summary>
/// Event delegate, gets called when new tiles coordinates are registered.
/// </summary>
public event Action<List<UnwrappedTileId>> OnTilesStarting = delegate { };
/// <summary>
/// Event delegate, gets called before a tile is getting recycled.
/// </summary>
public event Action<List<UnwrappedTileId>> OnTilesDisposing = delegate { };
#endregion
}
}