From 5e0200c35062fee81bc525be5a80aca74150579d Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Tue, 11 Aug 2026 13:54:12 +0200 Subject: [PATCH 1/2] update dependencies --- src/i3dm.export.csproj | 6 +++--- tests/InstancesRepositoryCompatibilityTests.cs | 16 +++++++++------- tests/i3dm.export.tests.csproj | 8 ++++---- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/i3dm.export.csproj b/src/i3dm.export.csproj index 383b57c..11fdf74 100644 --- a/src/i3dm.export.csproj +++ b/src/i3dm.export.csproj @@ -24,15 +24,15 @@ - + - + - + diff --git a/tests/InstancesRepositoryCompatibilityTests.cs b/tests/InstancesRepositoryCompatibilityTests.cs index 0f46a4f..a174f46 100644 --- a/tests/InstancesRepositoryCompatibilityTests.cs +++ b/tests/InstancesRepositoryCompatibilityTests.cs @@ -36,13 +36,15 @@ public void OrientationSelect_WhenYawPitchRollMissing_NonGpuFallsBackToRotation( [Test] public void OrientationSelect_WhenYawPitchRollMissing_GpuThrows() { - Assert.Throws(() => - { - InvokeGetOrientationSelectFromColumns(new HashSet(StringComparer.OrdinalIgnoreCase) - { - "rotation" - }, useGpuInstancing: true); - }); + Action action = () => + InvokeGetOrientationSelectFromColumns( + new HashSet(StringComparer.OrdinalIgnoreCase) + { + "rotation" + }, + useGpuInstancing: true); + + Assert.Throws(action); } private static (string Select, bool UsedRotation) InvokeGetOrientationSelectFromColumns(HashSet columns, bool useGpuInstancing) diff --git a/tests/i3dm.export.tests.csproj b/tests/i3dm.export.tests.csproj index 32075ca..fac93e3 100644 --- a/tests/i3dm.export.tests.csproj +++ b/tests/i3dm.export.tests.csproj @@ -7,11 +7,11 @@ - - + + - - + + From 5b3b75419b53e880661b846a57df62b3762b0dd6 Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Tue, 11 Aug 2026 14:09:46 +0200 Subject: [PATCH 2/2] fix scale_non_uniform sql query --- src/InstancesRepository.cs | 37 ++++++++++++++++--- .../InstancesRepositoryCompatibilityTests.cs | 37 +++++++++++++++++++ 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/InstancesRepository.cs b/src/InstancesRepository.cs index 0633ca6..d954eee 100644 --- a/src/InstancesRepository.cs +++ b/src/InstancesRepository.cs @@ -45,13 +45,14 @@ public static List GetInstances(NpgsqlConnection conn, string geometry var toX = ToInvariantCulture(bbox.XMax); var toY = ToInvariantCulture(bbox.YMax); - var scaleNonUniform = useScaleNonUniform ? "scale_non_uniform as scalenonuniform, " : string.Empty; conn.Open(); + var columns = GetColumns(conn, geometryTable); + var scaleSelect = GetScaleSelectFromColumns(columns, useScaleNonUniform, geometryTable); var select = keepProjection? - $"SELECT ST_ASBinary(st_force3d({geometryColumn})) as position, scale, {scaleNonUniform} model, tags": - $"SELECT ST_ASBinary(ST_Transform(st_force3d({geometryColumn}), {target_epsg})) as position, scale, {scaleNonUniform} model, tags"; + $"SELECT ST_ASBinary(st_force3d({geometryColumn})) as position, {scaleSelect} model, tags": + $"SELECT ST_ASBinary(ST_Transform(st_force3d({geometryColumn}), {target_epsg})) as position, {scaleSelect} model, tags"; - var orientationSelect = GetOrientationSelect(conn, geometryTable, useGpuInstancing); + var orientationSelect = GetOrientationSelect(columns, geometryTable, useGpuInstancing); select += orientationSelect; var sql = FormattableString.Invariant($"{select} FROM {geometryTable} where {GetWhere(geometryColumn, where, fromX, fromY, toX, toY, source_epsg, keepProjection)}"); @@ -104,9 +105,8 @@ public static (BoundingBox bbox, double zmin, double zmax) GetBoundingBoxForTabl return (bbox, zmin, zmax); } - private static string GetOrientationSelect(NpgsqlConnection conn, string geometryTable, bool useGpuInstancing) + private static string GetOrientationSelect(HashSet columns, string geometryTable, bool useGpuInstancing) { - var columns = GetColumns(conn, geometryTable); var select = GetOrientationSelectFromColumns(columns, useGpuInstancing, geometryTable, out var usesDeprecatedRotation); if (usesDeprecatedRotation) @@ -158,6 +158,31 @@ private static string GetOrientationSelectFromColumns(HashSet columns, b throw new InvalidOperationException($"Missing orientation columns for {mode}. Expected columns yaw/pitch/roll. For non-GPU you can use legacy rotation (deprecated).\n\nMigration example:\n alter table {geometryTable} add column if not exists yaw double precision default 0;\n alter table {geometryTable} add column if not exists pitch double precision default 0;\n alter table {geometryTable} add column if not exists roll double precision default 0;\n update {geometryTable} set yaw = rotation where rotation is not null;"); } + private static string GetScaleSelectFromColumns(HashSet columns, bool useScaleNonUniform, string geometryTable) + { + var hasScale = columns.Contains("scale"); + var hasScaleNonUniform = columns.Contains("scale_non_uniform"); + + if (useScaleNonUniform) + { + if (!hasScaleNonUniform) + { + throw new InvalidOperationException($"Missing column 'scale_non_uniform' on table {geometryTable}. Add it with:\n alter table {geometryTable} add column if not exists scale_non_uniform double precision[3];"); + } + + // scale column is optional when using scale_non_uniform + var scalePart = hasScale ? "scale, " : string.Empty; + return $"{scalePart}scale_non_uniform as scalenonuniform, "; + } + + if (!hasScale) + { + throw new InvalidOperationException($"Missing column 'scale' on table {geometryTable}. Add it with:\n alter table {geometryTable} add column if not exists scale double precision default 1;"); + } + + return "scale, "; + } + private static void WriteRotationDeprecatedWarning(string geometryTable) { if (_rotationDeprecatedWarningWritten) return; diff --git a/tests/InstancesRepositoryCompatibilityTests.cs b/tests/InstancesRepositoryCompatibilityTests.cs index a174f46..0080e30 100644 --- a/tests/InstancesRepositoryCompatibilityTests.cs +++ b/tests/InstancesRepositoryCompatibilityTests.cs @@ -60,4 +60,41 @@ private static (string Select, bool UsedRotation) InvokeGetOrientationSelectFrom var usedRotation = (bool)args[3]; return (select, usedRotation); } + + [Test] + public void ScaleSelect_WhenUseScaleNonUniformTrue_AndOnlyScaleNonUniformColumnExists_DoesNotRequireScaleColumn() + { + // Table only has scale_non_uniform, no scale column + var select = InvokeGetScaleSelectFromColumns(new HashSet(StringComparer.OrdinalIgnoreCase) + { + "scale_non_uniform" + }, useScaleNonUniform: true); + + Assert.That(select, Does.Not.Contain("scale,")); + Assert.That(select, Does.Contain("scale_non_uniform as scalenonuniform")); + } + + [Test] + public void ScaleSelect_WhenUseScaleNonUniformFalse_AndScaleColumnMissing_Throws() + { + Action action = () => + InvokeGetScaleSelectFromColumns(new HashSet(StringComparer.OrdinalIgnoreCase) + { + "scale_non_uniform" + }, useScaleNonUniform: false); + + Assert.Throws(action); + } + + private static string InvokeGetScaleSelectFromColumns(HashSet columns, bool useScaleNonUniform) + { + var method = typeof(InstancesRepository).GetMethod( + "GetScaleSelectFromColumns", + BindingFlags.NonPublic | BindingFlags.Static); + + Assert.That(method, Is.Not.Null); + + var args = new object[] { columns, useScaleNonUniform, "public.instances" }; + return (string)method!.Invoke(null, args)!; + } }