using System.Text; namespace OSMDatastructure; public static class ByteConverter { #region Region /* * | regionHash | Nodes | * |---------------+---------------+ * | 8 bytes | * | ulong | */ public static byte[] GetBytes(Region region) { int totalNodeBytes = 0; HashSet 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 = sizeof(int) + 8; //size + Coordinates HashSet 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 tags = new(); foreach (KeyValuePair 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 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 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 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(tagType, (OsmEdge.wayType)content[0]); case OsmEdge.tagType.maxspeed: return new KeyValuePair(tagType, content[0]); case OsmEdge.tagType.oneway: return new KeyValuePair(tagType, BitConverter.ToBoolean(content)); case OsmEdge.tagType.forward: return new KeyValuePair(tagType, BitConverter.ToBoolean(content)); case OsmEdge.tagType.id: return new KeyValuePair(tagType, BitConverter.ToUInt64(content)); default: return new KeyValuePair(tagType, content); } } #endregion }