Skip to content

Commit 762345a

Browse files
committed
to geometric error
1 parent f0061b8 commit 762345a

File tree

11 files changed

+59
-163
lines changed

11 files changed

+59
-163
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,9 @@ If --username and/or --dbname are not specified the current username is used as
192192
193193
-l, --lodcolumn (Default: '') LOD column (Cesium)
194194
195-
-g, --geometricerrors (Default: 2000,0) Geometric errors (Cesium)
195+
-g, --geometricerror (Default: 2000) Geometric error (Cesium)
196+
197+
--geometricerrorfactor (Default: 2) Geometric error factor (Cesium)
196198
197199
--shaderscolumn (Default: '') shaders column (Cesium)
198200

cesium_notes.md

Lines changed: 4 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ Some remarks about implicit tiling:
1818

1919
- Parameter '-l --lodcolumn' is ignored when using implicit tiling;
2020

21-
- Only the first value of parameter of geometric errors is used in tileset.json;
21+
- In the root tileset.json the maximum geometric error is used. For lower levels the geometric error is calculated based on the parent
22+
- geometric error and the geometric error factor. For example when the geometric error is 2000 and the geometric error factor is 2, the geometric error for the children will be 1000.
2223

2324
- When using larger geometries (that intersect partly with a quadtree tile) and implicit tiling there can be issues with feature visibility.
2425

@@ -29,10 +30,10 @@ For more information about Implicit Tiling see https://github.com/CesiumGS/3d-ti
2930
With the LOD function there can be multiple representations of features depending on the distance to the camera (geometric error). So
3031
for example visualize a simplified geometry when the camera is far away, and a more detailed geometry when the camera is close.
3132

32-
Sample command for using LOD's:
33+
Sample command for using LOD's using the delaware_buildings_lod table (see script 2_create_delaware_table.sql script in the database test project):
3334

3435
```
35-
-h localhost -U postgres -c geom_triangle --shaderscolumn shaders -t delaware_buildings_lod -d postgres -g 1000,100,0 --lodcolumn lodcolumn --use_implicit_tiling false --max_features_per_tile 1000
36+
-h localhost -U postgres -p 5432 -c geom_triangle -t delaware_buildings_lod -d postgres -g 2000 --shaderscolumn shaders --lodcolumn lodcolumn --use_implicit_tiling false -r REPLACE --geometricerrorfactor 8
3637
```
3738

3839
The LOD function will be enabled when parameter --lodcolumn is not empty.
@@ -49,70 +50,12 @@ Demo 2 LODS https://bertt.github.io/cesium_3dtiles_samples/samples/lod_bag3d/
4950

5051
![bag_lods](https://github.com/Geodan/pg2b3dm/assets/538812/cc5bd11e-0302-4271-b39d-7065b98177ba)
5152

52-
How to create Cesium viewer with multiple lods for Dutch Bag
53-
54-
1] Download geopackage
55-
56-
https://data.3dbag.nl/v20240228/tiles/7/512/560/7-512-560.gpkg
57-
58-
2] Inspect
59-
60-
Contains
61-
62-
lod12_3d
63-
lod13_3d
64-
lod22_3d
65-
66-
3] Load in PostGIS
67-
68-
69-
$ ogr2ogr -f PostgreSQL pg:"host=localhost user=postgres password=postgres" -t_srs epsg:4979 7-512-560.gpkg lod12_3d -nln werkhoven_lod12
70-
71-
$ ogr2ogr -f PostgreSQL pg:"host=localhost user=postgres password=postgres" -t_srs epsg:4979 7-512-560.gpkg lod13_3d -nln werkhoven_lod13
72-
73-
$ ogr2ogr -f PostgreSQL pg:"host=localhost user=postgres password=postgres" -t_srs epsg:4979 7-512-560.gpkg lod22_3d -nln werkhoven_lod22
74-
75-
4] Combine the data in PostgreSQL
76-
77-
```
78-
CREATE TABLE werkhoven_lods AS SELECT * FROM werkhoven_lod12
79-
alter table werkhoven_lods add lods decimal
80-
update werkhoven_lods set lods=1
81-
82-
insert into werkhoven_lods(fid, identificatie, b3_pand_deel_id, labels, geom, lods)
83-
select fid, identificatie, b3_pand_deel_id, labels, geom, 2 from werkhoven_lod13
84-
85-
insert into werkhoven_lods(fid, identificatie, b3_pand_deel_id, labels, geom, lods)
86-
select fid, identificatie, b3_pand_deel_id, labels, geom, 3 from werkhoven_lod22
87-
```
88-
89-
5] Tile the table
90-
91-
Note:
92-
93-
- Use --use_implicit_tiling false
94-
95-
- Add --lodcolumn lods
96-
97-
$ pg2b3dm -h localhost -U postgres -c geom -t werkhoven_lods -d postgres -a identificatie --lodcolumn lods --use_implicit_tiling false -g 2000,5,1,0
98-
99-
Tiles like 1_0_1_1.glb are created, last number is the lod level
100-
101-
6] View https://bertt.github.io/cesium_3dtiles_samples/samples/lod_bag3d/
102-
10353
Notes:
10454

10555
- if there are no features within a tile boundingbox, the tile (including children) will not be generated.
10656

10757
- LOD function is not available when implicit tiling is used.
10858

109-
## Geometric errors
110-
111-
By default, as geometric errors [2000,0] are used (for 1 LOD). When there multiple LOD's, there should be number_of_lod + 1 geometric errors specified in the -g option.
112-
When using multiple LOD and the -g option is not specified, the geometric errors are calculated using equal intervals
113-
between 2000 and 0.
114-
When using implicit tiling only the first value of the geometric errors is used, the rest is automatically calculated by implicit tiling.
115-
11659

11760
## Outlines
11861

src/b3dm.tileset.tests/CesiumTilerTests.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ public void TestCreateImplicitTileset()
1616
var outputDirectory = "test";
1717
Directory.CreateDirectory(outputDirectory);
1818
var translation = new double[] { 0, 0, 0 };
19-
var geometricErrors = new double[] { 0.1 };
19+
var geometricError = 100;
2020
var rootBoundingVolumeRegion = new double[] { 0, 0, 0, 0, 0, 0 };
2121
var subtreesDirectory = "output";
2222
var tile1 = new Tile(1, 1, 1);
2323
var tiles = new List<Tile>() { tile1 };
2424

2525
// Act
26-
CesiumTiler.CreateImplicitTileset(version, createGltf, outputDirectory, translation, geometricErrors, rootBoundingVolumeRegion, subtreesDirectory, tiles);
26+
CesiumTiler.CreateImplicitTileset(version, createGltf, outputDirectory, translation, geometricError, rootBoundingVolumeRegion, subtreesDirectory, tiles);
2727

2828
// Assert
2929
var json = File.ReadAllText("test/tileset.json");
@@ -40,7 +40,8 @@ public void TestCreateExplicitTilesetsJson()
4040
var zmin = 0.0;
4141
var zmax = 1.0;
4242
var translation = new double[] { 0, 0, 0 };
43-
var geometricErrors = new double[] { 100,0 };
43+
var geometricError = 100;
44+
var geometricErrorFactor = 2;
4445
var refinement = "add";
4546
var use10 = false;
4647
var rootBoundingVolumeRegion = new double[] { 0, 0, 0, 0, 0, 0, zmin, zmax };
@@ -61,7 +62,7 @@ public void TestCreateExplicitTilesetsJson()
6162
var tiles = new List<Tile>() { tile0, tile1, tile2, tile3};
6263

6364
// Act
64-
CesiumTiler.CreateExplicitTilesetsJson(version, outputDirectory, translation, geometricErrors, refinement, use10, rootBoundingVolumeRegion, tile1, tiles);
65+
CesiumTiler.CreateExplicitTilesetsJson(version, outputDirectory, translation, geometricError, geometricErrorFactor, refinement, use10, rootBoundingVolumeRegion, tile1, tiles);
6566

6667
// Assert
6768
var json = File.ReadAllText("testExplicit/tileset.json");

src/b3dm.tileset.tests/GeometricErrorCalculatorTests.cs

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,10 @@ namespace B3dm.Tileset.Tests;
66
public class GeometricErrorCalculatorTests
77
{
88
[Test]
9-
public void CalculateGeometricErrorFirstTest()
9+
public void GeometricErrorCalculator2()
1010
{
11-
var lods = new List<int> { 0, 1 };
12-
var geometricErrors = GeometricErrorCalculator.GetGeometricErrors(500, lods);
13-
14-
Assert.That(geometricErrors.Length == 3, Is.True);
15-
Assert.That(geometricErrors[0] == 500, Is.True);
16-
Assert.That(geometricErrors[1] == 250, Is.True);
17-
Assert.That(geometricErrors[2] == 0, Is.True);
18-
}
19-
20-
[Test]
21-
public void CalculateGeometricErrorForOnly1Level()
22-
{
23-
var lods = new List<int> { 0 };
24-
var geometricErrors = GeometricErrorCalculator.GetGeometricErrors(500, lods);
25-
26-
Assert.That(geometricErrors.Length == 2, Is.True);
27-
Assert.That(geometricErrors[0] == 500, Is.True);
28-
Assert.That(geometricErrors[1] == 0, Is.True);
29-
}
30-
31-
[Test]
32-
public void CalculateGeometricErrorRoundingTest(){
33-
var lods = new List<int> { 0,1,2 };
34-
var geometricErrors = GeometricErrorCalculator.GetGeometricErrors(100, lods);
35-
Assert.That(geometricErrors.Length == 4, Is.True);
36-
Assert.That(geometricErrors[0] == 100, Is.True);
37-
Assert.That(geometricErrors[1] == 67, Is.True);
38-
Assert.That(geometricErrors[2] == 33, Is.True);
39-
Assert.That(geometricErrors[3] == 0, Is.True);
11+
Assert.That(GeometricErrorCalculator.GetGeometricError(2000, 2, 1) == 1000);
12+
Assert.That(GeometricErrorCalculator.GetGeometricError(2000, 2, 0) == 2000);
13+
Assert.That(GeometricErrorCalculator.GetGeometricError(2000, 2, 2) == 500);
4014
}
4115
}

src/b3dm.tileset.tests/TreeSerializerTests.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public void SerializeTree()
4646
var bbox = new double[] { 0, 0, 1, 1, 0, 10 };
4747

4848
// assert
49-
var tileset = TreeSerializer.ToTileset(tiles, translation, bbox, new double[] { 500, 0 });
49+
var tileset = TreeSerializer.ToTileset(tiles, translation, bbox, 500, 2);
5050
Assert.That(tileset.root.children.Count == 2, Is.True);
5151
}
5252

@@ -76,11 +76,10 @@ public void SerializeTreeWithLods()
7676
var bbox = new double[] { 0, 0, 1, 1, 0, 10 };
7777

7878
// assert
79-
var tileset = TreeSerializer.ToTileset(tiles, translation, bbox, new double[] {500,100,0});
79+
var tileset = TreeSerializer.ToTileset(tiles, translation, bbox, 500);
8080
Assert.That(tileset.root.children[0].children.Count == 1, Is.True);
81-
Assert.That(tileset.root.geometricError==500, Is.True);
82-
Assert.That(tileset.root.children[0].geometricError == 100, Is.True);
83-
Assert.That(tileset.root.children[0].children[0].geometricError == 0, Is.True);
84-
81+
Assert.That(tileset.root.geometricError==250, Is.True);
82+
Assert.That(tileset.root.children[0].geometricError == 125, Is.True);
83+
Assert.That(tileset.root.children[0].children[0].geometricError == 15.625, Is.True);
8584
}
8685
}

src/b3dm.tileset/CesiumTiler.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
namespace B3dm.Tileset;
1111
public static class CesiumTiler
1212
{
13-
public static void CreateImplicitTileset(Version version, bool createGltf, string outputDirectory, double[] translation, double[] geometricErrors, double[] rootBoundingVolumeRegion, string subtreesDirectory, List<Tile> tiles)
13+
public static void CreateImplicitTileset(Version version, bool createGltf, string outputDirectory, double[] translation, double geometricError, double[] rootBoundingVolumeRegion, string subtreesDirectory, List<Tile> tiles)
1414
{
1515
if (!Directory.Exists(subtreesDirectory)) {
1616
Directory.CreateDirectory(subtreesDirectory);
@@ -28,20 +28,20 @@ public static void CreateImplicitTileset(Version version, bool createGltf, strin
2828
var availableLevels = tiles.Max(t => t.Z) + 1;
2929
Console.WriteLine("Available Levels: " + availableLevels);
3030
Console.WriteLine("Subtree Levels: " + subtreeLevels);
31-
var tilesetjson = TreeSerializer.ToImplicitTileset(translation, rootBoundingVolumeRegion, geometricErrors[0], availableLevels, subtreeLevels, version, createGltf);
31+
var tilesetjson = TreeSerializer.ToImplicitTileset(translation, rootBoundingVolumeRegion, geometricError, availableLevels, subtreeLevels, version, createGltf);
3232
var file = $"{outputDirectory}{Path.AltDirectorySeparatorChar}tileset.json";
3333
var json = JsonConvert.SerializeObject(tilesetjson, Formatting.Indented, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
3434
Console.WriteLine("SubdivisionScheme: QUADTREE");
3535
Console.WriteLine($"Writing {file}...");
3636
File.WriteAllText(file, json);
3737
}
3838

39-
public static void CreateExplicitTilesetsJson(Version version, string outputDirectory, double[] translation, double[] geometricErrors, string refinement, bool use10, double[] rootBoundingVolumeRegion, Tile tile, List<Tile> tiles)
39+
public static void CreateExplicitTilesetsJson(Version version, string outputDirectory, double[] translation, double geometricError, double geometricErrorFactor, string refinement, bool use10, double[] rootBoundingVolumeRegion, Tile tile, List<Tile> tiles)
4040
{
4141
var splitLevel = (int)Math.Ceiling((tiles.Max((Tile s) => s.Z) + 1.0) / 2.0);
4242

4343
var rootTiles = TileSelector.Select(tiles, tile, 0, splitLevel);
44-
var rootTileset = TreeSerializer.ToTileset(rootTiles, translation, rootBoundingVolumeRegion, geometricErrors, version, refinement, use10);
44+
var rootTileset = TreeSerializer.ToTileset(rootTiles, translation, rootBoundingVolumeRegion, geometricError, geometricErrorFactor, version, refinement, use10);
4545

4646
var maxlevel = tiles.Max((Tile s) => s.Z);
4747

@@ -61,7 +61,11 @@ public static void CreateExplicitTilesetsJson(Version version, string outputDire
6161
var zminmax = children.Select(t => new double[] { (double)t.ZMin, (double)t.ZMax }).SelectMany(t => t).ToArray();
6262
var childrenBoundingVolumeRegion = GetBoundingBox(children).ToRadians().ToRegion(zminmax[0], zminmax[1]);
6363
/// translation is the same as identity matrix in case of child tileset
64-
var tileset = TreeSerializer.ToTileset(children, null, childrenBoundingVolumeRegion, geometricErrors, version, refinement, use10);
64+
var tileset = TreeSerializer.ToTileset(children, null, childrenBoundingVolumeRegion, geometricError, geometricErrorFactor, version, refinement, use10);
65+
66+
var childGeometricError = GeometricErrorCalculator.GetGeometricError(geometricError, geometricErrorFactor, splitLevel);
67+
tileset.geometricError = childGeometricError;
68+
tileset.root.geometricError = GeometricErrorCalculator.GetGeometricError(childGeometricError,geometricErrorFactor,1);
6569
var detailedJson = JsonConvert.SerializeObject(tileset, Formatting.Indented, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
6670
var filename = $"tileset_{splitLevel}_{i}_{j}.json";
6771
Console.Write($"\rWriting {filename}...");
@@ -72,7 +76,8 @@ public static void CreateExplicitTilesetsJson(Version version, string outputDire
7276
var child = new Child();
7377
child.boundingVolume = new Boundingvolume() { region = childrenBoundingVolumeRegion };
7478
child.refine = refinement;
75-
child.geometricError = geometricErrors[0];
79+
child.geometricError = tileset.root.geometricError;
80+
7681
child.content = new Content() { uri = filename };
7782
rootTileset.root.children.Add(child);
7883
}

src/b3dm.tileset/GeometricErrorCalculator.cs

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,13 @@ namespace B3dm.Tileset;
55

66
public class GeometricErrorCalculator
77
{
8-
public static double[] GetGeometricErrors(double maxGeometricError, List<int> lods)
8+
public static double GetGeometricError(double maxGeometricError, double geometricErrorFactor, int z, int lod = 0)
99
{
10-
var nrOfLods = lods.Count;
10+
var geometricError = maxGeometricError / Math.Pow(geometricErrorFactor, z);
1111

12-
var res = new List<double>() { maxGeometricError };
13-
14-
if (lods.Count > 1) {
15-
var step = maxGeometricError / nrOfLods;
16-
17-
for (var i = 1; i <= lods.Count; i++) {
18-
res.Add(Math.Round(maxGeometricError - i * step));
19-
}
12+
if (lod>0) {
13+
geometricError = geometricError / Math.Pow(2, lod);
2014
}
21-
else {
22-
// only 1 leaf so make it 0
23-
res.Add(0);
24-
}
25-
26-
return res.ToArray();
27-
}
15+
return geometricError;
16+
}
2817
}

src/b3dm.tileset/TreeSerializer.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ public static TileSet ToImplicitTileset(double[] transform, double[] box, double
2828
return tileset;
2929
}
3030

31-
public static TileSet ToTileset(List<Tile> tiles, double[] transform, double[] region, double[] geometricErrors, Version version = null, string refine="ADD", bool use10 = false)
31+
public static TileSet ToTileset(List<Tile> tiles, double[] transform, double[] region, double geometricError, double geometricErrorFactor = 2, Version version = null, string refine="ADD", bool use10 = false)
3232
{
33-
var tileset = GetTilesetObject(version, geometricErrors[0], use10);
33+
var tileset = GetTilesetObject(version, geometricError, use10);
3434

3535
var t = new double[] { 1.0, 0.0, 0.0, 0.0,
3636
0.0,1.0, 0.0, 0.0,
@@ -44,9 +44,13 @@ public static TileSet ToTileset(List<Tile> tiles, double[] transform, double[] r
4444
transform[0], transform[1], transform[2], 1.0};
4545
}
4646

47-
var root = GetRoot(geometricErrors[0], t, region, refine);
48-
var children = GetChildren(tiles, geometricErrors.Skip(1).ToArray());
47+
var root = GetRoot(geometricError, t, region, refine);
48+
tileset.geometricError = geometricError;
49+
root.geometricError = GeometricErrorCalculator.GetGeometricError(geometricError, geometricErrorFactor, 1);
50+
var childrenGeometricError = GeometricErrorCalculator.GetGeometricError(geometricError, geometricErrorFactor, 2);
51+
var children = GetChildren(tiles, childrenGeometricError, geometricErrorFactor);
4952
root.children = children;
53+
5054
tileset.root = root;
5155
return tileset;
5256
}
@@ -75,15 +79,16 @@ public static Root GetRoot(double geometricError, double[] translation, double[]
7579
return root;
7680
}
7781

78-
private static List<Child> GetChildren(List<Tile> tiles, double[] geometricError)
82+
private static List<Child> GetChildren(List<Tile> tiles, double geometricError, double geometricErrorFactor)
7983
{
8084
var children = new List<Child>();
8185
foreach (var tile in tiles) {
8286
if (tile.Available) {
83-
var child = GetChild(tile, geometricError[0]);
87+
var ge = GeometricErrorCalculator.GetGeometricError(geometricError, geometricErrorFactor, tile.Z, tile.Lod);
88+
var child = GetChild(tile, ge);
8489

8590
if (tile.Children != null) {
86-
child.children = GetChildren(tile.Children, geometricError.Skip(1).ToArray());
91+
child.children = GetChildren(tile.Children, geometricError, geometricErrorFactor);
8792
}
8893
children.Add(child);
8994
}

src/pg2b3dm/CesiumOptions.cs

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/pg2b3dm/Options.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,11 @@ public class Options
6060
[Option('l', "lodcolumn", Required = false, Default = "", HelpText = "LOD column (Cesium)", SetName = "Cesium")]
6161
public string LodColumn { get; set; }
6262

63-
[Option('g', "geometricerrors", Required = false, Default = "2000,0", HelpText = "Geometric errors (Cesium)", SetName = "Cesium")]
64-
public string GeometricErrors { get; set; }
63+
[Option('g', "geometricerror", Required = false, Default = 2000, HelpText = "Geometric error (Cesium)", SetName = "Cesium")]
64+
public double GeometricError{ get; set; }
65+
66+
[Option("geometricerrorfactor", Required = false, Default = 2, HelpText = "Geometric Error factor (Cesium)", SetName = "Cesium")]
67+
public double GeometricErrorFactor { get; set; }
6568

6669
[Option("use_implicit_tiling", Required = false, Default = true, HelpText = "use 1.1 implicit tiling (Cesium)", SetName = "Cesium")]
6770
public bool? UseImplicitTiling { get; set; }

0 commit comments

Comments
 (0)