//-----------------------------------------------------------------------
//
// 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);
}
}
}
}