diff --git a/MapboxSdkCs.sln b/MapboxSdkCs.sln
index 6559738..98d1a8c 100644
--- a/MapboxSdkCs.sln
+++ b/MapboxSdkCs.sln
@@ -10,8 +10,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Geocoding", "src\Geocoding\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Directions", "src\Directions\Directions.csproj", "{08214364-755E-4D6F-B7FF-9D2D49011C20}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono", "src\Mono\Mono.csproj", "{346B1208-1587-490B-A7DE-A96B86E81CD6}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTest", "test\UnitTest\UnitTest.csproj", "{F04D4384-62B7-4F73-ACD9-C9A5112FC53F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Map", "src\Map\Map.csproj", "{6AECAE3C-A1F3-4B94-976B-D27AA4610879}"
@@ -26,8 +24,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeocodingUWP", "src\Geocodi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DirectionsUWP", "src\Directions\DirectionsUWP.csproj", "{44852FAF-423D-446D-BFDC-8A11DDA0C1C9}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoUWP", "src\Mono\MonoUWP.csproj", "{CD90BB97-AFF1-4699-96D6-2E9823FA7505}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapUWP", "src\Map\MapUWP.csproj", "{B01D8D98-78E8-4419-8138-4BE47BDD985B}"
EndProject
Global
@@ -102,21 +98,6 @@ Global
{08214364-755E-4D6F-B7FF-9D2D49011C20}.DebugUWP|x64.Build.0 = DebugUWP|Any CPU
{08214364-755E-4D6F-B7FF-9D2D49011C20}.DebugUWP|x86.ActiveCfg = DebugUWP|Any CPU
{08214364-755E-4D6F-B7FF-9D2D49011C20}.DebugUWP|x86.Build.0 = DebugUWP|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugNet|Any CPU.ActiveCfg = DebugNet|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugNet|Any CPU.Build.0 = DebugNet|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugNet|ARM.ActiveCfg = DebugNet|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugNet|ARM.Build.0 = DebugNet|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugNet|x64.ActiveCfg = DebugNet|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugNet|x64.Build.0 = DebugNet|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugNet|x86.ActiveCfg = DebugNet|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugNet|x86.Build.0 = DebugNet|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugUWP|Any CPU.ActiveCfg = DebugUWP|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugUWP|ARM.ActiveCfg = DebugUWP|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugUWP|ARM.Build.0 = DebugUWP|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugUWP|x64.ActiveCfg = DebugUWP|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugUWP|x64.Build.0 = DebugUWP|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugUWP|x86.ActiveCfg = DebugUWP|Any CPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}.DebugUWP|x86.Build.0 = DebugUWP|Any CPU
{F04D4384-62B7-4F73-ACD9-C9A5112FC53F}.DebugNet|Any CPU.ActiveCfg = DebugNet|Any CPU
{F04D4384-62B7-4F73-ACD9-C9A5112FC53F}.DebugNet|Any CPU.Build.0 = DebugNet|Any CPU
{F04D4384-62B7-4F73-ACD9-C9A5112FC53F}.DebugNet|ARM.ActiveCfg = DebugNet|Any CPU
@@ -222,21 +203,6 @@ Global
{44852FAF-423D-446D-BFDC-8A11DDA0C1C9}.DebugUWP|x64.Build.0 = DebugUWP|x64
{44852FAF-423D-446D-BFDC-8A11DDA0C1C9}.DebugUWP|x86.ActiveCfg = DebugUWP|x86
{44852FAF-423D-446D-BFDC-8A11DDA0C1C9}.DebugUWP|x86.Build.0 = DebugUWP|x86
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugNet|Any CPU.ActiveCfg = DebugUWP|Any CPU
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugNet|ARM.ActiveCfg = DebugNet|ARM
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugNet|ARM.Build.0 = DebugNet|ARM
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugNet|x64.ActiveCfg = DebugNet|x64
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugNet|x64.Build.0 = DebugNet|x64
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugNet|x86.ActiveCfg = DebugNet|x86
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugNet|x86.Build.0 = DebugNet|x86
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugUWP|Any CPU.ActiveCfg = DebugUWP|Any CPU
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugUWP|Any CPU.Build.0 = DebugUWP|Any CPU
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugUWP|ARM.ActiveCfg = DebugUWP|ARM
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugUWP|ARM.Build.0 = DebugUWP|ARM
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugUWP|x64.ActiveCfg = DebugUWP|x64
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugUWP|x64.Build.0 = DebugUWP|x64
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugUWP|x86.ActiveCfg = DebugUWP|x86
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}.DebugUWP|x86.Build.0 = DebugUWP|x86
{B01D8D98-78E8-4419-8138-4BE47BDD985B}.DebugNet|Any CPU.ActiveCfg = DebugUWP|Any CPU
{B01D8D98-78E8-4419-8138-4BE47BDD985B}.DebugNet|ARM.ActiveCfg = DebugNet|ARM
{B01D8D98-78E8-4419-8138-4BE47BDD985B}.DebugNet|ARM.Build.0 = DebugNet|ARM
diff --git a/src/Map/Map.csproj b/src/Map/Map.csproj
index f040bc3..345dfc0 100644
--- a/src/Map/Map.csproj
+++ b/src/Map/Map.csproj
@@ -34,22 +34,6 @@
MinimumRecommendedRules.ruleset
-
- ..\..\packages\Mapbox.VectorTile.1.0.3-alpha1\lib\net35\Mapbox.VectorTile.ExtensionMethods.dll
- True
-
-
- ..\..\packages\Mapbox.VectorTile.1.0.3-alpha1\lib\net35\Mapbox.VectorTile.Geometry.dll
- True
-
-
- ..\..\packages\Mapbox.VectorTile.1.0.3-alpha1\lib\net35\Mapbox.VectorTile.PbfReader.dll
- True
-
-
- ..\..\packages\Mapbox.VectorTile.1.0.3-alpha1\lib\net35\Mapbox.VectorTile.VectorTileReader.dll
- True
-
diff --git a/src/Map/Map.project.json b/src/Map/Map.project.json
index 8ceedc2..a078c50 100644
--- a/src/Map/Map.project.json
+++ b/src/Map/Map.project.json
@@ -6,6 +6,6 @@
"win": {}
},
"dependencies": {
- "Mapbox.VectorTile": "1.0.3-alpha2"
+ "Mapbox.VectorTile": "1.0.4-alpha1"
}
}
\ No newline at end of file
diff --git a/src/Map/Tile.cs b/src/Map/Tile.cs
index d92ed0a..4ff8ca8 100644
--- a/src/Map/Tile.cs
+++ b/src/Map/Tile.cs
@@ -4,28 +4,31 @@
//
//-----------------------------------------------------------------------
-namespace Mapbox.Map
-{
+namespace Mapbox.Map {
using System;
using Mapbox.Platform;
+ using System.Linq;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+
///
/// A Map tile, a square with vector or raster data representing a geographic
/// bounding box. More info
/// here .
///
- public abstract class Tile
- {
+ public abstract class Tile {
+
+
+ private CanonicalTileId _id;
+ private List _exceptions;
+ private State _state = State.New;
+ private IAsyncRequest _request;
+ private Action _callback;
- private CanonicalTileId id;
- private string error;
- private State state = State.New;
- private IAsyncRequest request;
- private Action callback;
/// Tile state.
- public enum State
- {
+ public enum State {
/// New tile, not yet initialized.
New,
/// Loading data.
@@ -38,67 +41,74 @@ public enum State
/// Gets the identifier.
/// The canonical tile identifier.
- public CanonicalTileId Id
- {
- get
- {
- return this.id;
- }
- set
- {
- this.id = value;
+ public CanonicalTileId Id {
+ get { return _id; }
+ set { _id = value; }
+ }
+
+
+ /// Flag to indicate if the request was successful
+ public bool HasError {
+ get {
+ return _exceptions == null ? false : _exceptions.Count > 0;
}
}
- /// Gets the error message if any.
- /// The error string.
- public string Error
- {
- get
- {
- return this.error;
+
+ /// Exceptions that might have occured during creation of the tile.
+ public ReadOnlyCollection Exceptions {
+ get { return null == _exceptions ? null : _exceptions.AsReadOnly(); }
+ }
+
+
+ /// Messages of exceptions otherwise empty string.
+ public string ExceptionsAsString {
+ get {
+ if (null == _exceptions || _exceptions.Count == 0) { return string.Empty; }
+ return string.Join(Environment.NewLine, _exceptions.Select(e => e.Message).ToArray());
}
}
+
///
/// Sets the error message.
///
///
- public void SetError(string errorMessage)
- {
- error = errorMessage;
+ internal void AddException(Exception ex) {
+ if (null == _exceptions) { _exceptions = new List(); }
+ _exceptions.Add(ex);
}
+
///
/// Gets the current state. When fully loaded, you must
/// check if the data actually arrived and if the tile
/// is accusing any error.
///
/// The tile state.
- public State CurrentState
- {
- get
- {
- return this.state;
+ public State CurrentState {
+ get {
+ return _state;
}
}
+
///
/// Initializes the object. It will
/// start a network request and fire the callback when completed.
///
/// Initialization parameters.
/// The completion callback.
- public void Initialize(Parameters param, Action callback)
- {
- this.Cancel();
-
- this.state = State.Loading;
- this.id = param.Id;
- this.request = param.Fs.Request(this.MakeTileResource(param.MapId).GetUrl(), this.HandleTileResponse);
- this.callback = callback;
+ public void Initialize(Parameters param, Action callback) {
+ Cancel();
+
+ _state = State.Loading;
+ _id = param.Id;
+ _request = param.Fs.Request(MakeTileResource(param.MapId).GetUrl(), HandleTileResponse);
+ _callback = callback;
}
+
///
/// Returns a that represents the current
/// .
@@ -107,11 +117,11 @@ public void Initialize(Parameters param, Action callback)
/// A that represents the current
/// .
///
- public override string ToString()
- {
- return this.Id.ToString();
+ public override string ToString() {
+ return Id.ToString();
}
+
///
/// Cancels the request for the object.
/// It will stop a network request and set the tile's state to Canceled.
@@ -121,7 +131,7 @@ public override string ToString()
/// // 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) =>
+ /// tiles.RemoveWhere((T tile) =>
/// {
/// if (cover.Remove(tile.Id))
/// {
@@ -130,50 +140,52 @@ public override string ToString()
/// else
/// {
/// tile.Cancel();
- /// this.NotifyNext(tile);
+ /// NotifyNext(tile);
/// return true;
/// }
/// });
///
///
- public void Cancel()
- {
- if (this.request != null)
- {
- this.request.Cancel();
- this.request = null;
+ public void Cancel() {
+ if (_request != null) {
+ _request.Cancel();
+ _request = null;
}
- this.state = State.Canceled;
+ _state = State.Canceled;
}
- public void SetState(State state) { this.state = state; }
// Get the tile resource (raster/vector/etc).
internal abstract TileResource MakeTileResource(string mapid);
+
// 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.
- private void HandleTileResponse(Response response)
- {
- if (!string.IsNullOrEmpty(response.Error))
- {
- this.error = response.Error;
- }
- else if (this.ParseTileData(response.Data) == false)
- {
- this.error = "ParseError";
+ private void HandleTileResponse(Response response) {
+
+ if (response.HasError) {
+ 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);
}
- this.state = State.Loaded;
- this.callback();
+ _state = State.Loaded;
+ _callback();
}
+
///
/// Parameters for initializing a Tile object.
///
@@ -185,8 +197,7 @@ private void HandleTileResponse(Response response)
/// parameters.MapId = "mapbox.mapbox-streets-v7";
///
///
- public struct Parameters
- {
+ public struct Parameters {
/// The tile id.
public CanonicalTileId Id;
@@ -200,5 +211,7 @@ public struct Parameters
/// The data source abstraction.
public IFileSource Fs;
}
+
+
}
}
diff --git a/src/Map/VectorTile.cs b/src/Map/VectorTile.cs
index 93e5bd7..144c313 100644
--- a/src/Map/VectorTile.cs
+++ b/src/Map/VectorTile.cs
@@ -4,8 +4,7 @@
//
//-----------------------------------------------------------------------
-namespace Mapbox.Map
-{
+namespace Mapbox.Map {
using System.Collections.ObjectModel;
using Mapbox.Utils;
using Mapbox.VectorTile;
@@ -40,8 +39,7 @@ namespace Mapbox.Map
/// }));
///
///
- public sealed class VectorTile : Tile, IDisposable
- {
+ public sealed class VectorTile : Tile, IDisposable {
// FIXME: Namespace here is very confusing and conflicts (sematically)
// with his class. Something has to be renamed here.
private Mapbox.VectorTile.VectorTile data;
@@ -50,10 +48,8 @@ public sealed class VectorTile : Tile, IDisposable
/// Gets the vector decoded using Mapbox.VectorTile library.
/// The GeoJson data.
- public Mapbox.VectorTile.VectorTile Data
- {
- get
- {
+ public Mapbox.VectorTile.VectorTile Data {
+ get {
return this.data;
}
}
@@ -67,23 +63,18 @@ public Mapbox.VectorTile.VectorTile Data
//}
- public void Dispose()
- {
+ public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
//TODO: change signature if 'VectorTile' class changes from 'sealed'
//protected override void Dispose(bool disposeManagedResources)
- public void Dispose(bool disposeManagedResources)
- {
- if (!isDisposed)
- {
- if (disposeManagedResources)
- {
+ public void Dispose(bool disposeManagedResources) {
+ if (!isDisposed) {
+ if (disposeManagedResources) {
//TODO implement IDisposable with Mapbox.VectorTile.VectorTile
- if (null != data)
- {
+ if (null != data) {
data = null;
}
}
@@ -105,10 +96,8 @@ public void Dispose(bool disposeManagedResources)
/// Console.Write("GeoJson: " + json);
///
///
- public string GeoJson
- {
- get
- {
+ public string GeoJson {
+ get {
return this.data.ToGeoJson((ulong)Id.Z, (ulong)Id.X, (ulong)Id.Y, 0);
}
}
@@ -129,8 +118,7 @@ public string GeoJson
/// }
///
///
- public ReadOnlyCollection LayerNames()
- {
+ public ReadOnlyCollection LayerNames() {
return this.data.LayerNames();
}
@@ -151,30 +139,25 @@ public ReadOnlyCollection LayerNames()
/// }
///
///
- public VectorTileLayer GetLayer(string layerName)
- {
+ public VectorTileLayer GetLayer(string layerName) {
return this.data.GetLayer(layerName);
}
- internal override TileResource MakeTileResource(string mapId)
- {
+ internal override TileResource MakeTileResource(string mapId) {
return TileResource.MakeVector(Id, mapId);
}
- internal override bool ParseTileData(byte[] data)
- {
- try
- {
+ internal override bool ParseTileData(byte[] data) {
+ try {
var decompressed = Compression.Decompress(data);
this.data = new Mapbox.VectorTile.VectorTile(decompressed);
return true;
}
- catch (Exception ex)
- {
- SetError("VectorTile parsing failed: " + ex.ToString());
+ catch (Exception ex) {
+ AddException(ex);
return false;
}
}
diff --git a/src/Mono/FileSource.cs b/src/Mono/FileSource.cs
deleted file mode 100644
index fdd1aa1..0000000
--- a/src/Mono/FileSource.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-//-----------------------------------------------------------------------
-//
-// Copyright (c) 2016 Mapbox. All rights reserved.
-//
-//-----------------------------------------------------------------------
-
-namespace Mapbox.Mono
-{
- using System;
- using System.Collections.Generic;
- using System.Threading;
- using Mapbox.Platform;
-
- ///
- /// Mono implementation of the FileSource class. It will use Mono's
- /// runtime to
- /// asynchronously fetch data from the network via HTTP or HTTPS requests.
- ///
- ///
- /// This implementation requires .NET 4.5 and later. The access token is expected to
- /// be exported to the environment as MAPBOX_ACCESS_TOKEN.
- ///
- public sealed class FileSource : IFileSource
- {
- private readonly List requests = new List();
- private readonly string accessToken = Environment.GetEnvironmentVariable("MAPBOX_ACCESS_TOKEN");
-
- /// Performs a request asynchronously.
- /// The HTTP/HTTPS url.
- /// Callback to be called after the request is completed.
- ///
- /// Returns a that can be used for canceling a pending
- /// request. This handle can be completely ignored if there is no intention of ever
- /// canceling the request.
- ///
- public IAsyncRequest Request(string url, Action callback)
- {
- if (this.accessToken != null)
- {
- url += "?access_token=" + this.accessToken;
- }
-
- var request = new HTTPRequest(url, callback);
- this.requests.Add(request);
-
- return request;
- }
-
- ///
- /// Block until all the requests are processed.
- ///
- public void WaitForAllRequests()
- {
- while (true)
- {
- // Reverse for safely removing while iterating.
- for (int i = this.requests.Count - 1; i >= 0; i--)
- {
- if (this.requests[i].Wait())
- {
- this.requests.RemoveAt(i);
- }
- }
-
- if (this.requests.Count == 0)
- {
- break;
- }
-
-#if !WINDOWS_UWP
- Thread.Sleep(10);
-#else
- System.Threading.Tasks.Task.Delay(5).Wait();
-#endif
- }
- }
- }
-}
diff --git a/src/Mono/HTTPRequest.cs b/src/Mono/HTTPRequest.cs
deleted file mode 100644
index 8c3d243..0000000
--- a/src/Mono/HTTPRequest.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-//-----------------------------------------------------------------------
-//
-// Copyright (c) 2016 Mapbox. All rights reserved.
-//
-//-----------------------------------------------------------------------
-
-namespace Mapbox.Mono
-{
- using System;
- using System.Net.Http;
- using System.Threading.Tasks;
- using Mapbox.Platform;
-
- internal sealed class HTTPRequest : IAsyncRequest
- {
- private static readonly HttpClient Client = new HttpClient();
-
- private Task task;
- private Action callback;
-
- public HTTPRequest(string url, Action callback)
- {
- this.callback = callback;
- this.task = this.DoRequestAsync(url);
- }
-
- public void Cancel()
- {
- // FIXME: CancellationTokenSource not available on Mono?
- // We should use it when it gets available.
- this.callback = null;
- }
-
- public bool Wait()
- {
- if (this.task.IsCompleted)
- {
- if (this.callback != null)
- {
- this.callback(this.task.Result);
- this.callback = null;
- }
- }
-
- return this.callback == null;
- }
-
- private async Task DoRequestAsync(string url)
- {
- var response = new Response();
-
- try
- {
- var message = await Client.GetAsync(url);
-
- if (message.IsSuccessStatusCode)
- {
- response.Data = await message.Content.ReadAsByteArrayAsync();
- }
- else
- {
- response.Error = message.StatusCode.ToString();
- }
- }
- catch (Exception exception)
- {
- response.Error = exception.Message;
- }
-
- return response;
- }
- }
-}
diff --git a/src/Mono/Mono.csproj b/src/Mono/Mono.csproj
deleted file mode 100644
index c8adc04..0000000
--- a/src/Mono/Mono.csproj
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- {346B1208-1587-490B-A7DE-A96B86E81CD6}
- Library
- Mapbox.Mono
- Mapbox.Mono
- 0.0.1
- v4.5
-
-
- true
- full
- false
- ..\..\bin\Debug\net35\
- DEBUG;
- prompt
- 4
- false
-
-
- ..\..\data\Settings.StyleCop
-
-
- true
- ..\..\bin\Debug\net35\
- DEBUG;
- full
- AnyCPU
- prompt
- MinimumRecommendedRules.ruleset
-
-
-
-
-
-
-
- Properties\SharedAssemblyInfo.cs
-
-
-
-
-
-
-
- {FE49745C-01F6-4A3F-BF08-828113D3E19F}
- Platform
-
-
- {48BE9D66-3A19-4248-BBDD-4DB4A52B3FE5}
- Utils
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Mono/Mono.project.json b/src/Mono/Mono.project.json
deleted file mode 100644
index e9e6723..0000000
--- a/src/Mono/Mono.project.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "frameworks": { "net45": {} },
- "runtimes": { "win": {} }
-}
\ No newline at end of file
diff --git a/src/Mono/MonoUWP.csproj b/src/Mono/MonoUWP.csproj
deleted file mode 100644
index f72fa36..0000000
--- a/src/Mono/MonoUWP.csproj
+++ /dev/null
@@ -1,144 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- {CD90BB97-AFF1-4699-96D6-2E9823FA7505}
- Library
- Properties
- Mapbox.Mono
- Mapbox.Mono
- en-US
- UAP
- 10.0.14393.0
- 10.0.10586.0
- 14
- 512
- {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
-
-
- AnyCPU
- true
- full
- false
- ..\..\bin\Debug\uap10\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- prompt
- 4
-
-
- x86
- true
- bin\x86\Debug\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- ;2008
- full
- x86
- false
- prompt
-
-
- ARM
- true
- bin\ARM\Debug\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- ;2008
- full
- ARM
- false
- prompt
-
-
- x64
- true
- bin\x64\Debug\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- ;2008
- full
- x64
- false
- prompt
-
-
-
-
-
-
-
- Properties\SharedAssemblyInfo.cs
-
-
-
-
-
-
-
-
- {74dca811-e397-491d-b8af-6b4cf91a42fa}
- PlatformUWP
-
-
- {fe92b261-daeb-4548-906f-7d2daac98ee4}
- UtilsUWP
-
-
-
- 14.0
-
-
- true
- ..\..\bin\Debug\uap10\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- true
- full
- AnyCPU
- false
- prompt
- MinimumRecommendedRules.ruleset
-
-
- true
- bin\x86\DebugUWP\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- ;2008
- true
- full
- x86
- false
- prompt
- MinimumRecommendedRules.ruleset
-
-
- true
- bin\ARM\DebugUWP\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- ;2008
- true
- full
- ARM
- false
- prompt
- MinimumRecommendedRules.ruleset
-
-
- true
- bin\x64\DebugUWP\
- DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
- ;2008
- true
- full
- x64
- false
- prompt
- MinimumRecommendedRules.ruleset
-
-
-
-
\ No newline at end of file
diff --git a/src/Mono/Properties/AssemblyInfo.cs b/src/Mono/Properties/AssemblyInfo.cs
deleted file mode 100644
index 8bbb600..0000000
--- a/src/Mono/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-//
-
-using System.Reflection;
-using System.Runtime.CompilerServices;
-
-// Information about this assembly is defined by the following attributes.
-// Change them to the values specific to your project.
-
-[assembly: AssemblyTitle("Mono")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-
-// The following attributes are used to specify the signing key for the assembly,
-// if desired. See the Mono documentation for more information about signing.
-
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("")]
diff --git a/src/Mono/Properties/MonoUWP.rd.xml b/src/Mono/Properties/MonoUWP.rd.xml
deleted file mode 100644
index 95c4ac8..0000000
--- a/src/Mono/Properties/MonoUWP.rd.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/src/Mono/packages.config b/src/Mono/packages.config
deleted file mode 100644
index f22d165..0000000
--- a/src/Mono/packages.config
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Mono/project.json b/src/Mono/project.json
deleted file mode 100644
index 92d1456..0000000
--- a/src/Mono/project.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "dependencies": {
- "Microsoft.NETCore.UniversalWindowsPlatform": "5.1.0"
- },
- "frameworks": {
- "uap10.0": {}
- },
- "runtimes": {
- "win10-arm": {},
- "win10-arm-aot": {},
- "win10-x86": {},
- "win10-x86-aot": {},
- "win10-x64": {},
- "win10-x64-aot": {}
- }
-}
\ No newline at end of file
diff --git a/src/Platform/FileSource.cs b/src/Platform/FileSource.cs
new file mode 100644
index 0000000..5f94d9e
--- /dev/null
+++ b/src/Platform/FileSource.cs
@@ -0,0 +1,149 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) 2016 Mapbox. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace Mapbox.Platform {
+
+
+ using System;
+ using System.Collections.Generic;
+ using System.Net;
+ using System.Net.Security;
+#if !NETFX_CORE
+ using System.Security.Cryptography.X509Certificates;
+#endif
+ using System.Threading;
+
+
+ ///
+ /// Mono implementation of the FileSource class. It will use Mono's
+ /// runtime to
+ /// asynchronously fetch data from the network via HTTP or HTTPS requests.
+ ///
+ ///
+ /// This implementation requires .NET 4.5 and later. The access token is expected to
+ /// be exported to the environment as MAPBOX_ACCESS_TOKEN.
+ ///
+ public sealed class FileSource : IFileSource {
+
+
+ private readonly Dictionary _requests = new Dictionary();
+ private readonly string _accessToken = Environment.GetEnvironmentVariable("MAPBOX_ACCESS_TOKEN");
+ private readonly object _lock = new object();
+
+ /// Length of rate-limiting interval in seconds. https://www.mapbox.com/api-documentation/#rate-limits
+ private int? XRateLimitInterval;
+ /// Maximum number of requests you may make in the current interval before reaching the limit. https://www.mapbox.com/api-documentation/#rate-limits
+ private long? XRateLimitLimit;
+ /// Timestamp of when the current interval will end and the ratelimit counter is reset. https://www.mapbox.com/api-documentation/#rate-limits
+ private DateTime? XRateLimitReset;
+
+
+ /// Performs a request asynchronously.
+ /// The HTTP/HTTPS url.
+ /// Callback to be called after the request is completed.
+ ///
+ /// Returns a that can be used for canceling a pending
+ /// request. This handle can be completely ignored if there is no intention of ever
+ /// canceling the request.
+ ///
+ public IAsyncRequest Request(string url, Action callback, int timeout = 10) {
+ if (_accessToken != null) {
+ url += "?access_token=" + _accessToken;
+ }
+
+ // TODO:
+ // * add queue for requests
+ // * evaluate rate limits (headers and status code)
+ // * throttle requests accordingly
+ //var request = new HTTPRequest(url, callback);
+ //IEnumerator proxy = proxyResponse(url, callback);
+ //proxy.MoveNext();
+ //IAsyncRequest request = proxy.Current;
+
+ //return request;
+
+ return proxyResponse(url, callback);
+ }
+
+
+ // TODO: look at requests and implement throttling if needed
+ //private IEnumerator proxyResponse(string url, Action callback) {
+ private IAsyncRequest proxyResponse(string url, Action callback) {
+
+ // TODO: plugin caching somewhere around here
+
+ var request = IAsyncRequestFactory.CreateRequest(url, (Response response) => {
+ if (response.XRateLimitInterval.HasValue) { XRateLimitInterval = response.XRateLimitInterval; }
+ if (response.XRateLimitLimit.HasValue) { XRateLimitLimit = response.XRateLimitLimit; }
+ if (response.XRateLimitReset.HasValue) { XRateLimitReset = response.XRateLimitReset; }
+ callback(response);
+ lock (_lock) {
+ //another place to catch if request has been cancelled
+ try {
+ _requests.Remove(response.Request);
+ }
+ catch (Exception ex) {
+ System.Diagnostics.Debug.WriteLine(ex);
+ }
+ }
+ });
+ lock (_lock) {
+ //sometimes we get here after the request has already finished
+ if (!request.IsCompleted) {
+ _requests.Add(request, 0);
+ }
+ }
+ //yield return request;
+ return request;
+ }
+
+
+ ///
+ /// Block until all the requests are processed.
+ ///
+ public void WaitForAllRequests() {
+ int waitTimeMs = 150;
+ while (_requests.Count > 0) {
+ lock (_lock) {
+ foreach (var req in _requests) {
+ if (((IAsyncRequest)req.Key).IsCompleted) {
+ // another place to watch out if request has been cancelled
+ try {
+ _requests.Remove(req.Key);
+ }
+ catch (Exception ex) {
+ System.Diagnostics.Debug.WriteLine(ex);
+ }
+ }
+ }
+ }
+
+#if !WINDOWS_UWP
+ //Thread.Sleep(50);
+ // TODO: get rid of DoEvents!!! and find non-blocking wait that works for Net3.5
+ //System.Windows.Forms.Application.DoEvents();
+
+ var resetEvent = new ManualResetEvent(false);
+ ThreadPool.QueueUserWorkItem(new WaitCallback(delegate {
+ Thread.Sleep(waitTimeMs);
+ resetEvent.Set();
+ }), null);
+ resetEvent.WaitOne();
+ resetEvent.Close();
+ resetEvent = null;
+
+#else
+ System.Threading.Tasks.Task.Delay(waitTimeMs).Wait();
+#endif
+ }
+ }
+
+
+
+
+
+ }
+}
diff --git a/src/Platform/HTTPRequestNonThreaded.cs b/src/Platform/HTTPRequestNonThreaded.cs
new file mode 100644
index 0000000..d6a35e9
--- /dev/null
+++ b/src/Platform/HTTPRequestNonThreaded.cs
@@ -0,0 +1,238 @@
+#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_ANDROID || UNITY_WP_8_1 || UNITY_WSA || UNITY_WEBGL || UNITY_IOS || UNITY_PS4 || UNITY_SAMSUNGTV || UNITY_XBOXONE || UNITY_TIZEN || UNITY_TVOS
+#define UNITY
+#endif
+
+//-----------------------------------------------------------------------
+//
+// Copyright (c) 2016 Mapbox. All rights reserved.
+// Based on http://stackoverflow.com/a/12606963 and http://wiki.unity3d.com/index.php/WebAsync
+//
+//-----------------------------------------------------------------------
+
+#if !UNITY
+
+namespace Mapbox.Platform {
+
+
+ using System;
+ using System.Net;
+#if !UNITY && !NETFX_CORE
+ using System.Net.Cache;
+#endif
+ using System.IO;
+ using System.Collections.Generic;
+ using System.Threading;
+ using System.ComponentModel;
+ using Utils;
+#if NETFX_CORE
+ using System.Net.Http;
+ using System.Linq;
+#endif
+
+ //using System.Windows.Threading;
+
+ internal sealed class HTTPRequestNonThreaded : IAsyncRequest {
+
+
+ public bool IsCompleted { get; private set; }
+
+
+ private Action _callback;
+#if !NETFX_CORE
+ private HttpWebRequest _request;
+#else
+ private HttpClient _request;
+ private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
+#endif
+#if !UNITY
+ private SynchronizationContext _sync = AsyncOperationManager.SynchronizationContext;
+#endif
+ private int _timeOut;
+ private string _requestUrl;
+ private readonly string _userAgent = "mapbox-sdk-cs";
+
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// seconds
+ public HTTPRequestNonThreaded(string url, Action callback, int timeOut = 10) {
+
+ IsCompleted = false;
+ _callback = callback;
+ _timeOut = timeOut;
+ _requestUrl = url;
+
+ setupRequest();
+
+ Action a = () => { getResponseNonThreaded(_request, EvaluateResponse); };
+ //Fire and forget ;-)
+ a.BeginInvoke(a.EndInvoke, null);
+ }
+
+
+ private void setupRequest() {
+
+#if !NETFX_CORE
+ _request = WebRequest.Create(_requestUrl) as HttpWebRequest;
+ _request.UserAgent = _userAgent;
+ //_hwr.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36";
+ //_hwr.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
+ _request.Credentials = CredentialCache.DefaultCredentials;
+ _request.KeepAlive = true;
+ _request.ProtocolVersion = HttpVersion.Version11; // improved performance
+
+ // improved performance.
+ // ServicePointManager.DefaultConnectionLimit doesn't seem to change anything
+ // set ConnectionLimit per request
+ // https://msdn.microsoft.com/en-us/library/system.net.httpwebrequest(v=vs.90).aspx#Remarks
+ // use a value that is 12 times the number of CPUs on the local computer
+ _request.ServicePoint.ConnectionLimit = Environment.ProcessorCount * 6;
+
+ _request.ServicePoint.UseNagleAlgorithm = true;
+ _request.ServicePoint.Expect100Continue = false;
+ _request.ServicePoint.MaxIdleTime = 2000;
+ _request.Method = "GET";
+ _request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");
+ _request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
+ //_hwr.Timeout = timeOut * 1000; doesn't work in async calls, see below
+
+#else
+ HttpClientHandler handler = new HttpClientHandler() {
+ AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
+ AllowAutoRedirect = true,
+ UseDefaultCredentials = true
+
+ };
+ _request = new HttpClient(handler);
+ _request.DefaultRequestHeaders.Add("User-Agent", _userAgent);
+ _request.Timeout = TimeSpan.FromSeconds(_timeOut);
+
+ // TODO: how to set ConnectionLimit? ServicePoint.ConnectionLimit doesn't seem to be available.
+#endif
+
+#if !UNITY && !NETFX_CORE
+ // 'NoCacheNoStore' greatly reduced the number of faulty request
+ // seems that .Net caching and Mapbox API don't play well together
+ _request.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
+#endif
+ }
+
+
+
+#if NETFX_CORE
+ private async void getResponseNonThreaded(HttpClient request, Action gotResponse) {
+
+ // TODO: implement a strategy similar to the full .Net one to avoid blocking of 'GetAsync()'
+ // see 'Remarks' https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.timeout?view=netcore-1.1#System_Net_Http_HttpClient_Timeout
+ // "A Domain Name System (DNS) query may take up to 15 seconds to return or time out."
+
+ HttpResponseMessage response = null;
+ try {
+ response = await request.GetAsync(_requestUrl, _cancellationTokenSource.Token);
+ gotResponse(response, null);
+ }
+ catch (Exception ex) {
+ gotResponse(response, ex);
+ }
+
+ }
+
+
+ private async void EvaluateResponse(HttpResponseMessage apiResponse, Exception apiEx) {
+
+ var response = await Response.FromWebResponse(this, apiResponse, apiEx);
+
+ // post (async) callback back to the main/UI thread
+ // Unity: SynchronizationContext doesn't do anything
+ // use the Dispatcher
+#if !UNITY
+ _sync.Post(delegate { callCallbackAndcleanUp(response); }, null);
+#else
+ UnityToolbag.Dispatcher.InvokeAsync(() => { callCallbackAndcleanUp(response); });
+#endif
+ }
+
+#endif
+
+
+#if !NETFX_CORE
+ private void getResponseNonThreaded(HttpWebRequest request, Action gotResponse) {
+
+ HttpWebResponse response = null;
+ try {
+ response = (HttpWebResponse)request.GetResponse();
+ gotResponse(response, null);
+ }
+ catch (WebException wex) {
+ //another place to watchout for HttpWebRequest.Abort to occur
+ if (wex.Status == WebExceptionStatus.RequestCanceled) {
+ gotResponse(null, wex);
+ } else {
+ HttpWebResponse hwr = wex.Response as HttpWebResponse;
+ if (null == hwr) {
+ gotResponse(null, wex);
+ } else {
+ gotResponse(hwr, wex);
+ }
+ }
+ }
+ catch (Exception ex) {
+ gotResponse(response, ex);
+ }
+ }
+
+
+ private void EvaluateResponse(HttpWebResponse apiResponse, Exception apiEx) {
+
+ var response = Response.FromWebResponse(this, apiResponse, apiEx);
+
+#if !UNITY
+ // post (async) callback back to the main/UI thread
+ // Unity: SynchronizationContext doesn't do anything
+ // use the Dispatcher
+ _sync.Post(delegate { callCallbackAndcleanUp(response); }, null);
+#else
+ // Unity is playing
+ if (UnityToolbag.Dispatcher._instanceExists) {
+ UnityToolbag.Dispatcher.InvokeAsync(() => { callCallbackAndcleanUp(response); });
+ } else { // Unity is in Edit Mode
+#if UNITY_EDITOR
+ Mapbox.Unity.DispatcherEditor.InvokeAsync(() => { callCallbackAndcleanUp(response); });
+#endif
+ }
+#endif
+ }
+#endif
+
+
+ private void callCallbackAndcleanUp(Response response) {
+ _callback(response);
+ IsCompleted = true;
+ _callback = null;
+#if NETFX_CORE
+ if (null != _request) {
+ _request.Dispose();
+ _request = null;
+ }
+#endif
+ }
+
+
+ public void Cancel() {
+
+#if !NETFX_CORE
+ if (null != _request) {
+ _request.Abort();
+ }
+#else
+ _cancellationTokenSource.Cancel();
+#endif
+ }
+
+
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Platform/HTTPRequestThreaded.cs b/src/Platform/HTTPRequestThreaded.cs
new file mode 100644
index 0000000..c8274b5
--- /dev/null
+++ b/src/Platform/HTTPRequestThreaded.cs
@@ -0,0 +1,318 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) 2016 Mapbox. All rights reserved.
+// Based on http://stackoverflow.com/a/12606963 and http://wiki.unity3d.com/index.php/WebAsync
+//
+//-----------------------------------------------------------------------
+
+#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_ANDROID || UNITY_WP_8_1 || UNITY_WSA || UNITY_WEBGL || UNITY_IOS || UNITY_PS4 || UNITY_SAMSUNGTV || UNITY_XBOXONE || UNITY_TIZEN || UNITY_TVOS
+#define UNITY
+#endif
+
+#if !UNITY
+
+namespace Mapbox.Platform {
+
+
+ using System;
+ using System.Net;
+#if !UNITY && !NETFX_CORE
+ using System.Net.Cache;
+#endif
+ using System.IO;
+ using System.Collections.Generic;
+ using System.Threading;
+ using System.ComponentModel;
+ using Utils;
+#if NETFX_CORE
+ using System.Net.Http;
+ using System.Linq;
+#endif
+
+ //using System.Windows.Threading;
+
+ internal sealed class HTTPRequestThreaded : IAsyncRequest {
+
+
+ public bool IsCompleted { get; private set; }
+
+
+ private Action _callback;
+#if !NETFX_CORE
+ private HttpWebRequest _request;
+#else
+ private HttpClient _request;
+ private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
+#endif
+#if !UNITY
+ private SynchronizationContext _sync = AsyncOperationManager.SynchronizationContext;
+#endif
+ private int _timeOut;
+ private string _requestUrl;
+ private readonly string _userAgent = "mapbox-sdk-cs";
+
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// seconds
+ public HTTPRequestThreaded(string url, Action callback, int timeOut = 10) {
+
+ IsCompleted = false;
+ _callback = callback;
+ _timeOut = timeOut;
+ _requestUrl = url;
+
+ setupRequest();
+ getResponseAsync(_request, EvaluateResponse);
+ }
+
+
+ private void setupRequest() {
+
+#if !NETFX_CORE
+ _request = WebRequest.Create(_requestUrl) as HttpWebRequest;
+ _request.UserAgent = _userAgent;
+ //_hwr.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36";
+ //_hwr.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
+ _request.Credentials = CredentialCache.DefaultCredentials;
+ _request.KeepAlive = true;
+ _request.ProtocolVersion = HttpVersion.Version11; // improved performance
+
+ // improved performance.
+ // ServicePointManager.DefaultConnectionLimit doesn't seem to change anything
+ // set ConnectionLimit per request
+ // https://msdn.microsoft.com/en-us/library/system.net.httpwebrequest(v=vs.90).aspx#Remarks
+ // use a value that is 12 times the number of CPUs on the local computer
+ _request.ServicePoint.ConnectionLimit = Environment.ProcessorCount * 6;
+
+ _request.ServicePoint.UseNagleAlgorithm = true;
+ _request.ServicePoint.Expect100Continue = false;
+ _request.ServicePoint.MaxIdleTime = 2000;
+ _request.Method = "GET";
+ _request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");
+ _request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
+ //_hwr.Timeout = timeOut * 1000; doesn't work in async calls, see below
+
+#else
+ HttpClientHandler handler = new HttpClientHandler() {
+ AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
+ AllowAutoRedirect = true,
+ UseDefaultCredentials = true
+
+ };
+ _request = new HttpClient(handler);
+ _request.DefaultRequestHeaders.Add("User-Agent", _userAgent);
+ _request.Timeout = TimeSpan.FromSeconds(_timeOut);
+
+ // TODO: how to set ConnectionLimit? ServicePoint.ConnectionLimit doesn't seem to be available.
+#endif
+
+#if !UNITY && !NETFX_CORE
+ // 'NoCacheNoStore' greatly reduced the number of faulty request
+ // seems that .Net caching and Mapbox API don't play well together
+ _request.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
+#endif
+ }
+
+
+
+#if NETFX_CORE
+
+ private async void getResponseAsync(HttpClient request, Action gotResponse) {
+
+ // TODO: implement a strategy similar to the full .Net one to avoid blocking of 'GetAsync()'
+ // see 'Remarks' https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.timeout?view=netcore-1.1#System_Net_Http_HttpClient_Timeout
+ // "A Domain Name System (DNS) query may take up to 15 seconds to return or time out."
+
+ HttpResponseMessage response = null;
+ try {
+ response = await request.GetAsync(_requestUrl, _cancellationTokenSource.Token);
+ gotResponse(response, null);
+ }
+ catch (Exception ex) {
+ gotResponse(response, ex);
+ }
+
+ }
+
+
+ private async void EvaluateResponse(HttpResponseMessage apiResponse, Exception apiEx) {
+
+ var response = await Response.FromWebResponse(this, apiResponse, apiEx);
+
+ // post (async) callback back to the main/UI thread
+ // Unity: SynchronizationContext doesn't do anything
+ // use the Dispatcher
+#if !UNITY
+ _sync.Post(delegate {
+ _callback(response);
+ IsCompleted = true;
+ _callback = null;
+#if NETFX_CORE
+ if (null != _request) {
+ _request.Dispose();
+ _request = null;
+ }
+#endif
+ }, null);
+#else
+ UnityToolbag.Dispatcher.InvokeAsync(() => {
+ _callback(response);
+ IsCompleted = true;
+ _callback = null;
+#if NETFX_CORE
+ if (null != _request) {
+ _request.Dispose();
+ _request = null;
+ }
+#endif
+ });
+#endif
+ }
+
+#endif
+
+
+#if !NETFX_CORE
+ private void getResponseAsync(HttpWebRequest request, Action gotResponse) {
+
+ // create an additional action wrapper, because of:
+ // https://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.begingetresponse.aspx
+ // The BeginGetResponse method requires some synchronous setup tasks to complete (DNS resolution,
+ //proxy detection, and TCP socket connection, for example) before this method becomes asynchronous.
+ // As a result, this method should never be called on a user interface (UI) thread because it might
+ // take considerable time(up to several minutes depending on network settings) to complete the
+ // initial synchronous setup tasks before an exception for an error is thrown or the method succeeds.
+
+ Action actionWrapper = () => {
+ try {
+ // BeginInvoke runs on a thread of the thread pool (!= main/UI thread)
+ // that's why we need SynchronizationContext when
+ // TODO: how to influence threadpool: nr of threads etc.
+ long startTicks = DateTime.Now.Ticks;
+ request.BeginGetResponse((asycnResult) => {
+ try { // there's a try/catch here because execution path is different from invokation one, exception here may cause a crash
+ long beforeEndGet = DateTime.Now.Ticks;
+ HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asycnResult);
+ //long finished = DateTime.Now.Ticks;
+ //long duration = finished - startTicks;
+ //TimeSpan ts = TimeSpan.FromTicks(duration);
+ //TimeSpan tsEndGet = TimeSpan.FromTicks(finished - beforeEndGet);
+ //TimeSpan tsBeginGet = TimeSpan.FromTicks(beforeEndGet - startTicks);
+ //UnityEngine.Debug.Log("received response - " + ts.Milliseconds + "ms" + " BeginGet: " + tsBeginGet.Milliseconds + " EndGet: " + tsEndGet.Milliseconds + " CompletedSynchronously: " + asycnResult.CompletedSynchronously);
+ gotResponse(response, null);
+ }
+ // EndGetResponse() throws on on some status codes, try to get response anyway (and status codes)
+ catch (WebException wex) {
+ //another place to watchout for HttpWebRequest.Abort to occur
+ if (wex.Status == WebExceptionStatus.RequestCanceled) {
+ gotResponse(null, wex);
+ } else {
+ HttpWebResponse hwr = wex.Response as HttpWebResponse;
+ if (null == hwr) {
+ throw;
+ }
+ gotResponse(hwr, wex);
+ }
+ }
+ catch (Exception ex) {
+ gotResponse(null, ex);
+ }
+ }
+ , null);
+ }
+ catch (Exception ex) {
+ //catch exception from HttpWebRequest.Abort
+ gotResponse(null, ex);
+ }
+ };
+
+ try {
+ actionWrapper.BeginInvoke(new AsyncCallback((iAsyncResult) => {
+ var action = (Action)iAsyncResult.AsyncState;
+ action.EndInvoke(iAsyncResult);
+ })
+ , actionWrapper);
+ }
+ catch (Exception ex) {
+ gotResponse(null, ex);
+ }
+ }
+
+
+
+ private void EvaluateResponse(HttpWebResponse apiResponse, Exception apiEx) {
+
+ var response = Response.FromWebResponse(this, apiResponse,apiEx);
+
+ // post (async) callback back to the main/UI thread
+ // Unity: SynchronizationContext doesn't do anything
+ // use the Dispatcher
+#if !UNITY
+ _sync.Post(delegate {
+ _callback(response);
+ IsCompleted = true;
+ _callback = null;
+#if NETFX_CORE
+ if (null != _request) {
+ _request.Dispose();
+ _request = null;
+ }
+#endif
+ }, null);
+#else
+ // Unity is playing
+ if (UnityToolbag.Dispatcher._instanceExists) {
+ UnityToolbag.Dispatcher.InvokeAsync(() => {
+ _callback(response);
+ IsCompleted = true;
+ _callback = null;
+#if NETFX_CORE
+ if (null != _request) {
+ _request.Dispose();
+ _request = null;
+ }
+#endif
+ });
+ } else { // Unity is in Edit Mode
+#if UNITY_EDITOR
+ Mapbox.Unity.DispatcherEditor.InvokeAsync(() => {
+ _callback(response);
+ IsCompleted = true;
+ _callback = null;
+#if NETFX_CORE
+ if (null != _request) {
+ _request.Dispose();
+ _request = null;
+ }
+#endif
+ });
+#endif
+
+ }
+#endif
+ }
+#endif
+
+
+
+ public void Cancel() {
+
+#if !NETFX_CORE
+ if (null != _request) {
+ _request.Abort();
+ }
+#else
+ _cancellationTokenSource.Cancel();
+#endif
+ }
+
+
+ }
+}
+
+
+#endif
\ No newline at end of file
diff --git a/src/Platform/IAsyncRequest.cs b/src/Platform/IAsyncRequest.cs
index 1e85342..ae08c4a 100644
--- a/src/Platform/IAsyncRequest.cs
+++ b/src/Platform/IAsyncRequest.cs
@@ -4,11 +4,15 @@
//
//-----------------------------------------------------------------------
-namespace Mapbox.Platform
-{
+namespace Mapbox.Platform {
+
+
/// A handle to an asynchronous request.
- public interface IAsyncRequest
- {
+ public interface IAsyncRequest {
+
+ /// True after the request has finished.
+ bool IsCompleted { get; }
+
/// Cancel the ongoing request, preventing it from firing a callback.
void Cancel();
}
diff --git a/src/Platform/IAsyncRequestFactory.cs b/src/Platform/IAsyncRequestFactory.cs
new file mode 100644
index 0000000..7067a61
--- /dev/null
+++ b/src/Platform/IAsyncRequestFactory.cs
@@ -0,0 +1,32 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) 2016 Mapbox. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_ANDROID || UNITY_WP_8_1 || UNITY_WSA || UNITY_WEBGL || UNITY_IOS || UNITY_PS4 || UNITY_SAMSUNGTV || UNITY_XBOXONE || UNITY_TIZEN || UNITY_TVOS
+#define UNITY
+#endif
+
+namespace Mapbox.Platform {
+
+ using System;
+
+ /// A handle to an asynchronous request.
+ public static class IAsyncRequestFactory {
+
+ public static IAsyncRequest CreateRequest(string url, Action callback, int timeout = 10) {
+#if !UNITY
+ if (Environment.ProcessorCount > 2) {
+ return new HTTPRequestThreaded(url, callback, timeout);
+ } else {
+ return new HTTPRequestNonThreaded(url, callback, timeout);
+ }
+#else
+ return new Mapbox.Unity.Utilities.HTTPRequest(url, callback, timeout);
+#endif
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/Platform/IFileSource.cs b/src/Platform/IFileSource.cs
index 2698201..e1e9699 100644
--- a/src/Platform/IFileSource.cs
+++ b/src/Platform/IFileSource.cs
@@ -4,8 +4,7 @@
//
//-----------------------------------------------------------------------
-namespace Mapbox.Platform
-{
+namespace Mapbox.Platform {
using System;
///
@@ -14,8 +13,7 @@ namespace Mapbox.Platform
/// IFileSource could fetch the data from the network, disk cache or even generate
/// the data at runtime.
///
- public interface IFileSource
- {
+ public interface IFileSource {
/// Performs a request asynchronously.
/// The resource description in the URI format.
/// Callback to be called after the request is completed.
@@ -24,6 +22,6 @@ public interface IFileSource
/// request. This handle can be completely ignored if there is no intention of ever
/// canceling the request.
///
- IAsyncRequest Request(string uri, Action callback);
+ IAsyncRequest Request(string uri, Action callback, int timeout = 10);
}
}
\ No newline at end of file
diff --git a/src/Platform/Platform.csproj b/src/Platform/Platform.csproj
index 792866b..32507d1 100644
--- a/src/Platform/Platform.csproj
+++ b/src/Platform/Platform.csproj
@@ -41,6 +41,10 @@
Properties\SharedAssemblyInfo.cs
+
+
+
+
diff --git a/src/Platform/PlatformUWP.csproj b/src/Platform/PlatformUWP.csproj
index 0f4452e..c0e0b73 100644
--- a/src/Platform/PlatformUWP.csproj
+++ b/src/Platform/PlatformUWP.csproj
@@ -68,7 +68,11 @@
Properties\SharedAssemblyInfo.cs
+
+
+
+
diff --git a/src/Platform/Response.cs b/src/Platform/Response.cs
index 613a620..99fedb1 100644
--- a/src/Platform/Response.cs
+++ b/src/Platform/Response.cs
@@ -4,20 +4,285 @@
//
//-----------------------------------------------------------------------
-using System.Collections.Generic;
+#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_ANDROID || UNITY_WP_8_1 || UNITY_WSA || UNITY_WEBGL || UNITY_IOS || UNITY_PS4 || UNITY_SAMSUNGTV || UNITY_XBOXONE || UNITY_TIZEN || UNITY_TVOS
+#define UNITY
+#endif
+
+namespace Mapbox.Platform {
+
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using Utils;
+
+#if NETFX_CORE
+ using System.Net.Http;
+ using System.Threading.Tasks;
+#endif
+#if UNITY
+ using UnityEngine.Networking;
+#endif
-namespace Mapbox.Platform
-{
/// A response from a request.
- public struct Response
- {
- /// Error description, set on error, empty otherwise.
- public string Error;
+ public class Response {
+
+
+ private Response() { }
+
+
+ public IAsyncRequest Request { get; private set; }
+
+
+ public bool RateLimitHit {
+ get { return StatusCode.HasValue ? 429 == StatusCode.Value : false; }
+ }
+
+
+ /// Flag to indicate if the request was successful
+ public bool HasError {
+ get { return _exceptions == null ? false : _exceptions.Count > 0; }
+ }
+
+
+ public int? StatusCode;
+
+
+ public string ContentType;
+
+
+ /// Length of rate-limiting interval in seconds. https://www.mapbox.com/api-documentation/#rate-limits
+ public int? XRateLimitInterval;
+
+
+ /// Maximum number of requests you may make in the current interval before reaching the limit. https://www.mapbox.com/api-documentation/#rate-limits
+ public long? XRateLimitLimit;
+
+
+ /// Timestamp of when the current interval will end and the ratelimit counter is reset. https://www.mapbox.com/api-documentation/#rate-limits
+ public DateTime? XRateLimitReset;
+
+
+ private List _exceptions;
+ /// Exceptions that might have occured during the request.
+ public ReadOnlyCollection Exceptions {
+ get { return null == _exceptions ? null : _exceptions.AsReadOnly(); }
+ }
+
+
+ /// Messages of exceptions otherwise empty string.
+ public string ExceptionsAsString {
+ get {
+ if (null == _exceptions || _exceptions.Count == 0) { return string.Empty; }
+ return string.Join(Environment.NewLine, _exceptions.Select(e => e.Message).ToArray());
+ }
+ }
+
/// Headers of the response.
public Dictionary Headers;
+
/// Raw data fetched from the request.
public byte[] Data;
+
+ public void AddException(Exception ex) {
+ if (null == _exceptions) { _exceptions = new List(); }
+ _exceptions.Add(ex);
+ }
+
+
+#if !NETFX_CORE && !UNITY // full .NET Framework
+ public static Response FromWebResponse(IAsyncRequest request, HttpWebResponse apiResponse, Exception apiEx) {
+
+ Response response = new Response();
+ response.Request = request;
+
+ if (null != apiEx) {
+ response.AddException(apiEx);
+ }
+
+ // timeout: API response is null
+ if (null == apiResponse) {
+ response.AddException(new Exception("No Reponse."));
+ } else {
+ // https://www.mapbox.com/api-documentation/#rate-limits
+ if (null != apiResponse.Headers) {
+ response.Headers = new Dictionary();
+ for (int i = 0; i < apiResponse.Headers.Count; i++) {
+ // TODO: implement .Net Core / UWP implementation
+ string key = apiResponse.Headers.Keys[i];
+ string val = apiResponse.Headers[i];
+ response.Headers.Add(key, val);
+ if (key.Equals("X-Rate-Limit-Interval", StringComparison.InvariantCultureIgnoreCase)) {
+ int limitInterval;
+ if (int.TryParse(val, out limitInterval)) { response.XRateLimitInterval = limitInterval; }
+ } else if (key.Equals("X-Rate-Limit-Limit", StringComparison.InvariantCultureIgnoreCase)) {
+ long limitLimit;
+ if (long.TryParse(val, out limitLimit)) { response.XRateLimitLimit = limitLimit; }
+ } else if (key.Equals("X-Rate-Limit-Reset", StringComparison.InvariantCultureIgnoreCase)) {
+ double unixTimestamp;
+ if (double.TryParse(val, out unixTimestamp)) {
+ response.XRateLimitReset = UnixTimestampUtils.From(unixTimestamp);
+ }
+ } else if (key.Equals("Content-Type", StringComparison.InvariantCultureIgnoreCase)) {
+ response.ContentType = val;
+ }
+ }
+ }
+
+ if (apiResponse.StatusCode != HttpStatusCode.OK) {
+ response.AddException(new Exception(string.Format("{0}: {1}", apiResponse.StatusCode, apiResponse.StatusDescription)));
+ }
+ int statusCode = (int)apiResponse.StatusCode;
+ response.StatusCode = statusCode;
+ if (429 == statusCode) {
+ response.AddException(new Exception("Rate limit hit"));
+ }
+
+ if (null != apiResponse) {
+ using (Stream responseStream = apiResponse.GetResponseStream()) {
+ byte[] buffer = new byte[0x1000];
+ int bytesRead;
+ using (MemoryStream ms = new MemoryStream()) {
+ while (0 != (bytesRead = responseStream.Read(buffer, 0, buffer.Length))) {
+ ms.Write(buffer, 0, bytesRead);
+ }
+ response.Data = ms.ToArray();
+ }
+ }
+ apiResponse.Close();
+ }
+ }
+
+ return response;
+ }
+#endif
+
+#if NETFX_CORE && !UNITY //UWP but not Unity
+ public static async Task FromWebResponse(IAsyncRequest request, HttpResponseMessage apiResponse, Exception apiEx) {
+
+ Response response = new Response();
+ response.Request = request;
+
+ if (null != apiEx) {
+ response.AddException(apiEx);
+ }
+
+ // timeout: API response is null
+ if (null == apiResponse) {
+ response.AddException(new Exception("No Reponse."));
+ } else {
+ // https://www.mapbox.com/api-documentation/#rate-limits
+ if (null != apiResponse.Headers) {
+ response.Headers = new Dictionary();
+ foreach (var hdr in apiResponse.Headers) {
+ string key = hdr.Key;
+ string val = hdr.Value.FirstOrDefault();
+ response.Headers.Add(key, val);
+ if (key.Equals("X-Rate-Limit-Interval", StringComparison.OrdinalIgnoreCase)) {
+ int limitInterval;
+ if (int.TryParse(val, out limitInterval)) { response.XRateLimitInterval = limitInterval; }
+ } else if (key.Equals("X-Rate-Limit-Limit", StringComparison.OrdinalIgnoreCase)) {
+ long limitLimit;
+ if (long.TryParse(val, out limitLimit)) { response.XRateLimitLimit = limitLimit; }
+ } else if (key.Equals("X-Rate-Limit-Reset", StringComparison.OrdinalIgnoreCase)) {
+ double unixTimestamp;
+ if (double.TryParse(val, out unixTimestamp)) {
+ DateTime beginningOfTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
+ response.XRateLimitReset = beginningOfTime.AddSeconds(unixTimestamp).ToLocalTime();
+ }
+ } else if (key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase)) {
+ response.ContentType = val;
+ }
+ }
+ }
+
+ if (apiResponse.StatusCode != HttpStatusCode.OK) {
+ response.AddException(new Exception(string.Format("{0}: {1}", apiResponse.StatusCode, apiResponse.ReasonPhrase)));
+ }
+ int statusCode = (int)apiResponse.StatusCode;
+ response.StatusCode = statusCode;
+ if (429 == statusCode) {
+ response.AddException(new Exception("Rate limit hit"));
+ }
+
+ if (null != apiResponse) {
+ response.Data = await apiResponse.Content.ReadAsByteArrayAsync();
+ }
+ }
+
+ return response;
+ }
+#endif
+
+#if UNITY // within Unity or UWP build from Unity
+ public static Response FromWebResponse(IAsyncRequest request, UnityWebRequest apiResponse, Exception apiEx) {
+
+ Response response = new Response();
+ response.Request = request;
+
+ if (null != apiEx) {
+ response.AddException(apiEx);
+ }
+
+ if (!string.IsNullOrEmpty(apiResponse.error)) {
+ response.AddException(new Exception(apiResponse.error));
+ }
+
+ if (null == apiResponse.downloadHandler.data) {
+ response.AddException(new Exception("Response has no data."));
+ }
+
+#if NETFX_CORE
+ StringComparison stringComp = StringComparison.OrdinalIgnoreCase;
+#else
+ StringComparison stringComp = StringComparison.InvariantCultureIgnoreCase;
+#endif
+
+ Dictionary apiHeaders = apiResponse.GetResponseHeaders();
+ if (null != apiHeaders) {
+ response.Headers = new Dictionary();
+ foreach (var apiHdr in apiHeaders) {
+ string key = apiHdr.Key;
+ string val = apiHdr.Value;
+ response.Headers.Add(key, val);
+ if (key.Equals("X-Rate-Limit-Interval", stringComp)) {
+ int limitInterval;
+ if (int.TryParse(val, out limitInterval)) { response.XRateLimitInterval = limitInterval; }
+ } else if (key.Equals("X-Rate-Limit-Limit", stringComp)) {
+ long limitLimit;
+ if (long.TryParse(val, out limitLimit)) { response.XRateLimitLimit = limitLimit; }
+ } else if (key.Equals("X-Rate-Limit-Reset", stringComp)) {
+ double unixTimestamp;
+ if (double.TryParse(val, out unixTimestamp)) {
+ response.XRateLimitReset = UnixTimestampUtils.From(unixTimestamp);
+ }
+ } else if (key.Equals("Content-Type", stringComp)) {
+ response.ContentType = val;
+ }
+ }
+ }
+
+ int statusCode = (int)apiResponse.responseCode;
+ response.StatusCode = statusCode;
+
+ if (statusCode != 200) {
+ response.AddException(new Exception(string.Format("Status Code {0}", apiResponse.responseCode)));
+ }
+ if (429 == statusCode) {
+ response.AddException(new Exception("Rate limit hit"));
+ }
+
+ response.Data = apiResponse.downloadHandler.data;
+
+ return response;
+ }
+#endif
+
+
+
}
}
\ No newline at end of file
diff --git a/src/Utils/UnixTimestampUtils.cs b/src/Utils/UnixTimestampUtils.cs
new file mode 100644
index 0000000..db6754b
--- /dev/null
+++ b/src/Utils/UnixTimestampUtils.cs
@@ -0,0 +1,43 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) 2016 Mapbox. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+namespace Mapbox.Utils {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+
+
+ ///
+ /// A set of Unix Timestamp utils.
+ ///
+ public static class UnixTimestampUtils {
+
+ // http://gigi.nullneuron.net/gigilabs/converting-tofrom-unix-timestamp-in-c/
+
+ ///
+ /// Convert from DateTime to Unix timestamp
+ ///
+ ///
+ ///
+ public static double To(DateTime date) {
+ //return date.ToLocalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
+ return date.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
+ }
+
+
+ ///
+ /// Convert from Unitx timestamp to DateTime
+ ///
+ ///
+ ///
+ public static DateTime From(double timestamp) {
+ //return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Add(TimeSpan.FromSeconds(timestamp)).ToLocalTime();
+ return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Add(TimeSpan.FromSeconds(timestamp));
+ }
+
+
+ }
+}
diff --git a/src/Utils/Utils.csproj b/src/Utils/Utils.csproj
index 842b086..2fd4058 100644
--- a/src/Utils/Utils.csproj
+++ b/src/Utils/Utils.csproj
@@ -49,6 +49,7 @@
Properties\SharedAssemblyInfo.cs
+
diff --git a/src/Utils/UtilsUWP.csproj b/src/Utils/UtilsUWP.csproj
index 072f13b..8f00d43 100644
--- a/src/Utils/UtilsUWP.csproj
+++ b/src/Utils/UtilsUWP.csproj
@@ -77,6 +77,7 @@
+
diff --git a/test/UnitTest/CompressionTest.cs b/test/UnitTest/CompressionTest.cs
index f455180..948d1b8 100644
--- a/test/UnitTest/CompressionTest.cs
+++ b/test/UnitTest/CompressionTest.cs
@@ -4,43 +4,37 @@
//
//-----------------------------------------------------------------------
-namespace Mapbox.UnitTest
-{
- using System.Text;
- using Mapbox.Platform;
- using Mapbox.Utils;
- using NUnit.Framework;
+namespace Mapbox.UnitTest {
+ using System.Text;
+ using Mapbox.Platform;
+ using Mapbox.Utils;
+ using NUnit.Framework;
- [TestFixture]
- internal class CompressionTest
- {
+ [TestFixture]
+ internal class CompressionTest {
[Test]
- public void Empty()
- {
+ public void Empty() {
var buffer = new byte[] { };
Assert.AreEqual(buffer, Compression.Decompress(buffer));
}
[Test]
- public void NotCompressed()
- {
+ public void NotCompressed() {
var buffer = Encoding.ASCII.GetBytes("foobar");
Assert.AreEqual(buffer, Compression.Decompress(buffer));
}
[Test]
- public void Corrupt()
- {
- var fs = new Mono.FileSource();
+ public void Corrupt() {
+ var fs = new FileSource();
var buffer = new byte[] { };
// Vector tiles are compressed.
fs.Request(
"https://api.mapbox.com/v4/mapbox.mapbox-streets-v7/0/0/0.vector.pbf",
- (Response res) =>
- {
- buffer = res.Data;
- });
+ (Response res) => {
+ buffer = res.Data;
+ });
fs.WaitForAllRequests();
@@ -54,22 +48,25 @@ public void Corrupt()
}
[Test]
- public void Decompress()
- {
- var fs = new Mono.FileSource();
+ public void Decompress() {
+ var fs = new FileSource();
var buffer = new byte[] { };
// Vector tiles are compressed.
fs.Request(
"https://api.mapbox.com/v4/mapbox.mapbox-streets-v7/0/0/0.vector.pbf",
- (Response res) =>
- {
- buffer = res.Data;
- });
+ (Response res) => {
+ buffer = res.Data;
+ });
fs.WaitForAllRequests();
+ //tiles are automatically decompressed during HttpRequest on full .Net framework
+#if NETFX_CORE
Assert.Less(buffer.Length, Compression.Decompress(buffer).Length);
+#else
+ Assert.AreEqual(buffer.Length, Compression.Decompress(buffer).Length);
+#endif
}
}
}
diff --git a/test/UnitTest/DirectionsTest.cs b/test/UnitTest/DirectionsTest.cs
index 2d4b2da..cd6b13c 100644
--- a/test/UnitTest/DirectionsTest.cs
+++ b/test/UnitTest/DirectionsTest.cs
@@ -7,6 +7,7 @@
namespace Mapbox.UnitTest {
using Directions;
using Mapbox.Json;
+ using Mapbox.Platform;
using NUnit.Framework;
///
@@ -16,7 +17,7 @@ namespace Mapbox.UnitTest {
internal class DirectionsTest {
private string basicResponse = "{\"routes\":[{\"legs\":[{\"steps\":[],\"summary\":\"\",\"duration\":214.4,\"distance\":1318.2}],\"geometry\":\"_urwFt}qbMuLp_@jWzPoHhRMK\",\"duration\":214.4,\"distance\":1318.2}],\"waypoints\":[{\"name\":\"East 13th Street\",\"location\":[-73.988909,40.733122]},{\"name\":\"6th Avenue\",\"location\":[-74.00001,40.733004]}],\"code\":\"Ok\"}";
private string responseWithSteps = "{\"routes\":[{\"legs\":[{\"steps\":[{\"intersections\":[{\"out\":0,\"entry\":[true],\"bearings\":[299],\"location\":[-73.988909,40.733122]},{\"out\":3,\"location\":[-73.989868,40.733528],\"bearings\":[15,120,195,300],\"entry\":[true,false,false,true],\"in\":1},{\"out\":3,\"location\":[-73.990945,40.733978],\"bearings\":[15,120,195,300],\"entry\":[false,false,true,true],\"in\":1},{\"out\":3,\"location\":[-73.992266,40.734532],\"bearings\":[30,120,210,300],\"entry\":[true,false,false,true],\"in\":1}],\"geometry\":\"_urwFt}qbMqA~DyAvEmBfG{CpJ\",\"maneuver\":{\"bearing_after\":299,\"type\":\"depart\",\"modifier\":\"left\",\"bearing_before\":0,\"location\":[-73.988909,40.733122],\"instruction\":\"Head northwest on East 13th Street\"},\"duration\":90.5,\"distance\":502.1,\"name\":\"East 13th Street\",\"mode\":\"driving\"},{\"intersections\":[{\"out\":2,\"location\":[-73.994118,40.735313],\"bearings\":[30,120,210,300],\"entry\":[false,false,true,true],\"in\":1},{\"out\":2,\"location\":[-73.994585,40.734672],\"bearings\":[30,120,210,300],\"entry\":[false,true,true,false],\"in\":0},{\"out\":2,\"location\":[-73.99505,40.734034],\"bearings\":[30,120,210,300],\"entry\":[false,false,true,true],\"in\":0},{\"out\":2,\"location\":[-73.995489,40.733437],\"bearings\":[30,120,210,300],\"entry\":[false,true,true,false],\"in\":0},{\"out\":2,\"location\":[-73.995914,40.732847],\"bearings\":[30,120,210,300],\"entry\":[false,false,true,true],\"in\":0},{\"out\":2,\"location\":[-73.996351,40.732255],\"bearings\":[30,120,210,300],\"entry\":[false,true,true,false],\"in\":0}],\"geometry\":\"ubswFf~rbM~B|A~BzAtBvAtBrAtBvAh@Vd@`@lAx@JH\",\"maneuver\":{\"bearing_after\":209,\"type\":\"turn\",\"modifier\":\"left\",\"bearing_before\":299,\"location\":[-73.994118,40.735313],\"instruction\":\"Turn left onto 5th Avenue\"},\"duration\":67.8,\"distance\":496.3,\"name\":\"5th Avenue\",\"mode\":\"driving\"},{\"intersections\":[{\"out\":2,\"location\":[-73.996976,40.731414],\"bearings\":[30,120,300],\"entry\":[false,true,true],\"in\":0}],\"geometry\":\"ijrwFbpsbMKPoChHEH\",\"maneuver\":{\"bearing_after\":305,\"type\":\"end of road\",\"modifier\":\"right\",\"bearing_before\":212,\"location\":[-73.996976,40.731414],\"instruction\":\"Turn right onto Washington Square North\"},\"duration\":21,\"distance\":164.2,\"name\":\"Washington Square North\",\"mode\":\"driving\"},{\"intersections\":[{\"out\":3,\"location\":[-73.998612,40.732215],\"bearings\":[30,120,210,300],\"entry\":[false,false,true,true],\"in\":1}],\"geometry\":\"korwFhzsbMmCbH\",\"maneuver\":{\"bearing_after\":303,\"type\":\"new name\",\"modifier\":\"straight\",\"bearing_before\":303,\"location\":[-73.998612,40.732215],\"instruction\":\"Continue straight onto Waverly Place\"},\"duration\":34.5,\"distance\":146,\"name\":\"Waverly Place\",\"mode\":\"driving\"},{\"intersections\":[{\"out\":0,\"location\":[-74.000066,40.732929],\"bearings\":[30,120,210,300],\"entry\":[true,false,false,true],\"in\":1}],\"geometry\":\"ysrwFlctbMMK\",\"maneuver\":{\"bearing_after\":30,\"type\":\"turn\",\"modifier\":\"right\",\"bearing_before\":303,\"location\":[-74.000066,40.732929],\"instruction\":\"Turn right onto 6th Avenue\"},\"duration\":0.6,\"distance\":9.6,\"name\":\"6th Avenue\",\"mode\":\"driving\"},{\"intersections\":[{\"in\":0,\"entry\":[true],\"bearings\":[210],\"location\":[-74.00001,40.733004]}],\"geometry\":\"gtrwF`ctbM\",\"maneuver\":{\"bearing_after\":0,\"location\":[-74.000066,40.732929],\"bearing_before\":30,\"type\":\"arrive\",\"instruction\":\"You have arrived at your destination\"},\"duration\":0,\"distance\":0,\"name\":\"6th Avenue\",\"mode\":\"driving\"}],\"summary\":\"East 13th Street, 5th Avenue\",\"duration\":214.4,\"distance\":1318.2}],\"geometry\":\"_urwFt}qbMuLp_@jWzPoHhRMK\",\"duration\":214.4,\"distance\":1318.2}],\"waypoints\":[{\"name\":\"East 13th Street\",\"location\":[-73.988909,40.733122]},{\"name\":\"6th Avenue\",\"location\":[-74.00001,40.733004]}],\"code\":\"Ok\"}";
- private Directions directions = new Directions(new Mono.FileSource());
+ private Directions directions = new Directions(new FileSource());
[Test]
public void SerializesAndDeserializesBasic() {
diff --git a/test/UnitTest/FileSourceMockApiTest.cs b/test/UnitTest/FileSourceMockApiTest.cs
new file mode 100644
index 0000000..fdd572a
--- /dev/null
+++ b/test/UnitTest/FileSourceMockApiTest.cs
@@ -0,0 +1,194 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c) 2017 Mapbox. All rights reserved.
+//
+//-----------------------------------------------------------------------
+
+
+namespace Mapbox.UnitTest {
+ using HttpMock;
+ using Mapbox.Json;
+ using Mapbox.Utils;
+ using Mapbox.Utils.JsonConverters;
+ using NUnit.Framework;
+ using Platform;
+ using System;
+ using System.Net;
+
+
+ [TestFixture]
+ internal class FileSourceMockApiTest {
+
+
+ private static readonly string _mockBaseUrl = "http://localhost:2345";
+ private FileSource _fs = new FileSource();
+ IHttpServer _mockApi;
+
+ private struct _testUrl {
+ public static string simpleJson = "/testmock1";
+ public static string customStatusCode = "/testmock2";
+ public static string rateLimitHit = "/ratelimithit";
+ public static string xrateheader = "/xrateheader";
+ public static string cancel = "/cancel";
+ }
+
+ [OneTimeTearDown]
+ public void Finished() {
+ if (null == _mockApi) { return; }
+ _mockApi.Dispose();
+ _mockApi = null;
+ }
+
+
+ [OneTimeSetUp]
+ public void SetupMockHttp() {
+
+ var serverFactory = new HttpServerFactory();
+ _mockApi = serverFactory.Get(new Uri(_mockBaseUrl)).WithNewContext();
+
+ _mockApi.Start();
+
+ _mockApi.Stub(r => r.Get(_testUrl.simpleJson))
+ .Return(@"{""name"":""first test""}")
+ .AsContentType("application/json")
+ .OK();
+
+ // test status code ('Unavailable For Legal Reasons') not available in .NET HttpStatusCode enum
+ _mockApi.Stub(r => r.Get(_testUrl.customStatusCode)).WithStatus((HttpStatusCode)451);
+
+ _mockApi.Stub(r => r.Get(_testUrl.rateLimitHit))
+ .Return(string.Empty)
+ .WithStatus((HttpStatusCode)429);
+
+ Func wait = delegate () { System.Threading.Thread.Sleep(1000); return string.Empty; };
+ _mockApi.Stub(r => r.Get(_testUrl.cancel))
+ .Return(wait)
+ .OK();
+
+ double unixTimestamp = UnixTimestampUtils.To(new DateTime(1981, 12, 2));
+ _mockApi.Stub(r => r.Get(_testUrl.xrateheader))
+ .AddHeader("X-Rate-Limit-Interval", "60")
+ .AddHeader("X-Rate-Limit-Limit", "100000")
+ .AddHeader("X-Rate-Limit-Reset", unixTimestamp.ToString())
+ .OK();
+
+ }
+
+
+ [Test]
+ public void SimpleJson() {
+
+ _fs.Request(
+ _mockBaseUrl + _testUrl.simpleJson
+ , (Response r) => {
+ Assert.IsTrue(r.StatusCode.HasValue, "mock api did not set status code");
+ Assert.AreEqual(200, r.StatusCode, "mock api returned wrong status code");
+ Assert.AreEqual("application/json", r.ContentType, "mock api didn't set correct content type");
+ Assert.AreEqual(@"{""name"":""first test""}", System.Text.Encoding.UTF8.GetString(r.Data), "mock api returned wrong response");
+ }
+ );
+
+ _fs.WaitForAllRequests();
+
+
+ _fs.Request(
+ _mockBaseUrl + _testUrl.customStatusCode
+ , (Response r) => {
+ Assert.IsTrue(r.StatusCode.HasValue, "mock api did not set status code");
+ Assert.AreEqual(451, r.StatusCode, "mock api returned wrong status code");
+ }
+ );
+
+ _fs.WaitForAllRequests();
+ }
+
+
+ [Test]
+ public void RateLimitHit() {
+
+ _fs.Request(
+ _mockBaseUrl + _testUrl.rateLimitHit
+ , (Response r) => {
+ Assert.IsTrue(r.StatusCode.HasValue, "request did not set status code");
+ Assert.AreEqual(429, r.StatusCode, "request did not set rate limit status code correctly");
+ Assert.IsTrue(r.HasError, "request did not set 'HasError'");
+ Assert.NotNull(r.Exceptions, "request did not set any exceptions");
+ Assert.GreaterOrEqual(r.Exceptions.Count, 1, "request did not set enough exceptions");
+ }
+ );
+
+ _fs.WaitForAllRequests();
+ }
+
+
+ [Test]
+ public void XRateHeaders() {
+
+ _fs.Request(
+ _mockBaseUrl + _testUrl.xrateheader
+ , (Response r) => {
+ Assert.IsTrue(r.XRateLimitInterval.HasValue, "request did not set XRateLimitInterval");
+ Assert.IsTrue(r.XRateLimitLimit.HasValue, "request did not set XRateLimitLimit");
+ Assert.IsTrue(r.XRateLimitReset.HasValue, "request did not set XRateLimitReset");
+
+ Assert.AreEqual(60, r.XRateLimitInterval.Value, "request did not set XRateLimitInterval value correctly");
+ Assert.AreEqual(100000, r.XRateLimitLimit.Value, "request did not set XRateLimitLimit value correctly");
+ DateTime dt = new DateTime(1981, 12, 2);
+ Assert.AreEqual(dt, r.XRateLimitReset.Value, "request did not set XRateLimitReset value correctly");
+ }
+ );
+
+ _fs.WaitForAllRequests();
+ }
+
+
+ [Test]
+ public void DoesNotExist404() {
+
+ _fs.Request(
+ _mockBaseUrl + "/doesnotexist/mvt.pbf"
+ , (Response r) => {
+ Assert.IsTrue(r.StatusCode.HasValue, "request did not set status code");
+ Assert.AreEqual(404, r.StatusCode, "request did not set 404 status code correctly");
+ Assert.IsTrue(r.HasError, "request did not set 'HasError'");
+ Assert.NotNull(r.Exceptions, "request did not set any exceptions");
+ Assert.GreaterOrEqual(r.Exceptions.Count, 1, "request did not set enough exceptions");
+ }
+ );
+
+ _fs.WaitForAllRequests();
+ }
+
+
+ [Test]
+ public void NonThreaded() {
+
+ _fs.Request(
+ _mockBaseUrl + _testUrl.simpleJson
+ , (Response r) => {
+ Assert.AreEqual(200, r.StatusCode);
+ }
+ );
+
+ _fs.WaitForAllRequests();
+ }
+
+
+ [Test]
+ public void Cancel() {
+
+ IAsyncRequest request = _fs.Request(
+ _mockBaseUrl + _testUrl.cancel
+ , (Response r) => {
+ Assert.IsTrue(r.HasError);
+ }
+ );
+
+ request.Cancel();
+
+ _fs.WaitForAllRequests();
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/test/UnitTest/FileSourceTest.cs b/test/UnitTest/FileSourceTest.cs
index f755f7a..f187d1f 100644
--- a/test/UnitTest/FileSourceTest.cs
+++ b/test/UnitTest/FileSourceTest.cs
@@ -4,109 +4,109 @@
//
//-----------------------------------------------------------------------
-namespace Mapbox.UnitTest
-{
- using System;
- using Mapbox.Platform;
- using NUnit.Framework;
-
- [TestFixture]
- internal class FileSourceTest
- {
- private const string Uri = "https://api.mapbox.com/geocoding/v5/mapbox.places/helsinki.json";
- private Mono.FileSource fs;
-
- [SetUp]
- public void SetUp()
- {
- this.fs = new Mono.FileSource();
- }
-
- [Test]
- public void AccessTokenSet()
- {
- Assert.IsNotNull(
- Environment.GetEnvironmentVariable("MAPBOX_ACCESS_TOKEN"),
- "MAPBOX_ACCESS_TOKEN not set in the environment.");
- }
-
- [Test]
- public void Request()
- {
- this.fs.Request(
- Uri,
- (Response res) =>
- {
- Assert.IsNotNull(res.Data, "No data received from the servers.");
- });
-
- this.fs.WaitForAllRequests();
- }
-
- [Test]
- public void MultipleRequests()
- {
- int count = 0;
-
- this.fs.Request(Uri, (Response res) => ++count);
- this.fs.Request(Uri, (Response res) => ++count);
- this.fs.Request(Uri, (Response res) => ++count);
-
- this.fs.WaitForAllRequests();
-
- Assert.AreEqual(count, 3, "Should have received 3 replies.");
- }
-
- [Test]
- public void RequestCancel()
- {
- var request = this.fs.Request(
- Uri,
- (Response res) =>
- {
- Assert.Fail("Should never happen.");
- });
-
- request.Cancel();
-
- this.fs.WaitForAllRequests();
- }
-
- [Test]
- public void RequestDnsError()
- {
- this.fs.Request(
- "https://dnserror.shouldnotwork",
- (Response res) =>
- {
- // Do no assume any error message. Mono != .NET.
- Assert.NotNull(res.Error);
- });
-
- this.fs.WaitForAllRequests();
- }
-
- [Test]
- public void RequestForbidden()
- {
- // Mapbox servers will return a forbidden when attempting
- // to access a page outside the API space with a token
- // on the query. Let's hope the behaviour stay like this.
- this.fs.Request(
- "https://mapbox.com/forbidden",
- (Response res) =>
- {
- Assert.AreEqual(res.Error, "Forbidden");
- });
-
- this.fs.WaitForAllRequests();
- }
-
- [Test]
- public void WaitWithNoRequests()
- {
- // This should simply not block.
- this.fs.WaitForAllRequests();
- }
- }
+namespace Mapbox.UnitTest {
+ using System;
+ using Mapbox.Platform;
+ using NUnit.Framework;
+ using System.Net;
+
+
+ [TestFixture]
+ internal class FileSourceTest {
+ private const string _url = "https://api.mapbox.com/geocoding/v5/mapbox.places/helsinki.json";
+ private FileSource _fs;
+
+
+ [SetUp]
+ public void SetUp() {
+ _fs = new FileSource();
+ }
+
+
+ [Test]
+ public void AccessTokenSet() {
+ Assert.IsNotNull(
+ Environment.GetEnvironmentVariable("MAPBOX_ACCESS_TOKEN"),
+ "MAPBOX_ACCESS_TOKEN not set in the environment.");
+ }
+
+
+ [Test]
+ public void Request() {
+ _fs.Request(
+ _url,
+ (Response res) => {
+ Assert.IsNotNull(res.Data, "No data received from the servers.");
+ });
+
+ _fs.WaitForAllRequests();
+ }
+
+
+ [Test]
+ public void MultipleRequests() {
+ int count = 0;
+
+ _fs.Request(_url, (Response res) => ++count);
+ _fs.Request(_url, (Response res) => ++count);
+ _fs.Request(_url, (Response res) => ++count);
+
+ _fs.WaitForAllRequests();
+
+ Assert.AreEqual(count, 3, "Should have received 3 replies.");
+ }
+
+
+ [Test]
+ public void RequestCancel() {
+ var request = _fs.Request(
+ _url,
+ (Response res) => {
+ Assert.IsTrue(res.HasError);
+ WebException wex = res.Exceptions[0] as WebException;
+ Assert.IsNotNull(wex);
+ Assert.AreEqual(wex.Status, WebExceptionStatus.RequestCanceled);
+ });
+
+ request.Cancel();
+
+ _fs.WaitForAllRequests();
+ }
+
+
+ [Test]
+ public void RequestDnsError() {
+ _fs.Request(
+ "https://dnserror.shouldnotwork",
+ (Response res) => {
+ Assert.IsTrue(res.HasError);
+ });
+
+ _fs.WaitForAllRequests();
+ }
+
+
+ [Test]
+ public void RequestForbidden() {
+ // Mapbox servers will return a forbidden when attempting
+ // to access a page outside the API space with a token
+ // on the query. Let's hope the behaviour stay like this.
+ _fs.Request(
+ "https://mapbox.com/forbidden",
+ (Response res) => {
+ Assert.IsTrue(res.HasError);
+ });
+
+ _fs.WaitForAllRequests();
+ }
+
+
+ [Test]
+ public void WaitWithNoRequests() {
+ // This should simply not block.
+ _fs.WaitForAllRequests();
+ }
+
+
+ }
}
\ No newline at end of file
diff --git a/test/UnitTest/GeocoderTest.cs b/test/UnitTest/GeocoderTest.cs
index 017c2e6..9cddd4d 100644
--- a/test/UnitTest/GeocoderTest.cs
+++ b/test/UnitTest/GeocoderTest.cs
@@ -4,26 +4,24 @@
//
//-----------------------------------------------------------------------
-namespace Mapbox.UnitTest
-{
- using Geocoding;
- using Mapbox.Json;
- using Mapbox.Utils.JsonConverters;
- using NUnit.Framework;
-
- ///
- /// Test that Geocoder serializes and deserializes responses correctly.
- ///
- [TestFixture]
- internal class GeocoderTest
- {
- private readonly Geocoder geocoder = new Geocoder(new Mono.FileSource());
+namespace Mapbox.UnitTest {
+ using Geocoding;
+ using Mapbox.Json;
+ using Mapbox.Platform;
+ using Mapbox.Utils.JsonConverters;
+ using NUnit.Framework;
+
+ ///
+ /// Test that Geocoder serializes and deserializes responses correctly.
+ ///
+ [TestFixture]
+ internal class GeocoderTest {
+ private readonly Geocoder geocoder = new Geocoder(new FileSource());
private string forwardResponse = "{\"type\":\"FeatureCollection\",\"query\":[\"minneapolis\"],\"features\":[{\"id\":\"place.12871500125885940\",\"type\":\"Feature\",\"text\":\"Minneapolis\",\"place_name\":\"Minneapolis, Minnesota, United States\",\"relevance\":0.99,\"properties\":{\"wikidata\":\"Q36091\"},\"bbox\":[-93.5226520099878,44.7853029900244,-93.1424209928836,45.2129100099882],\"center\":[-93.2655,44.9773],\"geometry\":{\"type\":\"Point\",\"coordinates\":[-93.2655,44.9773]},\"context\":[{\"id\":\"postcode.11389548391063390\",\"text\":\"55415\"},{\"id\":\"region.12225983719702200\",\"text\":\"Minnesota\",\"short_code\":\"US-MN\",\"wikidata\":\"Q1527\"},{\"id\":\"country.12862386939497690\",\"text\":\"United States\",\"short_code\":\"us\",\"wikidata\":\"Q30\"}]},{\"id\":\"poi.15555644443768740\",\"type\":\"Feature\",\"text\":\"Minneapolis City Hall\",\"place_name\":\"Minneapolis City Hall, Minneapolis, Minnesota 55415, United States\",\"relevance\":0.99,\"properties\":{\"wikidata\":\"Q1384874\",\"landmark\":true,\"tel\":null,\"address\":null,\"category\":\"other\"},\"center\":[-93.265277777778,44.977222222222],\"geometry\":{\"type\":\"Point\",\"coordinates\":[-93.265277777778,44.977222222222]},\"context\":[{\"id\":\"neighborhood.13081559486410050\",\"text\":\"Greater Central\"},{\"id\":\"place.12871500125885940\",\"text\":\"Minneapolis\",\"wikidata\":\"Q36091\"},{\"id\":\"postcode.11389548391063390\",\"text\":\"55415\"},{\"id\":\"region.12225983719702200\",\"text\":\"Minnesota\",\"short_code\":\"US-MN\",\"wikidata\":\"Q1527\"},{\"id\":\"country.12862386939497690\",\"text\":\"United States\",\"short_code\":\"us\",\"wikidata\":\"Q30\"}]},{\"id\":\"poi.6527299549845510\",\"type\":\"Feature\",\"text\":\"Minneapolis Grain Exchange\",\"place_name\":\"Minneapolis Grain Exchange, Minneapolis, Minnesota 55415, United States\",\"relevance\":0.99,\"properties\":{\"wikidata\":\"Q1540984\",\"landmark\":true,\"tel\":null,\"address\":null,\"category\":\"other\"},\"center\":[-93.2636,44.9775],\"geometry\":{\"type\":\"Point\",\"coordinates\":[-93.2636,44.9775]},\"context\":[{\"id\":\"neighborhood.13081559486410050\",\"text\":\"Greater Central\"},{\"id\":\"place.12871500125885940\",\"text\":\"Minneapolis\",\"wikidata\":\"Q36091\"},{\"id\":\"postcode.11389548391063390\",\"text\":\"55415\"},{\"id\":\"region.12225983719702200\",\"text\":\"Minnesota\",\"short_code\":\"US-MN\",\"wikidata\":\"Q1527\"},{\"id\":\"country.12862386939497690\",\"text\":\"United States\",\"short_code\":\"us\",\"wikidata\":\"Q30\"}]},{\"id\":\"poi.12655750184890630\",\"type\":\"Feature\",\"text\":\"Minneapolis Armory\",\"place_name\":\"Minneapolis Armory, Minneapolis, Minnesota 55415, United States\",\"relevance\":0.99,\"properties\":{\"wikidata\":\"Q745327\",\"landmark\":true,\"tel\":null,\"address\":null,\"category\":\"other\"},\"center\":[-93.263278,44.975092],\"geometry\":{\"type\":\"Point\",\"coordinates\":[-93.263278,44.975092]},\"context\":[{\"id\":\"neighborhood.13081559486410050\",\"text\":\"Greater Central\"},{\"id\":\"place.12871500125885940\",\"text\":\"Minneapolis\",\"wikidata\":\"Q36091\"},{\"id\":\"postcode.11389548391063390\",\"text\":\"55415\"},{\"id\":\"region.12225983719702200\",\"text\":\"Minnesota\",\"short_code\":\"US-MN\",\"wikidata\":\"Q1527\"},{\"id\":\"country.12862386939497690\",\"text\":\"United States\",\"short_code\":\"us\",\"wikidata\":\"Q30\"}]},{\"id\":\"poi.4855757554573390\",\"type\":\"Feature\",\"text\":\"Minneapolis Chain of Lakes Park\",\"place_name\":\"Minneapolis Chain of Lakes Park, Minneapolis, Minnesota 55405, United States\",\"relevance\":0.99,\"properties\":{\"wikidata\":null,\"landmark\":true,\"tel\":null,\"address\":null,\"category\":\"park\",\"maki\":\"picnic-site\"},\"bbox\":[-93.330260720104,44.9504758437682,-93.3013567328453,44.969400319872],\"center\":[-93.310259,44.959942],\"geometry\":{\"type\":\"Point\",\"coordinates\":[-93.310259,44.959942]},\"context\":[{\"id\":\"neighborhood.12530456224376080\",\"text\":\"Kenwood\"},{\"id\":\"place.12871500125885940\",\"text\":\"Minneapolis\",\"wikidata\":\"Q36091\"},{\"id\":\"postcode.10829535691218220\",\"text\":\"55405\"},{\"id\":\"region.12225983719702200\",\"text\":\"Minnesota\",\"short_code\":\"US-MN\",\"wikidata\":\"Q1527\"},{\"id\":\"country.12862386939497690\",\"text\":\"United States\",\"short_code\":\"us\",\"wikidata\":\"Q30\"}]}],\"attribution\":\"NOTICE: \u00A9 2016 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained.\"}";
private string reverseResponse = "{\"type\":\"FeatureCollection\",\"query\":[-77.0268808,38.925326999999996],\"features\":[{\"id\":\"address.5375777428110760\",\"type\":\"Feature\",\"text\":\"11th St NW\",\"place_name\":\"2717 11th St NW, Washington, District of Columbia 20001, United States\",\"relevance\":1.0,\"properties\":{},\"center\":[-77.026824,38.925306],\"geometry\":{\"type\":\"Point\",\"coordinates\":[-77.026824,38.925306]},\"address\":\"2717\",\"context\":[{\"id\":\"neighborhood.11736072639395000\",\"text\":\"Pleasant Plains\"},{\"id\":\"place.12334081418246050\",\"text\":\"Washington\",\"wikidata\":\"Q61\"},{\"id\":\"postcode.3526019892841050\",\"text\":\"20001\"},{\"id\":\"region.6884744206035790\",\"text\":\"District of Columbia\",\"short_code\":\"US-DC\",\"wikidata\":\"Q61\"},{\"id\":\"country.12862386939497690\",\"text\":\"United States\",\"wikidata\":\"Q30\",\"short_code\":\"us\"}]},{\"id\":\"neighborhood.11736072639395000\",\"type\":\"Feature\",\"text\":\"Pleasant Plains\",\"place_name\":\"Pleasant Plains, Washington, 20001, District of Columbia, United States\",\"relevance\":1.0,\"properties\":{},\"bbox\":[-77.0367101373528,38.9177500315001,-77.0251464843832,38.9273657639],\"center\":[-77.0303,38.9239],\"geometry\":{\"type\":\"Point\",\"coordinates\":[-77.0303,38.9239]},\"context\":[{\"id\":\"place.12334081418246050\",\"text\":\"Washington\",\"wikidata\":\"Q61\"},{\"id\":\"postcode.3526019892841050\",\"text\":\"20001\"},{\"id\":\"region.6884744206035790\",\"text\":\"District of Columbia\",\"short_code\":\"US-DC\",\"wikidata\":\"Q61\"},{\"id\":\"country.12862386939497690\",\"text\":\"United States\",\"wikidata\":\"Q30\",\"short_code\":\"us\"}]},{\"id\":\"place.12334081418246050\",\"type\":\"Feature\",\"text\":\"Washington\",\"place_name\":\"Washington, District of Columbia, United States\",\"relevance\":1.0,\"properties\":{\"wikidata\":\"Q61\"},\"bbox\":[-77.1197590084041,38.8031129900659,-76.90939299,38.9955480080759],\"center\":[-77.0366,38.895],\"geometry\":{\"type\":\"Point\",\"coordinates\":[-77.0366,38.895]},\"context\":[{\"id\":\"postcode.3526019892841050\",\"text\":\"20001\"},{\"id\":\"region.6884744206035790\",\"text\":\"District of Columbia\",\"short_code\":\"US-DC\",\"wikidata\":\"Q61\"},{\"id\":\"country.12862386939497690\",\"text\":\"United States\",\"wikidata\":\"Q30\",\"short_code\":\"us\"}]},{\"id\":\"postcode.3526019892841050\",\"type\":\"Feature\",\"text\":\"20001\",\"place_name\":\"20001, District of Columbia, United States\",\"relevance\":1.0,\"properties\":{},\"bbox\":[-77.028082,38.890834,-77.007177,38.929058],\"center\":[-77.018017,38.909197],\"geometry\":{\"type\":\"Point\",\"coordinates\":[-77.018017,38.909197]},\"context\":[{\"id\":\"region.6884744206035790\",\"text\":\"District of Columbia\",\"short_code\":\"US-DC\",\"wikidata\":\"Q61\"},{\"id\":\"country.12862386939497690\",\"text\":\"United States\",\"wikidata\":\"Q30\",\"short_code\":\"us\"}]},{\"id\":\"region.6884744206035790\",\"type\":\"Feature\",\"text\":\"District of Columbia\",\"place_name\":\"District of Columbia, United States\",\"relevance\":1.0,\"properties\":{\"short_code\":\"US-DC\",\"wikidata\":\"Q61\"},\"bbox\":[-77.2081379659453,38.7177026348658,-76.909393,38.995548],\"center\":[-76.990661,38.89657],\"geometry\":{\"type\":\"Point\",\"coordinates\":[-76.990661,38.89657]},\"context\":[{\"id\":\"country.12862386939497690\",\"text\":\"United States\",\"wikidata\":\"Q30\",\"short_code\":\"us\"}]},{\"id\":\"country.12862386939497690\",\"type\":\"Feature\",\"text\":\"United States\",\"place_name\":\"United States\",\"relevance\":1.0,\"properties\":{\"wikidata\":\"Q30\",\"short_code\":\"us\"},\"bbox\":[-179.330950579,18.765563302,179.959578044,71.540723637],\"center\":[-97.922211,39.381266],\"geometry\":{\"type\":\"Point\",\"coordinates\":[-97.922211,39.381266]}}],\"attribution\":\"NOTICE: © 2016 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained.\"}";
[Test]
- public void SerializesAndDeserializesReverse()
- {
+ public void SerializesAndDeserializesReverse() {
// First, deserialize the example response
ReverseGeocodeResponse reverseResp = this.geocoder.Deserialize(this.reverseResponse);
@@ -35,8 +33,7 @@ public void SerializesAndDeserializesReverse()
}
[Test]
- public void SerializesAndDeserializesForward()
- {
+ public void SerializesAndDeserializesForward() {
// First, deserialize the example response
ForwardGeocodeResponse forwardResp = this.geocoder.Deserialize(this.forwardResponse);
diff --git a/test/UnitTest/MapTest.cs b/test/UnitTest/MapTest.cs
index f0b4a06..4b4671d 100644
--- a/test/UnitTest/MapTest.cs
+++ b/test/UnitTest/MapTest.cs
@@ -4,27 +4,24 @@
//
//-----------------------------------------------------------------------
-namespace Mapbox.UnitTest
-{
- using System.Drawing;
- using Mapbox.Map;
- using Mapbox.Utils;
- using NUnit.Framework;
-
- [TestFixture]
- internal class MapTest
- {
- private Mono.FileSource fs;
+namespace Mapbox.UnitTest {
+ using System.Drawing;
+ using Mapbox.Map;
+ using Mapbox.Platform;
+ using Mapbox.Utils;
+ using NUnit.Framework;
+
+ [TestFixture]
+ internal class MapTest {
+ private FileSource fs;
[SetUp]
- public void SetUp()
- {
- this.fs = new Mono.FileSource();
+ public void SetUp() {
+ this.fs = new FileSource();
}
[Test]
- public void World()
- {
+ public void World() {
var map = new Map(this.fs);
map.Vector2dBounds = Vector2dBounds.World();
@@ -42,8 +39,7 @@ public void World()
}
[Test]
- public void RasterHelsinki()
- {
+ public void RasterHelsinki() {
var map = new Map(this.fs);
map.Center = new Vector2d(60.163200, 24.937700);
@@ -64,8 +60,7 @@ public void RasterHelsinki()
}
[Test]
- public void ChangeMapId()
- {
+ public void ChangeMapId() {
var map = new Map(this.fs);
var mapObserver = new Utils.ClassicRasterMapObserver();
@@ -98,8 +93,7 @@ public void ChangeMapId()
}
[Test]
- public void SetVector2dBoundsZoom()
- {
+ public void SetVector2dBoundsZoom() {
var map1 = new Map(this.fs);
var map2 = new Map(this.fs);
@@ -112,8 +106,7 @@ public void SetVector2dBoundsZoom()
}
[Test]
- public void TileMax()
- {
+ public void TileMax() {
var map = new Map(this.fs);
map.SetVector2dBoundsZoom(Vector2dBounds.World(), 2);
@@ -127,8 +120,7 @@ public void TileMax()
}
[Test]
- public void Zoom()
- {
+ public void Zoom() {
var map = new Map(this.fs);
map.Zoom = 50;
diff --git a/test/UnitTest/TileTest.cs b/test/UnitTest/TileTest.cs
index 9d4f34d..3b6d30a 100644
--- a/test/UnitTest/TileTest.cs
+++ b/test/UnitTest/TileTest.cs
@@ -7,17 +7,18 @@
namespace Mapbox.UnitTest
{
using Mapbox.Map;
+ using Mapbox.Platform;
using NUnit.Framework;
[TestFixture]
internal class TileTest
{
- private Mono.FileSource fs;
+ private FileSource fs;
[SetUp]
public void SetUp()
{
- this.fs = new Mono.FileSource();
+ this.fs = new FileSource();
}
[Test]
diff --git a/test/UnitTest/UnitTest.csproj b/test/UnitTest/UnitTest.csproj
index 0c75b1e..bffaa4a 100644
--- a/test/UnitTest/UnitTest.csproj
+++ b/test/UnitTest/UnitTest.csproj
@@ -34,6 +34,18 @@
MinimumRecommendedRules.ruleset
+
+ ..\..\packages\HttpMock.2.0.1\lib\net45\HttpMock.dll
+ True
+
+
+ ..\..\packages\Kayak.0.7.2\lib\Kayak.dll
+ True
+
+
+ ..\..\packages\log4net.2.0.7\lib\net45-full\log4net.dll
+ True
+
..\..\3rdparty\Json.Net.Unity3D\Net35\Mapbox.Json.dll
@@ -58,8 +70,10 @@
..\..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll
+
+
@@ -90,10 +104,6 @@
{89F8BAB2-2E7A-425E-B715-2CC4519D561F}
Geocoding
-
- {346B1208-1587-490B-A7DE-A96B86E81CD6}
- Mono
-
{FE49745C-01F6-4A3F-BF08-828113D3E19F}
Platform
@@ -108,6 +118,7 @@
+
diff --git a/test/UnitTest/Utils.cs b/test/UnitTest/Utils.cs
index 5cf0cac..4e4815c 100644
--- a/test/UnitTest/Utils.cs
+++ b/test/UnitTest/Utils.cs
@@ -5,14 +5,14 @@
//-----------------------------------------------------------------------
namespace Mapbox.UnitTest {
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.IO;
- using Mapbox.Map;
- using Mapbox.Platform;
-
- internal static class Utils {
+ using System;
+ using System.Collections.Generic;
+ using System.Drawing;
+ using System.IO;
+ using Mapbox.Map;
+ using Mapbox.Platform;
+
+ internal static class Utils {
internal class VectorMapObserver : Mapbox.Utils.IObserver {
private List tiles = new List();
@@ -39,7 +39,7 @@ public List Tiles {
}
public void OnNext(RasterTile tile) {
- if (tile.CurrentState == Tile.State.Loaded && string.IsNullOrEmpty(tile.Error)) {
+ if (tile.CurrentState == Tile.State.Loaded && !tile.HasError) {
var image = Image.FromStream(new MemoryStream(tile.Data));
tiles.Add(image);
}
@@ -56,62 +56,65 @@ public List Tiles {
}
public void OnNext(ClassicRasterTile tile) {
- if (tile.CurrentState == Tile.State.Loaded && string.IsNullOrEmpty(tile.Error)) {
+ if (tile.CurrentState == Tile.State.Loaded && !tile.HasError) {
var image = Image.FromStream(new MemoryStream(tile.Data));
tiles.Add(image);
}
}
}
- internal class MockFileSource : IFileSource {
- private Dictionary responses = new Dictionary();
- private List requests = new List();
-
- public IAsyncRequest Request(string uri, Action callback) {
- var response = new Response();
- if (this.responses.ContainsKey(uri)) {
- response = this.responses[uri];
- }
-
- var request = new MockRequest(response, callback);
- this.requests.Add(request);
-
- return request;
- }
-
- public void SetReponse(string uri, Response response) {
- this.responses[uri] = response;
- }
-
- public void WaitForAllRequests() {
- while (this.requests.Count > 0) {
- var req = this.requests[0];
- this.requests.RemoveAt(0);
-
- req.Run();
- }
- }
-
- public class MockRequest : IAsyncRequest {
- private Response response;
- private Action callback;
-
- public MockRequest(Response response, Action callback) {
- this.response = response;
- this.callback = callback;
- }
-
- public void Run() {
- if (this.callback != null) {
- this.callback(this.response);
- this.callback = null;
- }
- }
-
- public void Cancel() {
- this.callback = null;
- }
- }
- }
+ //internal class MockFileSource : IFileSource {
+ // private Dictionary responses = new Dictionary();
+ // private List requests = new List();
+
+ // public IAsyncRequest Request(string uri, Action callback, Action progress = null, Action finished = null, int timeout = 10) {
+ // var response = new Response();
+ // if (this.responses.ContainsKey(uri)) {
+ // response = this.responses[uri];
+ // }
+
+ // var request = new MockRequest(response, callback);
+ // this.requests.Add(request);
+
+ // return request;
+ // }
+
+ // public void SetReponse(string uri, Response response) {
+ // this.responses[uri] = response;
+ // }
+
+ // public void WaitForAllRequests() {
+ // while (this.requests.Count > 0) {
+ // var req = this.requests[0];
+ // this.requests.RemoveAt(0);
+
+ // req.Run();
+ // }
+ // }
+
+ // public class MockRequest : IAsyncRequest {
+ // public bool IsCompleted { get; private set; }
+ // private Response response;
+ // private Action callback;
+
+ // public MockRequest(Response response, Action callback) {
+ // this.response = response;
+ // this.callback = callback;
+ // }
+
+ // public void Run() {
+ // if (this.callback != null) {
+ // this.callback(this.response);
+ // this.callback = null;
+ // IsCompleted = true;
+ // }
+ // }
+
+ // public void Cancel() {
+ // this.callback = null;
+ // IsCompleted = true;
+ // }
+ // }
+ //}
}
}
diff --git a/test/UnitTest/VectorTileTest.cs b/test/UnitTest/VectorTileTest.cs
index feda959..3038d0c 100644
--- a/test/UnitTest/VectorTileTest.cs
+++ b/test/UnitTest/VectorTileTest.cs
@@ -5,26 +5,31 @@
//-----------------------------------------------------------------------
namespace Mapbox.UnitTest {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using Mapbox.Map;
- using Mapbox.Platform;
- using Mapbox.Utils;
- using NUnit.Framework;
-
- [TestFixture]
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using Mapbox.Map;
+ using Mapbox.Platform;
+ using Mapbox.Utils;
+ using NUnit.Framework;
+
+
+ [TestFixture]
internal class VectorTileTest {
- private Mono.FileSource fs;
+
+
+ private FileSource _fs;
+
[SetUp]
public void SetUp() {
- this.fs = new Mono.FileSource();
+ _fs = new FileSource();
}
+
[Test]
public void ParseSuccess() {
- var map = new Map(this.fs);
+ var map = new Map(_fs);
var mapObserver = new Utils.VectorMapObserver();
map.Subscribe(mapObserver);
@@ -35,7 +40,7 @@ public void ParseSuccess() {
for (int zoom = 0; zoom < 15; ++zoom) {
map.Zoom = zoom;
map.Update();
- this.fs.WaitForAllRequests();
+ _fs.WaitForAllRequests();
}
// We must have all the tiles for Helsinki from 0-15.
@@ -54,37 +59,39 @@ public void ParseSuccess() {
map.Unsubscribe(mapObserver);
}
- [Test]
- public void ParseFailure() {
- var resource = TileResource.MakeVector(new CanonicalTileId(13, 5465, 2371), null);
- var response = new Response();
- response.Data = Enumerable.Repeat((byte)0, 5000).ToArray();
+ //[Test]
+ //public void ParseFailure() {
+ // var resource = TileResource.MakeVector(new CanonicalTileId(13, 5465, 2371), null);
- var mockFs = new Utils.MockFileSource();
- mockFs.SetReponse(resource.GetUrl(), response);
+ // var response = new Response();
+ // response.Data = Enumerable.Repeat((byte)0, 5000).ToArray();
- var map = new Map(mockFs);
+ // var mockFs = new Utils.MockFileSource();
+ // mockFs.SetReponse(resource.GetUrl(), response);
- var mapObserver = new Utils.VectorMapObserver();
- map.Subscribe(mapObserver);
+ // var map = new Map(mockFs);
- map.Center = new Vector2d(60.163200, 60.163200);
- map.Zoom = 13;
- map.Update();
+ // var mapObserver = new Utils.VectorMapObserver();
+ // map.Subscribe(mapObserver);
- mockFs.WaitForAllRequests();
+ // map.Center = new Vector2d(60.163200, 60.163200);
+ // map.Zoom = 13;
+ // map.Update();
- // TODO: Assert.AreEqual("Parse error.", mapObserver.Error);
- Assert.AreEqual(1, mapObserver.Tiles.Count);
- Assert.IsNull(mapObserver.Tiles[0].Data);
+ // mockFs.WaitForAllRequests();
+
+ // // TODO: Assert.AreEqual("Parse error.", mapObserver.Error);
+ // Assert.AreEqual(1, mapObserver.Tiles.Count);
+ // Assert.IsNull(mapObserver.Tiles[0].Data);
+
+ // map.Unsubscribe(mapObserver);
+ //}
- map.Unsubscribe(mapObserver);
- }
[Test]
public void SeveralTiles() {
- var map = new Map(this.fs);
+ var map = new Map(_fs);
var mapObserver = new Utils.VectorMapObserver();
map.Subscribe(mapObserver);
@@ -93,20 +100,21 @@ public void SeveralTiles() {
map.Zoom = 3; // 64 tiles.
map.Update();
- this.fs.WaitForAllRequests();
+ _fs.WaitForAllRequests();
Assert.AreEqual(64, mapObserver.Tiles.Count);
foreach (var tile in mapObserver.Tiles) {
- if (string.IsNullOrEmpty(tile.Error)) {
+ if (!tile.HasError) {
Assert.Greater(tile.GeoJson.Length, 41);
} else {
- // NotFound is fine.
- Assert.AreNotEqual("ParseError", tile.Error);
+ Assert.GreaterOrEqual(tile.Exceptions.Count, 1, "not set enough exceptions set on 'Tile'");
}
}
map.Unsubscribe(mapObserver);
}
+
+
}
}
diff --git a/test/UnitTest/app.config b/test/UnitTest/app.config
new file mode 100644
index 0000000..b0571f9
--- /dev/null
+++ b/test/UnitTest/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/UnitTest/packages.config b/test/UnitTest/packages.config
index 3546b28..2850921 100644
--- a/test/UnitTest/packages.config
+++ b/test/UnitTest/packages.config
@@ -1,5 +1,8 @@
+
+
+