[TASK] Initial commit with basic product setup

This commit is contained in:
2019-08-18 13:50:14 +02:00
commit 01a66a8e1f
2548 changed files with 167528 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 17120e4ab0b97434486f85ff6e0a9196
folderAsset: yes
timeCreated: 1491243030
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,244 @@
//-----------------------------------------------------------------------
// <copyright file="DirectionResource.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Directions
{
using System;
using System.Collections.Generic;
using Mapbox.Utils;
using Platform;
/// <summary> A directions request. </summary>
public class DirectionResource : Resource
{
private string apiEndpoint = "directions/v5/";
// Required
private RoutingProfile profile;
// Optional
private Vector2d[] coordinates;
// Optional
private bool? alternatives;
// Optional
private BearingFilter[] bearings;
// Optional
private bool? continueStraight;
// Optional
private Overview overview;
// Optional
private double[] radiuses;
// Optional
private bool? steps;
/// <summary> Initializes a new instance of the <see cref="DirectionResource" /> class.</summary>
/// <param name="coordinates">
/// Array of LatLng points along route, between 2 and 25 elements in length.
/// </param>
/// <param name="profile">
/// A routing profile, <see cref="RoutingProfile"/> for all profile options.
/// </param>
public DirectionResource(Vector2d[] coordinates, RoutingProfile profile)
{
this.Coordinates = coordinates;
this.RoutingProfile = profile;
}
/// <summary> Gets the API endpoint as a partial URL path. </summary>
public override string ApiEndpoint {
get {
return this.apiEndpoint;
}
}
/// <summary>
/// Gets or sets the coordinates. Array of LatLng points along route,
/// between 2 and 25 elements in length.
/// </summary>
public Vector2d[] Coordinates {
get {
return this.coordinates;
}
set {
if (value.Length < 2 || value.Length > 25)
{
throw new Exception("Must be between 2 and 25 elements in coordinates array.");
}
this.coordinates = value;
}
}
/// <summary>
/// Gets or sets the routing profile, <see cref="RoutingProfile"/> for all profile options.
/// </summary>
public RoutingProfile RoutingProfile {
get {
return this.profile;
}
set {
this.profile = value;
}
}
/// <summary>
/// Gets or sets the alternative option. Controls whether direction request should
/// return alternative routes.
/// </summary>
public bool? Alternatives {
get {
return this.alternatives;
}
set {
this.alternatives = value;
}
}
/// <summary>
/// Gets or sets the bearing option. An array of bearing filters. Each filter is composed of
/// a bearing as decimal degrees clockwise between 0 and 360, and a range of variation from
/// the bearing as decimal degrees between 0 and 180.
/// </summary>
public BearingFilter[] Bearings {
get {
return this.bearings;
}
set {
if (value != null && value.Length != this.coordinates.Length)
{
throw new Exception("There must be as many bearings as there are coordinates in the request.");
}
this.bearings = value;
}
}
/// <summary>
/// Gets or sets the continue_straight option. Controls whether to route will
/// continue in same direction of travel or if route may continue in opposite
/// direction of travel at intermediate waypoints.
/// </summary>
public bool? ContinueStraight {
get {
return this.continueStraight;
}
set {
this.continueStraight = value;
}
}
/// <summary>
/// Gets or sets the overview option. See <see cref="Overview"/> for all overview options.
/// </summary>
public Overview Overview {
get {
return this.overview;
}
set {
this.overview = value;
}
}
/// <summary>
/// Gets or sets the radiuses option. Controls maximum distance in meters that
/// each coordinate is allowed to move when snapped to a nearby road segment.
/// </summary>
public double[] Radiuses {
get {
return this.radiuses;
}
set {
if (value != null)
{
if (value.Length != this.coordinates.Length)
{
throw new Exception("There must be as many radiuses as there are coordinates in the request.");
}
for (int i = 0; i < value.Length; i++)
{
if (value[i] <= 0)
{
throw new Exception("Radius must be greater than 0");
}
}
}
this.radiuses = value;
}
}
/// <summary> Gets or sets the steps option. Controls whether to return steps and turn-by-turn instructions.</summary>
public bool? Steps {
get {
return this.steps;
}
set {
this.steps = value;
}
}
/// <summary>
/// Gets the URL string.
/// </summary>
/// <returns>The URL string.</returns>
public override string GetUrl()
{
Dictionary<string, string> opts = new Dictionary<string, string>();
if (this.Alternatives != null)
{
opts.Add("alternatives", this.Alternatives.ToString().ToLower());
}
if (this.Bearings != null)
{
opts.Add("bearings", GetUrlQueryFromArray(this.Bearings, ";"));
}
if (this.ContinueStraight != null)
{
opts.Add("continue_straight", this.ContinueStraight.ToString().ToLower());
}
if (this.Overview != null)
{
opts.Add("overview", this.Overview.ToString());
}
if (this.Radiuses != null)
{
opts.Add("radiuses", GetUrlQueryFromArray(this.Radiuses));
}
if (this.Steps != null)
{
opts.Add("steps", this.Steps.ToString().ToLower());
}
return Constants.BaseAPI +
this.ApiEndpoint +
this.RoutingProfile +
GetUrlQueryFromArray<Vector2d>(this.Coordinates, ";") +
".json" +
EncodeQueryString(opts);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 78ecfcb30aefc4289b8d2a83619a2d89
timeCreated: 1493218361
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,69 @@
//-----------------------------------------------------------------------
// <copyright file="Directions.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Directions
{
using System;
using System.Text;
using Mapbox.Json;
using Mapbox.Platform;
using Mapbox.Utils.JsonConverters;
/// <summary>
/// Wrapper around the <see href="https://www.mapbox.com/api-documentation/#directions">
/// Mapbox Directions API</see>. The Mapbox Directions API will show you how to get where
/// you're going.
/// </summary>
public sealed class Directions
{
private readonly IFileSource fileSource;
/// <summary> Initializes a new instance of the <see cref="Directions" /> class. </summary>
/// <param name="fileSource"> Network access abstraction. </param>
public Directions(IFileSource fileSource)
{
this.fileSource = fileSource;
}
/// <summary> Performs asynchronously a directions lookup. </summary>
/// <param name="direction"> Direction resource. </param>
/// <param name="callback"> Callback to be called after the request is completed. </param>
/// <returns>
/// Returns a <see cref="IAsyncRequest" /> 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.
/// </returns>
public IAsyncRequest Query(DirectionResource direction, Action<DirectionsResponse> callback)
{
return this.fileSource.Request(
direction.GetUrl(),
(Response response) =>
{
var str = Encoding.UTF8.GetString(response.Data);
var data = Deserialize(str);
callback(data);
});
}
/// <summary>
/// Deserialize the geocode response string into a <see cref="DirectionsResponse"/>.
/// </summary>
/// <param name="str">JSON String.</param>
/// <returns>A <see cref="DirectionsResponse"/>.</returns>
public DirectionsResponse Deserialize(string str)
{
return JsonConvert.DeserializeObject<DirectionsResponse>(str, JsonConverters.Converters);
}
public string Serialize(DirectionsResponse response)
{
return JsonConvert.SerializeObject(response, JsonConverters.Converters);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a4fc862e3db034a0b8591a58cd15afdc
timeCreated: 1491243034
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
//-----------------------------------------------------------------------
// <copyright file="Overview.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Directions
{
/// <summary>
/// Type of returned overview geometry. Can be full (the most detailed geometry available),
/// simplified (a simplified version of the full geometry), or false (no overview geometry).
/// </summary>
public sealed class Overview
{
/// <summary> Use the most detailed geometry available. </summary>
public static readonly Overview Full = new Overview("full");
/// <summary> Use simplified geometry. This is the default value. </summary>
public static readonly Overview Simplified = new Overview("simplified");
/// <summary> Use no overview geometry. </summary>
public static readonly Overview False = new Overview("false");
private readonly string overview;
private Overview(string overview)
{
this.overview = overview;
}
/// <summary> Converts the overview type to a string. </summary>
/// <returns> A string to use as an optional value in the direction query URL. </returns>
public override string ToString()
{
return this.overview;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 794258e75ed8d4cee96d4b5ba225cfc8
timeCreated: 1491243034
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 41da431a6ab1548a29eecc30eebabe26
folderAsset: yes
timeCreated: 1491243031
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
//-----------------------------------------------------------------------
// <copyright file="Leg.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Directions
{
using System.Collections.Generic;
using Mapbox.Json;
/// <summary>
/// <para>An annotations object contains additional details about each line segment along the route geometry.</para>
/// <para></para>Each entry in an annotations field corresponds to a coordinate along the route geometry.
/// </summary>
public class Annotation
{
[JsonProperty("distance")]
public double[] Distance { get; set; }
[JsonProperty("duration")]
public double[] Duration { get; set; }
[JsonProperty("speed")]
public string[] Speed { get; set; }
[JsonProperty("congestion")]
public string[] Congestion { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 204896b65f98440449415ca9b9c1d643
timeCreated: 1508343130
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,41 @@
//-----------------------------------------------------------------------
// <copyright file="DirectionsResponse.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Directions {
using System;
using System.Collections.Generic;
using Mapbox.Json;
/// <summary>
/// Directions response.
/// </summary>
#if !WINDOWS_UWP
// http://stackoverflow.com/a/12903628
[Serializable]
#endif
public class DirectionsResponse {
/// <summary>
/// Gets or sets the routes.
/// </summary>
/// <value>The routes.</value>
[JsonProperty("routes")]
public List<Route> Routes { get; set; }
/// <summary>
/// Gets or sets the waypoints.
/// </summary>
/// <value>The waypoints.</value>
[JsonProperty("waypoints")]
public List<Waypoint> Waypoints { get; set; }
/// <summary>
/// Gets or sets the code.
/// </summary>
/// <value>The code.</value>
[JsonProperty("code")]
public string Code { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f69de6bfcf63e4d2ab766574bca2caf6
timeCreated: 1491243035
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,55 @@
//-----------------------------------------------------------------------
// <copyright file="Intersection.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Directions
{
using System.Collections.Generic;
using Mapbox.Json;
using Mapbox.Utils;
using Mapbox.Utils.JsonConverters;
/// <summary>
/// An Intersection from a Directions API call.
/// </summary>
public class Intersection
{
/// <summary>
/// Gets or sets the out.
/// </summary>
/// <value>The out.</value>
[JsonProperty("out", Order = 0)]
public int Out { get; set; }
/// <summary>
/// Gets or sets the entry.
/// </summary>
/// <value>The entry.</value>
[JsonProperty("entry", Order = 1)]
public List<bool> Entry { get; set; }
/// <summary>
/// Gets or sets the bearings.
/// </summary>
/// <value>The bearings.</value>
[JsonProperty("bearings", Order = 2)]
public List<int> Bearings { get; set; }
/// <summary>
/// Gets or sets the location.
/// </summary>
/// <value>The location.</value>
[JsonProperty("location", Order = 3)]
[JsonConverter(typeof(LonLatToVector2dConverter))]
public Vector2d Location { get; set; }
/// <summary>
/// Gets or sets the in.
/// </summary>
/// <value>The in.</value>
[JsonProperty("in", Order = 4, NullValueHandling = NullValueHandling.Ignore)]
public int? In { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 38b89d19d5fc44375966036ad1331f3d
timeCreated: 1493218361
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
//-----------------------------------------------------------------------
// <copyright file="Leg.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Directions
{
using System.Collections.Generic;
using Mapbox.Json;
/// <summary>
/// A Leg from a Directions API call.
/// </summary>
public class Leg
{
/// <summary>
/// Depending on the steps parameter, either an Array of RouteStep objects (true, default) or an empty array (false)
/// </summary>
/// <value>The steps.</value>
[JsonProperty("steps")]
public List<Step> Steps { get; set; }
/// <summary>
/// Depending on the summary parameter, either a String summarizing the route (true, default) or an empty String (false).
/// </summary>
/// <value>The summary.</value>
[JsonProperty("summary")]
public string Summary { get; set; }
/// <summary>
/// Number indicating the estimated travel time in seconds.
/// </summary>
[JsonProperty("duration")]
public double Duration { get; set; }
/// <summary>
/// Number indicating the distance traveled in meters.
/// </summary>
[JsonProperty("distance")]
public double Distance { get; set; }
/// <summary>
/// An annotations object that contains additional details about each line segment along the route geometry. Each entry in an annotations field corresponds to a coordinate along the route geometry.
/// </summary>
[JsonProperty("annotation")]
public Annotation Annotation { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7847e2706c04347a19461bd848b5e1bd
timeCreated: 1491243034
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,59 @@
//-----------------------------------------------------------------------
// <copyright file="Maneuver.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Directions
{
using Mapbox.Json;
using Mapbox.Utils;
/// <summary>
/// A Maneuver from a directions API call.
/// </summary>
public class Maneuver
{
/// <summary>
/// Gets or sets the bearing after.
/// </summary>
/// <value>The bearing after.</value>
[JsonProperty("bearing_after")]
public int BearingAfter { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
[JsonProperty("type")]
public string Type { get; set; }
/// <summary>
/// Gets or sets the modifier.
/// </summary>
/// <value>The modifier.</value>
[JsonProperty("modifier")]
public string Modifier { get; set; }
/// <summary>
/// Gets or sets the bearing before.
/// </summary>
/// <value>The bearing before.</value>
[JsonProperty("bearing_before")]
public int BearingBefore { get; set; }
/// <summary>
/// Gets or sets the location.
/// </summary>
/// <value>The location.</value>
[JsonProperty("Location")]
public Vector2d Location { get; set; }
/// <summary>
/// Gets or sets the instruction.
/// </summary>
/// <value>The instruction.</value>
[JsonProperty("instruction")]
public string Instruction { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 41f778a9c28ef4b17ace4d80f89dbc7a
timeCreated: 1493218361
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,61 @@
//-----------------------------------------------------------------------
// <copyright file="Route.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Directions
{
using System.Collections.Generic;
using Mapbox.Json;
using Mapbox.Utils;
using Mapbox.Utils.JsonConverters;
/// <summary>
/// A Route from a Directions API call.
/// </summary>
public class Route
{
/// <summary>
/// Gets or sets the legs.
/// </summary>
/// <value>The legs.</value>
[JsonProperty("legs")]
public List<Leg> Legs { get; set; }
/// <summary>
/// Gets or sets the geometry. Polyline is an array of LatLng's.
/// </summary>
/// <value>The geometry.</value>
[JsonProperty("geometry")]
[JsonConverter(typeof(PolylineToVector2dListConverter))]
public List<Vector2d> Geometry { get; set; }
/// <summary>
/// Gets or sets the duration.
/// </summary>
/// <value>The duration.</value>
[JsonProperty("duration")]
public double Duration { get; set; }
/// <summary>
/// Gets or sets the distance.
/// </summary>
/// <value>The distance.</value>
[JsonProperty("distance")]
public double Distance { get; set; }
/// <summary>
/// Float indicating the weight in units described by 'weight_name'.
/// </summary>
[JsonProperty("weight")]
public float Weight { get; set; }
/// <summary>
/// String indicating which weight was used. The default is routability which is duration based, with additional penalties for less desirable maneuvers.
/// </summary>
[JsonProperty("weight_name")]
public string WeightName { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 67e17dbf44b85417d90ee1b71d9749f3
timeCreated: 1493218361
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,69 @@
//-----------------------------------------------------------------------
// <copyright file="Step.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Directions
{
using System.Collections.Generic;
using Mapbox.Json;
using Mapbox.Utils;
using Mapbox.Utils.JsonConverters;
/// <summary>
/// A step from a Directions API call.
/// </summary>
public class Step
{
/// <summary>
/// Gets or sets the intersections.
/// </summary>
/// <value>The intersections.</value>
[JsonProperty("intersections")]
public List<Intersection> Intersections { get; set; }
/// <summary>
/// Gets or sets the geometry.
/// </summary>
/// <value>The geometry.</value>
[JsonProperty("geometry")]
[JsonConverter(typeof(PolylineToVector2dListConverter))]
public List<Vector2d> Geometry { get; set; }
/// <summary>
/// Gets or sets the maneuver.
/// </summary>
/// <value>The maneuver.</value>
[JsonProperty("maneuver")]
public Maneuver Maneuver { get; set; }
/// <summary>
/// Gets or sets the duration.
/// </summary>
/// <value>The duration.</value>
[JsonProperty("duration")]
public double Duration { get; set; }
/// <summary>
/// Gets or sets the distance.
/// </summary>
/// <value>The distance.</value>
[JsonProperty("distance")]
public double Distance { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the mode.
/// </summary>
/// <value>The mode.</value>
[JsonProperty("mode")]
public string Mode { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cb67b81b6de1f41fca077297633b5a7d
timeCreated: 1493218361
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
//-----------------------------------------------------------------------
// <copyright file="Waypoint.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Directions
{
using Mapbox.Json;
using Mapbox.Utils;
using Mapbox.Utils.JsonConverters;
/// <summary>
/// A Waypoint from a Directions API call.
/// </summary>
public class Waypoint
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the location.
/// </summary>
/// <value>The location.</value>
[JsonProperty("location")]
[JsonConverter(typeof(LonLatToVector2dConverter))]
public Vector2d Location { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 813ff4647909949fdbd67f232221cab3
timeCreated: 1493218361
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
//-----------------------------------------------------------------------
// <copyright file="RoutingProfile.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Directions
{
/// <summary>
/// Routing profile, affects how the route is calculated, prioritizing routes that fit
/// the profile the best.
/// </summary>
public sealed class RoutingProfile
{
/// <summary> The driving profile. </summary>
public static readonly RoutingProfile Driving = new RoutingProfile("mapbox/driving/");
/// <summary> The walking profile. </summary>
public static readonly RoutingProfile Walking = new RoutingProfile("mapbox/walking/");
/// <summary> The cycling profile. </summary>
public static readonly RoutingProfile Cycling = new RoutingProfile("mapbox/cycling/");
private readonly string profile;
private RoutingProfile(string profile)
{
this.profile = profile;
}
/// <summary> Converts the profile to a URL snippet. </summary>
/// <returns> A string to be appened to the direction query URL. </returns>
public override string ToString()
{
return this.profile;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 02efec76b920540358fd490e1c105268
timeCreated: 1491243033
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: d1dc4d11759e044eabaa4d5496a96275
folderAsset: yes
timeCreated: 1491243031
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,202 @@
//-----------------------------------------------------------------------
// <copyright file="ForwardGeocodeResource.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Geocoding
{
using System;
using System.Collections.Generic;
using Mapbox.Utils;
using UnityEngine;
/// <summary> A forward geocode request. </summary>
public sealed class ForwardGeocodeResource : GeocodeResource<string>
{
/// <summary>
/// ISO 3166-1 alpha-2 country codes.
/// See <see href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">for all options</see>.
/// </summary>
private static readonly List<string> CountryCodes = new List<string>()
{
"ad", "ae", "af", "ag", "ai", "al", "am", "ao", "aq", "ar", "as", "at", "au", "aw", "ax", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi", "bj", "bl", "bm", "bn", "bo", "bq", "br", "bs", "bt", "bv", "bw", "by", "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr", "cu", "cv", "cw", "cx", "cy", "cz", "de", "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er", "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gb", "gd", "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq", "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr", "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir", "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc", "md", "me", "mf", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq", "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na", "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu", "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm", "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "rs", "ru", "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "ss", "st", "sv", "sx", "sy", "sz", "tc", "td", "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", "to", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "um", "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu", "wf", "ws", "ye", "yt", "za", "zm", "zw"
};
// Required
private string query;
// Optional
private bool? autocomplete;
// Optional
private string[] country;
// Optional
private Vector2d? proximity;
// Optional
private Vector2dBounds? bbox;
/// <summary> Initializes a new instance of the <see cref="ForwardGeocodeResource" /> class.</summary>
/// <param name="query"> Place name for forward geocoding. </param>
public ForwardGeocodeResource(string query)
{
this.Query = query;
}
/// <summary> Gets or sets the place name for forward geocoding. </summary>
public override string Query
{
get
{
return this.query;
}
set
{
this.query = value;
}
}
/// <summary> Gets or sets the autocomplete option. </summary>
public bool? Autocomplete
{
get
{
return this.autocomplete;
}
set
{
this.autocomplete = value;
}
}
/// <summary>
/// Gets or sets the bounding box option. Bounding box is a rectangle within which to
/// limit results, given as <see cref="Bbox"/>.
/// </summary>
public Vector2dBounds? Bbox
{
get
{
return this.bbox;
}
set
{
this.bbox = value;
}
}
/// <summary>
/// Gets or sets the country option. Country is an Array of ISO 3166 alpha 2 country codes.
/// For all possible values, <see cref="CountryCodes"/>.
/// </summary>
public string[] Country
{
get
{
return this.country;
}
set
{
if (value == null)
{
this.country = value;
return;
}
for (int i = 0; i < value.Length; i++)
{
// Validate that provided countries exist
if (!CountryCodes.Contains(value[i]))
{
throw new Exception("Invalid country shortcode. See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2.");
}
}
this.country = value;
}
}
/// <summary>
/// Gets or sets the proximity option, which is a location around which to bias results,
/// given as <see cref="Vector2d"/>.
/// </summary>
public Vector2d? Proximity
{
get
{
return this.proximity;
}
set
{
this.proximity = value;
}
}
/// <summary> Builds a forward geocode URL string. </summary>
/// <returns> A complete, valid forward geocode URL. </returns>
public override string GetUrl()
{
Dictionary<string, string> opts = new Dictionary<string, string>();
if (this.Autocomplete != null)
{
opts.Add("autocomplete", this.Autocomplete.ToString().ToLower());
}
if (this.Bbox != null)
{
var nonNullableBbox = (Vector2dBounds)this.Bbox;
opts.Add("bbox", nonNullableBbox.ToString());
}
if (this.Country != null)
{
opts.Add("country", ForwardGeocodeResource.GetUrlQueryFromArray<string>(this.Country));
}
if (this.Proximity != null)
{
var nonNullableProx = (Vector2d)this.Proximity;
opts.Add("proximity", nonNullableProx.ToString());
}
if (this.Types != null)
{
opts.Add("types", GetUrlQueryFromArray(this.Types));
}
// !!!!!!!!!! HACK !!!!!!!
// we are seeing super weird behaviour on some iOS devices:
// crashes with properly escaped whitespaces %20 and commas %2C - and other special characters
// 'NSAllowsArbitraryLoads' and 'NSURLConnection finished with error - code - 1002'
// Use 'CFNETWORK_DIAGNOSTICS=1' in XCode to get more details https://stackoverflow.com/a/46748461
// trying to get rid of at least the most common characters - other will still crash
#if UNITY_IOS
Query = Query
.Replace(",", " ")
.Replace(".", " ")
.Replace("-", " ");
#endif
return
Constants.BaseAPI +
ApiEndpoint +
Mode +
#if UNITY_IOS
WWW.EscapeURL(Query) +
#else
Uri.EscapeDataString(Query) +
#endif
".json" +
EncodeQueryString(opts);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 33cb5b2bc4f6f4397a21fdd87dfa0048
timeCreated: 1493833265
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,73 @@
//-----------------------------------------------------------------------
// <copyright file="GeocodeResource.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Geocoding
{
using System;
using System.Collections.Generic;
using Mapbox.Platform;
/// <summary> Base geocode class. </summary>
/// <typeparam name="T"> Type of Query field (either string or LatLng). </typeparam>
public abstract class GeocodeResource<T> : Resource
{
/// <summary> A List of all possible geocoding feature types. </summary>
public static readonly List<string> FeatureTypes = new List<string>
{
"country", "region", "postcode", "place", "locality", "neighborhood", "address", "poi"
};
private readonly string apiEndpoint = "geocoding/v5/";
private readonly string mode = "mapbox.places/";
// Optional
private string[] types;
/// <summary> Gets or sets the query. </summary>
public abstract T Query { get; set; }
/// <summary> Gets the API endpoint as a partial URL path. </summary>
public override string ApiEndpoint {
get {
return this.apiEndpoint;
}
}
/// <summary> Gets the mode. </summary>
public string Mode {
get {
return this.mode;
}
}
/// <summary> Gets or sets which feature types to return results for. </summary>
public string[] Types {
get {
return this.types;
}
set {
if (value == null)
{
this.types = value;
return;
}
for (int i = 0; i < value.Length; i++)
{
// Validate provided types
if (!FeatureTypes.Contains(value[i]))
{
throw new Exception("Invalid type. Must be \"country\", \"region\", \"postcode\", \"place\", \"locality\", \"neighborhood\", \"address\", or \"poi\".");
}
}
this.types = value;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: aba88323b8d91474fb3b991eb0bd9349
timeCreated: 1491243034
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,87 @@
//-----------------------------------------------------------------------
// <copyright file="Geocoder.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Geocoding
{
using System;
using System.Text;
using Mapbox.Json;
using Mapbox.Platform;
using Mapbox.Utils.JsonConverters;
/// <summary>
/// Wrapper around the <see href="https://www.mapbox.com/api-documentation/#geocoding">
/// Mapbox Geocoding API</see>. The Geocoder does two things: geocoding and reverse geocoding.
/// </summary>
public sealed class Geocoder
{
private readonly IFileSource fileSource;
/// <summary> Initializes a new instance of the <see cref="Geocoder" /> class. </summary>
/// <param name="fileSource"> Network access abstraction. </param>
public Geocoder(IFileSource fileSource)
{
this.fileSource = fileSource;
}
/// <summary> Performs asynchronously a geocoding lookup. </summary>
/// <param name="geocode"> Geocode resource. </param>
/// <param name="callback"> Callback to be called after the request is completed. </param>
/// <typeparam name="T"> String or LngLat. Should be automatically inferred. </typeparam>
/// <returns>
/// Returns a <see cref="IAsyncRequest" /> 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.
/// </returns>
public IAsyncRequest Geocode<T>(GeocodeResource<T> geocode, Action<ReverseGeocodeResponse> callback)
{
return this.fileSource.Request(
geocode.GetUrl(),
(Response response) =>
{
var str = Encoding.UTF8.GetString(response.Data);
var data = Deserialize<ReverseGeocodeResponse>(str);
callback(data);
});
}
/// <summary> Performs asynchronously a geocoding lookup. </summary>
/// <param name="geocode"> Geocode resource. </param>
/// <param name="callback"> Callback to be called after the request is completed. </param>
/// <typeparam name="T"> String or LngLat. Should be automatically inferred. </typeparam>
/// <returns>
/// Returns a <see cref="IAsyncRequest" /> 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.
/// </returns>
public IAsyncRequest Geocode<T>(GeocodeResource<T> geocode, Action<ForwardGeocodeResponse> callback)
{
return this.fileSource.Request(
geocode.GetUrl(),
(Response response) =>
{
var str = Encoding.UTF8.GetString(response.Data);
var data = Deserialize<ForwardGeocodeResponse>(str);
callback(data);
});
}
/// <summary>
/// Deserialize the geocode response string into a <see cref="GeocodeResponse"/>.
/// </summary>
/// <param name="str">JSON String.</param>
/// <returns>A <see cref="GeocodeResponse"/>.</returns>
/// <typeparam name="T">Forward or reverse geocode. </typeparam>
public T Deserialize<T>(string str)
{
return JsonConvert.DeserializeObject<T>(str, JsonConverters.Converters);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d304b174589dd496993060116409b23c
timeCreated: 1491336250
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 5ef566ef707b848c4bac9b1f13fccf17
folderAsset: yes
timeCreated: 1491243031
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,96 @@
//-----------------------------------------------------------------------
// <copyright file="Feature.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Geocoding {
using System;
using System.Collections.Generic;
using Mapbox.Json;
using Mapbox.Utils;
using Mapbox.Utils.JsonConverters;
/// <summary> A GeoJSON FeatureCollection of points returned by geocoding API.</summary>
#if !WINDOWS_UWP
//http://stackoverflow.com/a/12903628
[Serializable]
#endif
public class Feature {
/// <summary> Gets or sets the id. Ids are unique in the Mapbox geocoder. </summary>
/// <value>The id.</value>
[JsonProperty("id")]
public string Id { get; set; }
/// <summary>
/// Gets or sets feature type. One of country, region, postcode, place, locality, neighborhood, address, poi.
/// </summary>
/// <value>The type.</value>
[JsonProperty("type")]
public string Type { get; set; }
/// <summary>
/// Gets or sets the text.
/// </summary>
/// <value>The text.</value>
[JsonProperty("text")]
public string Text { get; set; }
/// <summary>
/// Gets or sets the name of the place.
/// </summary>
/// <value>The name of the place.</value>
[JsonProperty("place_name")]
public string PlaceName { get; set; }
/// <summary>
/// Gets or sets the relevance.
/// </summary>
/// <value>The relevance.</value>
[JsonProperty("relevance")]
public double Relevance { get; set; }
/// <summary>
/// Gets or sets the properties.
/// </summary>
/// <value>The properties.</value>
[JsonProperty("properties")]
public Dictionary<string, object> Properties { get; set; }
/// <summary>
/// Gets or sets the bbox.
/// </summary>
/// <value>The bbox.</value>
[JsonProperty("bbox", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(BboxToVector2dBoundsConverter))]
public Vector2dBounds? Bbox { get; set; }
/// <summary>
/// Gets or sets the center.
/// </summary>
/// <value>The center.</value>
[JsonProperty("center")]
[JsonConverter(typeof(LonLatToVector2dConverter))]
public Vector2d Center { get; set; }
/// <summary>
/// Gets or sets the geometry.
/// </summary>
/// <value>The geometry.</value>
[JsonProperty("geometry")]
public Geometry Geometry { get; set; }
/// <summary>
/// Gets or sets the address.
/// </summary>
[JsonProperty("address", NullValueHandling = NullValueHandling.Ignore)]
public string Address { get; set; }
/// <summary>
/// Gets or sets the context.
/// </summary>
/// <value>The context.</value>
[JsonProperty("context", NullValueHandling = NullValueHandling.Ignore)]
public List<Dictionary<string, string>> Context { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f0645486f47864cd390751dd94d0302f
timeCreated: 1493218361
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,71 @@
//-----------------------------------------------------------------------
// <copyright file="GeocodeResponse.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Geocoding {
using System;
using System.Collections.Generic;
using Mapbox.Json;
/// <summary> Base geocode response. </summary>
#if !WINDOWS_UWP
//http://stackoverflow.com/a/12903628
[Serializable]
#endif
public abstract class GeocodeResponse {
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
[JsonProperty("type", Order = 0)]
public string Type { get; set; }
/// <summary>
/// Gets or sets the features.
/// </summary>
/// <value>The features.</value>
[JsonProperty("features", Order = 2)]
public List<Feature> Features { get; set; }
/// <summary>
/// Gets or sets the attribution.
/// </summary>
/// <value>The attribution.</value>
[JsonProperty("attribution", Order = 3)]
public string Attribution { get; set; }
}
/// <summary>
/// Reverse Geocode response.
/// </summary>
#if !WINDOWS_UWP
//http://stackoverflow.com/a/12903628
[Serializable]
#endif
public class ReverseGeocodeResponse : GeocodeResponse {
/// <summary>
/// Gets or sets the query.
/// </summary>
/// <value>The query.</value>
[JsonProperty("query", Order = 1)]
public List<double> Query { get; set; }
}
/// <summary>
/// Forward geocode response.
/// </summary>
#if !WINDOWS_UWP
//http://stackoverflow.com/a/12903628
[Serializable]
#endif
public class ForwardGeocodeResponse : GeocodeResponse {
/// <summary>
/// Gets or sets the query.
/// </summary>
/// <value>The query.</value>
[JsonProperty("query", Order = 1)]
public List<string> Query { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f1cccf508c40144308be9518f5085a69
timeCreated: 1491243035
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
//-----------------------------------------------------------------------
// <copyright file="Geometry.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Geocoding {
using System;
using Mapbox.Json;
using Mapbox.Utils;
using Mapbox.Utils.JsonConverters;
/// <summary> Point geometry representing location of geocode result. </summary>
#if !WINDOWS_UWP
//http://stackoverflow.com/a/12903628
[Serializable]
#endif
public class Geometry {
/// <summary>
/// Gets or sets type. Geocode results will always be type: point.
/// </summary>
/// <value>The GeoJSON geometry type.</value>
[JsonProperty("type")]
public string Type { get; set; }
/// <summary>
/// Gets or sets coordinates. Because they are points, Geocode results will always be a single Geocoordinate.
/// </summary>
/// <value>The coordinates.</value>
[JsonConverter(typeof(LonLatToVector2dConverter))]
[JsonProperty("coordinates")]
public Vector2d Coordinates { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b599e78a14b3d42c8822cec5d5775397
timeCreated: 1493218361
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,55 @@
//-----------------------------------------------------------------------
// <copyright file="ReverseGeocodeResource.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Geocoding
{
using System.Collections.Generic;
using Mapbox.Utils;
/// <summary> A reverse geocode request. </summary>
public sealed class ReverseGeocodeResource : GeocodeResource<Vector2d>
{
// Required
private Vector2d query;
/// <summary> Initializes a new instance of the <see cref="ReverseGeocodeResource" /> class.</summary>
/// <param name="query"> Location to reverse geocode. </param>
public ReverseGeocodeResource(Vector2d query)
{
this.Query = query;
}
/// <summary> Gets or sets the location. </summary>
public override Vector2d Query {
get {
return this.query;
}
set {
this.query = value;
}
}
/// <summary> Builds a complete reverse geocode URL string. </summary>
/// <returns> A complete, valid reverse geocode URL string. </returns>
public override string GetUrl()
{
Dictionary<string, string> opts = new Dictionary<string, string>();
if (this.Types != null)
{
opts.Add("types", GetUrlQueryFromArray(this.Types));
}
return Constants.BaseAPI +
this.ApiEndpoint +
this.Mode +
this.Query.ToString() +
".json" +
EncodeQueryString(opts);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 23c6d0bd3662648d3859495c35635b64
timeCreated: 1493833265
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 67003c49d480847189c0d8e81c14da83
folderAsset: yes
timeCreated: 1491243031
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,117 @@
//-----------------------------------------------------------------------
// <copyright file="CanonicalTileId.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Map
{
using System;
using Mapbox.Utils;
/// <summary>
/// Data type to store <see href="https://en.wikipedia.org/wiki/Web_Mercator"> Web Mercator</see> tile scheme.
/// <see href="http://www.maptiler.org/google-maps-coordinates-tile-bounds-projection/"> See tile IDs in action. </see>
/// </summary>
public struct CanonicalTileId : IEquatable<CanonicalTileId>
{
/// <summary> The zoom level. </summary>
public readonly int Z;
/// <summary> The X coordinate in the tile grid. </summary>
public readonly int X;
/// <summary> The Y coordinate in the tile grid. </summary>
public readonly int Y;
/// <summary>
/// Initializes a new instance of the <see cref="CanonicalTileId"/> struct,
/// representing a tile coordinate in a slippy map.
/// </summary>
/// <param name="z"> The z coordinate or the zoom level. </param>
/// <param name="x"> The x coordinate. </param>
/// <param name="y"> The y coordinate. </param>
public CanonicalTileId(int z, int x, int y)
{
this.Z = z;
this.X = x;
this.Y = y;
}
internal CanonicalTileId(UnwrappedTileId unwrapped)
{
var z = unwrapped.Z;
var x = unwrapped.X;
var y = unwrapped.Y;
var wrap = (x < 0 ? x - (1 << z) + 1 : x) / (1 << z);
this.Z = z;
this.X = x - wrap * (1 << z);
this.Y = y < 0 ? 0 : Math.Min(y, (1 << z) - 1);
}
/// <summary>
/// Get the cordinate at the top left of corner of the tile.
/// </summary>
/// <returns> The coordinate. </returns>
public Vector2d ToVector2d()
{
double n = Math.PI - ((2.0 * Math.PI * this.Y) / Math.Pow(2.0, this.Z));
double lat = 180.0 / Math.PI * Math.Atan(Math.Sinh(n));
double lng = (this.X / Math.Pow(2.0, this.Z) * 360.0) - 180.0;
// FIXME: Super hack because of rounding issues.
return new Vector2d(lat - 0.0001, lng + 0.0001);
}
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current
/// <see cref="T:Mapbox.Map.CanonicalTileId"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current
/// <see cref="T:Mapbox.Map.CanonicalTileId"/>.
/// </returns>
public override string ToString()
{
return this.Z + "/" + this.X + "/" + this.Y;
}
#region Equality
public bool Equals(CanonicalTileId other)
{
return this.X == other.X && this.Y == other.Y && this.Z == other.Z;
}
public override int GetHashCode()
{
return X ^ Y ^ Z;
}
public static bool operator ==(CanonicalTileId a, CanonicalTileId b)
{
return a.X == b.X && a.Y == b.Y && a.Z == b.Z;
}
public static bool operator !=(CanonicalTileId a, CanonicalTileId b)
{
return !(a == b);
}
public override bool Equals(object obj)
{
if (obj is CanonicalTileId)
{
return this.Equals((CanonicalTileId)obj);
}
else
{
return false;
}
}
#endregion
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b032ac6adcdd94a899ef544e06b9e828
timeCreated: 1493952209
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
//-----------------------------------------------------------------------
// <copyright file="ClassicRasterTile.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Map
{
/// <summary>
/// A raster tile from the Mapbox Map API, a encoded image representing a geographic
/// bounding box. Usually JPEG or PNG encoded.
/// See <see cref="T:Mapbox.Map.RasterTile"/> for usage.
/// Read more about <see href="https://www.mapbox.com/api-documentation/pages/static_classic.html"> static classic maps </see>.
/// </summary>
public class ClassicRasterTile : RasterTile
{
internal override TileResource MakeTileResource(string mapId)
{
return TileResource.MakeClassicRaster(Id, mapId);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 26ff02ff702b3417b9d4514af452c64f
timeCreated: 1494603018
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
//-----------------------------------------------------------------------
// <copyright file="ClassicRetinaRasterTile.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Map
{
/// <summary>
/// A retina-resolution raster tile from the Mapbox Map API, a encoded image representing a geographic
/// bounding box. Usually JPEG or PNG encoded.
/// Like <see cref="T:Mapbox.Map.ClassicRasterTile"/>, but higher resolution.
/// See <see href="https://www.mapbox.com/api-documentation/#retina"> retina documentation </see>.
/// </summary>
public class ClassicRetinaRasterTile : ClassicRasterTile
{
internal override TileResource MakeTileResource(string mapId)
{
return TileResource.MakeClassicRetinaRaster(Id, mapId);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 83237273afa4e4a8a9a0600d8c694e38
timeCreated: 1494603018
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 24bb3ef727dad4e1385a77beef324ff0
timeCreated: 1494441866
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
//-----------------------------------------------------------------------
// <copyright file="MapUtils.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Map
{
using System;
using Mapbox.Utils;
/// <summary>
/// Utilities for working with Map APIs.
/// </summary>
public static class MapUtils
{
/// <summary>
/// Normalizes a static style URL.
/// </summary>
/// <returns>The static style URL.</returns>
/// <param name="url">A url, either a Mapbox URI (mapbox://{username}/{styleid}) or a full url to a map.</param>
public static string NormalizeStaticStyleURL(string url)
{
bool isMapboxUrl = url.StartsWith("mapbox://", StringComparison.Ordinal);
// Support full Mapbox URLs by returning here if a mapbox URL is not detected.
if (!isMapboxUrl)
{
return url;
}
string[] split = url.Split('/');
var user = split[3];
var style = split[4];
var draft = string.Empty;
if (split.Length > 5)
{
draft = "/draft";
}
return Constants.BaseAPI + "styles/v1/" + user + "/" + style + draft + "/tiles";
}
/// <summary>
/// Converts a MapId to a URL.
/// </summary>
/// <returns>The identifier to URL.</returns>
/// <param name="id">The style id.</param>
public static string MapIdToUrl(string id)
{
// TODO: Validate that id is a real id
const string MapBaseApi = Constants.BaseAPI + "v4/";
return MapBaseApi + id;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a237cc1b965e34670a7c74ecf2bdbee3
timeCreated: 1491243034
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,69 @@
//-----------------------------------------------------------------------
// <copyright file="RasterTile.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Map
{
/// <summary>
/// A raster tile from the Mapbox Style API, an encoded image representing a geographic
/// bounding box. Usually JPEG or PNG encoded.
/// </summary>
/// <example>
/// Making a RasterTile request:
/// <code>
/// var parameters = new Tile.Parameters();
/// parameters.Fs = MapboxAccess.Instance;
/// parameters.Id = new CanonicalTileId(_zoom, _tileCoorindateX, _tileCoordinateY);
/// parameters.MapId = "mapbox://styles/mapbox/satellite-v9";
/// var rasterTile = new RasterTile();
///
/// // Make the request.
/// rasterTile.Initialize(parameters, (Action)(() =>
/// {
/// if (!string.IsNullOrEmpty(rasterTile.Error))
/// {
/// // Handle the error.
/// }
///
/// // Consume the <see cref="Data"/>.
/// }));
/// </code>
/// </example>
public class RasterTile : Tile
{
private byte[] data;
/// <summary> Gets the raster tile raw data. </summary>
/// <value> The raw data, usually an encoded JPEG or PNG. </value>
/// <example>
/// Consuming data in Unity to create a Texture2D:
/// <code>
/// var texture = new Texture2D(0, 0);
/// texture.LoadImage(rasterTile.Data);
/// _sampleMaterial.mainTexture = texture;
/// </code>
/// </example>
public byte[] Data
{
get
{
return this.data;
}
}
internal override TileResource MakeTileResource(string styleUrl)
{
return TileResource.MakeRaster(Id, styleUrl);
}
internal override bool ParseTileData(byte[] data)
{
// We do not parse raster tiles as they are
this.data = data;
return true;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 01d65e8417e21498f88939119e4fe3ab
timeCreated: 1493756686
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
//-----------------------------------------------------------------------
// <copyright file="RawPngRasterTile.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Map
{
/// <summary>
/// A raster tile containing an encoded RGBA PNG.
/// <see href="https://www.mapbox.com/blog/terrain-rgb/"> Read about global elevation data. </see>
/// </summary>
/// <example>
/// Print the real world height, in meters, for each pixel:
/// <code>
/// var texture = new Texture2D(0, 0);
/// texture.LoadImage(tile.Data);
/// for (int i = 0; i &lt; texture.width; i++)
/// {
/// for (int j = 0; j &lt; texture.height; j++)
/// {
/// var color = texture.GetPixel(i, j);
/// var height = Conversions.GetAbsoluteHeightFromColor(color);
/// Console.Write("Height: " + height);
/// }
/// }
/// </code>
/// </example>
public sealed class RawPngRasterTile : RasterTile
{
internal override TileResource MakeTileResource(string mapId)
{
return TileResource.MakeRawPngRaster(Id, mapId);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ab4a5e34c15df42b3b4d5bf5c9b6b9b1
timeCreated: 1493952209
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
//-----------------------------------------------------------------------
// <copyright file="RetinaClassicRasterTile.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Map
{
/// <summary>
/// A retin-resolution raster tile from the Mapbox Style API, an encoded image representing a geographic
/// bounding box. Usually JPEG or PNG encoded.
/// Like <see cref="T:Mapbox.Map.RasterTile"/>, but higher resolution.
/// See <see href="https://www.mapbox.com/api-documentation/#retina"> retina documentation </see>.
/// </summary>
public class RetinaRasterTile : RasterTile
{
internal override TileResource MakeTileResource(string mapId)
{
return TileResource.MakeRetinaRaster(Id, mapId);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9cd3514d26c4b4c7685f804af02d388b
timeCreated: 1494603018
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8010c0fffeba24190a0e4a5a5f4317c6
timeCreated: 1494951007
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,174 @@
//-----------------------------------------------------------------------
// <copyright file="TileCover.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Map
{
using System;
using System.Collections.Generic;
using Mapbox.Utils;
using UnityEngine;
/// <summary>
/// Helper funtions to get a tile cover, i.e. a set of tiles needed for
/// covering a bounding box.
/// </summary>
public static class TileCover
{
/// <summary> Get a tile cover for the specified bounds and zoom. </summary>
/// <param name="bounds"> Geographic bounding box.</param>
/// <param name="zoom"> Zoom level. </param>
/// <returns> The tile cover set. </returns>
/// <example>
/// Build a map of Colorado using TileCover:
/// <code>
/// var sw = new Vector2d(36.997749, -109.0524961);
/// var ne = new Vector2d(41.0002612, -102.0609668);
/// var coloradoBounds = new Vector2dBounds(sw, ne);
/// var tileCover = TileCover.Get(coloradoBounds, 8);
/// Console.Write("Tiles Needed: " + tileCover.Count);
/// foreach (var id in tileCover)
/// {
/// var tile = new RasterTile();
/// var parameters = new Tile.Parameters();
/// parameters.Id = id;
/// parameters.Fs = MapboxAccess.Instance;
/// parameters.MapId = "mapbox://styles/mapbox/outdoors-v10";
/// tile.Initialize(parameters, (Action)(() =&gt;
/// {
/// // Place tiles and load textures.
/// }));
/// }
/// </code>
/// </example>
public static HashSet<CanonicalTileId> Get(Vector2dBounds bounds, int zoom)
{
var tiles = new HashSet<CanonicalTileId>();
if (bounds.IsEmpty() ||
bounds.South > Constants.LatitudeMax ||
bounds.North < -Constants.LatitudeMax)
{
return tiles;
}
var hull = Vector2dBounds.FromCoordinates(
new Vector2d(Math.Max(bounds.South, -Constants.LatitudeMax), bounds.West),
new Vector2d(Math.Min(bounds.North, Constants.LatitudeMax), bounds.East));
var sw = CoordinateToTileId(hull.SouthWest, zoom);
var ne = CoordinateToTileId(hull.NorthEast, zoom);
// Scanlines.
for (var x = sw.X; x <= ne.X; ++x)
{
for (var y = ne.Y; y <= sw.Y; ++y)
{
tiles.Add(new UnwrappedTileId(zoom, x, y).Canonical);
}
}
return tiles;
}
public static HashSet<UnwrappedTileId> GetWithWebMerc(Vector2dBounds bounds, int zoom)
{
HashSet<UnwrappedTileId> tiles = new HashSet<UnwrappedTileId>();
HashSet<CanonicalTileId> canonicalTiles = new HashSet<CanonicalTileId>();
if (bounds.IsEmpty()) { return tiles; }
//stay within WebMerc bounds
Vector2d swWebMerc = new Vector2d(Math.Max(bounds.SouthWest.x, -Constants.WebMercMax), Math.Max(bounds.SouthWest.y, -Constants.WebMercMax));
Vector2d neWebMerc = new Vector2d(Math.Min(bounds.NorthEast.x, Constants.WebMercMax), Math.Min(bounds.NorthEast.y, Constants.WebMercMax));
//UnityEngine.Debug.LogFormat("swWebMerc:{0}/{1} neWebMerc:{2}/{3}", swWebMerc.x, swWebMerc.y, neWebMerc.x, neWebMerc.y);
UnwrappedTileId swTile = WebMercatorToTileId(swWebMerc, zoom);
UnwrappedTileId neTile = WebMercatorToTileId(neWebMerc, zoom);
//UnityEngine.Debug.LogFormat("swTile:{0} neTile:{1}", swTile, neTile);
for (int x = swTile.X; x <= neTile.X; x++)
{
for (int y = neTile.Y; y <= swTile.Y; y++)
{
UnwrappedTileId uwtid = new UnwrappedTileId(zoom, x, y);
//hack: currently too many tiles are created at lower zoom levels
//investigate formulas, this worked before
if (!canonicalTiles.Contains(uwtid.Canonical))
{
//Debug.LogFormat("TileCover.GetWithWebMerc: {0}/{1}/{2}", zoom, x, y);
tiles.Add(uwtid);
canonicalTiles.Add(uwtid.Canonical);
}
}
}
return tiles;
}
/// <summary> Converts a coordinate to a tile identifier. </summary>
/// <param name="coord"> Geographic coordinate. </param>
/// <param name="zoom"> Zoom level. </param>
/// <returns>The to tile identifier.</returns>
/// <example>
/// Convert a geocoordinate to a TileId:
/// <code>
/// var unwrappedTileId = TileCover.CoordinateToTileId(new Vector2d(40.015, -105.2705), 18);
/// Console.Write("UnwrappedTileId: " + unwrappedTileId.ToString());
/// </code>
/// </example>
public static UnwrappedTileId CoordinateToTileId(Vector2d coord, int zoom)
{
var lat = coord.x;
var lng = coord.y;
// See: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
var x = (int)Math.Floor((lng + 180.0) / 360.0 * Math.Pow(2.0, zoom));
var y = (int)Math.Floor((1.0 - Math.Log(Math.Tan(lat * Math.PI / 180.0)
+ 1.0 / Math.Cos(lat * Math.PI / 180.0)) / Math.PI) / 2.0 * Math.Pow(2.0, zoom));
return new UnwrappedTileId(zoom, x, y);
}
/// <summary>
/// Converts a Web Mercator coordinate to a tile identifier. https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Derivation_of_tile_names
/// </summary>
/// <param name="webMerc">Web Mercator coordinate</param>
/// <param name="zoom">Zoom level</param>
/// <returns>The to tile identifier.</returns>
public static UnwrappedTileId WebMercatorToTileId(Vector2d webMerc, int zoom)
{
// See: https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Derivation_of_tile_names
double tileCount = Math.Pow(2, zoom);
//this SDK defines Vector2d.x as latitude and Vector2d.y as longitude
//same for WebMerc, so we have to flip x/y to make this formula work
double dblX = webMerc.x / Constants.WebMercMax;
double dblY = webMerc.y / Constants.WebMercMax;
//dblX = 1 + dblX;
//dblY = 1 - dblY;
//dblX /= 2;
//dblY /= 2;
//dblX *= tileCount;
//dblY *= tileCount;
//int x = (int)Math.Floor(dblX);
//int y = (int)Math.Floor(dblY);
//return new UnwrappedTileId(zoom, x, y);
int x = (int)Math.Floor((1 + dblX) / 2 * tileCount);
int y = (int)Math.Floor((1 - dblY) / 2 * tileCount);
return new UnwrappedTileId(zoom, x, y);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: fc67290bfffab4457b83802ef2f4c824
timeCreated: 1494945808
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
namespace Mapbox.Map
{
using System;
using Mapbox.Unity.MeshGeneration.Data;
using System.Collections.Generic;
using System.Collections.ObjectModel;
public class TileErrorEventArgs : EventArgs
{
/// <summary>
/// The tile identifier.
/// </summary>
public CanonicalTileId TileId;
/// <summary>
/// The exceptions.
/// </summary>
public List<Exception> Exceptions;
/// <summary>
/// The unity tile instance.
/// </summary>
public UnityTile UnityTileInstance;
/// <summary>
/// The type of the tile.
/// </summary>
public Type TileType;
/// <summary>
/// Initializes a new instance of the <see cref="T:Mapbox.Map.TileErrorEventArgs"/> class.
/// </summary>
/// <param name="TileId">Tile identifier.</param>
/// <param name="TileType">Tile type.</param>
/// <param name="UnityTileInstance">Unity tile instance.</param>
/// <param name="Exceptions">Exceptions as a List</param>
public TileErrorEventArgs(CanonicalTileId TileId, Type TileType, UnityTile UnityTileInstance, List<System.Exception> Exceptions)
{
this.TileId = TileId;
this.Exceptions = Exceptions;
this.UnityTileInstance = UnityTileInstance;
this.TileType = TileType;
}
/// <summary>
/// Initializes a new instance of the <see cref="T:Mapbox.Map.TileErrorEventArgs"/> class.
/// </summary>
/// <param name="TileId">Tile identifier.</param>
/// <param name="TileType">Tile type.</param>
/// <param name="UnityTileInstance">Unity tile instance.</param>
/// <param name="Exceptions">Exceptions as a ReadOnlyCollection</param>
public TileErrorEventArgs(CanonicalTileId TileId, Type TileType, UnityTile UnityTileInstance, ReadOnlyCollection<Exception> Exceptions)
{
this.TileId = TileId;
List<Exception> _exceptions = new List<Exception>();
foreach (var exception in Exceptions)
{
_exceptions.Add(exception);
}
this.Exceptions = _exceptions;
this.UnityTileInstance = UnityTileInstance;
this.TileType = TileType;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 59c42a951767c4031966d5ae5c20a47a
timeCreated: 1509068021
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,72 @@
//-----------------------------------------------------------------------
// <copyright file="TileResource.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Map
{
using Platform;
using System;
using Mapbox.Unity.Telemetry;
public sealed class TileResource : IResource
{
readonly string _query;
internal TileResource(string query)
{
_query = query;
}
public static TileResource MakeRaster(CanonicalTileId id, string styleUrl)
{
return new TileResource(string.Format("{0}/{1}", MapUtils.NormalizeStaticStyleURL(styleUrl ?? "mapbox://styles/mapbox/satellite-v9"), id));
}
internal static TileResource MakeRetinaRaster(CanonicalTileId id, string styleUrl)
{
return new TileResource(string.Format("{0}/{1}@2x", MapUtils.NormalizeStaticStyleURL(styleUrl ?? "mapbox://styles/mapbox/satellite-v9"), id));
}
public static TileResource MakeClassicRaster(CanonicalTileId id, string mapId)
{
return new TileResource(string.Format("{0}/{1}.png", MapUtils.MapIdToUrl(mapId ?? "mapbox.satellite"), id));
}
internal static TileResource MakeClassicRetinaRaster(CanonicalTileId id, string mapId)
{
return new TileResource(string.Format("{0}/{1}@2x.png", MapUtils.MapIdToUrl(mapId ?? "mapbox.satellite"), id));
}
public static TileResource MakeRawPngRaster(CanonicalTileId id, string mapId)
{
return new TileResource(string.Format("{0}/{1}.pngraw", MapUtils.MapIdToUrl(mapId ?? "mapbox.terrain-rgb"), id));
}
public static TileResource MakeVector(CanonicalTileId id, string mapId)
{
return new TileResource(string.Format("{0}/{1}.vector.pbf", MapUtils.MapIdToUrl(mapId ?? "mapbox.mapbox-streets-v7"), id));
}
internal static TileResource MakeStyleOptimizedVector(CanonicalTileId id, string mapId, string optimizedStyleId, string modifiedDate)
{
return new TileResource(string.Format("{0}/{1}.vector.pbf?style={2}@{3}", MapUtils.MapIdToUrl(mapId ?? "mapbox.mapbox-streets-v7"), id, optimizedStyleId, modifiedDate));
}
public string GetUrl()
{
var uriBuilder = new UriBuilder(_query);
if (uriBuilder.Query != null && uriBuilder.Query.Length > 1)
{
uriBuilder.Query = uriBuilder.Query.Substring(1) + "&" + TelemetryFactory.EventQuery;
}
else
{
uriBuilder.Query = TelemetryFactory.EventQuery;
}
//return uriBuilder.ToString();
return uriBuilder.Uri.ToString();
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b56f5c7f0b51844c19d1267530b5acb1
timeCreated: 1494603018
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,148 @@
using System;
namespace Mapbox.Map
{
/// <summary>
/// Unwrapped tile identifier in a slippy map. Similar to <see cref="CanonicalTileId"/>,
/// but might go around the globe.
/// </summary>
public struct UnwrappedTileId : IEquatable<UnwrappedTileId>
{
/// <summary> The zoom level. </summary>
public readonly int Z;
/// <summary> The X coordinate in the tile grid. </summary>
public readonly int X;
/// <summary> The Y coordinate in the tile grid. </summary>
public readonly int Y;
/// <summary>
/// Initializes a new instance of the <see cref="UnwrappedTileId"/> struct,
/// representing a tile coordinate in a slippy map that might go around the
/// globe.
/// </summary>
/// <param name="z">The z coordinate.</param>
/// <param name="x">The x coordinate.</param>
/// <param name="y">The y coordinate.</param>
public UnwrappedTileId(int z, int x, int y)
{
this.Z = z;
this.X = x;
this.Y = y;
}
/// <summary> Gets the canonical tile identifier. </summary>
/// <value> The canonical tile identifier. </value>
public CanonicalTileId Canonical
{
get
{
return new CanonicalTileId(this);
}
}
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current
/// <see cref="T:Mapbox.Map.UnwrappedTileId"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current
/// <see cref="T:Mapbox.Map.UnwrappedTileId"/>.
/// </returns>
public override string ToString()
{
return this.Z + "/" + this.X + "/" + this.Y;
}
public bool Equals(UnwrappedTileId other)
{
return this.X == other.X && this.Y == other.Y && this.Z == other.Z;
}
public override int GetHashCode()
{
return (X << 6) ^ (Y << 16) ^ (Z << 8);
//return X ^ Y ^ Z;
}
public override bool Equals(object obj)
{
return this.X == ((UnwrappedTileId)obj).X && this.Y == ((UnwrappedTileId)obj).Y && this.Z == ((UnwrappedTileId)obj).Z;
}
public static bool operator ==(UnwrappedTileId a, UnwrappedTileId b)
{
return a.X == b.X && a.Y == b.Y && a.Z == b.Z;
}
public static bool operator !=(UnwrappedTileId a, UnwrappedTileId b)
{
return !(a == b);
}
public UnwrappedTileId North
{
get
{
return new UnwrappedTileId(Z, X, Y - 1);
}
}
public UnwrappedTileId East
{
get
{
return new UnwrappedTileId(Z, X + 1, Y);
}
}
public UnwrappedTileId South
{
get
{
return new UnwrappedTileId(Z, X, Y + 1);
}
}
public UnwrappedTileId West
{
get
{
return new UnwrappedTileId(Z, X - 1, Y);
}
}
public UnwrappedTileId NorthEast
{
get
{
return new UnwrappedTileId(Z, X + 1, Y - 1);
}
}
public UnwrappedTileId SouthEast
{
get
{
return new UnwrappedTileId(Z, X + 1, Y + 1);
}
}
public UnwrappedTileId NorthWest
{
get
{
return new UnwrappedTileId(Z, X - 1, Y - 1);
}
}
public UnwrappedTileId SouthWest
{
get
{
return new UnwrappedTileId(Z, X - 1, Y + 1);
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b05b9f9c791be434db474b25089ff310
timeCreated: 1494945808
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,211 @@
//-----------------------------------------------------------------------
// <copyright file="VectorTile.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.Map
{
using System.Collections.ObjectModel;
using Mapbox.Utils;
using Mapbox.VectorTile;
using Mapbox.VectorTile.ExtensionMethods;
using System;
/// <summary>
/// A decoded vector tile, as specified by the
/// <see href="https://www.mapbox.com/vector-tiles/specification/">
/// Mapbox Vector Tile specification</see>.
/// See available layers and features <see href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/">here</see>.
/// The tile might be incomplete if the network request and parsing are still pending.
/// </summary>
/// <example>
/// Making a VectorTile request:
/// <code>
/// var parameters = new Tile.Parameters();
/// parameters.Fs = MapboxAccess.Instance;
/// parameters.Id = new CanonicalTileId(_zoom, _tileCoorindateX, _tileCoordinateY);
/// parameters.MapId = "mapbox.mapbox-streets-v7";
/// var vectorTile = new VectorTile();
///
/// // Make the request.
/// vectorTile.Initialize(parameters, (Action)(() =>
/// {
/// if (!string.IsNullOrEmpty(vectorTile.Error))
/// {
/// // Handle the error.
/// }
///
/// // Consume the <see cref="Data"/>.
/// }));
/// </code>
/// </example>
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;
bool _isStyleOptimized = false;
string _optimizedStyleId;
string _modifiedDate;
private bool isDisposed = false;
/// <summary> Gets the vector decoded using Mapbox.VectorTile library. </summary>
/// <value> The GeoJson data. </value>
public Mapbox.VectorTile.VectorTile Data
{
get
{
return this.data;
}
}
public VectorTile()
{
_isStyleOptimized = false;
}
public VectorTile(string styleId, string modifiedDate)
{
if (string.IsNullOrEmpty(styleId) || string.IsNullOrEmpty(modifiedDate))
{
UnityEngine.Debug.LogWarning("Style Id or Modified Time cannot be empty for style optimized tilesets. Switching to regular tilesets!");
_isStyleOptimized = false;
}
else
{
_isStyleOptimized = true;
_optimizedStyleId = styleId;
_modifiedDate = modifiedDate;
}
}
//TODO: uncomment if 'VectorTile' class changes from 'sealed'
//protected override void Dispose(bool disposeManagedResources)
//~VectorTile()
//{
// Dispose(false);
//}
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)
{
//TODO implement IDisposable with Mapbox.VectorTile.VectorTile
if (null != data)
{
data = null;
}
}
}
}
/// <summary>
/// <para>Gets the vector in a GeoJson format.</para>
/// <para>
/// This method should be avoided as it fully decodes the whole tile and might pose performance and memory bottle necks.
/// </para>
/// </summary>
/// <value> The GeoJson data. </value>
/// <example>
/// Inspect the GeoJson.
/// <code>
/// var json = VectorTile.GeoJson;
/// Console.Write("GeoJson: " + json);
/// </code>
/// </example>
public string GeoJson
{
get
{
return this.data.ToGeoJson((ulong)Id.Z, (ulong)Id.X, (ulong)Id.Y, 0);
}
}
/// <summary>
/// Gets all availble layer names.
/// See available layers and features <see href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/">here</see>.
/// </summary>
/// <returns>Collection of availble layers.</returns>
/// <example>
/// Inspect the LayerNames.
/// <code>
/// var layerNames = vectorTile.LayerNames();
/// foreach (var layer in layerNames)
/// {
/// Console.Write("Layer: " + layer);
/// }
/// </code>
/// </example>
public ReadOnlyCollection<string> LayerNames()
{
return this.data.LayerNames();
}
// FIXME: Why don't these work?
/// <summary>
/// Decodes the requested layer.
/// </summary>
/// <param name="layerName">Name of the layer to decode.</param>
/// <returns>Decoded VectorTileLayer or 'null' if an invalid layer name was specified.</returns>
/// <example>
/// Inspect a layer of the vector tile.
/// <code>
/// var countryLabelLayer = vectorTile.GetLayer("country_label");
/// var count = countryLabelLayer.Keys.Count;
/// for (int i = 0; i &lt; count; i++)
/// {
/// Console.Write(string.Format("{0}:{1}", countryLabelLayer.Keys[i], countryLabelLayer.Values[i]));
/// }
/// </code>
/// </example>
public VectorTileLayer GetLayer(string layerName)
{
return this.data.GetLayer(layerName);
}
internal override TileResource MakeTileResource(string mapId)
{
return (_isStyleOptimized) ?
TileResource.MakeStyleOptimizedVector(Id, mapId, _optimizedStyleId, _modifiedDate)
: TileResource.MakeVector(Id, mapId);
}
internal override bool ParseTileData(byte[] data)
{
try
{
var decompressed = Compression.Decompress(data);
this.data = new Mapbox.VectorTile.VectorTile(decompressed);
return true;
}
catch (Exception ex)
{
AddException(ex);
return false;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: bfd8ce307104a47389d62ff6cd67802c
timeCreated: 1494951007
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 2878fdebfe9305f4cab4f21e3c398837
folderAsset: yes
timeCreated: 1508237858
licenseType: Pro
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,74 @@
//-----------------------------------------------------------------------
// <copyright file="MapMatcher.cs" company="Mapbox">
// Copyright (c) 2017 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.MapMatching
{
using System;
using System.Text;
using Mapbox.Json;
using Mapbox.Platform;
using Mapbox.Utils.JsonConverters;
/// <summary>
/// Wrapper around the <see href="https://www.mapbox.com/api-documentation/#map-matching">
/// Mapbox Map Matching API</see>.
/// </summary>
public class MapMatcher
{
private readonly IFileSource _fileSource;
private int _timeout;
/// <summary> Initializes a new instance of the <see cref="MapMatcher" /> class. </summary>
/// <param name="fileSource"> Network access abstraction. </param>
public MapMatcher(IFileSource fileSource, int timeout)
{
_fileSource = fileSource;
_timeout = timeout;
}
/// <summary> Performs asynchronously a geocoding lookup. </summary>
/// <param name="geocode"> Geocode resource. </param>
/// <param name="callback"> Callback to be called after the request is completed. </param>
/// <typeparam name="T"> String or LngLat. Should be automatically inferred. </typeparam>
/// <returns>
/// Returns a <see cref="IAsyncRequest" /> 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.
/// </returns>
public IAsyncRequest Match(MapMatchingResource match, Action<MapMatchingResponse> callback)
{
string url = match.GetUrl();
return _fileSource.Request(
url,
(Response response) =>
{
var str = Encoding.UTF8.GetString(response.Data);
var data = Deserialize<MapMatchingResponse>(str);
if (response.HasError)
{
data.SetRequestExceptions(response.Exceptions);
}
callback(data);
},
_timeout
);
}
/// <summary>
/// Deserialize the map match response string into a <see cref="MapMatchingResponse"/>.
/// </summary>
/// <param name="str">JSON String.</param>
/// <returns>A <see cref="MapMatchingResponse"/>.</returns>
/// <typeparam name="T">Map Matcher. </typeparam>
internal T Deserialize<T>(string str)
{
return JsonConvert.DeserializeObject<T>(str, JsonConverters.Converters);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: dfdfc6d95bc72494e8ae78580d2e6b44
timeCreated: 1508239177
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,115 @@
//-----------------------------------------------------------------------
// <copyright file="MapMatchingParameters.cs" company="Mapbox">
// Copyright (c) 2017 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.ComponentModel;
using Mapbox.VectorTile.Geometry;
namespace Mapbox.MapMatching
{
/// <summary>Directions profile id</summary>
public enum Profile
{
[Description("mapbox/driving")]
MapboxDriving,
[Description("mapbox/driving-traffic")]
MapboxDrivingTraffic,
[Description("mapbox/walking")]
MapboxWalking,
[Description("mapbox/cycling")]
MapboxCycling
}
/// <summary>Format of the returned geometry. Default value 'Polyline' with precision 5.</summary>
public enum Geometries
{
/// <summary>Default, precision 5.</summary>
[Description("polyline")]
Polyline,
/// <summary>Precision 6.</summary>
[Description("polyline6")]
Polyline6,
/// <summary>Geojson.</summary>
[Description("geojson")]
GeoJson
}
/// <summary>Type of returned overview geometry. </summary>
public enum Overview
{
/// <summary>The most detailed geometry available </summary>
[Description("full")]
Full,
/// <summary>A simplified version of the full geometry</summary>
[Description("simplified")]
Simplified,
/// <summary>No overview geometry </summary>
[Description("false")]
None
}
/// <summary>Whether or not to return additional metadata along the route. Several annotations can be used.</summary>
[System.Flags]
public enum Annotations
{
[Description("duration")]
Duration,
[Description("distance")]
Distance,
[Description("speed")]
Speed,
[Description("congestion")]
Congestion
}
/// <summary>
/// https://www.mapbox.com/api-documentation/#retrieve-directions
/// </summary>
public enum InstructionLanguages
{
[Description("de")]
German,
[Description("en")]
English,
[Description("eo")]
Esperanto,
[Description("es")]
Spanish,
[Description("es-ES")]
SpanishSpain,
[Description("fr")]
French,
[Description("id")]
Indonesian,
[Description("it")]
Italian,
[Description("nl")]
Dutch,
[Description("pl")]
Polish,
[Description("pt-BR")]
PortugueseBrazil,
[Description("ro")]
Romanian,
[Description("ru")]
Russian,
[Description("sv")]
Swedish,
[Description("tr")]
Turkish,
[Description("uk")]
Ukrainian,
[Description("vi")]
Vietnamese,
[Description("zh-Hans")]
ChineseSimplified
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 991f42eef2c18204cb4a6b79c6f98eed
timeCreated: 1508237904
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,209 @@
//-----------------------------------------------------------------------
// <copyright file="MapMatchingResource.cs" company="Mapbox">
// Copyright (c) 2017 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.MapMatching
{
using System;
using System.Collections.Generic;
using System.Linq;
using Mapbox.Platform;
using Mapbox.Utils;
using Mapbox.VectorTile.ExtensionMethods;
/// <summary> Base geocode class. </summary>
/// <typeparam name="T"> Type of Query field (either string or LatLng). </typeparam>
public class MapMatchingResource : Resource
{
private readonly string _apiEndpoint = "matching/v5/";
private Vector2d[] _coordinates;
private uint[] _radiuses;
private long[] _timestamps;
/// <summary> Gets the API endpoint as a partial URL path. </summary>
public override string ApiEndpoint
{
get { return _apiEndpoint; }
}
/// <summary>A directions profile ID.</summary>
public Profile Profile = Profile.MapboxDriving;
/// <summary> Coordinate to visit in order; there can be between 2 and 100 coordinates. </summary>
public Vector2d[] Coordinates
{
get { return _coordinates; }
set
{
if (null == value)
{
throw new Exception("Coordinates cannot be null.");
}
if (value.Length < 2 || value.Length > 100)
{
throw new Exception("Must be between 2 and 100 elements in coordinates array");
}
_coordinates = value;
}
}
/// <summary>
/// <para>Format of the returned geometry.</para>
/// <para>Allowed values are: geojson (as LineString ), polyline with precision 5, polyline6 (polyline with precision 6).</para>
/// <para>The default value is polyline.</para>
/// </summary>
public Nullable<Geometries> Geometries;
/// <summary>
/// <para>A list of uints in meters indicating the assumed precision of the used tracking device.</para>
/// <para>There must be as many radiuses as there are coordinates in the request.</para>
/// <para>Values can be a number between 0 and 30.</para>
/// <para>Use higher numbers (20-30) for noisy traces and lower numbers (1-10) for clean traces.</para>
/// <para>The default value is 5.</para>
/// </summary>
public uint[] Radiuses
{
get { return _radiuses; }
set
{
if (null == _coordinates) { throw new Exception("Coordinates not set"); }
//allow for nulling radiuses
if (null == value)
{
_radiuses = null;
return;
}
if (value.Length != _coordinates.Length) { throw new Exception("There must be as many radiuses as there are coordinates in the request."); }
if (value.Where(r => r == 0).Count() > 0) { throw new Exception("Radius must be greater than 0"); }
_radiuses = value;
}
}
/// <summary>
/// <para>Whether to return steps and turn-by-turn instructions.</para>
/// <para>Can be true or false.</para>
/// <para>The default is false.</para>
/// </summary>
public bool? Steps;
/// <summary>
/// <para>Type of returned overview geometry.</para>
/// <para>Can be full (the most detailed geometry available), simplified (a simplified version of the full geometry), or none (no overview geometry).</para>
/// <para>The default is simplified.</para>
/// </summary>
public Nullable<Overview> Overview;
/// <summary>
/// <para>Timestamps corresponding to each coordinate provided in the request.</para>
/// <para>Must be numbers in Unix time (seconds since the Unix epoch).</para>
/// <para>There must be as many timestamps as there are coordinates in the request.</para>
/// </summary>
public long[] Timestamps
{
get { return _timestamps; }
set
{
if (null == _coordinates) { throw new Exception("Coordinates not set"); }
//allow for nulling timestamps
if (null == value)
{
_timestamps = null;
return;
}
if (value.Length != _coordinates.Length) { throw new Exception("There must be as many timestapms as there are coordinates in the request."); }
_timestamps = value;
}
}
/// <summary>
/// <para>Whether or not to return additional metadata along the route.</para>
/// <para>Possible values are: duration, distance and speed.</para>
/// <para>Several annotations can be used.</para>
/// <para>Combine via '|'.</para>
/// </summary>
public Nullable<Annotations> Annotations;
/// <summary>
/// <para>Whether or not to transparently remove clusters and re-sample traces for improved map matching results.</para>
/// <para>Removed tracepoints are set to 'null' in the response!</para>
/// <para>Can be true or false.</para>
/// <para>The default is false.</para>
/// </summary>
public bool? Tidy;
/// <summary>
/// <para>Language of returned turn-by-turn text instructions.</para>
/// <para>The default is English.</para>
/// </summary>
public Nullable<InstructionLanguages> Language;
public override string GetUrl()
{
if (null == _coordinates)
{
throw new Exception("Coordinates cannot be null.");
}
Dictionary<string, string> options = new Dictionary<string, string>();
if (Geometries.HasValue) { options.Add("geometries", Geometries.Value.Description()); }
if (null != _radiuses) { options.Add("radiuses", GetUrlQueryFromArray(_radiuses, ";")); }
if (Steps.HasValue) { options.Add("steps", Steps.ToString().ToLower()); }
if (Overview.HasValue) { options.Add("overview", Overview.Value.Description()); }
if (null != _timestamps) { options.Add("timestamps", GetUrlQueryFromArray(_timestamps, ";")); }
if (Annotations.HasValue) { options.Add("annotations", getUrlQueryFromAnnotations(Annotations.Value, ",")); }
if (Tidy.HasValue) { options.Add("tidy", Tidy.Value.ToString().ToLower()); }
if (Language.HasValue) { options.Add("language", Language.Value.Description()); }
return
Constants.BaseAPI
+ _apiEndpoint
+ Profile.Description() + "/"
+ GetUrlQueryFromArray<Vector2d>(_coordinates, ";")
+ ".json"
+ EncodeQueryString(options);
}
/// <summary>
/// Convert Annotations (several could be combined) into a string of their descriptions.
/// </summary>
/// <param name="annotation">Current annotation</param>
/// <param name="separator">Character to use for separating items in string.</param>
/// <returns></returns>
private string getUrlQueryFromAnnotations(Annotations anno, string separator)
{
List<string> descriptions = new List<string>();
//iterate through all possible 'Annotations' values
foreach (var a in Enum.GetValues(typeof(Annotations)).Cast<Annotations>())
{
//if current value is set, add its description
if (a == (anno & a)) { descriptions.Add(a.Description()); }
}
return string.Join(separator, descriptions.ToArray());
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 8762d1b8a83c7ba4b91f9a1c64717bee
timeCreated: 1508240506
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,79 @@
//-----------------------------------------------------------------------
// <copyright file="MapMatchingResponse.cs" company="Mapbox">
// Copyright (c) 2016 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.MapMatching
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Mapbox.Json;
/// <summary> Base geocode response. </summary>
#if !WINDOWS_UWP
//http://stackoverflow.com/a/12903628
[Serializable]
#endif
public class MapMatchingResponse
{
/// <summary>Simple constructor for deserialization </summary>
public MapMatchingResponse() { }
///// <summary>Constructor for bubbling errors of underlying web request </summary>
//public MapMatchingResponse(ReadOnlyCollection<Exception> requestExceptions)
//{
// _requestExceptions = requestExceptions;
//}
[JsonProperty("code")]
public string Code;
[JsonProperty("message")]
public string Message;
[JsonProperty("tracepoints")]
public Tracepoint[] Tracepoints;
[JsonProperty("matchings")]
public MatchObject[] Matchings;
#if !WINDOWS_UWP
/// <summary>Error occured during matching</summary>
public bool HasMatchingError { get { return !"ok".Equals(Code, StringComparison.InvariantCultureIgnoreCase); } }
#else
/// <summary>Error occured during matching</summary>
public bool HasMatchingError { get { return !"ok".Equals(Code, StringComparison.OrdinalIgnoreCase); } }
#endif
public string MatchingError
{
get
{
string matchCode = Code.ToLower();
switch (matchCode)
{
case "ok": return "";
case "nomatch": return "The input did not produce any matches. features will be an empty array.";
case "toomanycoordinates": return "There are to many points in the request.";
case "InvalidInput": return "Invalid input: 'message' will hold an explanation of the invalid input.";
case "ProfileNotFound": return "Invalid profile.";
case "nosegment": return "Could not find a matching segment for input coordinates.";
default:
return "Unexpected error: check 'message'";
}
}
}
/// <summary>Errors occured during web request </summary>
public bool HasRequestError { get { return _requestExceptions.Count > 0; } }
private ReadOnlyCollection<Exception> _requestExceptions = new List<Exception>().AsReadOnly();
/// <summary>Errors of underlying web request </summary>
public ReadOnlyCollection<Exception> RequestExceptions { get { return _requestExceptions; } }
/// <summary>Assign errors of underlying web request </summary>
public void SetRequestExceptions(ReadOnlyCollection<Exception> exceptions) { _requestExceptions = exceptions; }
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 6b0f824b73d2d8f41aeed79f2a15666d
timeCreated: 1508247612
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
//-----------------------------------------------------------------------
// <copyright file="MatchObject.cs" company="Mapbox">
// Copyright (c) 2017 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.MapMatching
{
using Mapbox.Directions;
using Mapbox.Json;
/// <summary>
/// A Match object from a Map Matching API call.
/// </summary>
public class MatchObject : Route
{
/// <summary>
/// A number between 0 (low) and 1 (high) indicating level of confidence in the returned match
/// </summary>
[JsonProperty("confidence")]
public float Confidence { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: d6d1f89031e0a60479b516a76cf1a96a
timeCreated: 1508309612
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,39 @@
//-----------------------------------------------------------------------
// <copyright file="Tracepoint.cs" company="Mapbox">
// Copyright (c) 2017 Mapbox. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Mapbox.MapMatching
{
using Mapbox.Directions;
using Mapbox.Json;
using Mapbox.Utils;
using Mapbox.Utils.JsonConverters;
/// <summary>
/// A Waypoint from a Directions API call.
/// </summary>
public class Tracepoint: Waypoint
{
/// <summary>
/// Index of the waypoint inside the matched route.
/// </summary>
[JsonProperty("waypoint_index")]
public int WaypointIndex { get; set; }
/// <summary>
/// Index to the match object in matchings the sub-trace was matched to.
/// </summary>
[JsonProperty("matchings_index")]
public int MatchingsIndex { get; set; }
/// <summary>
/// Number of probable alternative matchings for this trace point. A value of zero indicates that this point was matched unambiguously. Split the trace at these points for incremental map matching.
/// </summary>
[JsonProperty("alternatives_count")]
public int AlternativesCount { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: cd23eaecbb5aa1947ad2b7a595c2393e
timeCreated: 1508251969
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 271709ca6cfd047e9ab142950d086b5f
folderAsset: yes
timeCreated: 1491243031
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 15f35c4d53ec8ae459a497d0bee43f77
folderAsset: yes
timeCreated: 1495030845
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
namespace Mapbox.Platform.Cache
{
using System;
public class CacheItem
{
/// <summary> Raw response data- </summary>
public byte[] Data;
/// <summary> UTC ticks when item was added to the cache. </summary>
public long AddedToCacheTicksUtc;
/// <summary> ETag value of API response. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag </summary>
public string ETag;
/// <summary> Can be 'null' as not all APIs populated this value. Last-Modified value of API response in GMT: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified </summary>
public DateTime? LastModified;
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 3bad456c58de85d47b4ff050dbd99915
timeCreated: 1510665832
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,319 @@
namespace Mapbox.Platform.Cache
{
using System;
using Mapbox.Platform;
using System.Collections.Generic;
using Mapbox.Unity.Utilities;
using Mapbox.Map;
using System.Collections;
using System.Linq;
public class CachingWebFileSource : IFileSource, IDisposable
{
#if MAPBOX_DEBUG_CACHE
private string _className;
#endif
private bool _disposed;
private List<ICache> _caches = new List<ICache>();
private string _accessToken;
private bool _autoRefreshCache;
public CachingWebFileSource(string accessToken, bool autoRefreshCache)
{
#if MAPBOX_DEBUG_CACHE
_className = this.GetType().Name;
#endif
_accessToken = accessToken;
_autoRefreshCache = autoRefreshCache;
}
#region idisposable
~CachingWebFileSource()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposeManagedResources)
{
if (!_disposed)
{
if (disposeManagedResources)
{
for (int i = 0; i < _caches.Count; i++)
{
IDisposable cache = _caches[i] as IDisposable;
if (null != cache)
{
cache.Dispose();
cache = null;
}
}
}
_disposed = true;
}
}
#endregion
/// <summary>
/// Add an ICache instance
/// </summary>
/// <param name="cache">Implementation of ICache</param>
/// <returns></returns>
public CachingWebFileSource AddCache(ICache cache)
{
// don't add cache when cache size is 0
if (0 == cache.MaxCacheSize)
{
return this;
}
_caches.Add(cache);
return this;
}
/// <summary>
/// Clear all caches
/// </summary>
public void Clear()
{
foreach (var cache in _caches)
{
cache.Clear();
}
}
public void ReInit() {
foreach (var cache in _caches)
{
cache.ReInit();
}
}
public IAsyncRequest Request(
string uri
, Action<Response> callback
, int timeout = 10
, CanonicalTileId tileId = new CanonicalTileId()
, string mapId = null
)
{
if (string.IsNullOrEmpty(mapId))
{
throw new Exception("Cannot cache without a map id");
}
CacheItem cachedItem = null;
// go through existing caches and check if we already have the requested tile available
foreach (var cache in _caches)
{
cachedItem = cache.Get(mapId, tileId);
if (null != cachedItem)
{
break;
}
}
var uriBuilder = new UriBuilder(uri);
if (!string.IsNullOrEmpty(_accessToken))
{
string accessTokenQuery = "access_token=" + _accessToken;
if (uriBuilder.Query != null && uriBuilder.Query.Length > 1)
{
uriBuilder.Query = uriBuilder.Query.Substring(1) + "&" + accessTokenQuery;
}
else
{
uriBuilder.Query = accessTokenQuery;
}
}
string finalUrl = uriBuilder.ToString();
#if MAPBOX_DEBUG_CACHE
string methodName = _className + "." + new System.Diagnostics.StackFrame().GetMethod().Name;
#endif
// if tile was available call callback with it, propagate to all other caches and check if a newer one is available
if (null != cachedItem)
{
#if MAPBOX_DEBUG_CACHE
UnityEngine.Debug.LogFormat("{0} {1} {2} {3}", methodName, mapId, tileId, null != cachedItem.Data ? cachedItem.Data.Length.ToString() : "cachedItem.Data is NULL");
#endif
// immediately return cached tile
callback(Response.FromCache(cachedItem.Data));
// check for updated tiles online if this is enabled in the settings
if (_autoRefreshCache)
{
// check if tile on the web is newer than the one we already have locally
IAsyncRequestFactory.CreateRequest(
finalUrl,
(Response headerOnly) =>
{
// on error getting information from API just return. tile we have locally has already been returned above
if (headerOnly.HasError)
{
return;
}
// TODO: remove Debug.Log before PR
//UnityEngine.Debug.LogFormat(
// "{1}{0}cached:{2}{0}header:{3}"
// , Environment.NewLine
// , finalUrl
// , cachedItem.ETag
// , headerOnly.Headers["ETag"]
//);
// data from cache is the same as on the web:
// * tile has already been returned above
// * make sure all all other caches have it too, but don't force insert via cache.add(false)
// additional ETag empty check: for backwards compability with old caches
if (!string.IsNullOrEmpty(cachedItem.ETag) && cachedItem.ETag.Equals(headerOnly.Headers["ETag"]))
{
foreach (var cache in _caches)
{
cache.Add(mapId, tileId, cachedItem, false);
}
}
else
{
// TODO: remove Debug.Log before PR
UnityEngine.Debug.LogWarningFormat(
"updating cached tile {1} mapid:{2}{0}cached etag:{3}{0}remote etag:{4}{0}{5}"
, Environment.NewLine
, tileId
, mapId
, cachedItem.ETag
, headerOnly.Headers["ETag"]
, finalUrl
);
// request updated tile and pass callback to return new data to subscribers
requestTileAndCache(finalUrl, mapId, tileId, timeout, callback);
}
}
, timeout
, HttpRequestType.Head
);
}
return new MemoryCacheAsyncRequest(uri);
}
else
{
// requested tile is not in any of the caches yet, get it
#if MAPBOX_DEBUG_CACHE
UnityEngine.Debug.LogFormat("{0} {1} {2} not cached", methodName, mapId, tileId);
#endif
return requestTileAndCache(finalUrl, mapId, tileId, timeout, callback);
}
}
private IAsyncRequest requestTileAndCache(string url, string mapId, CanonicalTileId tileId, int timeout, Action<Response> callback)
{
return IAsyncRequestFactory.CreateRequest(
url,
(Response r) =>
{
// if the request was successful add tile to all caches
if (!r.HasError && null != r.Data)
{
//UnityEngine.Debug.Log(uri);
string eTag = string.Empty;
DateTime? lastModified = null;
if (!r.Headers.ContainsKey("ETag"))
{
UnityEngine.Debug.LogWarningFormat("no 'ETag' header present in response for {0}", url);
}
else
{
eTag = r.Headers["ETag"];
}
// not all APIs populate 'Last-Modified' header
// don't log error if it's missing
if (r.Headers.ContainsKey("Last-Modified"))
{
lastModified = DateTime.ParseExact(r.Headers["Last-Modified"], "r", null);
}
// propagate to all caches forcing update
foreach (var cache in _caches)
{
cache.Add(
mapId
, tileId
, new CacheItem()
{
Data = r.Data,
ETag = eTag,
LastModified = lastModified
}
, true // force insert/update
);
}
}
if (null != callback)
{
r.IsUpdate = true;
callback(r);
}
}, timeout);
}
class MemoryCacheAsyncRequest : IAsyncRequest
{
public string RequestUrl { get; private set; }
public MemoryCacheAsyncRequest(string requestUrl)
{
RequestUrl = requestUrl;
}
public bool IsCompleted
{
get
{
return true;
}
}
public HttpRequestType RequestType { get { return HttpRequestType.Get; } }
public void Cancel()
{
// Empty. We can't cancel an instantaneous response.
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 149bb7da83149324dab3982be7bc8cca
timeCreated: 1494581732
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,53 @@
namespace Mapbox.Platform.Cache
{
using Mapbox.Map;
using System;
public interface ICache
{
/// <summary>
/// Maximum number of tiles to store
/// </summary>
uint MaxCacheSize { get; }
/// <summary>
/// Add tile data to the cache
/// </summary>
/// <param name="mapId">Tile set name</param>
/// <param name="tileId">Tile ID</param>
/// <param name="item">Item to cache</param>
/// <param name="replaceIfExists">Force insert even if item already exists.</param>
void Add(string mapId, CanonicalTileId tileId, CacheItem item, bool replaceIfExists);
/// <summary>
/// Get tile
/// </summary>
/// <param name="mapId"></param>
/// <param name="tileId"></param>
/// <returns>byte[] with tile data. Null if requested tile is not in cache</returns>
CacheItem Get(string mapId, CanonicalTileId tileId);
/// <summary>Clear cache for all tile sets</summary>
void Clear();
/// <summary>
/// Clear cache for one tile set
/// </summary>
/// <param name="mapId"></param>
void Clear(string mapId);
/// <summary>
/// Reinitialize cache. Might be needed after 'Clear', eg for SQLiteCache
/// </summary>
void ReInit();
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4ebb2d197f248a7469620904e2adb43e
timeCreated: 1494580896
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,112 @@
using Mapbox.Map;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Mapbox.Platform.Cache
{
public class MemoryCache : ICache
{
// TODO: add support for disposal strategy (timestamp, distance, etc.)
public MemoryCache(uint maxCacheSize)
{
#if MAPBOX_DEBUG_CACHE
_className = this.GetType().Name;
#endif
_maxCacheSize = maxCacheSize;
_cachedResponses = new Dictionary<string, CacheItem>();
}
#if MAPBOX_DEBUG_CACHE
private string _className;
#endif
private uint _maxCacheSize;
private object _lock = new object();
private Dictionary<string, CacheItem> _cachedResponses;
public uint MaxCacheSize
{
get { return _maxCacheSize; }
}
public void ReInit()
{
_cachedResponses = new Dictionary<string, CacheItem>();
}
public void Add(string mapdId, CanonicalTileId tileId, CacheItem item, bool forceInsert)
{
string key = mapdId + "||" + tileId;
lock (_lock)
{
if (_cachedResponses.Count >= _maxCacheSize)
{
_cachedResponses.Remove(_cachedResponses.OrderBy(c => c.Value.AddedToCacheTicksUtc).First().Key);
}
// TODO: forceInsert
if (!_cachedResponses.ContainsKey(key))
{
item.AddedToCacheTicksUtc = DateTime.UtcNow.Ticks;
_cachedResponses.Add(key, item);
}
}
}
public CacheItem Get(string mapId, CanonicalTileId tileId)
{
string key = mapId + "||" + tileId;
#if MAPBOX_DEBUG_CACHE
string methodName = _className + "." + new System.Diagnostics.StackFrame().GetMethod().Name;
UnityEngine.Debug.LogFormat("{0} {1}", methodName, key);
#endif
lock (_lock)
{
if (!_cachedResponses.ContainsKey(key))
{
return null;
}
return _cachedResponses[key];
}
}
public void Clear()
{
lock (_lock)
{
_cachedResponses.Clear();
}
}
public void Clear(string mapId)
{
lock (_lock)
{
mapId += "||";
List<string> toDelete = _cachedResponses.Keys.Where(k => k.Contains(mapId)).ToList();
foreach (string key in toDelete)
{
_cachedResponses.Remove(key);
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2bd70b309ec2fd645af3e189d1d7ea5f
timeCreated: 1494580911
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c6f1c408c0acc43538b678d17f6ea53d
folderAsset: yes
timeCreated: 1497883478
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,495 @@
namespace Mapbox.Platform.Cache
{
using Mapbox.Map;
using Mapbox.Utils;
using SQLite4Unity3d;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
public class SQLiteCache : ICache, IDisposable
{
/// <summary>
/// maximum number tiles that get cached
/// </summary>
public uint MaxCacheSize { get { return _maxTileCount; } }
/// <summary>
/// Check cache size every n inserts
/// </summary>
public uint PruneCacheDelta { get { return _pruneCacheDelta; } }
#if MAPBOX_DEBUG_CACHE
private string _className;
#endif
private bool _disposed;
private string _dbName;
private string _dbPath;
private SQLiteConnection _sqlite;
private readonly uint _maxTileCount;
/// <summary>check cache size only every '_pruneCacheDelta' calls to 'Add()' to avoid being too chatty with the database</summary>
private const int _pruneCacheDelta = 20;
/// <summary>counter to keep track of calls to `Add()`</summary>
private int _pruneCacheCounter = 0;
private object _lock = new object();
public SQLiteCache(uint? maxTileCount = null, string dbName = "cache.db")
{
_maxTileCount = maxTileCount ?? 3000;
_dbName = dbName;
init();
}
#region idisposable
~SQLiteCache()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposeManagedResources)
{
if (!_disposed)
{
if (disposeManagedResources)
{
if (null != _sqlite)
{
_sqlite.Execute("VACUUM;"); // compact db to keep file size small
_sqlite.Close();
_sqlite.Dispose();
_sqlite = null;
}
}
_disposed = true;
}
}
#endregion
private void init()
{
#if MAPBOX_DEBUG_CACHE
_className = this.GetType().Name;
#endif
openOrCreateDb(_dbName);
//hrmpf: multiple PKs not supported by sqlite.net
//https://github.com/praeclarum/sqlite-net/issues/282
//do it via plain SQL
List<SQLiteConnection.ColumnInfo> colInfoTileset = _sqlite.GetTableInfo(typeof(tilesets).Name);
if (0 == colInfoTileset.Count)
{
string cmdCreateTableTilesets = @"CREATE TABLE tilesets(
id INTEGER PRIMARY KEY ASC AUTOINCREMENT NOT NULL UNIQUE,
name STRING NOT NULL
);";
_sqlite.Execute(cmdCreateTableTilesets);
string cmdCreateIdxNames = @"CREATE UNIQUE INDEX idx_names ON tilesets (name ASC);";
_sqlite.Execute(cmdCreateIdxNames);
}
List<SQLiteConnection.ColumnInfo> colInfoTiles = _sqlite.GetTableInfo(typeof(tiles).Name);
if (0 == colInfoTiles.Count)
{
//sqlite does not support multiple PK columns, create table manually
//_sqlite.CreateTable<tiles>();
string cmdCreateTableTiles = @"CREATE TABLE tiles(
tile_set INTEGER REFERENCES tilesets (id) ON DELETE CASCADE ON UPDATE CASCADE,
zoom_level INTEGER NOT NULL,
tile_column BIGINT NOT NULL,
tile_row BIGINT NOT NULL,
tile_data BLOB NOT NULL,
timestamp INTEGER NOT NULL,
etag TEXT,
lastmodified INTEGER,
PRIMARY KEY(
tile_set ASC,
zoom_level ASC,
tile_column ASC,
tile_row ASC
)
);";
_sqlite.Execute(cmdCreateTableTiles);
string cmdIdxTileset = "CREATE INDEX idx_tileset ON tiles (tile_set ASC);";
_sqlite.Execute(cmdIdxTileset);
string cmdIdxTimestamp = "CREATE INDEX idx_timestamp ON tiles (timestamp ASC);";
_sqlite.Execute(cmdIdxTimestamp);
}
// some pragmas to speed things up a bit :-)
// inserting 1,000 tiles takes 1-2 sec as opposed to ~20 sec
string[] cmds = new string[]
{
"PRAGMA synchronous=OFF",
"PRAGMA count_changes=OFF",
"PRAGMA journal_mode=MEMORY",
"PRAGMA temp_store=MEMORY"
};
foreach (var cmd in cmds)
{
try
{
_sqlite.Execute(cmd);
}
catch (SQLiteException ex)
{
// workaround for sqlite.net's exeception:
// https://stackoverflow.com/a/23839503
if (ex.Result != SQLite3.Result.Row)
{
UnityEngine.Debug.LogErrorFormat("{0}: {1}", cmd, ex);
// TODO: when mapbox-sdk-cs gets backported to its own repo -> throw
//throw; // to throw or not to throw???
}
}
}
}
private void openOrCreateDb(string dbName)
{
_dbPath = GetFullDbPath(dbName);
_sqlite = new SQLiteConnection(_dbPath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create);
//Debug.LogFormat("SQLiteCache path ----> {0}", _dbPath);
}
/// <summary>
/// <para>Reinitialize cache.</para>
/// <para>This is needed after 'Clear()' to recreate the cache database.</para>
/// <para>And has been implemented on purpose to not hold on to any references to the cache directory after 'Clear()'</para>
/// </summary>
public void ReInit()
{
if (null != _sqlite)
{
_sqlite.Dispose();
_sqlite = null;
}
init();
}
public static string GetFullDbPath(string dbName)
{
string dbPath = Path.Combine(Application.persistentDataPath, "cache");
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_WSA
dbPath = Path.GetFullPath(dbPath);
#endif
if (!Directory.Exists(dbPath)) { Directory.CreateDirectory(dbPath); }
dbPath = Path.Combine(dbPath, dbName);
return dbPath;
}
public void Add(string tilesetName, CanonicalTileId tileId, CacheItem item, bool forceInsert = false)
{
#if MAPBOX_DEBUG_CACHE
string methodName = _className + "." + new System.Diagnostics.StackFrame().GetMethod().Name;
UnityEngine.Debug.LogFormat("{0} {1} {2} forceInsert:{3}", methodName, tileset, tileId, forceInsert);
#endif
try
{
// tile exists and we don't want to overwrite -> exit early
if (
TileExists(tilesetName, tileId)
&& !forceInsert
)
{
return;
}
int? tilesetId = null;
lock (_lock)
{
tilesetId = getTilesetId(tilesetName);
if (!tilesetId.HasValue)
{
tilesetId = insertTileset(tilesetName);
}
}
if (tilesetId < 0)
{
Debug.LogErrorFormat("could not get tilesetID for [{0}] tile: {1}", tilesetName, tileId);
return;
}
//_sqlite.BeginTransaction();
int rowsAffected = _sqlite.InsertOrReplace(new tiles
{
tile_set = tilesetId.Value,
zoom_level = tileId.Z,
tile_column = tileId.X,
tile_row = tileId.Y,
tile_data = item.Data,
timestamp = (int)UnixTimestampUtils.To(DateTime.Now),
etag = item.ETag
});
if (1 != rowsAffected)
{
throw new Exception(string.Format("tile [{0} / {1}] was not inserted, rows affected:{2}", tilesetName, tileId, rowsAffected));
}
}
catch (Exception ex)
{
Debug.LogErrorFormat("Error inserting {0} {1} {2} ", tilesetName, tileId, ex);
}
finally
{
//_sqlite.Commit();
}
// update counter only when new tile gets inserted
if (!forceInsert)
{
_pruneCacheCounter++;
}
if (0 == _pruneCacheCounter % _pruneCacheDelta)
{
_pruneCacheCounter = 0;
prune();
}
}
private void prune()
{
long tileCnt = _sqlite.ExecuteScalar<long>("SELECT COUNT(zoom_level) FROM tiles");
if (tileCnt < _maxTileCount) { return; }
long toDelete = tileCnt - _maxTileCount;
#if MAPBOX_DEBUG_CACHE
string methodName = _className + "." + new System.Diagnostics.StackFrame().GetMethod().Name;
Debug.LogFormat("{0} {1} about to prune()", methodName, _tileset);
#endif
try
{
// no 'ORDER BY' or 'LIMIT' possible if sqlite hasn't been compiled with 'SQLITE_ENABLE_UPDATE_DELETE_LIMIT'
// https://sqlite.org/compile.html#enable_update_delete_limit
// int rowsAffected = _sqlite.Execute("DELETE FROM tiles ORDER BY timestamp ASC LIMIT ?", toDelete);
_sqlite.Execute("DELETE FROM tiles WHERE rowid IN ( SELECT rowid FROM tiles ORDER BY timestamp ASC LIMIT ? );", toDelete);
}
catch (Exception ex)
{
Debug.LogErrorFormat("error pruning: {0}", ex);
}
}
/// <summary>
/// Returns the tile data, otherwise null
/// </summary>
/// <param name="tileId">Canonical tile id to identify the tile</param>
/// <returns>tile data as byte[], if tile is not cached returns null</returns>
public CacheItem Get(string tilesetName, CanonicalTileId tileId)
{
#if MAPBOX_DEBUG_CACHE
string methodName = _className + "." + new System.Diagnostics.StackFrame().GetMethod().Name;
Debug.LogFormat("{0} {1} {2}", methodName, _tileset, tileId);
#endif
tiles tile = null;
try
{
int? tilesetId = getTilesetId(tilesetName);
if (!tilesetId.HasValue)
{
return null;
}
tile = _sqlite
.Table<tiles>()
.Where(t =>
t.tile_set == tilesetId.Value
&& t.zoom_level == tileId.Z
&& t.tile_column == tileId.X
&& t.tile_row == tileId.Y
)
.FirstOrDefault();
}
catch (Exception ex)
{
Debug.LogErrorFormat("error getting tile {1} {2} from cache{0}{3}", Environment.NewLine, tilesetName, tileId, ex);
return null;
}
if (null == tile)
{
return null;
}
DateTime? lastModified = null;
if (tile.lastmodified.HasValue) { lastModified = UnixTimestampUtils.From((double)tile.lastmodified.Value); }
return new CacheItem()
{
Data = tile.tile_data,
AddedToCacheTicksUtc = tile.timestamp,
ETag = tile.etag,
LastModified = lastModified
};
}
/// <summary>
/// Check if tile exists
/// </summary>
/// <param name="tileId">Canonical tile id</param>
/// <returns>True if tile exists</returns>
public bool TileExists(string tilesetName, CanonicalTileId tileId)
{
int? tilesetId = getTilesetId(tilesetName);
if (!tilesetId.HasValue)
{
return false;
}
return null != _sqlite
.Table<tiles>()
.Where(t =>
t.tile_set == tilesetId.Value
&& t.zoom_level == tileId.Z
&& t.tile_column == tileId.X
&& t.tile_row == tileId.Y
)
.FirstOrDefault();
}
private int insertTileset(string tilesetName)
{
try
{
_sqlite.BeginTransaction(true);
//return _sqlite.Insert(new tilesets { name = tilesetName });
tilesets newTileset = new tilesets { name = tilesetName };
int rowsAffected = _sqlite.Insert(newTileset);
if (1 != rowsAffected)
{
throw new Exception(string.Format("tileset [{0}] was not inserted, rows affected:{1}", tilesetName, rowsAffected));
}
return newTileset.id;
}
catch (Exception ex)
{
Debug.LogErrorFormat("could not insert tileset [{0}]: {1}", tilesetName, ex);
return -1;
}
finally
{
_sqlite.Commit();
}
}
private int? getTilesetId(string tilesetName)
{
tilesets tileset = _sqlite
.Table<tilesets>()
.Where(ts => ts.name.Equals(tilesetName))
.FirstOrDefault();
return null == tileset ? (int?)null : tileset.id;
}
/// <summary>
/// FOR INTERNAL DEBUGGING ONLY - DON'T RELY ON IN PRODUCTION
/// </summary>
/// <param name="tilesetName"></param>
/// <returns></returns>
public long TileCount(string tilesetName)
{
int? tilesetId = getTilesetId(tilesetName);
if (!tilesetId.HasValue) { return 0; }
return _sqlite
.Table<tiles>()
.Where(t => t.tile_set == tilesetId.Value)
.LongCount();
}
/// <summary>
/// Clear cache for one tile set
/// </summary>
/// <param name="tilesetName"></param>
public void Clear(string tilesetName)
{
int? tilesetId = getTilesetId(tilesetName);
if (!tilesetId.HasValue) { return; }
//just delete on table 'tilesets', we've setup cascading which should take care of tabls 'tiles'
_sqlite.Delete<tilesets>(tilesetId.Value);
}
/// <summary>
/// <para>Delete the database file.</para>
/// <para>Call 'ReInit()' if you intend to continue using the cache after 'Clear()!</para>
/// </summary>
public void Clear()
{
//already disposed
if (null == _sqlite) { return; }
_sqlite.Close();
_sqlite.Dispose();
_sqlite = null;
Debug.LogFormat("deleting {0}", _dbPath);
// try several times in case SQLite needs a bit more time to dispose
for (int i = 0; i < 5; i++)
{
try
{
File.Delete(_dbPath);
return;
}
catch
{
#if !WINDOWS_UWP
System.Threading.Thread.Sleep(100);
#else
System.Threading.Tasks.Task.Delay(100).Wait();
#endif
}
}
// if we got till here, throw on last try
File.Delete(_dbPath);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8aeee08edfa6c46648cf1dc623b72a1e
timeCreated: 1497883479
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SQLite4Unity3d;
namespace Mapbox.Platform.Cache
{
/// <summary>
/// Don't change the class name: sqlite-net uses it for table creation
/// </summary>
public class tiles
{
public int tile_set { get; set; }
//hrmpf: multiple PKs not supported by sqlite.net
//https://github.com/praeclarum/sqlite-net/issues/282
//TODO: do it via plain SQL
//[PrimaryKey]
public int zoom_level { get; set; }
//[PrimaryKey]
public long tile_column { get; set; }
//[PrimaryKey]
public long tile_row { get; set; }
public byte[] tile_data { get; set; }
/// <summary>Unix epoch for simple FIFO pruning </summary>
public int timestamp { get; set; }
/// <summary> ETag Header value of the reponse for auto updating cache</summary>
public string etag { get; set; }
/// <summary>Last-Modified header value of API response. Not all APIs populate it, will be -1 in that case. </summary>
public int? lastmodified { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7d8f9ca4f2240481ba4023eff7781487
timeCreated: 1497883479
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SQLite4Unity3d;
namespace Mapbox.Platform.Cache
{
/// <summary>
/// Don't change the class name: sqlite-net uses it for table creation
/// </summary>
public class tilesets
{
//hrmpf: multiple PKs not supported by sqlite.net
//https://github.com/praeclarum/sqlite-net/issues/282
//TODO: do it via plain SQL
[PrimaryKey, AutoIncrement]
public int id { get; set; }
public string name { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More