using System.Globalization; using System.Xml; using OSMDatastructure; using OSMDatastructure.Graph; namespace Server; public static class XmlImporter { private static readonly XmlReaderSettings readerSettings = new() { IgnoreWhitespace = true, IgnoreComments = true }; public static HashSet ImportXml(string filePath) { if (!File.Exists(filePath)) throw new FileNotFoundException(); Console.WriteLine(string.Format("[{0}] Getting highwayNodeIds...", DateTime.Now.ToLocalTime())); FileStream xmlFileStream = new FileStream(filePath, FileMode.Open); HashSet requiredNodeIds = GetHighwayNodeIds(XmlReader.Create(xmlFileStream, readerSettings)); xmlFileStream.Position = 0; Console.WriteLine(string.Format("[{0}] Importing Nodes...", DateTime.Now.ToLocalTime())); Dictionary nodes = GetHighwayNodesFromIds(XmlReader.Create(xmlFileStream, readerSettings), requiredNodeIds); requiredNodeIds.Clear(); xmlFileStream.Position = 0; Console.WriteLine(string.Format("[{0}] Importing Ways...", DateTime.Now.ToLocalTime())); HashSet retNodes = ConnectNodes(XmlReader.Create(xmlFileStream, readerSettings), nodes); nodes.Clear(); return retNodes; } public static HashSet SplitIntoRegions(HashSet nodes) { Console.WriteLine(string.Format("[{0}] Splitting into Regions...", DateTime.Now.ToLocalTime())); Dictionary retRegions = new(); foreach (OsmNode node in nodes) { int regionHash = Coordinates.GetRegionHashCode(node.coordinates); if(retRegions.ContainsKey(regionHash)) { retRegions[regionHash].nodes.Add(node); } else { Region newRegion = new Region(regionHash); newRegion.nodes.Add(node); retRegions.Add(regionHash, newRegion); } } return retRegions.Values.ToHashSet(); } private static readonly NumberFormatInfo decimalInfo = new() { NumberDecimalSeparator = "." }; public 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 GetHighwayNodesFromIds(XmlReader xmlReader, HashSet ids) { Dictionary retDict = new(); while (xmlReader.ReadToFollowing("node")) { ulong id = Convert.ToUInt64(xmlReader.GetAttribute("id")); if (ids.Contains(id)) { float lat = Convert.ToSingle(xmlReader.GetAttribute("lat")!, decimalInfo); float lon = Convert.ToSingle(xmlReader.GetAttribute("lon")!, decimalInfo); retDict.Add(id, new OsmNode(lat, lon)); } } xmlReader.Close(); return retDict; } private static HashSet ConnectNodes(XmlReader xmlReader, Dictionary dict) { while (xmlReader.ReadToFollowing("way")) { Dictionary tags = new(); List wayNodeIds = new(); XmlReader wayReader = xmlReader.ReadSubtree(); while (wayReader.Read()) { if (xmlReader.Name == "tag") { KeyValuePair tag = OsmEdge.ConvertToTag(wayReader.GetAttribute("k")!, wayReader.GetAttribute("v")!); tags.TryAdd(tag.Key, tag.Value); } else if (xmlReader.Name == "nd") { wayNodeIds.Add(Convert.ToUInt64(xmlReader.GetAttribute("ref"))); } } if (tags.ContainsKey(OsmEdge.tagType.highway)) { ulong[] ids = wayNodeIds.Where(dict.ContainsKey).ToArray(); for (int i = 0; i < ids.Length - 1; i++) { OsmNode n1 = dict[ids[i]]; OsmNode n2 = dict[ids[i + 1]]; if (tags.ContainsKey(OsmEdge.tagType.oneway) && (bool)tags[OsmEdge.tagType.oneway]) { if (tags.ContainsKey(OsmEdge.tagType.forward) && !(bool)tags[OsmEdge.tagType.forward]) { n2.edges.Add(new OsmEdge(n1.coordinates, tags)); } else { n1.edges.Add(new OsmEdge(n2.coordinates, tags)); } } else { n1.edges.Add(new OsmEdge(n2.coordinates, tags)); n2.edges.Add(new OsmEdge(n1.coordinates, tags)); } } } wayReader.Close(); } xmlReader.Close(); return dict.Values.ToHashSet(); } }