OSMServer/OSMDatastructure/ByteConverter.cs

273 lines
8.6 KiB
C#

using System.Text;
using OSMDatastructure.Graph;
namespace OSMDatastructure;
public static class ByteConverter
{
#region Region
/*
* | regionHash | Nodes |
* |---------------+---------------+
* | 4 bytes |
* | int |
*/
public static byte[] GetBytes(Region region)
{
int totalNodeBytes = 0;
HashSet<byte[]> nodes = new();
foreach (OsmNode node in region.nodes)
{
byte[] nodeBytes = GetBytes(node);
totalNodeBytes += nodeBytes.Length;
nodes.Add(nodeBytes);
}
byte[] retBytes = new byte[sizeof(int) + totalNodeBytes];
Array.Copy(BitConverter.GetBytes(region.regionHash), 0, retBytes, 0, sizeof(int));
int offset = sizeof(int);
foreach (byte[] node in nodes)
{
Array.Copy(node, 0, retBytes, offset, node.Length);
offset += node.Length;
}
return retBytes;
}
public static Region ToRegion(byte[] bytes)
{
Region retRegion = new Region(BitConverter.ToInt32(bytes, 0));
int offset = sizeof(int);
while (offset < bytes.Length)
{
int size = BitConverter.ToInt32(bytes, offset);
byte[] nodeBytes = new byte[size];
Array.Copy(bytes, offset, nodeBytes, 0, size);
offset += size;
retRegion.nodes.Add(ToNode(nodeBytes));
}
return retRegion;
}
#endregion
#region Coordinates
/*
* | Latitude | Longitude |
* |---------------+---------------+
* | 4 bytes | 4 bytes |
* | float | float |
*/
public static byte[] GetBytes(Coordinates coordinates)
{
byte[] retBytes = new byte[8];
Array.Copy(BitConverter.GetBytes(coordinates.latitude), 0, retBytes, 0, sizeof(float));
Array.Copy(BitConverter.GetBytes(coordinates.longitude), 0, retBytes, sizeof(float), sizeof(float));
return retBytes;
}
public static Coordinates ToCoordinates(byte[] bytes)
{
if (bytes.Length != 8)
throw new ArgumentException("Needs exactly 8 bytes", nameof(bytes));
float latitude = BitConverter.ToSingle(bytes, 0);
float longitude = BitConverter.ToSingle(bytes, sizeof(float));
return new Coordinates(latitude, longitude);
}
#endregion
#region OsmNode
/*
* | Size | Coordinates | Edges
* |---------------+---------------+---------------+
* | 4 bytes | 8 bytes |
* | int | byte[] |
*/
public static byte[] GetBytes(OsmNode node)
{
int totalBytes = sizeof(int) + 8; //size + Coordinates
HashSet<byte[]> edges = new();
foreach (OsmEdge edge in node.edges)
{
byte[] edgeBytes = GetBytes(edge);
edges.Add(edgeBytes);
totalBytes += edgeBytes.Length;
}
byte[] retBytes = new byte[totalBytes];
Array.Copy(BitConverter.GetBytes(totalBytes), retBytes, 4);
Array.Copy(GetBytes(node.coordinates), 0, retBytes, 4, 8);
int offset = 4 + 8;
foreach (byte[] edgeBytes in edges)
{
Array.Copy(edgeBytes, 0, retBytes, offset, edgeBytes.Length);
offset += edgeBytes.Length;
}
return retBytes;
}
public static OsmNode ToNode(byte[] bytes)
{
byte[] coordinateBytes = new byte[8];
Array.Copy(bytes, 4, coordinateBytes, 0, 8);
Coordinates coordinates = ToCoordinates(coordinateBytes);
OsmNode retNode = new OsmNode(coordinates);
int offset = sizeof(int) + 8;
while (offset < bytes.Length)
{
int tagsSize = BitConverter.ToInt32(bytes, offset);
byte[] edgeBytes = new byte[sizeof(int) + 8 + tagsSize];
Array.Copy(bytes, offset, edgeBytes, 0, edgeBytes.Length);
offset += edgeBytes.Length;
retNode.edges.Add(ToEdge(edgeBytes));
}
return retNode;
}
#endregion
#region OsmEdge
/* Edge:
* | tagsSize | neighborCoord | tags
* |---------------+---------------+--------------
* | 4 bytes | 8 bytes |
* | int | byte[] |
*
*/
public static byte[] GetBytes(OsmEdge edge)
{
int totalBytes = sizeof(int) + 8; //size + Coordinates
int offset = totalBytes;
int tagsSize = 0;
HashSet<byte[]> tags = new();
foreach (KeyValuePair<OsmEdge.tagType, object> kv in edge.tags)
{
byte[] tagBytes = GetBytes(kv);
tags.Add(tagBytes);
tagsSize += tagBytes.Length;
}
totalBytes += tagsSize;
byte[] retBytes = new byte[totalBytes];
Array.Copy(BitConverter.GetBytes(tagsSize), retBytes, 4);
Array.Copy(GetBytes(edge.neighborCoordinates), 0, retBytes, sizeof(int), 8);
foreach (byte[] tag in tags)
{
Array.Copy(tag, 0, retBytes, offset, tag.Length);
offset += tag.Length;
}
return retBytes;
}
public static OsmEdge ToEdge(byte[] bytes)
{
byte[] coordinateBytes = new byte[8];
Array.Copy(bytes, 4, coordinateBytes, 0, 8);
Coordinates coordinates = ToCoordinates(coordinateBytes);
OsmEdge retEdge = new OsmEdge(coordinates);
int offset = sizeof(int) + 8;
while (offset < bytes.Length)
{
int tagContentSize = BitConverter.ToInt32(bytes, offset);
byte[] tagBytes = new byte[sizeof(int) + 1 + tagContentSize];
Array.Copy(bytes, offset, tagBytes, 0, tagBytes.Length);
offset += tagBytes.Length;
KeyValuePair<OsmEdge.tagType, object> tag = ToTag(tagBytes);
retEdge.tags.Add(tag.Key, tag.Value);
}
return retEdge;
}
#endregion
#region OsmEdge.Tag
/*
* Tag:
* | contentSize | tagType | tagContent
* |---------------+---------------+---------------
* | 4 bytes | 1 byte |
* | int | byte |
*/
public static byte[] GetBytes(KeyValuePair<OsmEdge.tagType, object> tag)
{
byte[] objectBytes;
Type objectType = tag.Value.GetType();
if (objectType == Type.GetType("System.String"))
{
objectBytes = Encoding.ASCII.GetBytes((string)tag.Value);
}
else if (objectType == Type.GetType("System.Boolean"))
{
objectBytes = BitConverter.GetBytes((bool)tag.Value);
}
else if (objectType == Type.GetType("System.Int32"))
{
objectBytes = BitConverter.GetBytes((int)tag.Value);
}
else if (objectType == Type.GetType("System.UInt64"))
{
objectBytes = BitConverter.GetBytes((ulong)tag.Value);
}
else if (objectType == Type.GetType("System.Byte") || objectType.Name == "wayType")
{
objectBytes = new[] { (byte)tag.Value };
}
else
{
throw new Exception(string.Format("Tag-Bytes object-Type: {0}", tag.Value.GetType()));
}
byte[] retBytes = new byte[sizeof(int) + 1 + objectBytes.Length];
Array.Copy(BitConverter.GetBytes(objectBytes.Length), 0, retBytes, 0, sizeof(int));
retBytes[sizeof(int)] = (byte)tag.Key;
Array.Copy(objectBytes, 0, retBytes, sizeof(int) + 1, objectBytes.Length);
return retBytes;
}
public static KeyValuePair<OsmEdge.tagType, object> ToTag(byte[] bytes)
{
OsmEdge.tagType tagType = (OsmEdge.tagType)bytes[sizeof(int)];
int contentSize = BitConverter.ToInt32(bytes, 0);
byte[] content = new byte[contentSize];
Array.Copy(bytes, sizeof(int) + 1, content, 0, contentSize);
switch (tagType)
{
case OsmEdge.tagType.highway:
return new KeyValuePair<OsmEdge.tagType, object>(tagType, (OsmEdge.wayType)content[0]);
case OsmEdge.tagType.maxspeed:
return new KeyValuePair<OsmEdge.tagType, object>(tagType, content[0]);
case OsmEdge.tagType.oneway:
return new KeyValuePair<OsmEdge.tagType, object>(tagType, BitConverter.ToBoolean(content));
case OsmEdge.tagType.forward:
return new KeyValuePair<OsmEdge.tagType, object>(tagType, BitConverter.ToBoolean(content));
case OsmEdge.tagType.id:
return new KeyValuePair<OsmEdge.tagType, object>(tagType, BitConverter.ToUInt64(content));
default:
return new KeyValuePair<OsmEdge.tagType, object>(tagType, content);
}
}
#endregion
}