diff --git a/OpenStreetMap Importer/Importer.cs b/OpenStreetMap Importer/Importer.cs index 689d6b0..a2772ca 100644 --- a/OpenStreetMap Importer/Importer.cs +++ b/OpenStreetMap Importer/Importer.cs @@ -7,142 +7,37 @@ namespace OpenStreetMap_Importer public class Importer { + private static XmlReaderSettings readerSettings = new() + { + IgnoreWhitespace = true, + IgnoreComments = true + }; + public static Dictionary Import(string filePath = "", Logger ?logger = null) { - List ways = new(); - Dictionary nodes = new(); - Stream mapData; - XmlReaderSettings readerSettings = new() - { - IgnoreWhitespace = true, - IgnoreComments = true - }; - - bool wayTag = false; - Way currentWay = new(); - Dictionary count = new(); + Dictionary nodes; /* * First iteration * Import "ways" with a tag "highway" * Count occurances of "nodes" to find junctions */ - - if (!File.Exists(filePath)) - { - mapData = new MemoryStream(OSM_Data.map); - logger?.Log(LogLevel.INFO, "Filepath '{0}' does not exist. Using standard file.", filePath); - } - else - { - mapData = new FileStream(filePath, FileMode.Open, FileAccess.Read); - logger?.Log(LogLevel.INFO, "Using file '{0}'", filePath); - } - XmlReader reader = XmlReader.Create(mapData, readerSettings); - reader.MoveToContent(); - logger?.Log(LogLevel.INFO, "Importing ways and counting nodes..."); - while (reader.Read()) - { - if (reader.Name == "way" && reader.IsStartElement()) - { - logger?.Log(LogLevel.VERBOSE, "WAY {0} nodes {1}", currentWay.highway.ToString(), currentWay.nodeIds.Count); - if (currentWay.highway != Way.highwayType.NONE) - { - ways.Add(currentWay); - foreach (ulong id in currentWay.nodeIds) - if(count.ContainsKey(id)) - count[id]++; - else - count.TryAdd(id, 1); - } - wayTag = true; - currentWay = new Way(); - } - else if (reader.Name == "tag" && wayTag) - { -#pragma warning disable CS8600 //tags will always have a value and key -#pragma warning disable CS8604 - string value = reader.GetAttribute("v"); - string key = reader.GetAttribute("k"); - logger?.Log(LogLevel.VERBOSE, "TAG {0} {1}", key, value); -#pragma warning restore CS8600 - switch (key) - { - case "highway": - currentWay.SetHighwayType(value); - break; - case "oneway": - switch (value) - { - case "yes": - currentWay.oneway = true; - break; - /*case "no": - currentWay.oneway = false; - break;*/ - case "-1": - currentWay.oneway = true; - currentWay.direction = Way.wayDirection.backward; - break; - } - break; - /*case "name": - - break;*/ - } -#pragma warning restore CS8604 - } - else if(reader.Name == "nd" && wayTag) - { - ulong id = Convert.ToUInt64(reader.GetAttribute("ref")); - currentWay.nodeIds.Add(id); - logger?.Log(LogLevel.VERBOSE, "nd: {0}", id); - } - else if(reader.Name == "node") - { - wayTag = false; - } - } - - logger?.Log(LogLevel.DEBUG, "Loaded Ways: {0} Required Nodes: {1}", ways.Count, count.Count); - - reader.Close(); - GC.Collect(); - if (!File.Exists(filePath)) - { - mapData = new MemoryStream(OSM_Data.map); - logger?.Log(LogLevel.INFO, "Filepath '{0}' does not exist. Using standard file.", filePath); - } - else - { - mapData = new FileStream(filePath, FileMode.Open, FileAccess.Read); - logger?.Log(LogLevel.INFO, "Using file '{0}'", filePath); - } - reader = XmlReader.Create(mapData, readerSettings); - reader.MoveToContent(); + Stream mapData = File.Exists(filePath) ? new FileStream(filePath, FileMode.Open, FileAccess.Read) : new MemoryStream(OSM_Data.map); + Dictionary occuranceCount = new(); + List ways = GetWays(mapData, ref occuranceCount); + mapData.Close(); + logger?.Log(LogLevel.DEBUG, "Loaded Ways: {0} Required Nodes: {1}", ways.Count, occuranceCount.Count); /* * Second iteration * Import nodes that are needed by the "ways" */ - logger?.Log(LogLevel.INFO, "Importing nodes..."); - while (reader.Read()) - { - if (reader.Name == "node") - { - ulong id = Convert.ToUInt64(reader.GetAttribute("id")); - if (count.ContainsKey(id)) - { -#pragma warning disable CS8602 //node will always have a lat and lon - float lat = Convert.ToSingle(reader.GetAttribute("lat").Replace('.', ',')); - float lon = Convert.ToSingle(reader.GetAttribute("lon").Replace('.', ',')); -#pragma warning restore CS8602 - nodes.TryAdd(id, new Node(lat, lon)); - logger?.Log(LogLevel.VERBOSE, "NODE {0} {1} {2} {3}", id, lat, lon, count[id]); - } - } - } + mapData = File.Exists(filePath) ? new FileStream(filePath, FileMode.Open, FileAccess.Read) : new MemoryStream(OSM_Data.map); + nodes = ImportNodes(mapData, occuranceCount, logger); + mapData.Close(); + + /* * Add connections between nodes (only junctions, e.g. nodes are referenced more than once) * Remove non-junction nodes @@ -153,7 +48,7 @@ namespace OpenStreetMap_Importer { Node junction1 = nodes[way.nodeIds[0]]; Node junction2; - double weight = 0; + float weight = 0; //Iterate Node-ids in current way forwards or backwards (depending on way.direction) if (way.direction == Way.wayDirection.forward) { @@ -162,7 +57,7 @@ namespace OpenStreetMap_Importer Node currentNode = nodes[way.nodeIds[index]]; Node nextNode = nodes[way.nodeIds[index + 1]]; weight += Utils.DistanceBetweenNodes(currentNode, nextNode); - if (count[way.nodeIds[index + 1]] > 1 || index == way.nodeIds.Count - 2) + if (occuranceCount[way.nodeIds[index + 1]] > 1 || index == way.nodeIds.Count - 2) { /* * If Node is referenced more than once => Junction @@ -193,7 +88,7 @@ namespace OpenStreetMap_Importer Node currentNode = nodes[way.nodeIds[index]]; Node nextNode = nodes[way.nodeIds[index - 1]]; weight += Utils.DistanceBetweenNodes(currentNode, nextNode); - if (count[way.nodeIds[index - 1]] > 1 || index == 1) + if (occuranceCount[way.nodeIds[index - 1]] > 1 || index == 1) { /* * If Node is referenced more than once => Junction @@ -218,11 +113,115 @@ namespace OpenStreetMap_Importer } } } - reader.Close(); - GC.Collect(); logger?.Log(LogLevel.DEBUG, "Loaded Edges: {0}", edges); - return nodes.Where(node => count[node.Key] > 1).ToDictionary(node => node.Key, node => node.Value); + return nodes.Where(node => occuranceCount[node.Key] > 1).ToDictionary(node => node.Key, node => node.Value); + } + + private static List GetWays(Stream mapData, ref Dictionary occuranceCount, Logger? logger = null) + { + List _ways = new(); + bool _isWay = false; + Way _currentWay = new(); + + XmlReader _reader = XmlReader.Create(mapData, readerSettings); + _reader.MoveToContent(); + + logger?.Log(LogLevel.INFO, "Importing ways and counting nodes..."); + while (_reader.Read()) + { + if (_reader.Name == "way" && _reader.IsStartElement()) + { + logger?.Log(LogLevel.VERBOSE, "WAY {0} nodes {1}", _currentWay.highway.ToString(), _currentWay.nodeIds.Count); + if (_currentWay.highway != Way.highwayType.NONE) + { + _ways.Add(_currentWay); + foreach (ulong id in _currentWay.nodeIds) + if (occuranceCount.ContainsKey(id)) + occuranceCount[id]++; + else + occuranceCount.TryAdd(id, 1); + } + _isWay = true; + _currentWay = new Way(); + } + else if (_reader.Name == "tag" && _isWay) + { +#pragma warning disable CS8600 //tags will always have a value and key +#pragma warning disable CS8604 + string value = _reader.GetAttribute("v"); + string key = _reader.GetAttribute("k"); + logger?.Log(LogLevel.VERBOSE, "TAG {0} {1}", key, value); +#pragma warning restore CS8600 + switch (key) + { + case "highway": + _currentWay.SetHighwayType(value); + break; + case "oneway": + switch (value) + { + case "yes": + _currentWay.oneway = true; + break; + /*case "no": + currentWay.oneway = false; + break;*/ + case "-1": + _currentWay.oneway = true; + _currentWay.direction = Way.wayDirection.backward; + break; + } + break; + /*case "name": + + break;*/ + } +#pragma warning restore CS8604 + } + else if (_reader.Name == "nd" && _isWay) + { + ulong id = Convert.ToUInt64(_reader.GetAttribute("ref")); + _currentWay.nodeIds.Add(id); + logger?.Log(LogLevel.VERBOSE, "nd: {0}", id); + } + else if (_reader.Name == "node") + { + _isWay = false; + } + } + + _reader.Close(); + GC.Collect(); + return _ways; + } + + private static Dictionary ImportNodes(Stream mapData, Dictionary occuranceCount, Logger? logger = null) + { + Dictionary nodes = new(); + XmlReader reader = XmlReader.Create(mapData, readerSettings); + reader.MoveToContent(); + + logger?.Log(LogLevel.INFO, "Importing nodes..."); + while (reader.Read()) + { + if (reader.Name == "node") + { + ulong id = Convert.ToUInt64(reader.GetAttribute("id")); + if (occuranceCount.ContainsKey(id)) + { +#pragma warning disable CS8602 //node will always have a lat and lon + float lat = Convert.ToSingle(reader.GetAttribute("lat").Replace('.', ',')); + float lon = Convert.ToSingle(reader.GetAttribute("lon").Replace('.', ',')); +#pragma warning restore CS8602 + nodes.TryAdd(id, new Node(lat, lon)); + logger?.Log(LogLevel.VERBOSE, "NODE {0} {1} {2} {3}", id, lat, lon, occuranceCount[id]); + } + } + } + reader.Close(); + GC.Collect(); + return nodes; } internal struct Way @@ -232,38 +231,39 @@ namespace OpenStreetMap_Importer public wayDirection direction; public highwayType highway; public enum wayDirection { forward, backward } - public enum highwayType : uint - { - NONE = 1, - motorway = 130, - trunk = 125, - primary = 110, - secondary = 100, - tertiary = 80, - unclassified = 40, - residential = 23, - motorway_link = 55, - trunk_link = 50, - primary_link = 30, - secondary_link = 25, - tertiary_link = 24, - living_street = 20, - service = 14, - pedestrian = 12, - track = 6, - bus_guideway = 15, - escape = 3, - raceway = 4, - road = 28, - busway = 13, - footway = 8, - bridleway = 7, - steps = 5, - corridor = 9, - path = 10, - cycleway = 11, - construction = 2 - } + + public Dictionary speed = new() { + { highwayType.NONE, 1 }, + { highwayType.motorway, 130 }, + { highwayType.trunk, 125 }, + { highwayType.primary, 110 }, + { highwayType.secondary, 100 }, + { highwayType.tertiary, 90 }, + { highwayType.unclassified, 40 }, + { highwayType.residential, 20 }, + { highwayType.motorway_link, 50 }, + { highwayType.trunk_link, 50 }, + { highwayType.primary_link, 30 }, + { highwayType.secondary_link, 25 }, + { highwayType.tertiary_link, 25 }, + { highwayType.living_street, 20 }, + { highwayType.service, 10 }, + { highwayType.pedestrian, 10 }, + { highwayType.track, 1 }, + { highwayType.bus_guideway, 5 }, + { highwayType.escape, 1 }, + { highwayType.raceway, 1 }, + { highwayType.road, 25 }, + { highwayType.busway, 5 }, + { highwayType.footway, 1 }, + { highwayType.bridleway, 1 }, + { highwayType.steps, 1 }, + { highwayType.corridor, 1 }, + { highwayType.path, 10 }, + { highwayType.cycleway, 5 }, + { highwayType.construction, 1 } + }; + public enum highwayType { NONE, motorway, trunk, primary, secondary, tertiary, unclassified, residential, motorway_link, trunk_link, primary_link, secondary_link, tertiary_link, living_street, service, pedestrian, track, bus_guideway, escape, raceway, road, busway, footway, bridleway, steps, corridor, path, cycleway, construction } public Way()