using System.Globalization; using System.Text; using System.Xml; namespace OSMImporter { public static class Importer { public const float regionSize = 0.01f; private static readonly XmlReaderSettings readerSettings = new() { IgnoreWhitespace = true, IgnoreComments = true }; private static readonly NumberFormatInfo coordinateFormat = new NumberFormatInfo() { NumberDecimalSeparator = "." }; public static Region ImportRegion(string folderPath, Coordinates coordinates) { string fullPath = Path.Combine(folderPath, coordinates.GetRegionHash().ToString()); Console.WriteLine(fullPath); Region retRegion = new Region(coordinates); if (!File.Exists(fullPath)) return retRegion; FileStream fileStream = new FileStream(fullPath, FileMode.Open); while (fileStream.Position < fileStream.Length) { byte[] nodeIdBytes = new byte[sizeof(ulong)]; fileStream.ReadExactly(nodeIdBytes,0,nodeIdBytes.Length); ulong[] nodeId = new ulong[1]; Buffer.BlockCopy(nodeIdBytes,0,nodeId,0,nodeIdBytes.Length); byte[] latBytes = new byte[sizeof(float)]; fileStream.ReadExactly(latBytes, 0, latBytes.Length); byte[] lonBytes = new byte[sizeof(float)]; fileStream.ReadExactly(lonBytes, 0, latBytes.Length); Node newNode = NodeFromBytes(latBytes, lonBytes); retRegion.AddNode(nodeId[0], newNode); byte connectionsAmount = Convert.ToByte(fileStream.ReadByte()); for (byte connectionInt = 0; connectionInt < connectionsAmount; connectionInt++) { byte[] endIdBytes = new byte[sizeof(ulong)]; fileStream.ReadExactly(endIdBytes, 0, endIdBytes.Length); Connection connection = ConnectionFromByte(endIdBytes); newNode.AddConnection(connection); byte tagsAmount = Convert.ToByte(fileStream.ReadByte()); for (byte tagInt = 0; tagInt < tagsAmount; tagInt++) { byte keyByte = Convert.ToByte(fileStream.ReadByte()); byte valueByte = Convert.ToByte(fileStream.ReadByte()); connection.AddTag(keyByte, valueByte); } } } fileStream.Close(); return retRegion; } private static Node NodeFromBytes(byte[] latByte, byte[] lonByte) { float[] lat = new float[1]; float[] lon = new float[1]; Buffer.BlockCopy(latByte, 0, lat, 0, latByte.Length); Buffer.BlockCopy(lonByte, 0, lon, 0, lonByte.Length); return new Node(lat[0], lon[0]); } private static Connection ConnectionFromByte(byte[] endIdByte) { ulong[] endId = new ulong[1]; Buffer.BlockCopy(endIdByte, 0, endId, 0, endIdByte.Length); return new Connection(endId[0]); } public static void Split(string xmlFilePath, string outputFolderPath) { if (!File.Exists(xmlFilePath)) throw new FileNotFoundException(); FileStream xmlFileStream = File.OpenRead(xmlFilePath); Console.WriteLine("Reading ways once..."); Dictionary nodes = ReturnNodeIdDictionary(XmlReader.Create(xmlFileStream, readerSettings)); xmlFileStream.Position = 0; RegionManager regionManager = new RegionManager(); Console.WriteLine("Reading nodes..."); LoadNodesIntoDictionary(ref nodes, XmlReader.Create(xmlFileStream, readerSettings), ref regionManager); xmlFileStream.Position = 0; Console.WriteLine("Reading ways twice..."); CreateConnections(XmlReader.Create(xmlFileStream, readerSettings), ref nodes); Console.WriteLine("Writing..."); foreach(Region region in regionManager.GetAllRegions()) WriteRegion(region, outputFolderPath); } private static Dictionary ReturnNodeIdDictionary(XmlReader xmlReader) { Dictionary retSet = new Dictionary(); while (xmlReader.ReadToFollowing("way")) { byte[] byteArray = Encoding.ASCII.GetBytes(xmlReader.ReadOuterXml()); MemoryStream wayStream = new MemoryStream(byteArray); XmlReader wayReader = XmlReader.Create(wayStream); bool addNodes = false; while (wayReader.ReadToFollowing("tag") && !addNodes) { if (wayReader.GetAttribute("v")!.Equals("highway")) { addNodes = true; break; } } if (addNodes) { wayStream.Position = 0; wayReader = XmlReader.Create(wayStream); while (wayReader.ReadToFollowing("nd")) { retSet.TryAdd(Convert.ToUInt64(wayReader.GetAttribute("ref")!), null); } } } xmlReader.Close(); return retSet; } private static void LoadNodesIntoDictionary(ref Dictionary nodes, XmlReader xmlReader, ref RegionManager regionManager) { while (xmlReader.ReadToFollowing("node")) { ulong id = Convert.ToUInt64(xmlReader.GetAttribute("id")); if (nodes.ContainsKey(id)) { float lat = Convert.ToSingle(xmlReader.GetAttribute("lat")!, coordinateFormat); float lon = Convert.ToSingle(xmlReader.GetAttribute("lon")!, coordinateFormat); Node newNode = new Node(lat, lon); nodes[id] = newNode; regionManager.GetRegion(newNode).AddNode(id, newNode); } } xmlReader.Close(); } private static void CreateConnections(XmlReader xmlReader, ref Dictionary nodes) { while (xmlReader.ReadToFollowing("way")) { byte[] byteArray = Encoding.ASCII.GetBytes(xmlReader.ReadOuterXml()); MemoryStream wayStream = new MemoryStream(byteArray); XmlReader wayReader = XmlReader.Create(wayStream); Dictionary tags = new Dictionary(); bool addNodes = false; while (wayReader.ReadToFollowing("tag")) { if (wayReader.GetAttribute("v")!.Equals("highway")) { addNodes = true; } tags.Add(wayReader.GetAttribute("k")!, value: wayReader.GetAttribute("v")!); } if (addNodes) { HashSet nodesInWay = new HashSet(); wayStream.Position = 0; wayReader = XmlReader.Create(wayStream); while (wayReader.ReadToFollowing("nd")) { nodesInWay.Add(Convert.ToUInt64(wayReader.GetAttribute("ref"))); } ConnectNodesOfWay(nodes, nodesInWay.ToArray(), tags); } } xmlReader.Close(); } private static void ConnectNodesOfWay(Dictionary nodes, ulong[] nodeIds, Dictionary tags) { string oneWayString = tags.ContainsKey("oneway") ? tags["oneway"] : "no"; bool oneWay = false; bool forward = true; switch (oneWayString) { case "yes": oneWay = true; break; case "-1": forward = false; break; } for (int i = 0; i < nodeIds.Length - 1; i++) { if (oneWay) { if(nodes.ContainsKey(nodeIds[i])) nodes[nodeIds[i]]!.AddConnection(new Connection(nodeIds[i + 1], tags)); if(nodes.ContainsKey(nodeIds[i + 1])) nodes[nodeIds[i + 1]]!.AddConnection(new Connection(nodeIds[i], tags)); } else if (forward) { if(nodes.ContainsKey(nodeIds[i])) nodes[nodeIds[i]]!.AddConnection(new Connection(nodeIds[i + 1], tags)); } else { if(nodes.ContainsKey(nodeIds[i + 1])) nodes[nodeIds[i + 1]]!.AddConnection(new Connection(nodeIds[i], tags)); } } } /* * Value x: nodeId (4bytes) * Value x+1: Node (xbytes) */ private static void WriteRegion(Region region, string outputFolderPath) { if (!Directory.Exists(outputFolderPath)) Directory.CreateDirectory(outputFolderPath); string fileName = region.regionHash.ToString(); string fullPath = Path.Combine(outputFolderPath, fileName); if (!File.Exists(fullPath)) File.Create(fullPath).Close(); FileStream fileStream = new FileStream(fullPath, FileMode.Append); foreach (KeyValuePair nodeKeyValuePair in region.GetNodes()) { byte[] nodeByte = nodeKeyValuePair.Value.ToByte(); byte[] nodeIdByte = new byte[sizeof(long)]; ulong[] nodeId = new ulong[]{nodeKeyValuePair.Key}; Buffer.BlockCopy(nodeId,0,nodeIdByte,0,nodeIdByte.Length); fileStream.Write(nodeIdByte); fileStream.Write(nodeByte, 0, nodeByte.Length ); } fileStream.Close(); } } }