OSMServer/OsmXmlToRegionConverter/Manager.cs
2023-03-30 16:29:42 +02:00

228 lines
9.3 KiB
C#

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<ulong> nodeIdsToImport = GetHighwayNodeIds(XmlReader.Create(xmlFileStream, readerSettings));
xmlFileStream.Position = 0;
Console.WriteLine("Converting Nodes...");
Dictionary<ulong, ulong> 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<ulong> 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<ulong> GetHighwayNodeIds(XmlReader xmlReader)
{
HashSet<ulong> retSet = new();
bool isHighway;
HashSet<ulong> 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<ulong, ulong> ImportNodesToRegionsTmp(XmlReader xmlReader, HashSet<ulong> nodeIdsToImport, string outputFolderPath)
{
Dictionary<ulong, FileStream> regionWriters = new Dictionary<ulong, FileStream>();
Dictionary<ulong, ulong> nodeRegions = new Dictionary<ulong, ulong>();
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<ulong, ulong> nodeRegions, string outputFolderPath)
{
Dictionary<ulong, FileStream> regionWriters = new Dictionary<ulong, FileStream>();
bool isHighway = false;
HashSet<ulong> currentIds = new();
while (xmlReader.ReadToFollowing("way"))
{
HashSet<Tag> tagsSet = new HashSet<Tag>();
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<ulong> regionsToWrite = new HashSet<ulong>();
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<ulong> 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<ulong, Node> nodes = new Dictionary<ulong, Node>();
byte[] nodeBytes = File.ReadAllBytes(nodesFilePath);
HashSet<object> 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<object> 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);
}
}
}