using System.Globalization; using System.Runtime.Serialization.Formatters.Binary; using System.Xml; using OSMDatastructure.Graph; namespace Server; public class RegionConverter { private static readonly XmlReaderSettings ReaderSettings = new() { IgnoreWhitespace = true, IgnoreComments = true }; private static readonly NumberFormatInfo decimalInfo = new() { NumberDecimalSeparator = "." }; public const string NodesFileName = "region.nodes"; public const string WaysFileName = "region.ways"; public const string tagsFileName = "waytags"; public static void ConvertXMLToRegions(string filePath, string outputPath) { if (!File.Exists(filePath)) throw new FileNotFoundException(); if (!Directory.Exists(outputPath)) Directory.CreateDirectory(outputPath); Console.WriteLine("Converting Nodes..."); FileStream xmlFileStream = new FileStream(filePath, FileMode.Open); Dictionary nodeIdRegionDict = GetNodesAndRegions(XmlReader.Create(xmlFileStream, ReaderSettings), outputPath); xmlFileStream.Position = 0; Console.WriteLine("Converting Ways..."); ImportWays(XmlReader.Create(xmlFileStream, ReaderSettings), nodeIdRegionDict, outputPath); } private static Dictionary GetNodesAndRegions(XmlReader xmlReader, string outputPath) { BinaryFormatter bFormatter = new BinaryFormatter(); Dictionary nodeRegions = new(); Dictionary regionFileStreams = new(); Dictionary tmpAllNodes = new(); bool isHighway; HashSet currentIds = new(); while (xmlReader.Read()) { if (xmlReader.Name == "node") { ulong id = Convert.ToUInt64(xmlReader.GetAttribute("id")!); float lat = Convert.ToSingle(xmlReader.GetAttribute("lat")!, decimalInfo); float lon = Convert.ToSingle(xmlReader.GetAttribute("lon")!, decimalInfo); tmpAllNodes.TryAdd(id, new OsmNode(id, lat, lon)); } else if (xmlReader.Name == "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 { ulong id = Convert.ToUInt64(xmlReader.GetAttribute("ref")!); currentIds.Add(id); } catch (FormatException) { }; } } wayReader.Close(); foreach (ulong nodeId in currentIds.Where(key => tmpAllNodes.ContainsKey(key))) { if (isHighway) { ulong regionHash = Coordinates.GetRegionHashCode(tmpAllNodes[nodeId].coordinates); FileStream nodesRegionStream; if(regionFileStreams.ContainsKey(regionHash)) nodesRegionStream = regionFileStreams[regionHash]; else { string regionPath = Path.Combine(outputPath, regionHash.ToString()); Directory.CreateDirectory(regionPath); string nodesRegionPath = Path.Combine(regionPath, NodesFileName); nodesRegionStream = new FileStream(nodesRegionPath, FileMode.Create); regionFileStreams.Add(regionHash, nodesRegionStream); } nodeRegions.Add(nodeId, regionHash); #pragma warning disable SYSLIB0011 //eheheh bFormatter.Serialize(nodesRegionStream, tmpAllNodes[nodeId]); #pragma warning restore SYSLIB0011 } tmpAllNodes.Remove(nodeId); } } } xmlReader.Close(); return nodeRegions; } //TODO write all tags in region to single file instead of separate wayfiles private static void ImportWays(XmlReader xmlReader, Dictionary nodeRegions, string outputPath) { BinaryFormatter bFormatter = new BinaryFormatter(); bool currentWayIsHighway; ulong currentWayId = 0; List currentNodeIds = new(); Dictionary currentTags = new(); Dictionary regionWaysFileStreams = new(); string wayFileStreamPath = ""; FileStream wayFileStream = new FileStream(Path.GetTempFileName(), FileMode.Create); while (xmlReader.ReadToFollowing("way")) { ulong wayId = Convert.ToUInt64(xmlReader.GetAttribute("id")!); currentWayIsHighway = false; currentNodeIds.Clear(); currentTags.Clear(); XmlReader wayReader = xmlReader.ReadSubtree(); while (wayReader.Read()) { currentWayId = Convert.ToUInt64(wayReader.GetAttribute("id")!); if (wayReader.Name == "tag") { Tag wayTag = Tag.ConvertToTag(wayReader.GetAttribute("k")!, wayReader.GetAttribute("v")!); currentTags.TryAdd(wayTag.key, wayTag.value); if(wayTag.key == Tag.TagType.highway) currentWayIsHighway = true; } else if (wayReader.Name == "nd") { try { ulong nodeId = Convert.ToUInt64(wayReader.GetAttribute("ref")); currentNodeIds.Add(nodeId); } catch (FormatException) { }; } } wayReader.Close(); if (currentWayIsHighway) { for (int i = 0; i < currentNodeIds.Count - 1; i++) { ulong node1Id = currentNodeIds[i]; ulong node2Id = currentNodeIds[i+1]; if (currentTags.ContainsKey(Tag.TagType.oneway) && (bool)currentTags[Tag.TagType.oneway] && nodeRegions.ContainsKey(node1Id) && nodeRegions.ContainsKey(node2Id)) { if (currentTags.ContainsKey(Tag.TagType.forward) && !(bool)currentTags[Tag.TagType.forward]) { OsmWay n21e = new OsmWay(currentWayId, node2Id, node1Id, nodeRegions[node2Id]); if (!regionWaysFileStreams.ContainsKey(nodeRegions[node2Id])) { string waysRegionPath = Path.Combine(outputPath, nodeRegions[node2Id].ToString(), WaysFileName); regionWaysFileStreams.Add(nodeRegions[node2Id], new FileStream(waysRegionPath, FileMode.OpenOrCreate)); } #pragma warning disable SYSLIB0011 bFormatter.Serialize(regionWaysFileStreams[nodeRegions[node2Id]], n21e); wayFileStreamPath = Path.Combine(outputPath, nodeRegions[node2Id].ToString(), $"{wayId}.{tagsFileName}"); wayFileStream = new FileStream(wayFileStreamPath, FileMode.OpenOrCreate); bFormatter.Serialize(wayFileStream, currentTags); wayFileStream.Dispose(); } else { OsmWay n12e = new OsmWay(currentWayId, node1Id, node2Id, nodeRegions[node2Id]); if (!regionWaysFileStreams.ContainsKey(nodeRegions[node1Id])) { string waysRegionPath = Path.Combine(outputPath, nodeRegions[node1Id].ToString(), WaysFileName); regionWaysFileStreams.Add(nodeRegions[node1Id], new FileStream(waysRegionPath, FileMode.OpenOrCreate)); } bFormatter.Serialize(regionWaysFileStreams[nodeRegions[node1Id]], n12e); wayFileStreamPath = Path.Combine(outputPath, nodeRegions[node1Id].ToString(), $"{wayId}.{tagsFileName}"); wayFileStream = new FileStream(wayFileStreamPath, FileMode.OpenOrCreate); bFormatter.Serialize(wayFileStream, currentTags); wayFileStream.Dispose(); } } else if(nodeRegions.ContainsKey(node1Id) && nodeRegions.ContainsKey(node2Id)) { OsmWay n12e = new OsmWay(currentWayId, node1Id, node2Id, nodeRegions[node2Id]); if (!regionWaysFileStreams.ContainsKey(nodeRegions[node1Id])) { string waysRegionPath = Path.Combine(outputPath, nodeRegions[node1Id].ToString(), WaysFileName); regionWaysFileStreams.Add(nodeRegions[node1Id], new FileStream(waysRegionPath, FileMode.OpenOrCreate)); } bFormatter.Serialize(regionWaysFileStreams[nodeRegions[node1Id]], n12e); wayFileStreamPath = Path.Combine(outputPath, nodeRegions[node1Id].ToString(), $"{wayId}.{tagsFileName}"); wayFileStream = new FileStream(wayFileStreamPath, FileMode.OpenOrCreate); bFormatter.Serialize(wayFileStream, currentTags); wayFileStream.Dispose(); OsmWay n21e = new OsmWay(currentWayId, node2Id, node1Id, nodeRegions[node2Id]); if (!regionWaysFileStreams.ContainsKey(nodeRegions[node2Id])) { string waysRegionPath = Path.Combine(outputPath, nodeRegions[node2Id].ToString(), WaysFileName); regionWaysFileStreams.Add(nodeRegions[node2Id], new FileStream(waysRegionPath, FileMode.OpenOrCreate)); } bFormatter.Serialize(regionWaysFileStreams[nodeRegions[node1Id]], n21e); wayFileStreamPath = Path.Combine(outputPath, nodeRegions[node2Id].ToString(), $"{wayId}.{tagsFileName}"); wayFileStream = new FileStream(wayFileStreamPath, FileMode.OpenOrCreate); bFormatter.Serialize(wayFileStream, currentTags); wayFileStream.Dispose(); #pragma warning restore SYSLIB0011 } } } } xmlReader.Close(); foreach (FileStream f in regionWaysFileStreams.Values) { f.Dispose(); } } }