我正在开发一个Objective C
应用,可以从服务器接收GPS路线。路由已编码,我有用PHP
和C#
编写的解码脚本。
据我所知,不可能将脚本编译或导入Xcode项目。
我已将下面的两个脚本与编码的GPS路线的副本一起附加。
我已经研究了相当多的脚本并且理解(大部分)正在发生的事情。)将此代码移植到Objective C
是否可行?如果是这样,最好的方法是什么?我将如何移植byte streams
?
我有点困惑,因为我之前从未使用过编码/解码,但我们非常感谢任何帮助。
编码的GPS路线
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
C#解码脚本
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
// Classes for managing IOF XML 3.0 Route element.
// IOF XML 3.0 specification: http://code.google.com/p/iofdatastandard/source/browse/trunk/IOF.xsd.
// IOF XML 3.0 example result list file with Route element: http://code.google.com/p/iofdatastandard/source/browse/trunk/Examples/ResultList1.xml.
/// <summary>
/// Class representing a route, including logic for converting to/from an IOF XML 3.0 route stored in binary format.
/// </summary>
public class IofXml30Route
{
private double? length;
private IEnumerable<IofXml30Waypoint> waypoints = new List<IofXml30Waypoint>();
/// <summary>
/// The waypoints of the route.
/// </summary>
public IEnumerable<IofXml30Waypoint> Waypoints
{
get { return waypoints; }
set { waypoints = value ?? new List<IofXml30Waypoint>(); }
}
/// <summary>
/// Writes the route in IOF XML 3.0 binary format to the specified stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
public void WriteToStream(Stream stream)
{
IofXml30Waypoint previousWaypoint = null;
foreach (var waypoint in Waypoints)
{
waypoint.WriteToStream(stream, previousWaypoint);
previousWaypoint = waypoint;
}
}
/// <summary>
/// Converts the route to IOF XML 3.0 binary format and returns it as a base64-encoded string.
/// </summary>
/// <param name="formattingOptions">The formatting options for the base64-encoded string.</param>
public string ToBase64String(Base64FormattingOptions formattingOptions = Base64FormattingOptions.None)
{
return Convert.ToBase64String(ToByteArray(), formattingOptions);
}
/// <summary>
/// Converts the route to IOF XML 3.0 binary format and returns it as a byte array.
/// </summary>
public byte[] ToByteArray()
{
using (var ms = new MemoryStream())
{
WriteToStream(ms);
return ms.ToArray();
}
}
/// <summary>
/// Reads a route in IOF XML 3.0 binary format from a stream.
/// </summary>
/// <param name="stream">The stream to read from.</param>
public static IofXml30Route FromStream(Stream stream)
{
var waypoints = new List<IofXml30Waypoint>();
while (stream.Position < stream.Length)
{
waypoints.Add(IofXml30Waypoint.FromStream(stream, waypoints.LastOrDefault()));
}
return new IofXml30Route() { Waypoints = waypoints };
}
/// <summary>
/// Reads a route in IOF XML 3.0 binary format from a base64-encoded string.
/// </summary>
/// <param name="base64String">The base64-encoded string to read from.</param>
public static IofXml30Route FromBase64String(string base64String)
{
return FromByteArray(Convert.FromBase64String(base64String));
}
/// <summary>
/// Reads a route in IOF XML 3.0 binary format from a byte array.
/// </summary>
/// <param name="bytes">The bytes to read from.</param>
public static IofXml30Route FromByteArray(byte[] bytes)
{
using (var ms = new MemoryStream(bytes))
{
return FromStream(ms);
}
}
/// <summary>
/// Gets the length of the route in meters.
/// </summary>
public double Length
{
get { return length ?? (length = CalculateLength()).Value; }
}
/// <summary>
/// Gets the start time of the route.
/// </summary>
public DateTime StartTime
{
get { return Waypoints.Any() ? Waypoints.First().Time : DateTime.MinValue; }
}
/// <summary>
/// Gets the end time of the route.
/// </summary>
public DateTime EndTime
{
get { return Waypoints.Any() ? Waypoints.Last().Time : DateTime.MinValue; }
}
/// <summary>
/// Gets the duration of the route.
/// </summary>
public TimeSpan Duration
{
get { return EndTime - StartTime; }
}
private double CalculateLength()
{
var sum = 0.0;
var wpList = Waypoints.ToList();
for(var i=1; i<Waypoints.Count(); i++)
{
sum += GetDistanceBetweenWaypoints(wpList[i - 1], wpList[i]);
}
return sum;
}
private static double GetDistanceBetweenWaypoints(IofXml30Waypoint w1, IofXml30Waypoint w2)
{
// use spherical coordinates: rho, phi, theta
const double rho = 6378200; // earth radius in metres
double sinPhi0 = Math.Sin(0.5 * Math.PI + w1.Latitude / 180.0 * Math.PI);
double cosPhi0 = Math.Cos(0.5 * Math.PI + w1.Latitude / 180.0 * Math.PI);
double sinTheta0 = Math.Sin(w1.Longitude / 180.0 * Math.PI);
double cosTheta0 = Math.Cos(w1.Longitude / 180.0 * Math.PI);
double sinPhi1 = Math.Sin(0.5 * Math.PI + w2.Latitude / 180.0 * Math.PI);
double cosPhi1 = Math.Cos(0.5 * Math.PI + w2.Latitude / 180.0 * Math.PI);
double sinTheta1 = Math.Sin(w2.Longitude / 180.0 * Math.PI);
double cosTheta1 = Math.Cos(w2.Longitude / 180.0 * Math.PI);
var x1 = rho * sinPhi0 * cosTheta0;
var y1 = rho * sinPhi0 * sinTheta0;
var z1 = rho * cosPhi0;
var x2 = rho * sinPhi1 * cosTheta1;
var y2 = rho * sinPhi1 * sinTheta1;
var z2 = rho * cosPhi1;
return DistancePointToPoint(x1, y1, z1, x2, y2, z2);
}
private static double DistancePointToPoint(double x1, double y1, double z1, double x2, double y2, double z2)
{
var sum = (x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1) + (z2 - z1)*(z2 - z1);
return Math.Sqrt(sum);
}
}
/// <summary>
/// Class representing a waypoint, including logic for converting to/from an IOF XML 3.0 waypoint stored in binary format.
/// </summary>
public class IofXml30Waypoint
{
private static readonly DateTime zeroTime = new DateTime(1900, 01, 01, 00, 00, 00, DateTimeKind.Utc);
private const long timeSecondsThreshold = 255;
private const long timeMillisecondsThreshold = 65535;
private const int lanLngBigDeltaLowerThreshold = -32768;
private const int lanLngBigDeltaUpperThreshold = 32767;
private const int lanLngSmallDeltaLowerThreshold = -128;
private const int lanLngSmallDeltaUpperThreshold = 127;
private const int altitudeDeltaLowerThreshold = -128;
private const int altitudeDeltaUpperThreshold = 127;
/// <summary>
/// Gets or sets the type of the waypoint; normal or interruption.
/// </summary>
public IofXml30WaypointType Type { get; set; }
/// <summary>
/// Gets or sets the time when the waypoint was recorded.
/// </summary>
public DateTime Time { get; set; }
/// <summary>
/// Gets or sets the latitude of the waypoint.
/// </summary>
public double Latitude { get; set; }
/// <summary>
/// Gets or sets the longitude of the waypoint.
/// </summary>
public double Longitude { get; set; }
/// <summary>
/// Gets or sets the altitude of the waypoint.
/// </summary>
public double? Altitude { get; set; }
/// <summary>
/// Gets or sets the the time when the waypoint was recorded in the internal storage mode.
/// </summary>
public ulong StorageTime
{
get { return (ulong)Math.Round((Time - zeroTime).TotalMilliseconds); }
set { Time = zeroTime.AddMilliseconds(value); }
}
/// <summary>
/// Gets or sets the latitude of the waypoint in the internal storage mode.
/// </summary>
public int StorageLatitude
{
get { return (int)Math.Round(Latitude * 1000000); }
set { Latitude = (double)value / 1000000; }
}
/// <summary>
/// Gets or sets the longitude of the waypoint in the internal storage mode.
/// </summary>
public int StorageLongitude
{
get { return (int)Math.Round(Longitude * 1000000); }
set { Longitude = (double)value / 1000000; }
}
/// <summary>
/// Gets or sets the altitude of the waypoint in the internal storage mode.
/// </summary>
public int? StorageAltitude
{
get { return Altitude == null ? (int?)null : (int)Math.Round(Altitude.Value * 10); }
set { Altitude = value == null ? (double?)null : (double)value / 10; }
}
/// <summary>
/// Writes the waypoint in IOF XML 3.0 binary format to a stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="previousWaypoint">The previous waypoint of the route, or null if this is the first waypoint.</param>
public void WriteToStream(Stream stream, IofXml30Waypoint previousWaypoint)
{
var timeStorageMode = TimeStorageMode.Full;
if (previousWaypoint != null)
{
if ((StorageTime - previousWaypoint.StorageTime) % 1000 == 0 && (StorageTime - previousWaypoint.StorageTime) / 1000 <= timeSecondsThreshold)
{
timeStorageMode = TimeStorageMode.Seconds;
}
else if (StorageTime - previousWaypoint.StorageTime <= timeMillisecondsThreshold)
{
timeStorageMode = TimeStorageMode.Milliseconds;
}
}
var positionStorageMode = PositionStorageMode.Full;
if (previousWaypoint != null &&
(StorageAltitude == null || (previousWaypoint.StorageAltitude != null && StorageAltitude - previousWaypoint.StorageAltitude >= altitudeDeltaLowerThreshold && StorageAltitude - previousWaypoint.StorageAltitude <= altitudeDeltaUpperThreshold)))
{
if (StorageLatitude - previousWaypoint.StorageLatitude >= lanLngSmallDeltaLowerThreshold && StorageLatitude - previousWaypoint.StorageLatitude <= lanLngSmallDeltaUpperThreshold &&
StorageLongitude - previousWaypoint.StorageLongitude >= lanLngSmallDeltaLowerThreshold && StorageLongitude - previousWaypoint.StorageLongitude <= lanLngSmallDeltaUpperThreshold)
{
positionStorageMode = PositionStorageMode.SmallDelta;
}
else if (StorageLatitude - previousWaypoint.StorageLatitude >= lanLngBigDeltaLowerThreshold && StorageLatitude - previousWaypoint.StorageLatitude <= lanLngBigDeltaUpperThreshold &&
StorageLongitude - previousWaypoint.StorageLongitude >= lanLngBigDeltaLowerThreshold && StorageLongitude - previousWaypoint.StorageLongitude <= lanLngBigDeltaUpperThreshold)
{
positionStorageMode = PositionStorageMode.BigDelta;
}
}
var headerByte = 0;
if (Type == IofXml30WaypointType.Interruption) headerByte |= (1 << 7);
if (timeStorageMode == TimeStorageMode.Milliseconds) headerByte |= (1 << 6);
if (timeStorageMode == TimeStorageMode.Seconds) headerByte |= (1 << 5);
if (positionStorageMode == PositionStorageMode.BigDelta) headerByte |= (1 << 4);
if (positionStorageMode == PositionStorageMode.SmallDelta) headerByte |= (1 << 3);
if (StorageAltitude != null) headerByte |= (1 << 2);
// header byte
stream.WriteByte((byte)headerByte);
// time byte(s)
switch (timeStorageMode)
{
case TimeStorageMode.Full: // 6 bytes
stream.Write(BitConverter.GetBytes(StorageTime).Reverse().ToArray(), 2, 6);
break;
case TimeStorageMode.Milliseconds: // 2 bytes
stream.Write(BitConverter.GetBytes((ushort)(StorageTime - previousWaypoint.StorageTime)).Reverse().ToArray(), 0, 2);
break;
case TimeStorageMode.Seconds: // 1 byte
stream.WriteByte((byte)((StorageTime - previousWaypoint.StorageTime) / 1000));
break;
}
// position bytes
switch (positionStorageMode)
{
case PositionStorageMode.Full: // 4 + 4 + 3 bytes
stream.Write(BitConverter.GetBytes(StorageLatitude).Reverse().ToArray(), 0, 4);
stream.Write(BitConverter.GetBytes(StorageLongitude).Reverse().ToArray(), 0, 4);
if (StorageAltitude != null) stream.Write(BitConverter.GetBytes(StorageAltitude.Value).Reverse().ToArray(), 1, 3);
break;
case PositionStorageMode.BigDelta: // 2 + 2 + 1 bytes
stream.Write(BitConverter.GetBytes((short)(StorageLatitude - previousWaypoint.StorageLatitude)).Reverse().ToArray(), 0, 2);
stream.Write(BitConverter.GetBytes((short)(StorageLongitude - previousWaypoint.StorageLongitude)).Reverse().ToArray(), 0, 2);
if (StorageAltitude != null) stream.Write(BitConverter.GetBytes((sbyte)(StorageAltitude - previousWaypoint.StorageAltitude).Value), 0, 1);
break;
case PositionStorageMode.SmallDelta: // 1 + 1 + 1 bytes
stream.Write(BitConverter.GetBytes((sbyte)(StorageLatitude - previousWaypoint.StorageLatitude)), 0, 1);
stream.Write(BitConverter.GetBytes((sbyte)(StorageLongitude - previousWaypoint.StorageLongitude)), 0, 1);
if (StorageAltitude != null) stream.Write(BitConverter.GetBytes((sbyte)(StorageAltitude - previousWaypoint.StorageAltitude).Value), 0, 1);
break;
}
}
/// <summary>
/// Reads a waypoint in IOF XML 3.0 binary format from a stream.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <param name="previousWaypoint">The previous waypoint of the route, or null if this is the first waypoint.</param>
/// <returns></returns>
public static IofXml30Waypoint FromStream(Stream stream, IofXml30Waypoint previousWaypoint)
{
var waypoint = new IofXml30Waypoint();
// header byte
var headerByte = stream.ReadByte();
waypoint.Type = (headerByte & (1 << 7)) == 0 ? IofXml30WaypointType.Normal : IofXml30WaypointType.Interruption;
var timeStorageMode = TimeStorageMode.Full;
if ((headerByte & (1 << 6)) > 0)
{
timeStorageMode = TimeStorageMode.Milliseconds;
}
else if ((headerByte & (1 << 5)) > 0)
{
timeStorageMode = TimeStorageMode.Seconds;
}
var positionStorageMode = PositionStorageMode.Full;
if ((headerByte & (1 << 4)) > 0)
{
positionStorageMode = PositionStorageMode.BigDelta;
}
else if ((headerByte & (1 << 3)) > 0)
{
positionStorageMode = PositionStorageMode.SmallDelta;
}
var altitudePresent = (headerByte & (1 << 2)) > 0;
byte[] bytes;
int b;
// time byte(s)
switch (timeStorageMode)
{
case TimeStorageMode.Full: // 4 bytes
bytes = new byte[8];
stream.Read(bytes, 2, 6);
waypoint.StorageTime = BitConverter.ToUInt64(bytes.Reverse().ToArray(), 0);
break;
case TimeStorageMode.Milliseconds: // 2 bytes
bytes = new byte[2];
stream.Read(bytes, 0, 2);
waypoint.StorageTime = previousWaypoint.StorageTime + BitConverter.ToUInt16(bytes.Reverse().ToArray(), 0);
break;
case TimeStorageMode.Seconds: // 1 byte
b = stream.ReadByte();
waypoint.StorageTime = previousWaypoint.StorageTime + (ulong)b * 1000;
break;
}
// position bytes
switch (positionStorageMode)
{
case PositionStorageMode.Full: // 4 + 4 + 3 bytes
bytes = new byte[4];
stream.Read(bytes, 0, 4);
waypoint.StorageLatitude = BitConverter.ToInt32(bytes.Reverse().ToArray(), 0);
bytes = new byte[4];
stream.Read(bytes, 0, 4);
waypoint.StorageLongitude = BitConverter.ToInt32(bytes.Reverse().ToArray(), 0);
if (altitudePresent)
{
bytes = new byte[4];
stream.Read(bytes, 1, 3);
waypoint.StorageAltitude = BitConverter.ToInt32(bytes.Reverse().ToArray(), 0);
}
break;
case PositionStorageMode.BigDelta: // 2 + 2 + 1 bytes
bytes = new byte[2];
stream.Read(bytes, 0, 2);
waypoint.StorageLatitude = previousWaypoint.StorageLatitude + BitConverter.ToInt16(bytes.Reverse().ToArray(), 0);
bytes = new byte[2];
stream.Read(bytes, 0, 2);
waypoint.StorageLongitude = previousWaypoint.StorageLongitude + BitConverter.ToInt16(bytes.Reverse().ToArray(), 0);
if (altitudePresent)
{
b = stream.ReadByte();
waypoint.StorageAltitude = previousWaypoint.StorageAltitude + (sbyte)b;
}
break;
case PositionStorageMode.SmallDelta: // 1 + 1 + 1 bytes
b = stream.ReadByte();
waypoint.StorageLatitude = previousWaypoint.StorageLatitude + (sbyte)b;
b = stream.ReadByte();
waypoint.StorageLongitude = previousWaypoint.StorageLongitude + (sbyte)b;
if (altitudePresent)
{
b = stream.ReadByte();
waypoint.StorageAltitude = previousWaypoint.StorageAltitude + (sbyte)b;
}
break;
}
return waypoint;
}
/// <summary>
/// The storage mode for the time of a waypoint.
/// </summary>
private enum TimeStorageMode
{
/// <summary>
/// The time is stored as a 6-byte unsigned integer, and shows the number of milliseconds since January 1, 1900, 00:00:00 UTC.
/// </summary>
Full,
/// <summary>
/// The time is stored as a 2-byte unsigned integer, and shows the number of seconds since the previous waypoint's time.
/// </summary>
Seconds,
/// <summary>
/// The time is stored as a 4-byte unsigned integer, and shows the number of milliseconds since the previous waypoint's time.
/// </summary>
Milliseconds
}
/// <summary>
/// The storage mode for the position (latitude, longitude, altitude) of a waypoint.
/// </summary>
private enum PositionStorageMode
{
/// <summary>
/// The longitude and latitude are stored as microdegrees in 4-byte signed integers, and the altitude is stored as decimeters in a 3-byte signed integer.
/// </summary>
Full,
/// <summary>
/// The longitude and latitude are stored as microdegrees relative to the previous waypoint in 2-byte signed integers, and the altitude is stored as decimeters relative to the previous waypoint in a 3-byte signed integer>.
/// </summary>
BigDelta,
/// <summary>
/// The longitude and latitude are stored as microdegrees relative to the previous waypoint in 1-byte signed integers, and the altitude is stored as decimeters relative to the previous waypoint in a 1-byte signed integer.
/// </summary>
SmallDelta
}
}
/// <summary>
/// The type of waypoint.
/// </summary>
public enum IofXml30WaypointType
{
/// <summary>
/// A normal waypoint.
/// </summary>
Normal,
/// <summary>
/// A waypoint that is the last waypoint before an interruption in the route occurs.
/// </summary>
Interruption
}
PHP解码脚本
PHP
脚本非常相似。由于这篇文章的大小限制,我无法附加,但是如果需要,我可以提供它。