diff --git a/OsmXmlToRegionConverter/ByteConverter.cs b/OsmXmlToRegionConverter/ByteConverter.cs new file mode 100644 index 0000000..6482ba6 --- /dev/null +++ b/OsmXmlToRegionConverter/ByteConverter.cs @@ -0,0 +1,259 @@ +using System.Text; + +namespace OsmXmlToRegionConverter; + +public static class Extensions +{ + public static T[] SubArray(this T[] array, int offset, int length) + { + T[] result = new T[length]; + Buffer.BlockCopy(array, offset, result, 0, length); + return result; + } +} + +public class ByteConverter +{ + + private enum ContentType : byte + { + Node = 0, + Coordinates = 1, + Edge = 2, + TmpNode = 3, + TmpWay = 4, + Region = 5, + TagManager = 6 + } + + public static HashSet GetObjectsFromBytes(byte[] bytes) + { + HashSet ret = new HashSet(); + while (bytes.Length > 0) + { + ContentType contentType = (ContentType)bytes[0]; + switch (contentType) + { + case ContentType.Node: + int edgeCount = BitConverter.ToInt32(bytes, 1); + int byteSizeNode = Node.ByteSizeEmpty + 1 + edgeCount * Edge.ByteSize; //+1 for contenttype of coordinates + ret.Add(Node.FromBytes(bytes.SubArray(1, byteSizeNode))); + bytes = bytes.SubArray(1 + byteSizeNode, bytes.Length - 1 - byteSizeNode); //-1 for overflow + break; + case ContentType.Coordinates: + ret.Add(Coordinates.FromBytes(bytes.SubArray(1, Coordinates.ByteSize))); + bytes = bytes.SubArray(1 + Coordinates.ByteSize, bytes.Length - 1 - Coordinates.ByteSize); + break; + case ContentType.Edge: + ret.Add(Edge.FromBytes(bytes.SubArray(1, Edge.ByteSize))); + bytes = bytes.SubArray(1 + Edge.ByteSize, bytes.Length - 1 - Edge.ByteSize); + break; + case ContentType.TmpNode: + ret.Add(TmpNode.FromBytes(bytes.SubArray(1, TmpNode.ByteSize + 1))); //+1 for contenttype of coordinates + bytes = bytes.SubArray(1 + TmpNode.ByteSize + 1, bytes.Length - 1 - (TmpNode.ByteSize + 1)); + break; + case ContentType.TmpWay: + int byteSizeTmpWay = BitConverter.ToInt32(bytes, 1); + ret.Add(TmpWay.FromBytes(bytes.SubArray(1, byteSizeTmpWay))); + bytes = bytes.SubArray(1 + byteSizeTmpWay, bytes.Length - 1 - byteSizeTmpWay); + break; + case ContentType.TagManager: + int totalBytesTagManager = BitConverter.ToInt32(bytes, 1); + ret.Add(TagManager.FromBytes(bytes.SubArray(1 + sizeof(int), totalBytesTagManager))); + bytes = bytes.SubArray(1 + sizeof(int) + totalBytesTagManager, bytes.Length - 1 - sizeof(int) - totalBytesTagManager); + break; + default: + throw new Exception("Unknown Type"); + } + } + + return ret; + } + + public static byte[] GetBytes(Node n) + { + using (MemoryStream m = new MemoryStream()) + { + using (BinaryWriter b = new BinaryWriter(m)) + { + b.Write((byte)ContentType.Node); + b.Write(n.edges.Count); + b.Write((byte)ContentType.Node); + b.Write(n.id); + b.Write(GetBytes(n.coordinates)); + foreach(Edge e in n.edges) + b.Write(GetBytes(e)); + } + + return m.ToArray(); + } + } + + public static byte[] GetBytes(Coordinates c) + { + using (MemoryStream m = new MemoryStream()) + { + using (BinaryWriter b = new BinaryWriter(m)) + { + b.Write((byte)ContentType.Coordinates); + b.Write(c.latitude); + b.Write(c.longitude); + } + + return m.ToArray(); + } + } + + public static byte[] GetBytes(Edge e) + { + using (MemoryStream m = new MemoryStream()) + { + using (BinaryWriter b = new BinaryWriter(m)) + { + b.Write((byte)ContentType.Edge); + b.Write(e.wayId); + b.Write(e.neighborId); + b.Write(e.neighborRegion); + } + + return m.ToArray(); + } + } + + public static byte[] GetBytes(TmpNode tn) + { + using (MemoryStream m = new MemoryStream()) + { + using (BinaryWriter b = new BinaryWriter(m)) + { + b.Write((byte)ContentType.TmpNode); + b.Write(tn.id); + b.Write(GetBytes(tn.coordinates)); + } + + return m.ToArray(); + } + } + + public static byte[] GetBytes(TmpWay tw) + { + using (MemoryStream m = new MemoryStream()) + { + using (BinaryWriter b = new BinaryWriter(m)) + { + b.Write((byte)ContentType.TmpWay); + HashSet tagBytesSet = new HashSet(); + int totalTagBytes = 0; + foreach (Tag tag in tw.tags) + { + byte[] tagBytes = GetBytes(tag); + totalTagBytes += tagBytes.Length; + tagBytesSet.Add(tagBytes); + } + + b.Write(sizeof(ulong) + sizeof(int) + tw.wayNodeIds.Length * sizeof(ulong) + totalTagBytes); + b.Write(tw.id); + b.Write(tw.wayNodeIds.Length); + foreach (ulong wayNodeId in tw.wayNodeIds) + b.Write(wayNodeId); + foreach (byte[] tagBytes in tagBytesSet) + b.Write(tagBytes); + } + + return m.ToArray(); + } + } + + public static byte[] GetBytes(Region r) + { + using (MemoryStream m = new MemoryStream()) + { + using (BinaryWriter b = new BinaryWriter(m)) + { + b.Write((byte)ContentType.Region); + b.Write(r.GetNodes().Length); + b.Write(r.regionHash); + foreach (Node n in r.GetNodes()) + b.Write(GetBytes(n)); + } + + return m.ToArray(); + } + } + + public static byte[] GetBytes(Tag tag) + { + using (MemoryStream m = new MemoryStream()) + { + using (BinaryWriter b = new BinaryWriter(m)) + { + byte[] valueBytes; + if (tag.value is ulong valueUlong) + { + valueBytes = BitConverter.GetBytes(valueUlong); + }else if (tag.value is byte valueByte) + { + valueBytes = new[] { valueByte }; + }else if (tag.value is bool valueBool) + { + valueBytes = BitConverter.GetBytes(valueBool); + }else if (tag.value is string valueString) + { + valueBytes = Encoding.ASCII.GetBytes(valueString); + }else if (tag.value is int valueInt) + { + valueBytes = BitConverter.GetBytes(valueInt); + } + else + { + throw new ArgumentException($"Unknown type {tag.value.GetType()}"); + } + b.Write(sizeof(byte) + valueBytes.Length); + b.Write((byte)tag.key); + b.Write(valueBytes); + } + return m.ToArray(); + } + } + + public static byte[] GetBytes(TagManager t) + { + using (MemoryStream m = new MemoryStream()) + { + using (BinaryWriter b = new BinaryWriter(m)) + { + HashSet wayBytesSet = new HashSet(); + int totalBytes = 0; + foreach (KeyValuePair> way in t.wayTags) + { + HashSet tagBytesSet = new HashSet(); + tagBytesSet.Add(BitConverter.GetBytes(way.Key)); + int totalWayBytes = sizeof(ulong); + + foreach (Tag tag in way.Value) + { + byte[] tagBytes = GetBytes(tag); + totalWayBytes += tagBytes.Length; + tagBytesSet.Add(tagBytes); + } + + wayBytesSet.Add(BitConverter.GetBytes(totalWayBytes)); + foreach (byte[] bytes in tagBytesSet) + { + wayBytesSet.Add(bytes); + } + + totalBytes += sizeof(int) + totalWayBytes; + } + + b.Write(totalBytes); + foreach (byte[] bytes in wayBytesSet) + { + b.Write(bytes); + } + } + + return m.ToArray(); + } + } +} \ No newline at end of file diff --git a/OsmXmlToRegionConverter/Coordinates.cs b/OsmXmlToRegionConverter/Coordinates.cs new file mode 100644 index 0000000..6ff0352 --- /dev/null +++ b/OsmXmlToRegionConverter/Coordinates.cs @@ -0,0 +1,71 @@ +namespace OsmXmlToRegionConverter; + +public class Coordinates +{ + public const float RegionSize = 0.01f; + public float latitude { get; } + public float longitude { get; } + + public Coordinates(float latitude, float longitude) + { + this.latitude = latitude; + this.longitude = longitude; + } + + public override bool Equals(object? obj) + { + if (obj == null || obj.GetType() != this.GetType()) + return false; + Coordinates convObj = (Coordinates)obj; + // ReSharper disable twice CompareOfFloatsByEqualityOperator static values + return convObj.latitude == this.latitude && convObj.longitude == this.longitude; + } + + public new ulong GetHashCode() + { + return GetHashCode(latitude, longitude); + } + + public static ulong GetRegionHashCode(float latitude, float longitude) + { + float latRegion = latitude - latitude % RegionSize; + float lonRegion = longitude - longitude % RegionSize; + return GetHashCode(latRegion, lonRegion); + } + + public static ulong GetRegionHashCode(Coordinates coordinates) + { + float latRegion = coordinates.latitude - coordinates.latitude % RegionSize; + float lonRegion = coordinates.longitude - coordinates.longitude % RegionSize; + return GetHashCode(latRegion, lonRegion); + } + + private const float decimalCoordsSave = 10000; + private const ulong offset = 10000000; + //Latitude maxChars = 7 + //Longitude maxChars = 8 + public static ulong GetHashCode(float latitude, float longitude) + { + ulong latHash = Convert.ToUInt64((latitude + 90) * decimalCoordsSave); + ulong lonHash = Convert.ToUInt64((longitude + 180) * decimalCoordsSave); + return latHash * offset + lonHash; + } + + public override string ToString() + { + return string.Format("COORDINATES Lat: {0} Lon: {1}", this.latitude, this.longitude); + } + + public const int ByteSize = sizeof(float) * 2; + public static Coordinates FromBytes(byte[] bytes) + { + using (MemoryStream m = new MemoryStream(bytes)) { + using (BinaryReader r = new BinaryReader(m)) + { + float latitude = r.ReadSingle(); + float longitude = r.ReadSingle(); + return new Coordinates(latitude, longitude); + } + } + } +} \ No newline at end of file diff --git a/OsmXmlToRegionConverter/Edge.cs b/OsmXmlToRegionConverter/Edge.cs new file mode 100644 index 0000000..17d458a --- /dev/null +++ b/OsmXmlToRegionConverter/Edge.cs @@ -0,0 +1,36 @@ +namespace OsmXmlToRegionConverter; + +public class Edge +{ + public ulong wayId { get; } + public ulong neighborId { get; } + public ulong neighborRegion { get; } + + public Edge(ulong wayId, ulong neighborId, ulong neighborRegion) + { + this.wayId = wayId; + this.neighborId = neighborId; + this.neighborRegion = neighborRegion; + } + + public Edge(ulong wayId, Node neighborNode) + { + this.wayId = wayId; + this.neighborId = neighborNode.id; + this.neighborRegion = Coordinates.GetRegionHashCode(neighborNode.coordinates); + } + + public const int ByteSize = sizeof(ulong) * 3; + public static Edge FromBytes(byte[] bytes) + { + using (MemoryStream m = new MemoryStream(bytes)) { + using (BinaryReader r = new BinaryReader(m)) + { + ulong wayId = r.ReadUInt64(); + ulong neighborId = r.ReadUInt64(); + ulong neighborRegion = r.ReadUInt64(); + return new Edge(wayId, neighborId, neighborRegion); + } + } + } +} \ No newline at end of file diff --git a/OsmXmlToRegionConverter/Manager.cs b/OsmXmlToRegionConverter/Manager.cs new file mode 100644 index 0000000..ae562cd --- /dev/null +++ b/OsmXmlToRegionConverter/Manager.cs @@ -0,0 +1,228 @@ +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); + } + + } +} \ No newline at end of file diff --git a/OsmXmlToRegionConverter/Node.cs b/OsmXmlToRegionConverter/Node.cs new file mode 100644 index 0000000..1a60c87 --- /dev/null +++ b/OsmXmlToRegionConverter/Node.cs @@ -0,0 +1,48 @@ +namespace OsmXmlToRegionConverter; + +public class Node +{ + public ulong id { get; } + public Coordinates coordinates { get; } + public HashSet edges { get; } + + public Node(ulong id, float latitude, float longitude) + { + this.id = id; + coordinates = new Coordinates(latitude, longitude); + edges = new HashSet(); + } + + public Node(ulong id, Coordinates coordinates) + { + this.id = id; + this.coordinates = coordinates; + edges = new HashSet(); + } + + public void AddEdge(Edge edge) + { + edges.Add(edge); + } + + public const int ByteSizeEmpty = sizeof(int) + sizeof(ulong) + Coordinates.ByteSize; + public static Node FromBytes(byte[] bytes) + { + using (MemoryStream m = new MemoryStream(bytes)) { + using (BinaryReader r = new BinaryReader(m)) + { + int edgeCount = r.ReadInt32(); + ulong id = r.ReadUInt64(); + byte[] coordinateBytes = r.ReadBytes(sizeof(float) * 2); + Coordinates coordinates = Coordinates.FromBytes(coordinateBytes); + + Node ret = new Node(id, coordinates); + + for (int i = 0; i < edgeCount; i++) + ret.AddEdge(Edge.FromBytes(r.ReadBytes(Edge.ByteSize))); + + return ret; + } + } + } +} \ No newline at end of file diff --git a/OsmXmlToRegionConverter/OsmXmlToRegionConverter.csproj b/OsmXmlToRegionConverter/OsmXmlToRegionConverter.csproj new file mode 100644 index 0000000..6836c68 --- /dev/null +++ b/OsmXmlToRegionConverter/OsmXmlToRegionConverter.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/OsmXmlToRegionConverter/Region.cs b/OsmXmlToRegionConverter/Region.cs new file mode 100644 index 0000000..e883b50 --- /dev/null +++ b/OsmXmlToRegionConverter/Region.cs @@ -0,0 +1,47 @@ +namespace OsmXmlToRegionConverter; + +public class Region +{ + private HashSet nodes; + public ulong regionHash { get; } + + private TagManager tagManager { get; } + + public Region(ulong regionHash) + { + this.regionHash = regionHash; + nodes = new HashSet(); + tagManager = new TagManager(); + } + + public void AddNode(Node nodeToAdd) + { + if (nodes.Contains(nodeToAdd)) + throw new Exception("Node already in region"); + else + nodes.Add(nodeToAdd); + } + + public Node[] GetNodes() + { + return this.nodes.ToArray(); + } + + public const int ByteSizeEmpty = sizeof(int) + sizeof(ulong); + public static Region FromBytes(byte[] bytes) + { + using (MemoryStream m = new MemoryStream(bytes)) { + using (BinaryReader r = new BinaryReader(m)) + { + int nodesInRegionCount = r.ReadInt32(); + ulong regionHash = r.ReadUInt64(); + Region retRegion = new Region(regionHash); + HashSet nodesSet = + ByteConverter.GetObjectsFromBytes(bytes.SubArray(ByteSizeEmpty, bytes.Length - ByteSizeEmpty)); + foreach(object node in nodesSet) + retRegion.AddNode((Node)node); + return retRegion; + } + } + } +} \ No newline at end of file diff --git a/OsmXmlToRegionConverter/Tag.cs b/OsmXmlToRegionConverter/Tag.cs new file mode 100644 index 0000000..3f4ead0 --- /dev/null +++ b/OsmXmlToRegionConverter/Tag.cs @@ -0,0 +1,149 @@ +namespace OsmXmlToRegionConverter; + +public class Tag +{ + public TagType key { get; } + public dynamic value { get; } + + + public Tag(TagType key, dynamic value) + { + this.key = key; + this.value = value; + } + + public static Tag FromBytes(byte[] bytes) + { + TagType type = (TagType)bytes[0]; + dynamic value = false; + switch (type) + { + case TagType.highway: + case TagType.oneway: + case TagType.forward: + value = BitConverter.ToBoolean(bytes, 1); + break; + case TagType.maxspeed: + value = bytes[1]; + break; + } + + return new Tag(type, value); + } + + public static Tag ConvertToTag(string key, string value) + { + switch (key) + { + case "highway": + try + { + return new Tag(Tag.TagType.highway, (Tag.WayType)Enum.Parse(typeof(Tag.WayType), value, true)); + } + catch (ArgumentException) + { + return new Tag(Tag.TagType.highway, Tag.WayType.unclassified); + } + case "maxspeed": + try + { + byte speed = Convert.ToByte(value); + if (speed == 255) + return new Tag(Tag.TagType.highway, false); + else + return new Tag(Tag.TagType.maxspeed, speed); + } + catch (Exception) + { + //Console.WriteLine(e); + //Console.WriteLine("Continuing..."); + return new Tag(Tag.TagType.maxspeed, byte.MaxValue); + } + case "oneway": + switch (value) + { + case "yes": + return new Tag(Tag.TagType.oneway, true); + case "-1": + return new Tag(Tag.TagType.forward, false); + case "no": + return new Tag(Tag.TagType.oneway, false); + } + + break; + } + return new Tag(Tag.TagType.EMPTY, false); + } + + public enum TagType : byte + { + highway, oneway, footway, sidewalk, cycleway, busway, forward, maxspeed, name, surface, lanes, access, tracktype, id, EMPTY + } + + public static readonly Dictionary defaultSpeedCar = new() { + { WayType.NONE, 0 }, + { WayType.motorway, 110 }, + { WayType.trunk, 100 }, + { WayType.primary, 80 }, + { WayType.secondary, 80 }, + { WayType.tertiary, 70 }, + { WayType.unclassified, 20 }, + { WayType.residential, 10 }, + { WayType.motorway_link, 50 }, + { WayType.trunk_link, 50 }, + { WayType.primary_link, 30 }, + { WayType.secondary_link, 25 }, + { WayType.tertiary_link, 25 }, + { WayType.living_street, 10 }, + { WayType.service, 0 }, + { WayType.pedestrian, 0 }, + { WayType.track, 0 }, + { WayType.bus_guideway, 0 }, + { WayType.escape, 0 }, + { WayType.raceway, 0 }, + { WayType.road, 25 }, + { WayType.busway, 0 }, + { WayType.footway, 0 }, + { WayType.bridleway, 0 }, + { WayType.steps, 0 }, + { WayType.corridor, 0 }, + { WayType.path, 0 }, + { WayType.cycleway, 0 }, + { WayType.construction, 0 } + }; + + public static readonly Dictionary defaultSpeedPedestrian = new() { + { WayType.NONE, 0 }, + { WayType.motorway, 0 }, + { WayType.trunk, 0 }, + { WayType.primary, 0 }, + { WayType.secondary, 0 }, + { WayType.tertiary, 0 }, + { WayType.unclassified, 1 }, + { WayType.residential, 3 }, + { WayType.motorway_link, 0 }, + { WayType.trunk_link, 0 }, + { WayType.primary_link, 0 }, + { WayType.secondary_link, 0 }, + { WayType.tertiary_link, 0 }, + { WayType.living_street, 5 }, + { WayType.service, 2 }, + { WayType.pedestrian, 5 }, + { WayType.track, 0 }, + { WayType.bus_guideway, 0 }, + { WayType.escape, 0 }, + { WayType.raceway, 0 }, + { WayType.road, 3 }, + { WayType.busway, 0 }, + { WayType.footway, 4 }, + { WayType.bridleway, 1 }, + { WayType.steps, 2 }, + { WayType.corridor, 3 }, + { WayType.path, 4 }, + { WayType.cycleway, 2 }, + { WayType.construction, 0 } + }; + public enum WayType : byte { 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 enum SpeedType { pedestrian, car, road } +} \ No newline at end of file diff --git a/OsmXmlToRegionConverter/TagManager.cs b/OsmXmlToRegionConverter/TagManager.cs new file mode 100644 index 0000000..9368e6b --- /dev/null +++ b/OsmXmlToRegionConverter/TagManager.cs @@ -0,0 +1,70 @@ +using System.Runtime.CompilerServices; + +namespace OsmXmlToRegionConverter; + +public class TagManager +{ + public readonly Dictionary> wayTags = new(); + + public bool ContainsKey(ulong wayId, Tag.TagType key) + { + return wayTags.ContainsKey(wayId) && wayTags[wayId].Any(tag => tag.key == key); + } + + public object? GetTag(ulong wayId, Tag.TagType key) + { + return ContainsKey(wayId, key) ? wayTags[wayId].First(tag => tag.key == key) : null; + } + + public void AddTag(ulong wayId, string key, string value) + { + Tag tag = Tag.ConvertToTag(key, value); + AddTag(wayId, tag); + } + + private void AddTag(ulong wayId, Tag tag) + { + if (tag.key != Tag.TagType.EMPTY) + { + if(!wayTags.ContainsKey(wayId)) + wayTags.Add(wayId, new HashSet()); + wayTags[wayId].Add(tag); + } + } + + public static TagManager FromBytes(byte[] bytes) + { + TagManager newTagManager = new TagManager(); + + using (MemoryStream m = new MemoryStream(bytes)) { + using (BinaryReader r = new BinaryReader(m)) + { + while (r.BaseStream.Position < bytes.Length) + { + int wayByteSize = r.ReadInt32(); + byte[] wayBytes = r.ReadBytes(wayByteSize); + AddWayFromBytes(ref newTagManager, wayBytes); + } + } + } + + return newTagManager; + } + + private static void AddWayFromBytes(ref TagManager tagManager, byte[] bytes) + { + using (MemoryStream m = new MemoryStream(bytes)) { + using (BinaryReader r = new BinaryReader(m)) + { + ulong wayId = r.ReadUInt64(); + while (r.BaseStream.Position < bytes.Length) + { + int tagsByteSize = r.ReadInt32(); + byte[] tagBytes = r.ReadBytes(tagsByteSize); + Tag tag = Tag.FromBytes(tagBytes); + tagManager.AddTag(wayId, tag); + } + } + } + } +} \ No newline at end of file diff --git a/OsmXmlToRegionConverter/TmpNode.cs b/OsmXmlToRegionConverter/TmpNode.cs new file mode 100644 index 0000000..b17970d --- /dev/null +++ b/OsmXmlToRegionConverter/TmpNode.cs @@ -0,0 +1,35 @@ +namespace OsmXmlToRegionConverter; + +public class TmpNode +{ + public ulong id { get; } + + public Coordinates coordinates { get; } + + public TmpNode(ulong id, float latitude, float longitude) + { + this.id = id; + this.coordinates = new Coordinates(latitude, longitude); + } + + public TmpNode(ulong id, Coordinates coordinates) + { + this.id = id; + this.coordinates = coordinates; + } + + public const int ByteSize = sizeof(ulong) + Coordinates.ByteSize; + public static TmpNode FromBytes(byte[] bytes) + { + using (MemoryStream m = new MemoryStream(bytes)) { + using (BinaryReader r = new BinaryReader(m)) + { + ulong id = r.ReadUInt64(); + byte coordinatesType = r.ReadByte(); + byte[] coordinateBytes = r.ReadBytes(sizeof(float) * 2); + Coordinates coordinates = Coordinates.FromBytes(coordinateBytes); + return new TmpNode(id, coordinates); + } + } + } +} \ No newline at end of file diff --git a/OsmXmlToRegionConverter/TmpWay.cs b/OsmXmlToRegionConverter/TmpWay.cs new file mode 100644 index 0000000..83f7115 --- /dev/null +++ b/OsmXmlToRegionConverter/TmpWay.cs @@ -0,0 +1,30 @@ +namespace OsmXmlToRegionConverter; + +public class TmpWay +{ + public ulong id { get; } + public ulong[] wayNodeIds { get; } + public HashSet tags { get; } + + public TmpWay(ulong id, ulong[] wayNodeIds, HashSet tags) + { + this.id = id; + this.wayNodeIds = wayNodeIds; + this.tags = tags; + } + + public static TmpWay FromBytes(byte[] bytes) + { + using (MemoryStream m = new MemoryStream(bytes)) { + using (BinaryReader r = new BinaryReader(m)) + { + int wayNodeIdsLength = r.ReadInt32(); + ulong id = r.ReadUInt64(); + ulong[] wayNodeIds = new ulong[wayNodeIdsLength]; + for (int i = 0; i < wayNodeIds.Length; i++) + wayNodeIds[i] = r.ReadUInt64(); + return new TmpWay(id, wayNodeIds); + } + } + } +} \ No newline at end of file diff --git a/Server/Server.csproj b/Server/Server.csproj index 0469d09..2d13d05 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -8,6 +8,7 @@ +