using System.Globalization; using System.Text; using System.Xml; using OSMDatastructure; namespace OSMImporter { public static class XmlImporter { private static readonly XmlReaderSettings readerSettings = new() { IgnoreWhitespace = true, IgnoreComments = true }; private static readonly NumberFormatInfo coordinateFormat = new NumberFormatInfo() { NumberDecimalSeparator = "." }; 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; RegionCollection regionCollection = new RegionCollection(); Console.WriteLine("Reading nodes..."); LoadNodesIntoDictionary(ref nodes, XmlReader.Create(xmlFileStream, readerSettings), ref regionCollection); xmlFileStream.Position = 0; Console.WriteLine("Reading ways twice..."); CreateConnections(XmlReader.Create(xmlFileStream, readerSettings), ref nodes); Console.WriteLine("Writing..."); foreach(Region region in regionCollection.GetAllRegions()) WriteRegion(region, outputFolderPath); } private static Dictionary ReturnNodeIdDictionary(XmlReader xmlReader) { Dictionary retSet = new Dictionary(); while (xmlReader.ReadToFollowing("way")) { XmlReader wayReader = xmlReader.ReadSubtree(); bool addNodes = false; HashSet nodeIds = new(); while (wayReader.Read()) { if (xmlReader.IsStartElement() && xmlReader.Name.Equals("nd")) { nodeIds.Add(wayReader.GetAttribute("ref")!); } else if (xmlReader.IsStartElement() && !addNodes && xmlReader.Name.Equals("tag") && xmlReader.GetAttribute("k")!.Equals("highway")) { addNodes = true; } } wayReader.Close(); if (addNodes) { foreach (string nodeId in nodeIds) { retSet.TryAdd(Convert.ToUInt64(nodeId), null); } } } xmlReader.Close(); return retSet; } private static void LoadNodesIntoDictionary(ref Dictionary nodes, XmlReader xmlReader, ref RegionCollection regionCollection) { 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; regionCollection.GetRegion(newNode).AddNode(id, newNode); } } xmlReader.Close(); } private static void CreateConnections(XmlReader xmlReader, ref Dictionary nodes) { while (xmlReader.ReadToFollowing("way")) { XmlReader wayReader = xmlReader.ReadSubtree(); HashSet nodesInWay = new (); Dictionary tags = new (); bool addNodes = false; while (wayReader.Read()) { if (wayReader.IsStartElement() && wayReader.Name.Equals("nd")) { nodesInWay.Add(Convert.ToUInt64(wayReader.GetAttribute("ref"))); } else if (wayReader.IsStartElement() && wayReader.Name.Equals("tag")) { if (wayReader.GetAttribute("k")!.Equals("highway")) { addNodes = true; } tags.Add(wayReader.GetAttribute("k")!, value: wayReader.GetAttribute("v")!); } } wayReader.Close(); if (addNodes) { 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], nodes[nodeIds[i + 1]]!, tags)); if(nodes.ContainsKey(nodeIds[i + 1])) nodes[nodeIds[i + 1]]!.AddConnection(new Connection(nodeIds[i], nodes[nodeIds[i]]!, tags)); } else if (forward) { if(nodes.ContainsKey(nodeIds[i])) nodes[nodeIds[i]]!.AddConnection(new Connection(nodeIds[i + 1], nodes[nodeIds[i + 1]]!, tags)); } else { if(nodes.ContainsKey(nodeIds[i + 1])) nodes[nodeIds[i + 1]]!.AddConnection(new Connection(nodeIds[i], nodes[nodeIds[i]]!, tags)); } } } 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); fileStream.Write(ByteConverter.GetBytes(region)); fileStream.Close(); } } }