From 5469a123e1f0f14454a3a745aeb6a0450325ac05 Mon Sep 17 00:00:00 2001 From: KatKatKateryna <89912278+KatKatKateryna@users.noreply.github.com> Date: Thu, 30 May 2024 17:17:50 +0800 Subject: [PATCH] CNX-9446 create enums for arc gis custom types (#3429) * add layer geometry types class * fix dataset overwriting caused by the fallback conversions * class for FieldTypes * declutter CreateDatasetInDatabase * post-merge issues * post-merge issues2 --------- Co-authored-by: Alan Rynne --- .../Features/GisFeatureToSpeckleConverter.cs | 16 +-- .../PointcloudLayerToSpeckleConverter.cs | 3 +- .../Layers/VectorLayerToHostConverter.cs | 2 +- .../Layers/VectorLayerToSpeckleConverter.cs | 34 ++--- .../Utils/ArcGISFieldUtils.cs | 57 +-------- .../Utils/FeatureClassUtils.cs | 57 ++------- .../Utils/GISAttributeFieldType.cs | 119 ++++++++++++++++++ .../Utils/GISLayerGeometryType.cs | 12 ++ .../Utils/IArcGISFieldUtils.cs | 2 - .../Utils/IFeatureClassUtils.cs | 1 - .../Utils/NonNativeFeaturesUtils.cs | 22 ++-- 11 files changed, 175 insertions(+), 150 deletions(-) create mode 100644 DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs create mode 100644 DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISLayerGeometryType.cs diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Features/GisFeatureToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Features/GisFeatureToSpeckleConverter.cs index 10aac2ae25..a3c9d41e40 100644 --- a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Features/GisFeatureToSpeckleConverter.cs +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Features/GisFeatureToSpeckleConverter.cs @@ -3,6 +3,7 @@ using ArcGIS.Core.Data; using Speckle.Converters.ArcGIS3.Geometry; using Speckle.Converters.Common; +using Speckle.Converters.ArcGIS3.Utils; namespace Speckle.Converters.ArcGIS3.Features; @@ -57,32 +58,23 @@ public SGIS.GisFeature Convert(Row target) IReadOnlyList fields = target.GetFields(); foreach (Field field in fields) { - string name = field.Name; // POC: check for all possible reserved Shape names if (field.FieldType == FieldType.Geometry) // ignore the field with geometry itself { hasGeometry = true; - geometryField = name; + geometryField = field.Name; } // Raster FieldType is not properly supported through API else if ( field.FieldType == FieldType.Raster || field.FieldType == FieldType.Blob || field.FieldType == FieldType.XML ) { - attributes[name] = null; + attributes[field.Name] = null; } // to not break serializer (DateOnly) and to simplify complex types - else if ( - field.FieldType == FieldType.DateOnly - || field.FieldType == FieldType.TimeOnly - || field.FieldType == FieldType.TimestampOffset - ) - { - attributes[name] = target[name]?.ToString(); - } else { - attributes[name] = target[name]; + attributes[field.Name] = GISAttributeFieldType.FieldValueToSpeckle(target, field); } } diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Layers/PointcloudLayerToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Layers/PointcloudLayerToSpeckleConverter.cs index f4d3281629..bf23636895 100644 --- a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Layers/PointcloudLayerToSpeckleConverter.cs +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Layers/PointcloudLayerToSpeckleConverter.cs @@ -5,6 +5,7 @@ using ArcGIS.Core.CIM; using Speckle.Converters.ArcGIS3.Geometry; using Speckle.Core.Models; +using Speckle.Converters.ArcGIS3.Utils; namespace Speckle.Converters.ArcGIS3.Layers; @@ -88,7 +89,7 @@ public SGIS.VectorLayer Convert(LasDatasetLayer target) speckleLayer.name = target.Name; speckleLayer.units = _contextStack.Current.SpeckleUnits; speckleLayer.nativeGeomType = target.MapLayerType.ToString(); - speckleLayer.geomType = "Pointcloud"; + speckleLayer.geomType = GISLayerGeometryType.POINTCLOUD; // prepare data for pointcloud List specklePts = new(); diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Layers/VectorLayerToHostConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Layers/VectorLayerToHostConverter.cs index 8648970fa1..58297364df 100644 --- a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Layers/VectorLayerToHostConverter.cs +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Layers/VectorLayerToHostConverter.cs @@ -37,7 +37,7 @@ public string Convert(VectorLayer target) // pointcloud layers need to be checked separately, because there is no ArcGIS Geometry type // for Pointcloud. In ArcGIS it's a completely different layer class, so "GetLayerGeometryType" // will return "Invalid" type - if (target.geomType == "Pointcloud") + if (target.geomType == GISLayerGeometryType.POINTCLOUD) { return _pointcloudLayerConverter.Convert(target).Name; } diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Layers/VectorLayerToSpeckleConverter.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Layers/VectorLayerToSpeckleConverter.cs index 02a0917b0f..cf38ebc4d4 100644 --- a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Layers/VectorLayerToSpeckleConverter.cs +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Layers/VectorLayerToSpeckleConverter.cs @@ -6,6 +6,7 @@ using ArcGIS.Core.Data; using ArcGIS.Core.Geometry; using Speckle.Converters.ArcGIS3.Utils; +using ArcGIS.Core.CIM; namespace Speckle.Converters.ArcGIS3.Layers; @@ -35,26 +36,18 @@ public Base Convert(object target) return Convert((FeatureLayer)target); } - private string SpeckleGeometryType(string nativeGeometryType) + private string AssignSpeckleGeometryType(esriGeometryType nativeGeometryType) { - string spekleGeometryType = "None"; - if (nativeGeometryType.Contains("point", StringComparison.OrdinalIgnoreCase)) + return nativeGeometryType switch { - spekleGeometryType = "Point"; - } - else if (nativeGeometryType.Contains("polyline", StringComparison.OrdinalIgnoreCase)) - { - spekleGeometryType = "Polyline"; - } - else if (nativeGeometryType.Contains("polygon", StringComparison.OrdinalIgnoreCase)) - { - spekleGeometryType = "Polygon"; - } - else if (nativeGeometryType.Contains("multipatch", StringComparison.OrdinalIgnoreCase)) - { - spekleGeometryType = "Multipatch"; - } - return spekleGeometryType; + esriGeometryType.esriGeometryMultipoint => GISLayerGeometryType.POINT, + esriGeometryType.esriGeometryPoint => GISLayerGeometryType.POINT, + esriGeometryType.esriGeometryLine => GISLayerGeometryType.POLYLINE, + esriGeometryType.esriGeometryPolyline => GISLayerGeometryType.POLYLINE, + esriGeometryType.esriGeometryPolygon => GISLayerGeometryType.POLYGON, + esriGeometryType.esriGeometryMultiPatch => GISLayerGeometryType.MULTIPATCH, + _ => GISLayerGeometryType.NONE, + }; } public VectorLayer Convert(FeatureLayer target) @@ -94,14 +87,13 @@ public VectorLayer Convert(FeatureLayer target) continue; } addedFieldDescriptions.Add(field); - allLayerAttributes[name] = (int)field.Type; + allLayerAttributes[name] = GISAttributeFieldType.FieldTypeToSpeckle(field.Type); } } speckleLayer.attributes = allLayerAttributes; - speckleLayer.nativeGeomType = target.ShapeType.ToString(); // get a simple geometry type - string spekleGeometryType = SpeckleGeometryType(speckleLayer.nativeGeomType); + string spekleGeometryType = AssignSpeckleGeometryType(target.ShapeType); speckleLayer.geomType = spekleGeometryType; // search the rows diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ArcGISFieldUtils.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ArcGISFieldUtils.cs index f53ea51a07..1f98758611 100644 --- a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ArcGISFieldUtils.cs +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/ArcGISFieldUtils.cs @@ -16,54 +16,6 @@ public ArcGISFieldUtils(ICharacterCleaner characterCleaner) _characterCleaner = characterCleaner; } - public object? FieldValueToNativeType(FieldType fieldType, object? value) - { - // Geometry: ignored - // Blob, Raster, TimestampOffset, XML: converted to String (field type already converted to String on Send) - switch (fieldType) - { - case FieldType.GUID: - return value; - case FieldType.OID: - return value; - } - - if (value is not null) - { - try - { - switch (fieldType) - { - case FieldType.String: - return (string)value; - case FieldType.Single: - return (float)(double)value; - case FieldType.Integer: - // need this step because sent "ints" seem to be received as "longs" - return (int)(long)value; - case FieldType.BigInteger: - return (long)value; - case FieldType.SmallInteger: - return (short)(long)value; - case FieldType.Double: - return (double)value; - case FieldType.Date: - return DateTime.Parse((string)value, null); - case FieldType.DateOnly: - return DateOnly.Parse((string)value); - case FieldType.TimeOnly: - return TimeOnly.Parse((string)value); - } - } - catch (InvalidCastException) - { - return value; - } - } - - return value; - } - public RowBuffer AssignFieldValuesToRow(RowBuffer rowBuffer, List fields, GisFeature feat) { foreach (FieldDescription field in fields) @@ -79,7 +31,7 @@ public RowBuffer AssignFieldValuesToRow(RowBuffer rowBuffer, List GetFieldsFromSpeckleLayer(VectorLayer target) { List fields = new(); @@ -121,7 +68,7 @@ public List GetFieldsFromSpeckleLayer(VectorLayer target) if (field.Value is not null) { string key = field.Key; - FieldType fieldType = GetFieldTypeFromInt((int)(long)field.Value); + FieldType fieldType = GISAttributeFieldType.FieldTypeToNative(field.Value); FieldDescription fieldDescription = new(_characterCleaner.CleanCharacters(key), fieldType) { AliasName = key }; diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs index 49f46aba53..dce6ce6853 100644 --- a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/FeatureClassUtils.cs @@ -85,54 +85,15 @@ List fields public ACG.GeometryType GetLayerGeometryType(VectorLayer target) { string? originalGeomType = target.geomType != null ? target.geomType : target.nativeGeomType; - - if (string.IsNullOrEmpty(originalGeomType)) - { - throw new SpeckleConversionException($"Unknown geometry type for layer {target.name}"); - } - return GetGeometryTypeFromString(originalGeomType.ToLower()); - } - - public ACG.GeometryType GetGeometryTypeFromString(string target) - { - // POC: find better pattern - if (target.Contains("none", StringComparison.OrdinalIgnoreCase)) + return originalGeomType switch { - return ACG.GeometryType.Unknown; - } - else if (target.Contains("pointcloud", StringComparison.OrdinalIgnoreCase)) - { - return ACG.GeometryType.Unknown; - } - else if (target.Contains("point", StringComparison.OrdinalIgnoreCase)) - { - return ACG.GeometryType.Multipoint; - } - else if ( - target.Contains("line", StringComparison.OrdinalIgnoreCase) - || target.Contains("curve", StringComparison.OrdinalIgnoreCase) - || target.Contains("arc", StringComparison.OrdinalIgnoreCase) - || target.Contains("circle", StringComparison.OrdinalIgnoreCase) - || target.Contains("ellipse", StringComparison.OrdinalIgnoreCase) - ) - { - return ACG.GeometryType.Polyline; - } - else if (target.Contains("polygon", StringComparison.OrdinalIgnoreCase)) - { - return ACG.GeometryType.Polygon; - } - else if (target.Contains("multipatch", StringComparison.OrdinalIgnoreCase)) - { - return ACG.GeometryType.Multipatch; - } - else if (target.Contains("mesh", StringComparison.OrdinalIgnoreCase)) - { - return ACG.GeometryType.Multipatch; - } - else - { - throw new SpeckleConversionException($"Unknown geometry type {target}"); - } + GISLayerGeometryType.NONE => ACG.GeometryType.Unknown, + GISLayerGeometryType.POINT => ACG.GeometryType.Multipoint, + GISLayerGeometryType.POLYGON => ACG.GeometryType.Polygon, + GISLayerGeometryType.POLYLINE => ACG.GeometryType.Polyline, + GISLayerGeometryType.MULTIPATCH => ACG.GeometryType.Multipatch, + GISLayerGeometryType.POLYGON3D => ACG.GeometryType.Multipatch, + _ => throw new ArgumentOutOfRangeException(nameof(target)), + }; } } diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs new file mode 100644 index 0000000000..64cb937987 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs @@ -0,0 +1,119 @@ +using ArcGIS.Core.Data; + +namespace Speckle.Converters.ArcGIS3.Utils; + +public static class GISAttributeFieldType +{ + public const string GUID_TYPE = "Guid"; + public const string OID = "Oid"; // object identifier: int + public const string STRING_TYPE = "String"; + public const string FLOAT_TYPE = "Float"; // single-precision floating point number + public const string INTEGER_TYPE = "Integer"; // 32-bit int + public const string BIGINTEGER = "BigInteger"; // 64-bit int + public const string SMALLINTEGER = "SmallInteger"; // 16-bit int + public const string DOUBLE_TYPE = "Double"; + public const string DATETIME = "DateTime"; + public const string DATEONLY = "DateOnly"; + public const string TIMEONLY = "TimeOnly"; + public const string TIMESTAMPOFFSET = "TimeStampOffset"; + + public static string FieldTypeToSpeckle(FieldType fieldType) + { + return fieldType switch + { + FieldType.GUID => GUID_TYPE, + FieldType.OID => OID, + FieldType.String => STRING_TYPE, + FieldType.Single => FLOAT_TYPE, + FieldType.Integer => INTEGER_TYPE, + FieldType.BigInteger => BIGINTEGER, + FieldType.SmallInteger => SMALLINTEGER, + FieldType.Double => DOUBLE_TYPE, + FieldType.Date => DATETIME, + FieldType.DateOnly => DATEONLY, + FieldType.TimeOnly => TIMEONLY, + FieldType.TimestampOffset => TIMESTAMPOFFSET, + _ => throw new ArgumentOutOfRangeException(nameof(fieldType)), + }; + } + + public static FieldType FieldTypeToNative(object fieldType) + { + if (fieldType is string fieldStringType) + { + return fieldStringType switch + { + GUID_TYPE => FieldType.GUID, + OID => FieldType.OID, + STRING_TYPE => FieldType.String, + FLOAT_TYPE => FieldType.Single, + INTEGER_TYPE => FieldType.Integer, + BIGINTEGER => FieldType.BigInteger, + SMALLINTEGER => FieldType.SmallInteger, + DOUBLE_TYPE => FieldType.Double, + DATETIME => FieldType.Date, + DATEONLY => FieldType.DateOnly, + TIMEONLY => FieldType.TimeOnly, + TIMESTAMPOFFSET => FieldType.String, // sending and receiving as stings + _ => throw new ArgumentOutOfRangeException(nameof(fieldType)), + }; + } + // old way: + return (FieldType)(int)(long)fieldType; + } + + public static object? FieldValueToSpeckle(Row row, Field field) + { + if ( + field.FieldType == FieldType.DateOnly + || field.FieldType == FieldType.TimeOnly + || field.FieldType == FieldType.TimestampOffset + ) + { + return row[field.Name]?.ToString(); + } + else + { + return row[field.Name]; + } + } + + public static object? SpeckleValueToNativeFieldType(FieldType fieldType, object? value) + { + // Geometry: ignored + // Blob, Raster, TimestampOffset, XML: converted to String (field type already converted to String on Send) + switch (fieldType) + { + case FieldType.GUID: + return value; + case FieldType.OID: + return value; + } + + if (value is not null) + { + try + { + return fieldType switch + { + FieldType.String => (string)value, + FieldType.Single => (float)(double)value, + FieldType.Integer => (int)(long)value, // need this step because sent "ints" seem to be received as "longs" + FieldType.BigInteger => (long)value, + FieldType.SmallInteger => (short)(long)value, + FieldType.Double => (double)value, + FieldType.Date => DateTime.Parse((string)value, null), + FieldType.DateOnly => DateOnly.Parse((string)value), + FieldType.TimeOnly => TimeOnly.Parse((string)value), + _ => value, + }; + } + catch (InvalidCastException) + { + return value; + } + } + + return value; + } +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISLayerGeometryType.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISLayerGeometryType.cs new file mode 100644 index 0000000000..612eff4807 --- /dev/null +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISLayerGeometryType.cs @@ -0,0 +1,12 @@ +namespace Speckle.Converters.ArcGIS3.Utils; + +public static class GISLayerGeometryType +{ + public const string NONE = "None"; + public const string POINT = "Point"; + public const string POLYLINE = "Polyline"; + public const string POLYGON = "Polygon"; + public const string POLYGON3D = "Polygon3d"; + public const string MULTIPATCH = "Multipatch"; + public const string POINTCLOUD = "Pointcloud"; +} diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IArcGISFieldUtils.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IArcGISFieldUtils.cs index a60a980273..959e9793fe 100644 --- a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IArcGISFieldUtils.cs +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IArcGISFieldUtils.cs @@ -7,7 +7,5 @@ namespace Speckle.Converters.ArcGIS3.Utils; public interface IArcGISFieldUtils { public RowBuffer AssignFieldValuesToRow(RowBuffer rowBuffer, List fields, GisFeature feat); - public object? FieldValueToNativeType(FieldType fieldType, object? value); public List GetFieldsFromSpeckleLayer(VectorLayer target); - public FieldType GetFieldTypeFromInt(int fieldType); } diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IFeatureClassUtils.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IFeatureClassUtils.cs index 2946ac3931..289c05eeb6 100644 --- a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IFeatureClassUtils.cs +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/IFeatureClassUtils.cs @@ -21,5 +21,4 @@ List fields ); void AddFeaturesToTable(Table newFeatureClass, List gisFeatures, List fields); public ACG.GeometryType GetLayerGeometryType(VectorLayer target); - public ACG.GeometryType GetGeometryTypeFromString(string target); } diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/NonNativeFeaturesUtils.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/NonNativeFeaturesUtils.cs index 4f0fa7cdfc..544ccc553f 100644 --- a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/NonNativeFeaturesUtils.cs +++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/NonNativeFeaturesUtils.cs @@ -49,13 +49,15 @@ public NonNativeFeaturesUtils( (string parentPath, ACG.Geometry geom, string? parentId) = item.Value; // add dictionnary item if doesn't exist yet - if (!geometryGroups.TryGetValue(parentPath, out var value)) + // Key must be unique per parent and speckle_type + // Key is composed of parentId and parentPath (that contains speckle_type) + string uniqueKey = $"{parentId}_{parentPath}"; + if (!geometryGroups.TryGetValue(uniqueKey, out (List geometries, string? parentId) value)) { - value = (new List(), parentId); - geometryGroups[parentPath] = value; + geometryGroups[uniqueKey] = (new List(), parentId); } - value.geometries.Add(geom); + geometryGroups[uniqueKey].geometries.Add(geom); } catch (Exception ex) when (!ex.IsFatal()) { @@ -69,12 +71,13 @@ public NonNativeFeaturesUtils( { try { - string parentPath = item.Key; + string uniqueKey = item.Key; // parentId_parentPath + string parentPath = uniqueKey.Split('_', 2)[^1]; + string speckle_type = parentPath.Split("\\")[^1]; (List geomList, string? parentId) = item.Value; - ACG.GeometryType geomType = geomList[0].GeometryType; try { - string converted = CreateDatasetInDatabase(geomType, geomList, parentId); + string converted = CreateDatasetInDatabase(speckle_type, geomList, parentId); result.Add((parentPath, converted)); } catch (GeodatabaseGeometryException) @@ -91,7 +94,7 @@ public NonNativeFeaturesUtils( return result; } - private string CreateDatasetInDatabase(ACG.GeometryType geomType, List geomList, string? parentId) + private string CreateDatasetInDatabase(string speckle_type, List geomList, string? parentId) { string databasePath = _arcGISProjectUtils.GetDatabasePath(); FileGeodatabaseConnectionPath fileGeodatabaseConnectionPath = new(new Uri(databasePath)); @@ -105,7 +108,7 @@ private string CreateDatasetInDatabase(ACG.GeometryType geomType, List fields = new(); // _fieldsUtils.GetFieldsFromSpeckleLayer(target); // TODO: generate meaningful name - string featureClassName = $"speckleID_{geomType}_{parentId}"; + string featureClassName = $"speckleTYPE_{speckle_type}_speckleID_{parentId}"; // delete FeatureClass if already exists foreach (FeatureClassDefinition fClassDefinition in geodatabase.GetDefinitions()) @@ -123,6 +126,7 @@ private string CreateDatasetInDatabase(ACG.GeometryType geomType, List