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 = 4 + 8; //size + Coordinates HashSet 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 tags = new(); foreach (KeyValuePair 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 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 = Array.Empty(); 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 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 }