注:此案例中有用到Mapbox相关库
1. 创建由于Vector2d 结构体
原因:由于Unity中自带的Vector2是float类型,而我们需要用一个结构体来储存精度需求比较高的经纬度数据,故创建Vector2d 及double类型的来储存经纬度数据
namespace Mapbox.Utils
{
using Mapbox.Json;
using System;
using System.Globalization;
[Serializable]
public struct Vector2d
{
public const double kEpsilon = 1E-05d;
public double x;
public double y;
public double this[int index]
{
get
{
switch (index)
{
case 0:
return this.x;
case 1:
return this.y;
default:
throw new IndexOutOfRangeException("Invalid Vector2d index!");
}
}
set
{
switch (index)
{
case 0:
this.x = value;
break;
case 1:
this.y = value;
break;
default:
throw new IndexOutOfRangeException("Invalid Vector2d index!");
}
}
}
[JsonIgnore]
public Vector2d normalized
{
get
{
Vector2d vector2d = new Vector2d(this.x, this.y);
vector2d.Normalize();
return vector2d;
}
}
[JsonIgnore]
public double magnitude
{
get
{
return Mathd.Sqrt(this.x * this.x + this.y * this.y);
}
}
[JsonIgnore]
public double sqrMagnitude
{
get
{
return this.x * this.x + this.y * this.y;
}
}
public static Vector2d zero
{
get
{
return new Vector2d(0.0d, 0.0d);
}
}
public static Vector2d one
{
get
{
return new Vector2d(1d, 1d);
}
}
public static Vector2d up
{
get
{
return new Vector2d(0.0d, 1d);
}
}
public static Vector2d right
{
get
{
return new Vector2d(1d, 0.0d);
}
}
public static Vector2d ChongMingLatLon
{
get
{
return new Vector2d(Constants.ChongMingLatitude,Constants.ChongMingLongitude);
}
}
public Vector2d(double x, double y)
{
this.x = x;
this.y = y;
}
public static Vector2d operator +(Vector2d a, Vector2d b)
{
return new Vector2d(a.x + b.x, a.y + b.y);
}
public static Vector2d operator -(Vector2d a, Vector2d b)
{
return new Vector2d(a.x - b.x, a.y - b.y);
}
public static Vector2d operator -(Vector2d a)
{
return new Vector2d(-a.x, -a.y);
}
public static Vector2d operator *(Vector2d a, double d)
{
return new Vector2d(a.x * d, a.y * d);
}
public static Vector2d operator *(float d, Vector2d a)
{
return new Vector2d(a.x * d, a.y * d);
}
public static Vector2d operator /(Vector2d a, double d)
{
return new Vector2d(a.x / d, a.y / d);
}
public static bool operator ==(Vector2d lhs, Vector2d rhs)
{
return Vector2d.SqrMagnitude(lhs - rhs) < 0.0 / 1.0;
}
public static bool operator !=(Vector2d lhs, Vector2d rhs)
{
return (double)Vector2d.SqrMagnitude(lhs - rhs) >= 0.0 / 1.0;
}
public void Set(double new_x, double new_y)
{
this.x = new_x;
this.y = new_y;
}
public static Vector2d Lerp(Vector2d from, Vector2d to, double t)
{
t = Mathd.Clamp01(t);
return new Vector2d(from.x + (to.x - from.x) * t, from.y + (to.y - from.y) * t);
}
public static Vector2d MoveTowards(Vector2d current, Vector2d target, double maxDistanceDelta)
{
Vector2d vector2 = target - current;
double magnitude = vector2.magnitude;
if (magnitude <= maxDistanceDelta || magnitude == 0.0d)
return target;
else
return current + vector2 / magnitude * maxDistanceDelta;
}
public static Vector2d Scale(Vector2d a, Vector2d b)
{
return new Vector2d(a.x * b.x, a.y * b.y);
}
public void Scale(Vector2d scale)
{
this.x *= scale.x;
this.y *= scale.y;
}
public void Normalize()
{
double magnitude = this.magnitude;
if (magnitude > 9.99999974737875E-06)
this = this / magnitude;
else
this = Vector2d.zero;
}
public override string ToString()
{
return string.Format(NumberFormatInfo.InvariantInfo, "{0:F5},{1:F5}", this.y, this.x);
}
public override int GetHashCode()
{
return this.x.GetHashCode() ^ this.y.GetHashCode() << 2;
}
public override bool Equals(object other)
{
if (!(other is Vector2d))
return false;
Vector2d vector2d = (Vector2d)other;
if (this.x.Equals(vector2d.x))
return this.y.Equals(vector2d.y);
else
return false;
}
public static double Dot(Vector2d lhs, Vector2d rhs)
{
return lhs.x * rhs.x + lhs.y * rhs.y;
}
public static double Angle(Vector2d from, Vector2d to)
{
return Mathd.Acos(Mathd.Clamp(Vector2d.Dot(from.normalized, to.normalized), -1d, 1d)) * 57.29578d;
}
public static double Distance(Vector2d a, Vector2d b)
{
return (a - b).magnitude;
}
public static Vector2d ClampMagnitude(Vector2d vector, double maxLength)
{
if (vector.sqrMagnitude > maxLength * maxLength)
return vector.normalized * maxLength;
else
return vector;
}
public static double SqrMagnitude(Vector2d a)
{
return (a.x * a.x + a.y * a.y);
}
public double SqrMagnitude()
{
return (this.x * this.x + this.y * this.y);
}
public static Vector2d Min(Vector2d lhs, Vector2d rhs)
{
return new Vector2d(Mathd.Min(lhs.x, rhs.x), Mathd.Min(lhs.y, rhs.y));
}
public static Vector2d Max(Vector2d lhs, Vector2d rhs)
{
return new Vector2d(Mathd.Max(lhs.x, rhs.x), Mathd.Max(lhs.y, rhs.y));
}
public double[] ToArray()
{
double[] array =
{
this.x,
this.y
};
return array;
}
}
}
RectD 结构体
namespace Mapbox.Utils
{
public struct RectD
{
public Vector2d Min { get; private set; }
public Vector2d Max { get; private set; }
public Vector2d Size { get; private set; }
public Vector2d Center { get; private set; }
public RectD(Vector2d min, Vector2d size)
{
Min = min;
Max = min + size;
Center = new Vector2d(Min.x + size.x / 2, Min.y + size.y / 2);
Size = new Vector2d(Mathd.Abs(size.x), Mathd.Abs(size.y));
}
public bool Contains(Vector2d point)
{
bool flag = Size.x < 0.0 && point.x <= Min.x && point.x > (Min.x + Size.x) || Size.x >= 0.0 && point.x >= Min.x && point.x < (Min.x + Size.x);
return flag && (Size.y < 0.0 && point.y <= Min.y && point.y > (Min.y + Size.y) || Size.y >= 0.0 && point.y >= Min.y && point.y < (Min.y + Size.y));
}
}
}
以下为坐标转换代码(此代码中包含与MapBox Tile 相关转换方法,如没有用到Tile可忽略):
namespace Mapbox.Unity.Utilities
{
using System;
using Mapbox.Utils;
using UnityEngine;
using System.Globalization;
public static class Conversions
{
private const int TileSize = 256;
private const int EarthRadius = 6378137;
private const double InitialResolution = 2 * Math.PI * EarthRadius / TileSize;
private const double OriginShift = 2 * Math.PI * EarthRadius / 2;
private const double EARTH_RADIUS = 6378137.0;
public static Vector2d LatLonToMeters(Vector2d v)
{
return LatLonToMeters(v.x, v.y);
}
public static Vector2d StringToLatLon(string s)
{
var latLonSplit = s.Split(',');
if (latLonSplit.Length != 2)
{
throw new ArgumentException("Wrong number of arguments");
}
double latitude = 0;
double longitude = 0;
if (!double.TryParse(latLonSplit[0], NumberStyles.Any, NumberFormatInfo.InvariantInfo, out latitude))
{
throw new Exception(string.Format("Could not convert latitude to double: {0}", latLonSplit[0]));
}
if (!double.TryParse(latLonSplit[1], NumberStyles.Any, NumberFormatInfo.InvariantInfo, out longitude))
{
throw new Exception(string.Format("Could not convert longitude to double: {0}", latLonSplit[1]));
}
return new Vector2d(latitude, longitude);
}
public static Vector2d LatLonToMeters(double lat, double lon)
{
var posx = lon * OriginShift / 180;
var posy = Math.Log(Math.Tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
posy = posy * OriginShift / 180;
return new Vector2d(posx, posy);
}
public static Vector2d GeoToWorldPosition(double lat, double lon, Vector2d refPoint, float scale = 1)
{
var posx = lon * OriginShift / 180;
var posy = Math.Log(Math.Tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
posy = posy * OriginShift / 180;
return new Vector2d((posx - refPoint.x) * scale, (posy - refPoint.y) * scale);
}
public static Vector2d GeoToWorldPosition(Vector2d latLong, Vector2d refPoint, float scale = 1)
{
return GeoToWorldPosition(latLong.x, latLong.y, refPoint, scale);
}
public static Vector3 GeoToWorldGlobePosition(double lat, double lon, float radius)
{
double xPos = (radius) * Math.Cos(Mathf.Deg2Rad * lat) * Math.Cos(Mathf.Deg2Rad * lon);
double zPos = (radius) * Math.Cos(Mathf.Deg2Rad * lat) * Math.Sin(Mathf.Deg2Rad * lon);
double yPos = (radius) * Math.Sin(Mathf.Deg2Rad * lat);
return new Vector3((float)xPos, (float)yPos, (float)zPos);
}
public static Vector3 GeoToWorldGlobePosition(Vector2d latLong, float radius)
{
return GeoToWorldGlobePosition(latLong.x, latLong.y, radius);
}
public static Vector2d GeoFromGlobePosition(Vector3 point, float radius)
{
float latitude = Mathf.Asin(point.y / radius);
float longitude = Mathf.Atan2(point.z, point.x);
return new Vector2d(latitude * Mathf.Rad2Deg, longitude * Mathf.Rad2Deg);
}
public static Vector2d MetersToLatLon(Vector2d m)
{
var vx = (m.x / OriginShift) * 180;
var vy = (m.y / OriginShift) * 180;
vy = 180 / Math.PI * (2 * Math.Atan(Math.Exp(vy * Math.PI / 180)) - Math.PI / 2);
return new Vector2d(vy, vx);
}
public static Vector2 MetersToTile(Vector2d m, int zoom)
{
var p = MetersToPixels(m, zoom);
return PixelsToTile(p);
}
public static RectD TileBounds(Vector2 tileCoordinate, int zoom)
{
var min = PixelsToMeters(new Vector2d(tileCoordinate.x * TileSize, tileCoordinate.y * TileSize), zoom);
var max = PixelsToMeters(new Vector2d((tileCoordinate.x + 1) * TileSize, (tileCoordinate.y + 1) * TileSize), zoom);
return new RectD(min, max - min);
}
public static Vector2 LatitudeLongitudeToUnityTilePosition(Vector2d coordinate, UnityTile tile, ulong layerExtent = 4096)
{
return LatitudeLongitudeToUnityTilePosition(coordinate, tile.CurrentZoom, tile.TileScale, layerExtent);
}
public static Vector2 LatitudeLongitudeToUnityTilePosition(Vector2d coordinate, int tileZoom, float tileScale, ulong layerExtent = 4096)
{
var coordinateTileId = Conversions.LatitudeLongitudeToTileId(
coordinate.x, coordinate.y, tileZoom);
var _rect = Conversions.TileBounds(coordinateTileId);
var vectorTilePoint = LatitudeLongitudeToVectorTilePosition(coordinate, tileZoom, layerExtent);
var unityTilePoint = new Vector2((float)(vectorTilePoint.x / layerExtent * _rect.Size.x - (_rect.Size.x / 2)) * tileScale,
(float)((layerExtent - vectorTilePoint.y) / layerExtent * _rect.Size.y - (_rect.Size.y / 2)) * tileScale);
return unityTilePoint;
}
public static double TileXToNWLongitude(int x, int zoom)
{
var n = Math.Pow(2.0, zoom);
var lon_deg = x / n * 360.0 - 180.0;
return lon_deg;
}
public static double TileYToNWLatitude(int y, int zoom)
{
var n = Math.Pow(2.0, zoom);
var lat_rad = Math.Atan(Math.Sinh(Math.PI * (1 - 2 * y / n)));
var lat_deg = lat_rad * 180.0 / Math.PI;
return lat_deg;
}
public static Vector2dBounds TileIdToBounds(int x, int y, int zoom)
{
var sw = new Vector2d(TileYToNWLatitude(y, zoom), TileXToNWLongitude(x + 1, zoom));
var ne = new Vector2d(TileYToNWLatitude(y + 1, zoom), TileXToNWLongitude(x, zoom));
return new Vector2dBounds(sw, ne);
}
public static Vector2d TileIdToCenterLatitudeLongitude(int x, int y, int zoom)
{
var bb = TileIdToBounds(x, y, zoom);
var center = bb.Center;
return new Vector2d(center.x, center.y);
}
public static Vector2d TileIdToCenterWebMercator(int x, int y, int zoom)
{
double tileCnt = Math.Pow(2, zoom);
double centerX = x + 0.5;
double centerY = y + 0.5;
centerX = ((centerX / tileCnt * 2) - 1) * Constants.WebMercMax;
centerY = (1 - (centerY / tileCnt * 2)) * Constants.WebMercMax;
return new Vector2d(centerX, centerY);
}
public static float GetTileScaleInMeters(float latitude, int zoom)
{
return (float)(40075016.685578d * Math.Cos(Mathf.Deg2Rad * latitude) / Math.Pow(2f, zoom + 8));
}
public static float GetTileScaleInDegrees(float latitude, int zoom)
{
return (float)(360.0f / Math.Pow(2f, zoom + 8));
}
public static float GetRelativeHeightFromColor(Color color, float relativeScale)
{
return GetAbsoluteHeightFromColor(color) * relativeScale;
}
public static float GetAbsoluteHeightFromColor(Color color)
{
return (float)(-10000 + ((color.r * 255 * 256 * 256 + color.g * 255 * 256 + color.b * 255) * 0.1));
}
public static float GetAbsoluteHeightFromColor32(Color32 color)
{
return (float)(-10000 + ((color.r * 256 * 256 + color.g * 256 + color.b) * 0.1));
}
public static float GetAbsoluteHeightFromColor(float r, float g, float b)
{
return (float)(-10000 + ((r * 256 * 256 + g * 256 + b) * 0.1));
}
private static double Resolution(int zoom)
{
return InitialResolution / Math.Pow(2, zoom);
}
private static Vector2d PixelsToMeters(Vector2d p, int zoom)
{
var res = Resolution(zoom);
var met = new Vector2d();
met.x = (p.x * res - OriginShift);
met.y = -(p.y * res - OriginShift);
return met;
}
private static Vector2d MetersToPixels(Vector2d m, int zoom)
{
var res = Resolution(zoom);
var pix = new Vector2d(((m.x + OriginShift) / res), ((-m.y + OriginShift) / res));
return pix;
}
private static Vector2 PixelsToTile(Vector2d p)
{
var t = new Vector2((int)Math.Ceiling(p.x / (double)TileSize) - 1, (int)Math.Ceiling(p.y / (double)TileSize) - 1);
return t;
}
public static Vector3 LatLonToWorldPosition(Vector2d centerLatLon)
{
Vector2d centerMetersLatLon = LatLonToMeters(centerLatLon);
var offsetX = (float)(centerMetersLatLon.x - AbstractMap.Instance.CenterMercator.x);
var offsetY = (float)(centerMetersLatLon.y - AbstractMap.Instance.CenterMercator.y);
var px = (float)offsetX * AbstractMap.Instance.WorldRelativeScale;
var pz = (float)offsetY * AbstractMap.Instance.WorldRelativeScale;
return new Vector3(px, 0, pz);
}
public static Vector2d WorldPosToLatLonPosition(Vector3 realworldPoint)
{
return AbstractMap.Instance.WorldToGeoPosition(realworldPoint);
}
public static Vector3 CenterLatLonToPosition(Vector2d centerLatLon)
{
Vector2d centerMetersLatLon = LatLonToMeters(centerLatLon);
var offsetX = (float)(centerMetersLatLon.x - AbstractMap.Instance.CenterMercator.x);
var offsetY = (float)(centerMetersLatLon.y - AbstractMap.Instance.CenterMercator.y);
var px = (float)offsetX * AbstractMap.Instance.WorldRelativeScale;
var pz = (float)offsetY * AbstractMap.Instance.WorldRelativeScale;
var worldPx = px * AbstractMap.Instance.transform.localScale.x;
var worldPz = pz * AbstractMap.Instance.transform.localScale.z;
return new Vector3(worldPx, 0, worldPz);
}
public static Vector3 LatLonToPositionWorld(Vector2d centerlatLon)
{
return AbstractMap.Instance.GeoToWorldPosition(centerlatLon);
}
public static void UpdateMap(Vector2d latLon, float zoom)
{
AbstractMap.Instance.UpdateMap(latLon,zoom);
}
public static Vector2d WorldToGeoPosition(Vector3 realworldPoint)
{
float scale = AbstractMap.Instance.transform.localScale.x;
Vector3 worldPoint = new Vector3(realworldPoint.x*scale,realworldPoint.y*scale,realworldPoint.z*scale);
return AbstractMap.Instance.WorldToGeoPosition(worldPoint);
}
public static double GetDistance(double longitude1, double latitude1,
double longitude2, double latitude2)
{
double Lat1 = rad(latitude1);
double Lat2 = rad(latitude2);
double a = Lat1 - Lat2;
double b = rad(longitude1) - rad(longitude2);
double s = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2)
+ Math.Cos(Lat1) * Math.Cos(Lat2)
* Math.Pow(Math.Sin(b / 2), 2)));
s = s * EARTH_RADIUS;
s = Math.Round(s * 10000) / 10000;
return s;
}
private static double rad(double d)
{
return d * Math.PI / 180.0;
}
}
}
|