using System.Globalization; using System.Xml; namespace OsmXmlToRegionConverter; public class Manager { private static readonly XmlReaderSettings readerSettings = new() { IgnoreWhitespace = true, IgnoreComments = true }; private static readonly NumberFormatInfo decimalInfo = new() { NumberDecimalSeparator = "." }; private const string NodesTmpFilename = "nodes.tmp"; private const string WaysTmpFilename = "ways.tmp"; private const string TagsFilename = "tags"; public static void XmlToByteConverter(string inputFilePath, string outputFolderPath) { if (!File.Exists(inputFilePath)) throw new FileNotFoundException(inputFilePath); Directory.CreateDirectory(outputFolderPath); Console.WriteLine("Getting highwayNodeIDs..."); FileStream xmlFileStream = new FileStream(inputFilePath, FileMode.Open); HashSet nodeIdsToImport = GetHighwayNodeIds(XmlReader.Create(xmlFileStream, readerSettings)); xmlFileStream.Position = 0; Console.WriteLine("Converting Nodes..."); Dictionary nodeRegions = ImportNodesToRegionsTmp(XmlReader.Create(xmlFileStream, readerSettings), nodeIdsToImport, outputFolderPath); nodeIdsToImport.Clear(); //memory management xmlFileStream.Position = 0; Console.WriteLine("Converting Ways..."); ImportWaysToRegionTmp(XmlReader.Create(xmlFileStream, readerSettings), nodeRegions, outputFolderPath); HashSet regionHashes = nodeRegions.Values.ToHashSet(); nodeRegions.Clear(); //memory management Console.WriteLine("Converting Nodes and Ways to Nodes and Edges in regionfile..."); ConvertNodesAndWaysToRegion(outputFolderPath, regionHashes); } private static HashSet GetHighwayNodeIds(XmlReader xmlReader) { HashSet retSet = new(); bool isHighway; HashSet currentIds = new(); while (xmlReader.ReadToFollowing("way")) { isHighway = false; currentIds.Clear(); XmlReader wayReader = xmlReader.ReadSubtree(); while (wayReader.Read()) { if (xmlReader.Name == "tag" && xmlReader.GetAttribute("k")!.Equals("highway")) { isHighway = true; } else if (xmlReader.Name == "nd") { try { currentIds.Add(Convert.ToUInt64(xmlReader.GetAttribute("ref"))); } catch (FormatException) { }; } } if (isHighway) { retSet.UnionWith(currentIds); } wayReader.Close(); } xmlReader.Close(); return retSet; } private static Dictionary ImportNodesToRegionsTmp(XmlReader xmlReader, HashSet nodeIdsToImport, string outputFolderPath) { Dictionary regionWriters = new Dictionary(); Dictionary nodeRegions = new Dictionary(); while (xmlReader.ReadToFollowing("node")) { ulong currentNodeId = Convert.ToUInt64(xmlReader.GetAttribute("id")); if (nodeIdsToImport.Contains(currentNodeId)) { float lat = Convert.ToSingle(xmlReader.GetAttribute("lat")!, decimalInfo); float lon = Convert.ToSingle(xmlReader.GetAttribute("lon")!, decimalInfo); TmpNode newTmpNode = new TmpNode(currentNodeId, lat, lon); ulong regionHash = Coordinates.GetRegionHashCode(newTmpNode.coordinates); if (!regionWriters.ContainsKey(regionHash)) { Directory.CreateDirectory(Path.Combine(outputFolderPath, regionHash.ToString())); regionWriters.Add(regionHash, new FileStream(Path.Combine(outputFolderPath, regionHash.ToString(), NodesTmpFilename), FileMode.Append)); } regionWriters[regionHash].Write( ByteConverter.GetBytes(newTmpNode)); nodeRegions.Add(currentNodeId, regionHash); } } foreach(FileStream regionWriter in regionWriters.Values) regionWriter.Close(); xmlReader.Close(); return nodeRegions; } private static void ImportWaysToRegionTmp(XmlReader xmlReader, Dictionary nodeRegions, string outputFolderPath) { Dictionary regionWriters = new Dictionary(); bool isHighway = false; HashSet currentIds = new(); while (xmlReader.ReadToFollowing("way")) { HashSet tagsSet = new HashSet(); isHighway = false; currentIds.Clear(); ulong wayId = Convert.ToUInt64(xmlReader.GetAttribute("id")); XmlReader wayReader = xmlReader.ReadSubtree(); while (wayReader.Read()) { if (xmlReader.Name == "tag" && xmlReader.GetAttribute("k")!.Equals("highway")) { isHighway = true; } else if (xmlReader.Name == "nd") { try { currentIds.Add(Convert.ToUInt64(xmlReader.GetAttribute("ref"))); } catch (FormatException) { }; }else if (xmlReader.Name == "tag") { tagsSet.Add(Tag.ConvertToTag(xmlReader.GetAttribute("k")!, xmlReader.GetAttribute("v")!)); } } if (isHighway) { TmpWay newTmpWay = new TmpWay(wayId, currentIds.ToArray(), tagsSet); HashSet regionsToWrite = new HashSet(); foreach (ulong nodeId in currentIds) { if (nodeRegions.ContainsKey(nodeId)) { regionsToWrite.Add(nodeRegions[nodeId]); } } foreach (ulong regionHash in regionsToWrite) { if (!regionWriters.ContainsKey(regionHash)) { regionWriters.Add(regionHash, new FileStream(Path.Combine(outputFolderPath, regionHash.ToString(), WaysTmpFilename), FileMode.Append)); } regionWriters[regionHash].Write(ByteConverter.GetBytes(newTmpWay)); } } wayReader.Close(); } foreach(FileStream regionWriter in regionWriters.Values) regionWriter.Close(); xmlReader.Close(); } private static void ConvertNodesAndWaysToRegion(string outputFolderPath, HashSet regionHashes) { foreach (ulong regionHash in regionHashes) { Console.WriteLine($"Converting Region {regionHash}"); string regionFilePath = Path.Combine(outputFolderPath, $"{regionHash.ToString()}.region"); string regionFolderPath = Path.Combine(outputFolderPath, regionHash.ToString()); string nodesFilePath = Path.Combine(regionFolderPath, NodesTmpFilename); string waysFilePath = Path.Combine(regionFolderPath, WaysTmpFilename); Region newRegion = new Region(regionHash); Dictionary nodes = new Dictionary(); byte[] nodeBytes = File.ReadAllBytes(nodesFilePath); HashSet nodeObjects = ByteConverter.GetObjectsFromBytes(nodeBytes); foreach (object obj in nodeObjects) { if (obj.GetType() == typeof(TmpNode)) { TmpNode tmpNode = (TmpNode)obj; Node newNode = new Node(tmpNode.id, tmpNode.coordinates); newRegion.AddNode(newNode); nodes.TryAdd(newNode.id, newNode); } else throw new Exception("Unexpected object."); } byte[] wayBytes = File.ReadAllBytes(waysFilePath); HashSet ways = ByteConverter.GetObjectsFromBytes(wayBytes); foreach (object obj in ways) { if (obj.GetType() == typeof(TmpWay)) { TmpWay way = (TmpWay)obj; for (int nodeIdi = 0; nodeIdi < way.wayNodeIds.Length - 1; nodeIdi++) //TODO directional { if (nodes.ContainsKey(way.wayNodeIds[nodeIdi]) && nodes.ContainsKey(way.wayNodeIds[nodeIdi + 1])) { Node n1 = nodes[way.wayNodeIds[nodeIdi]]; Node n2 = nodes[way.wayNodeIds[nodeIdi + 1]]; n1.edges.Add(new Edge(way.id, n2)); n2.edges.Add(new Edge(way.id, n1)); } } } else throw new Exception("Unexpected object."); } File.WriteAllBytes(regionFilePath, ByteConverter.GetBytes(newRegion)); Directory.Delete(regionFolderPath, true); } } }