//----------------------------------------------------------------------- // // Copyright (c) 2016 Mapbox. All rights reserved. // //----------------------------------------------------------------------- namespace Mapbox.Map { using System; using System.Collections.Generic; using Mapbox.Platform; using Mapbox.Utils; /// /// The Mapbox Map abstraction will take care of fetching and decoding /// data for a geographic bounding box at a certain zoom level. /// /// /// The tile type, currently or /// . /// /// /// Request a map of the whole world: /// /// var map = new Map<RasterTile>(MapboxAccess.Instance); /// map.Zoom = 2 /// map.Vector2dBounds = Vector2dBounds.World(); /// map.TilesetId = "mapbox://styles/mapbox/streets-v10 /// /// // Register for tile updates. /// map.Subscribe(this); /// /// // Trigger the request. /// map.Update(); /// /// public sealed class Map : Mapbox.Utils.IObservable where T : Tile, new() { /// /// Arbitrary limit of tiles this class will handle simultaneously. /// public const int TileMax = 256; private readonly IFileSource fs; private Vector2dBounds latLngBounds; private int zoom; private string tilesetId; private HashSet tiles = new HashSet(); private List> observers = new List>(); /// /// Initializes a new instance of the class. /// /// The data source abstraction. public Map(IFileSource fs) { this.fs = fs; this.latLngBounds = new Vector2dBounds(); this.zoom = 0; } /// /// Gets or sets the tileset ID. If not set, it will use the default /// tileset ID for the tile type. I.e. "mapbox.satellite" for raster tiles /// and "mapbox.mapbox-streets-v7" for vector tiles. /// /// /// The tileset ID, usually in the format "user.mapid". Exceptionally, /// will take the full style URL /// from where the tile is composited from, like "mapbox://styles/mapbox/streets-v9". /// public string TilesetId { get { return this.tilesetId; } set { if (this.tilesetId == value) { return; } this.tilesetId = value; foreach (Tile tile in this.tiles) { tile.Cancel(); } this.tiles.Clear(); } } /// /// Gets the tiles, vector or raster. Tiles might be /// in a incomplete state. /// /// The tiles. public HashSet Tiles { get { return this.tiles; } } /// Gets or sets a geographic bounding box. /// New geographic bounding box. public Vector2dBounds Vector2dBounds { get { return this.latLngBounds; } set { this.latLngBounds = value; } } /// Gets or sets the central coordinate of the map. /// The central coordinate. public Vector2d Center { get { return this.latLngBounds.Center; } set { this.latLngBounds.Center = value; } } /// Gets or sets the map zoom level. /// The new zoom level. public int Zoom { get { return this.zoom; } set { this.zoom = Math.Max(0, Math.Min(20, value)); } } /// /// Sets the coordinates bounds and zoom at once. /// /// Coordinates bounds. /// Zoom level. public void SetVector2dBoundsZoom(Vector2dBounds bounds, int zoom) { this.latLngBounds = bounds; this.zoom = zoom; } /// Add an to the observer list. /// The object subscribing to events. public void Subscribe(Mapbox.Utils.IObserver observer) { this.observers.Add(observer); } /// Remove an to the observer list. /// The object unsubscribing to events. public void Unsubscribe(Mapbox.Utils.IObserver observer) { this.observers.Remove(observer); } private void NotifyNext(T next) { var copy = new List>(this.observers); foreach (Mapbox.Utils.IObserver observer in copy) { observer.OnNext(next); } } /// /// Request tiles after changing map properties. /// public void Update() { var cover = TileCover.Get(this.latLngBounds, this.zoom); if (cover.Count > TileMax) { return; } // Do not request tiles that we are already requesting // but at the same time exclude the ones we don't need // anymore, cancelling the network request. this.tiles.RemoveWhere((T tile) => { if (cover.Remove(tile.Id)) { return false; } else { tile.Cancel(); this.NotifyNext(tile); return true; } }); foreach (CanonicalTileId id in cover) { var tile = new T(); Tile.Parameters param; param.Id = id; param.TilesetId = this.tilesetId; param.Fs = this.fs; tile.Initialize(param, () => { this.NotifyNext(tile); }); this.tiles.Add(tile); } } } }