265 lines
8.3 KiB
C#
265 lines
8.3 KiB
C#
using System.Text;
|
|
using GeoGraph;
|
|
|
|
namespace OSMDatastructure;
|
|
|
|
public static class ByteConverter
|
|
{
|
|
#region Region
|
|
/*
|
|
* | regionHash | Nodes |
|
|
* |---------------+---------------+
|
|
* | 8 bytes |
|
|
* | ulong |
|
|
*/
|
|
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(ulong) + totalNodeBytes];
|
|
Array.Copy(BitConverter.GetBytes(region.regionHash), 0, retBytes, 0, sizeof(ulong));
|
|
int offset = sizeof(ulong);
|
|
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.ToUInt64(bytes, 0));
|
|
int offset = sizeof(ulong);
|
|
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 = 4 + 8; //size + Coordinates
|
|
HashSet<byte[]> edges = new();
|
|
foreach (OsmEdge edge in node.edges)
|
|
{
|
|
edges.Add(GetBytes(edge));
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 = Array.Empty<byte>();
|
|
if (tag.Value.GetType() == Type.GetType("string"))
|
|
{
|
|
objectBytes = Encoding.ASCII.GetBytes((string)tag.Value);
|
|
}
|
|
else if (tag.Value.GetType() == Type.GetType("bool"))
|
|
{
|
|
objectBytes = BitConverter.GetBytes((bool)tag.Value);
|
|
}
|
|
else if (tag.Value.GetType() == Type.GetType("int"))
|
|
{
|
|
objectBytes = BitConverter.GetBytes((int)tag.Value);
|
|
}
|
|
else if (tag.Value.GetType() == Type.GetType("ulong"))
|
|
{
|
|
objectBytes = BitConverter.GetBytes((ulong)tag.Value);
|
|
}
|
|
else if (tag.Value.GetType() == Type.GetType("byte"))
|
|
{
|
|
objectBytes = new[] { (byte)tag.Value };
|
|
}
|
|
|
|
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, retBytes.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, (Way.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
|
|
}
|
|
|