using System.Linq; using Mapbox.Utils; using System; using UnityEngine; using System.Collections.Generic; using Mapbox.Unity.MeshGeneration.Data; using Mapbox.Unity.MeshGeneration.Factories; using Mapbox.Unity.Utilities; namespace Mapbox.Unity.Map { [Serializable] public class VectorLayer : AbstractLayer, IVectorDataLayer { //Private Fields [SerializeField] private VectorLayerProperties _layerProperty = new VectorLayerProperties(); private VectorTileFactory _vectorTileFactory; //Events public EventHandler SubLayerAdded; public EventHandler SubLayerRemoved; //Properties [NodeEditorElement(" Vector Layer ")] public VectorLayerProperties LayerProperty { get { return _layerProperty; } } public MapLayerType LayerType { get { return MapLayerType.Vector; } } public bool IsLayerActive { get { return (_layerProperty.sourceType != VectorSourceType.None); } } public string LayerSourceId { get { return _layerProperty.sourceOptions.Id; } } public VectorTileFactory Factory { get { return _vectorTileFactory; } } //Public Methods public void Initialize(LayerProperties properties) { _layerProperty = (VectorLayerProperties)properties; Initialize(); } public void Initialize() { _vectorTileFactory = ScriptableObject.CreateInstance(); UpdateFactorySettings(); _layerProperty.PropertyHasChanged += RedrawVectorLayer; _layerProperty.SubLayerPropertyAdded += AddVectorLayer; _layerProperty.SubLayerPropertyRemoved += RemoveVectorLayer; _vectorTileFactory.TileFactoryHasChanged += OnVectorTileFactoryOnTileFactoryHasChanged; } public void Update(LayerProperties properties) { Initialize(properties); } public void UnbindAllEvents() { if (_vectorTileFactory != null) { _vectorTileFactory.UnbindEvents(); } } public void UpdateFactorySettings() { _vectorTileFactory.SetOptions(_layerProperty); } public void Remove() { _layerProperty = new VectorLayerProperties { sourceType = VectorSourceType.None }; } //Private Methods private void AddVectorLayer(object sender, EventArgs args) { VectorLayerUpdateArgs layerUpdateArgs = args as VectorLayerUpdateArgs; if (layerUpdateArgs.property is PrefabItemOptions) { layerUpdateArgs.visualizer = _vectorTileFactory.AddPOIVectorLayerVisualizer((PrefabItemOptions)layerUpdateArgs.property); } else if (layerUpdateArgs.property is VectorSubLayerProperties) { layerUpdateArgs.visualizer = _vectorTileFactory.AddVectorLayerVisualizer((VectorSubLayerProperties)layerUpdateArgs.property); } layerUpdateArgs.factory = _vectorTileFactory; if (SubLayerAdded != null) { SubLayerAdded(this, layerUpdateArgs); } } private void RemoveVectorLayer(object sender, EventArgs args) { VectorLayerUpdateArgs layerUpdateArgs = args as VectorLayerUpdateArgs; layerUpdateArgs.visualizer = _vectorTileFactory.FindVectorLayerVisualizer((VectorSubLayerProperties)layerUpdateArgs.property); layerUpdateArgs.factory = _vectorTileFactory; if (SubLayerRemoved != null) { SubLayerRemoved(this, layerUpdateArgs); } } private void RedrawVectorLayer(object sender, System.EventArgs e) { NotifyUpdateLayer(_vectorTileFactory, sender as MapboxDataProperty, true); } private void OnVectorTileFactoryOnTileFactoryHasChanged(object sender, EventArgs args) { NotifyUpdateLayer(args as LayerUpdateArgs); } #region Api Methods public virtual TileJsonData GetTileJsonData() { return _layerProperty.tileJsonData; } /// /// Add provided data source (TilesetId) to existing ones. /// Mapbox vector api supports comma separated TilesetIds and this method /// adds the provided TilesetId at the end of the existing source. /// /// Data source (TilesetId) to add to existing sources. public virtual void AddLayerSource(string vectorSource) { if (!string.IsNullOrEmpty(vectorSource)) { if (!_layerProperty.sourceOptions.Id.Contains(vectorSource)) { if (string.IsNullOrEmpty(_layerProperty.sourceOptions.Id)) { SetLayerSource(vectorSource); return; } var newLayerSource = _layerProperty.sourceOptions.Id + "," + vectorSource; SetLayerSource(newLayerSource); } } else { Debug.LogError("Empty source. Nothing was added to the list of data sources"); } } /// /// Change existing data source (TilesetId) with provided source. /// /// Data source (TilesetId) to use. public virtual void SetLayerSource(string vectorSource) { SetLayerSourceInternal(vectorSource); _layerProperty.HasChanged = true; } /// /// Change existing data source (TilesetId) with provided source. /// /// Data source (TilesetId) to use. public virtual void SetLayerSource(VectorSourceType vectorSource) { SetLayerSourceInternal(vectorSource); _layerProperty.HasChanged = true; } /// /// Sets the layer source as Style-optimized vector tiles /// /// Vector source. /// Style-Optimized style id. /// Modified date. /// Style name. public virtual void SetLayerSourceWithOptimizedStyle(string vectorSource, string styleId, string modifiedDate, string styleName = null) { SetLayerSourceInternal(vectorSource); SetOptimizedStyleInternal(styleId, modifiedDate, styleName); _layerProperty.HasChanged = true; } /// /// Sets the layer source as Style-optimized vector tiles /// /// Vector source. /// Style-Optimized style id. /// Modified date. /// Style name. public virtual void SetLayerSourceWithOptimizedStyle(VectorSourceType vectorSource, string styleId, string modifiedDate, string styleName = null) { SetLayerSourceInternal(vectorSource); SetOptimizedStyleInternal(styleId, modifiedDate, styleName); _layerProperty.HasChanged = true; } /// /// Enable coroutines for vector features, processing choosen amount /// of them each frame. /// /// Numbers of features to process each frame. /// public virtual void EnableVectorFeatureProcessingWithCoroutines(int entityPerCoroutine = 20) { if (_layerProperty.performanceOptions.isEnabled != true || _layerProperty.performanceOptions.entityPerCoroutine != entityPerCoroutine) { _layerProperty.performanceOptions.isEnabled = true; _layerProperty.performanceOptions.entityPerCoroutine = entityPerCoroutine; _layerProperty.performanceOptions.HasChanged = true; } } public void DisableVectorFeatureProcessingWithCoroutines() { _layerProperty.performanceOptions.isEnabled = false; } #endregion #region Poi Api Methods /// /// Creates the prefab layer. /// /// the options of the prefab layer. private void CreatePrefabLayer(PrefabItemOptions item) { if (LayerProperty.sourceType == VectorSourceType.None || !LayerProperty.sourceOptions.Id.Contains(MapboxDefaultVector.GetParameters(VectorSourceType.MapboxStreets).Id)) { Debug.LogError("In order to place location prefabs please add \"mapbox.mapbox-streets-v7\" to the list of vector data sources"); return; } AddPointsOfInterestSubLayer(item); } /// /// Places a prefab at the specified LatLon on the Map. /// /// A Game Object Prefab. /// A Vector2d(Latitude Longitude) object public virtual void SpawnPrefabAtGeoLocation(GameObject prefab, Vector2d LatLon, Action> callback = null, bool scaleDownWithWorld = true, string locationItemName = "New Location") { var latLonArray = new Vector2d[] { LatLon }; SpawnPrefabAtGeoLocation(prefab, latLonArray, callback, scaleDownWithWorld, locationItemName); } /// /// Places a prefab at all locations specified by the LatLon array. /// /// A Game Object Prefab. /// A Vector2d(Latitude Longitude) object public virtual void SpawnPrefabAtGeoLocation(GameObject prefab, Vector2d[] LatLon, Action> callback = null, bool scaleDownWithWorld = true, string locationItemName = "New Location") { var coordinateArray = new string[LatLon.Length]; for (int i = 0; i < LatLon.Length; i++) { coordinateArray[i] = LatLon[i].x + ", " + LatLon[i].y; } PrefabItemOptions item = new PrefabItemOptions() { findByType = LocationPrefabFindBy.AddressOrLatLon, prefabItemName = locationItemName, spawnPrefabOptions = new SpawnPrefabOptions() { prefab = prefab, scaleDownWithWorld = scaleDownWithWorld }, coordinates = coordinateArray }; if (callback != null) { item.OnAllPrefabsInstantiated += callback; } CreatePrefabLayer(item); } /// /// Places the prefab for supplied categories. /// /// GameObject Prefab /// For more than one category separate them by pipe /// (eg: LocationPrefabCategories.Food | LocationPrefabCategories.Nightlife) /// Density controls the number of POIs on the map.(Integer value between 1 and 30) /// Name of this location prefab item for future reference /// Should the prefab scale up/down along with the map game object? public virtual void SpawnPrefabByCategory(GameObject prefab, LocationPrefabCategories categories = LocationPrefabCategories.AnyCategory, int density = 30, Action> callback = null, bool scaleDownWithWorld = true, string locationItemName = "New Location") { PrefabItemOptions item = new PrefabItemOptions() { findByType = LocationPrefabFindBy.MapboxCategory, categories = categories, density = density, prefabItemName = locationItemName, spawnPrefabOptions = new SpawnPrefabOptions() { prefab = prefab, scaleDownWithWorld = scaleDownWithWorld } }; if (callback != null) { item.OnAllPrefabsInstantiated += callback; } CreatePrefabLayer(item); } /// /// Places the prefab at POI locations if its name contains the supplied string /// GameObject Prefab /// This is the string that will be checked against the POI name to see if is contained in it, and ony those POIs will be spawned /// Density (Integer value between 1 and 30) /// Name of this location prefab item for future reference /// Should the prefab scale up/down along with the map game object? /// public virtual void SpawnPrefabByName(GameObject prefab, string nameString, int density = 30, Action> callback = null, bool scaleDownWithWorld = true, string locationItemName = "New Location") { PrefabItemOptions item = new PrefabItemOptions() { findByType = LocationPrefabFindBy.POIName, nameString = nameString, density = density, prefabItemName = locationItemName, spawnPrefabOptions = new SpawnPrefabOptions() { prefab = prefab, scaleDownWithWorld = scaleDownWithWorld } }; CreatePrefabLayer(item); } #endregion #region LayerOperations // FEATURE LAYER OPERATIONS public virtual void AddFeatureSubLayer(VectorSubLayerProperties subLayerProperties) { if (_layerProperty.vectorSubLayers == null) { _layerProperty.vectorSubLayers = new List(); } _layerProperty.vectorSubLayers.Add(subLayerProperties); _layerProperty.OnSubLayerPropertyAdded(new VectorLayerUpdateArgs { property = _layerProperty.vectorSubLayers.Last() }); } public virtual void AddPolygonFeatureSubLayer(string assignedSubLayerName, string dataLayerNameInService) { VectorSubLayerProperties subLayer = PresetSubLayerPropertiesFetcher.GetSubLayerProperties(PresetFeatureType.Buildings); subLayer.coreOptions.layerName = dataLayerNameInService; subLayer.coreOptions.sublayerName = assignedSubLayerName; AddFeatureSubLayer(subLayer); } public virtual void AddLineFeatureSubLayer(string assignedSubLayerName, string dataLayerNameInService, float lineWidth = 1) { VectorSubLayerProperties subLayer = PresetSubLayerPropertiesFetcher.GetSubLayerProperties(PresetFeatureType.Roads); subLayer.coreOptions.layerName = dataLayerNameInService; subLayer.coreOptions.sublayerName = assignedSubLayerName; subLayer.lineGeometryOptions.Width = lineWidth; AddFeatureSubLayer(subLayer); } public virtual void AddPointFeatureSubLayer(string assignedSubLayerName, string dataLayerNameInService) { VectorSubLayerProperties subLayer = PresetSubLayerPropertiesFetcher.GetSubLayerProperties(PresetFeatureType.Points); subLayer.coreOptions.layerName = dataLayerNameInService; subLayer.coreOptions.sublayerName = assignedSubLayerName; AddFeatureSubLayer(subLayer); } public virtual void AddCustomFeatureSubLayer(string assignedSubLayerName, string dataLayerNameInService) { VectorSubLayerProperties subLayer = PresetSubLayerPropertiesFetcher.GetSubLayerProperties(PresetFeatureType.Custom); subLayer.coreOptions.layerName = dataLayerNameInService; subLayer.coreOptions.sublayerName = assignedSubLayerName; AddFeatureSubLayer(subLayer); } public virtual IEnumerable GetAllFeatureSubLayers() { return _layerProperty.vectorSubLayers.AsEnumerable(); } public virtual IEnumerable GetAllPolygonFeatureSubLayers() { foreach (var featureLayer in _layerProperty.vectorSubLayers) { if (featureLayer.coreOptions.geometryType == VectorPrimitiveType.Polygon) { yield return featureLayer; } } } public virtual IEnumerable GetAllLineFeatureSubLayers() { foreach (var featureLayer in _layerProperty.vectorSubLayers) { if (featureLayer.coreOptions.geometryType == VectorPrimitiveType.Line) { yield return featureLayer; } } } public virtual IEnumerable GetAllPointFeatureSubLayers() { foreach (var featureLayer in _layerProperty.vectorSubLayers) { if (featureLayer.coreOptions.geometryType == VectorPrimitiveType.Point) { yield return featureLayer; } } } public virtual IEnumerable GetFeatureSubLayerByQuery(Func query) { foreach (var featureLayer in _layerProperty.vectorSubLayers) { if (query(featureLayer)) { yield return featureLayer; } } } public virtual VectorSubLayerProperties GetFeatureSubLayerAtIndex(int i) { if (i < _layerProperty.vectorSubLayers.Count) { return _layerProperty.vectorSubLayers[i]; } else { return null; } } public virtual VectorSubLayerProperties FindFeatureSubLayerWithName(string featureLayerName) { int foundLayerIndex = -1; // Optimize for performance. for (int i = 0; i < _layerProperty.vectorSubLayers.Count; i++) { if (_layerProperty.vectorSubLayers[i].SubLayerNameMatchesExact(featureLayerName)) { foundLayerIndex = i; break; } } return (foundLayerIndex != -1) ? _layerProperty.vectorSubLayers[foundLayerIndex] : null; } public virtual void RemoveFeatureSubLayerWithName(string featureLayerName) { var layerToRemove = FindFeatureSubLayerWithName(featureLayerName); if (layerToRemove != null) { _layerProperty.OnSubLayerPropertyRemoved(new VectorLayerUpdateArgs { property = layerToRemove }); } } public virtual void RemoveFeatureSubLayer(VectorSubLayerProperties layer) { _layerProperty.vectorSubLayers.Remove(layer); _layerProperty.OnSubLayerPropertyRemoved(new VectorLayerUpdateArgs { property = layer }); } // POI LAYER OPERATIONS public virtual void AddPointsOfInterestSubLayer(PrefabItemOptions poiLayerProperties) { if (_layerProperty.locationPrefabList == null) { _layerProperty.locationPrefabList = new List(); } _layerProperty.locationPrefabList.Add(poiLayerProperties); _layerProperty.OnSubLayerPropertyAdded(new VectorLayerUpdateArgs { property = _layerProperty.locationPrefabList.Last() }); } public virtual IEnumerable GetAllPointsOfInterestSubLayers() { return _layerProperty.locationPrefabList.AsEnumerable(); } public virtual PrefabItemOptions GetPointsOfInterestSubLayerAtIndex(int i) { if (i < _layerProperty.vectorSubLayers.Count) { return _layerProperty.locationPrefabList[i]; } else { return null; } } public virtual IEnumerable GetPointsOfInterestSubLayerByQuery(Func query) { foreach (var poiLayer in _layerProperty.locationPrefabList) { if (query(poiLayer)) { yield return poiLayer; } } } public virtual PrefabItemOptions FindPointsofInterestSubLayerWithName(string poiLayerName) { int foundLayerIndex = -1; // Optimize for performance. for (int i = 0; i < _layerProperty.locationPrefabList.Count; i++) { if (_layerProperty.locationPrefabList[i].SubLayerNameMatchesExact(poiLayerName)) { foundLayerIndex = i; break; } } return (foundLayerIndex != -1) ? _layerProperty.locationPrefabList[foundLayerIndex] : null; } public virtual void RemovePointsOfInterestSubLayerWithName(string poiLayerName) { var layerToRemove = FindPointsofInterestSubLayerWithName(poiLayerName); if (layerToRemove != null) { _layerProperty.OnSubLayerPropertyRemoved(new VectorLayerUpdateArgs { property = layerToRemove }); } } public virtual void RemovePointsOfInterestSubLayer(PrefabItemOptions layer) { _layerProperty.locationPrefabList.Remove(layer); _layerProperty.OnSubLayerPropertyRemoved(new VectorLayerUpdateArgs { property = layer }); } #endregion #region Private helper methods private void SetLayerSourceInternal(VectorSourceType vectorSource) { if (vectorSource != VectorSourceType.Custom && vectorSource != VectorSourceType.None) { _layerProperty.sourceType = vectorSource; _layerProperty.sourceOptions.layerSource = MapboxDefaultVector.GetParameters(vectorSource); } else { Debug.LogWarning("Invalid style - trying to set " + vectorSource.ToString() + " as default style!"); } } private void SetLayerSourceInternal(string vectorSource) { if (!string.IsNullOrEmpty(vectorSource)) { _layerProperty.sourceType = VectorSourceType.Custom; _layerProperty.sourceOptions.Id = vectorSource; } else { _layerProperty.sourceType = VectorSourceType.None; Debug.LogWarning("Empty source - turning off vector data. "); } } private void SetOptimizedStyleInternal(string styleId, string modifiedDate, string styleName) { _layerProperty.useOptimizedStyle = true; _layerProperty.optimizedStyle = _layerProperty.optimizedStyle ?? new Style(); _layerProperty.optimizedStyle.Id = styleId; _layerProperty.optimizedStyle.Modified = modifiedDate; if (!String.IsNullOrEmpty(styleName)) { _layerProperty.optimizedStyle.Name = styleName; } } #endregion } }