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.

278 lines
7.1 KiB

6 months ago
//-----------------------------------------------------------------------
// <copyright file="Tile.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Map
{
using System;
using Mapbox.Platform;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Mapbox.Unity.Utilities;
/// <summary>
/// A Map tile, a square with vector or raster data representing a geographic
/// bounding box. More info <see href="https://en.wikipedia.org/wiki/Tiled_web_map">
/// here </see>.
/// </summary>
public abstract class Tile : IAsyncRequest
{
private CanonicalTileId _id;
private List<Exception> _exceptions;
private State _state = State.New;
private IAsyncRequest _request;
private Action _callback;
/// <summary> Tile state. </summary>
public enum State
{
/// <summary> New tile, not yet initialized. </summary>
New,
/// <summary> Loading data. </summary>
Loading,
/// <summary> Data loaded and parsed. </summary>
Loaded,
/// <summary> Data loading cancelled. </summary>
Canceled,
/// <summary> Data has been loaded before and got updated. </summary>
Updated
}
/// <summary> Gets the <see cref="T:Mapbox.Map.CanonicalTileId"/> identifier. </summary>
/// <value> The canonical tile identifier. </value>
public CanonicalTileId Id
{
get { return _id; }
set { _id = value; }
}
/// <summary>Flag to indicate if the request was successful</summary>
public bool HasError
{
get
{
return _exceptions == null ? false : _exceptions.Count > 0;
}
}
/// <summary> Exceptions that might have occured during creation of the tile. </summary>
public ReadOnlyCollection<Exception> Exceptions
{
get { return null == _exceptions ? null : _exceptions.AsReadOnly(); }
}
/// <summary> Messages of exceptions otherwise empty string. </summary>
public string ExceptionsAsString
{
get
{
if (null == _exceptions || _exceptions.Count == 0) { return string.Empty; }
return string.Join(Environment.NewLine, _exceptions.Select(e => e.Message).ToArray());
}
}
/// <summary>
/// Sets the error message.
/// </summary>
/// <param name="errorMessage"></param>
internal void AddException(Exception ex)
{
if (null == _exceptions) { _exceptions = new List<Exception>(); }
_exceptions.Add(ex);
}
/// <summary>
/// Gets the current state. When fully loaded, you must
/// check if the data actually arrived and if the tile
/// is accusing any error.
/// </summary>
/// <value> The tile state. </value>
public State CurrentState
{
get
{
return _state;
}
}
public HttpRequestType RequestType { get { return _request.RequestType; } }
public bool IsCompleted
{
get
{
return _state == State.Loaded;
}
}
/// <summary>
/// Initializes the <see cref="T:Mapbox.Map.Tile"/> object. It will
/// start a network request and fire the callback when completed.
/// </summary>
/// <param name="param"> Initialization parameters. </param>
/// <param name="callback"> The completion callback. </param>
public void Initialize(Parameters param, Action callback)
{
Cancel();
_state = State.Loading;
_id = param.Id;
_callback = callback;
_request = param.Fs.Request(MakeTileResource(param.TilesetId).GetUrl(), HandleTileResponse, tileId: _id, tilesetId: param.TilesetId);
}
internal void Initialize(IFileSource fileSource, CanonicalTileId canonicalTileId, string tilesetId, Action p)
{
Cancel();
_state = State.Loading;
_id = canonicalTileId;
_callback = p;
_request = fileSource.Request(MakeTileResource(tilesetId).GetUrl(), HandleTileResponse, tileId: _id, tilesetId: tilesetId);
}
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current
/// <see cref="T:Mapbox.Map.Tile"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current
/// <see cref="T:Mapbox.Map.Tile"/>.
/// </returns>
public override string ToString()
{
return Id.ToString();
}
/// <summary>
/// Cancels the request for the <see cref="T:Mapbox.Map.Tile"/> object.
/// It will stop a network request and set the tile's state to Canceled.
/// </summary>
/// <example>
/// <code>
/// // 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.
/// tiles.RemoveWhere((T tile) =>
/// {
/// if (cover.Remove(tile.Id))
/// {
/// return false;
/// }
/// else
/// {
/// tile.Cancel();
/// NotifyNext(tile);
/// return true;
/// }
/// });
/// </code>
/// </example>
public void Cancel()
{
if (_request != null)
{
_request.Cancel();
_request = null;
}
_state = State.Canceled;
}
// Get the tile resource (raster/vector/etc).
internal abstract TileResource MakeTileResource(string tilesetId);
// Decode the tile.
internal abstract bool ParseTileData(byte[] data);
// TODO: Currently the tile decoding is done on the main thread. We must implement
// a Worker class to abstract this, so on platforms that support threads (like Unity
// on the desktop, Android, etc) we can use worker threads and when building for
// the browser, we keep it single-threaded.
List<string> ids = new List<string>();
private void HandleTileResponse(Response response)
{
if (response.HasError)
{
if (!ids.Contains(_id.ToString()))
ids.Add(_id.ToString());
else
return;
response.Exceptions.ToList().ForEach(e => AddException(e));
}
else
{
// only try to parse if request was successful
// current implementation doesn't need to check if parsing is successful:
// * Mapbox.Map.VectorTile.ParseTileData() already adds any exception to the list
// * Mapbox.Map.RasterTile.ParseTileData() doesn't do any parsing
ParseTileData(response.Data);
}
// Cancelled is not the same as loaded!
if (_state != State.Canceled)
{
if (response.IsUpdate)
{
_state = State.Updated;
}
else
{
_state = State.Loaded;
}
}
_callback();
}
/// <summary>
/// Parameters for initializing a Tile object.
/// </summary>
/// <example>
/// <code>
/// var parameters = new Tile.Parameters();
/// parameters.Fs = MapboxAccess.Instance;
/// parameters.Id = new CanonicalTileId(_zoom, _tileCoorindateX, _tileCoordinateY);
/// parameters.TilesetId = "mapbox.mapbox-streets-v7";
/// </code>
/// </example>
public struct Parameters
{
/// <summary> The tile id. </summary>
public CanonicalTileId Id;
/// <summary>
/// The tileset ID, usually in the format "user.mapid". Exceptionally,
/// <see cref="T:Mapbox.Map.RasterTile"/> will take the full style URL
/// from where the tile is composited from, like mapbox://styles/mapbox/streets-v9.
/// </summary>
public string TilesetId;
/// <summary> The data source abstraction. </summary>
public IFileSource Fs;
}
}
}