diff --git a/TileBakeLibrary/CityJSONParsing/CityJSON.cs b/TileBakeLibrary/CityJSONParsing/CityJSON.cs index 72d4e82..d1cef09 100644 --- a/TileBakeLibrary/CityJSONParsing/CityJSON.cs +++ b/TileBakeLibrary/CityJSONParsing/CityJSON.cs @@ -180,11 +180,11 @@ private CityObject ReadCityObject(JSONNode node, string filter = "") if (geometrynode["type"] == "Solid") { JSONNode boundaries = geometrynode["boundaries"]; - surfaces = ReadSolid(geometrynode); + surfaces = ReadSolid(geometrynode, cityObject); } else if (geometrynode["type"] == "MultiSurface") { - surfaces = ReadMultiSurface(geometrynode); + surfaces = ReadMultiSurface(geometrynode, cityObject); } else { @@ -284,21 +284,21 @@ private CityObject ReadCityObject(JSONNode node, string filter = "") return cityObject; } - private List ReadSolid(JSONNode geometrynode) + private List ReadSolid(JSONNode geometrynode, CityObject sourceCityObject) { JSONNode boundariesNode = geometrynode["boundaries"]; List result = new List(); //Read exterior shell foreach (JSONNode surfacenode in boundariesNode[0]) - { - result.Add(ReadSurfaceVectors(surfacenode)); + { + result.Add(ReadSurfaceVectors(surfacenode,sourceCityObject,true,true)); } return result; } - private List ReadMultiSurface(JSONNode geometrynode) + private List ReadMultiSurface(JSONNode geometrynode, CityObject sourceCityObject) { JSONNode boundariesNode = geometrynode["boundaries"]; List result = new List(); @@ -372,31 +372,40 @@ private Surface AddSurfaceUVs(JSONNode UVValueNode, Surface surf) } return surf; } - private Surface ReadSurfaceVectors(JSONNode surfacenode) + private Surface ReadSurfaceVectors(JSONNode surfacenode, CityObject sourceCityObject, bool createOuterRing = true, bool createInnerRings = true) { Surface surf = new Surface(); //read exteriorRing List verts = new List(); - foreach (JSONNode vectornode in surfacenode[0]) + + if(createOuterRing) { - verts.Add(vertices[vectornode.AsInt]); - } - surf.outerRing = verts; + foreach (JSONNode vectornode in surfacenode[0]) + { + verts.Add(vertices[vectornode.AsInt]); + } + surf.outerRing = verts; + } - int maxHoles = 3; + if(!createInnerRings) + return surf; for (int i = 1; i < surfacenode.Count; i++) { - if(i > maxHoles) return surf; - verts = new List(); foreach (JSONNode vectornode in surfacenode[i]) { verts.Add(vertices[vectornode.AsInt]); } - surf.innerRings.Add(verts); + //Only more than triangle as holes + if(verts.Count > 3) { + surf.innerRings.Add(verts); + } + else{ + sourceCityObject.warnings += "- Holes in surface detected, but not supported. Ignoring holes.\n"; + } } - + return surf; } @@ -423,6 +432,8 @@ public class CityObject public string cityObjectType; public string keyName = ""; + public string warnings = ""; + public CityObject() { semantics = new(); diff --git a/TileBakeLibrary/CityJSONToTileConverter.cs b/TileBakeLibrary/CityJSONToTileConverter.cs index 584f9cb..529f81f 100644 --- a/TileBakeLibrary/CityJSONToTileConverter.cs +++ b/TileBakeLibrary/CityJSONToTileConverter.cs @@ -34,6 +34,7 @@ namespace TileBakeLibrary { public class CityJSONToTileConverter { + private string logFileName = ""; private string sourcePath = ""; private string outputPath = ""; private string identifier = ""; @@ -177,6 +178,13 @@ public void SetObjectFilters(CityObjectFilter[] cityObjectFilters) /// public void Convert() { + //Create log file (overwrite) + var readableDateTime = DateTime.Now.ToString("yyyy-MM-dd_HH_mm"); + var currentPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + + logFileName = currentPath + "/bakelog" + readableDateTime + ".txt"; + File.WriteAllText(logFileName, string.Empty); + //If no specific filename or wildcard was supplied, default to .json files var filter = Path.GetFileName(sourcePath); if (filter == "") filter = "*.json"; @@ -227,12 +235,14 @@ public void Convert() thread.Join(); cityJson = nextCityJSON; } + Console.WriteLine($"Log file: {currentPath}/bakelog{readableDateTime}.txt"); //Optional compressed variant if (brotliCompress) { CompressFiles(); } + Console.WriteLine($"Log file: {currentPath}/bakelog{readableDateTime}.txt"); } /// @@ -244,10 +254,21 @@ private void ReadCityJSON() watch.Start(); tiles = new List(); - var cityObjects = CityJSONParseProcess(cityJson); + //Warnings bag + var warnings = new ConcurrentBag(); + var cityObjects = CityJSONParseProcess(cityJson, warnings); allSubObjects.Clear(); allSubObjects = cityObjects; + //write warnings to log newlines + if(warnings.Count > 0) + File.AppendAllText(logFileName, $"Warnings for {cityJson.sourceFilePath}\n"); + foreach (var warning in warnings) + { + File.AppendAllText(logFileName, warning + "\n"); + } + File.AppendAllText(logFileName, "\n"); + Console.WriteLine($"\n{allSubObjects.Count} CityObjects with LOD{lod} were imported"); PrepareTiles(); WriteTileData(); @@ -430,7 +451,7 @@ private void ParseExistingBinaryTile(Tile tile) bmd = null; } - private List CityJSONParseProcess(CityJSON cityJson) + private List CityJSONParseProcess(CityJSON cityJson, ConcurrentBag warnings) { List filteredObjects = new List(); Console.WriteLine(""); @@ -444,6 +465,7 @@ private List CityJSONParseProcess(CityJSON cityJson) int simplifying = 0; int tiling = 0; var filterObjectsBucket = new ConcurrentBag(); + var failedSubObjects = new ConcurrentBag(); int[] indices = Enumerable.Range(0, cityObjectCount).ToArray(); //Turn cityobjects (and their children) into SubObject mesh data @@ -455,9 +477,15 @@ private List CityJSONParseProcess(CityJSON cityJson) CityObject cityObject = cityJson.LoadCityObjectByIndex(i, lod); var subObject = ToSubObjectMeshData(cityObject); + + if(!string.IsNullOrEmpty(cityObject.warnings)) + { + warnings.Add(cityObject.keyName + ":\n" + cityObject.warnings); + } + cityObject = null; Interlocked.Decrement(ref parsing); - cityObject = null; + if (subObject == null) { Interlocked.Increment(ref done); diff --git a/TileBakeLibrary/Triangulation/Poly2Mesh.cs b/TileBakeLibrary/Triangulation/Poly2Mesh.cs index dc63b97..42d5a38 100644 --- a/TileBakeLibrary/Triangulation/Poly2Mesh.cs +++ b/TileBakeLibrary/Triangulation/Poly2Mesh.cs @@ -266,6 +266,7 @@ public static void CreateMeshData(Polygon polygon, out Vector3[] vertices, out V { Console.WriteLine("Failed to triangulate polygon: " + e.Message); Console.WriteLine("With name: " + cityObject.keyName); + cityObject.warnings += "- Failed to triangulate polygon: " + e.Message + "\n"; return; } // Now, to get back to our original positions, use our code-to-position map. We do