From dd0a8b381c93c5af18eafdc9d20eb7158f06f4cb Mon Sep 17 00:00:00 2001
From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
Date: Sat, 8 Jun 2024 14:01:05 +0100
Subject: [PATCH] arc gis traversal cleanup (#3482)
* Changes to traversal for arcgis
* Changed filed type casting to use ConvertTo
* Simplified GetAcendents
* xml comment
* Shuffled some functions around
* More tweaks
* More changes
* dumbdumb net versioning
---
.../TraversalContextExtensions.cs | 30 +++--
.../TraversalContextExtensionsTests.cs | 10 ++
.../Operations/Receive/HostObjectBuilder.cs | 103 ++++++++----------
.../Receive/RhinoHostObjectBuilder.cs | 4 +-
.../Utils/GISAttributeFieldType.cs | 10 +-
.../Utils/INonNativeFeaturesUtils.cs | 4 +-
.../Utils/NonNativeFeaturesUtils.cs | 36 +++---
7 files changed, 102 insertions(+), 95 deletions(-)
diff --git a/Core/Core/Models/GraphTraversal/TraversalContextExtensions.cs b/Core/Core/Models/GraphTraversal/TraversalContextExtensions.cs
index b987cb1b45..dab65ed446 100644
--- a/Core/Core/Models/GraphTraversal/TraversalContextExtensions.cs
+++ b/Core/Core/Models/GraphTraversal/TraversalContextExtensions.cs
@@ -1,4 +1,6 @@
using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Linq;
namespace Speckle.Core.Models.GraphTraversal;
@@ -10,6 +12,7 @@ public static class TraversalContextExtensions
///
///
///
+ [Pure]
public static IEnumerable GetPropertyPath(this TraversalContext context)
{
TraversalContext? head = context;
@@ -26,22 +29,31 @@ public static IEnumerable GetPropertyPath(this TraversalContext context)
}
///
- /// Walks up the tree, returning all typed ascendant, starting the closest ,
- /// walking up nodes
+ /// Walks up the tree, returning all ascendant, including
///
///
- ///
- public static IEnumerable GetAscendantOfType(this TraversalContext context)
- where T : Base
+ /// and all its ascendants
+ [Pure]
+ public static IEnumerable GetAscendants(this TraversalContext context)
{
TraversalContext? head = context;
do
{
- if (head.Current is T c)
- {
- yield return c;
- }
+ yield return head.Current;
head = head.Parent;
} while (head != null);
}
+
+ ///
+ /// Walks up the tree, returning all typed ascendant, starting the closest ,
+ /// walking up nodes
+ ///
+ ///
+ /// and all its ascendants of type
+ [Pure]
+ public static IEnumerable GetAscendantOfType(this TraversalContext context)
+ where T : Base
+ {
+ return context.GetAscendants().OfType();
+ }
}
diff --git a/Core/Tests/Speckle.Core.Tests.Unit/Models/GraphTraversal/TraversalContextExtensionsTests.cs b/Core/Tests/Speckle.Core.Tests.Unit/Models/GraphTraversal/TraversalContextExtensionsTests.cs
index 9686e289bb..e8a38cf800 100644
--- a/Core/Tests/Speckle.Core.Tests.Unit/Models/GraphTraversal/TraversalContextExtensionsTests.cs
+++ b/Core/Tests/Speckle.Core.Tests.Unit/Models/GraphTraversal/TraversalContextExtensionsTests.cs
@@ -28,6 +28,16 @@ public void GetPropertyPath_ReturnsSequentialPath(int depth)
Assert.That(path, Is.EquivalentTo(expected));
}
+ [TestCaseSource(nameof(TestDepths))]
+ public void GetAscendant(int depth)
+ {
+ var testData = CreateLinkedList(depth, i => new());
+
+ var all = TraversalContextExtensions.GetAscendants(testData).ToArray();
+
+ Assert.That(all, Has.Length.EqualTo(depth));
+ }
+
[TestCaseSource(nameof(TestDepths))]
public void GetAscendantOfType_AllBase(int depth)
{
diff --git a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/HostObjectBuilder.cs b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/HostObjectBuilder.cs
index ce31b4775c..2b770782ef 100644
--- a/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/HostObjectBuilder.cs
+++ b/DUI3-DX/Connectors/ArcGIS/Speckle.Connectors.ArcGIS3/Operations/Receive/HostObjectBuilder.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.Contracts;
using ArcGIS.Desktop.Mapping;
using Speckle.Connectors.Utils.Builders;
using Speckle.Converters.Common;
@@ -9,6 +10,7 @@
using Speckle.Connectors.Utils.Conversion;
using Speckle.Core.Models.GraphTraversal;
using Speckle.Converters.ArcGIS3;
+using RasterLayer = Objects.GIS.RasterLayer;
namespace Speckle.Connectors.ArcGIS.Operations.Receive;
@@ -34,77 +36,40 @@ GraphTraversal traverseFunction
_traverseFunction = traverseFunction;
}
- public (string path, Geometry converted, string? parentId) ConvertNonNativeGeometries(
- Base obj,
- string[] path,
- string? parentId,
- List objectIds
- )
+ private (string path, Geometry converted) ConvertNonNativeGeometries(Base obj, string[] path)
{
Geometry converted = (Geometry)_converter.Convert(obj);
- objectIds.Add(obj.id);
List objPath = path.ToList();
objPath.Add(obj.speckle_type.Split(".")[^1]);
- return ($"{string.Join("\\", objPath)}", converted, parentId);
+ return (string.Join("\\", objPath), converted);
}
- public (string path, string converted) ConvertNativeLayers(Collection obj, string[] path, List objectIds)
+ private (string path, string converted) ConvertNativeLayers(Collection obj, string[] path)
{
string converted = (string)_converter.Convert(obj);
- objectIds.Add(obj.id);
string objPath = $"{string.Join("\\", path)}\\{obj.name}";
return (objPath, converted);
}
- public string AddDatasetsToMap((string, string) databaseObj)
+ private string AddDatasetsToMap((string nestedLayerName, string datasetId) databaseObj)
{
+ Uri uri =
+ new(
+ $"{_contextStack.Current.Document.SpeckleDatabasePath.AbsolutePath.Replace('/', '\\')}\\{databaseObj.datasetId}"
+ );
+ Map map = _contextStack.Current.Document.Map;
try
{
- return LayerFactory.Instance
- .CreateLayer(
- new Uri(
- $"{_contextStack.Current.Document.SpeckleDatabasePath.AbsolutePath.Replace('/', '\\')}\\{databaseObj.Item2}"
- ),
- _contextStack.Current.Document.Map,
- layerName: databaseObj.Item1
- )
- .URI;
+ return LayerFactory.Instance.CreateLayer(uri, map, layerName: databaseObj.nestedLayerName).URI;
}
catch (ArgumentException)
{
return StandaloneTableFactory.Instance
- .CreateStandaloneTable(
- new Uri(
- $"{_contextStack.Current.Document.SpeckleDatabasePath.AbsolutePath.Replace('/', '\\')}\\{databaseObj.Item2}"
- ),
- _contextStack.Current.Document.Map,
- tableName: databaseObj.Item1
- )
+ .CreateStandaloneTable(uri, map, tableName: databaseObj.nestedLayerName)
.URI;
}
}
- private string[] GetLayerPath(TraversalContext context)
- {
- string[] collectionBasedPath = context.GetAscendantOfType().Select(c => c.name).ToArray();
- string[] reverseOrderPath =
- collectionBasedPath.Length != 0 ? collectionBasedPath : context.GetPropertyPath().ToArray();
- return reverseOrderPath.Reverse().ToArray();
- }
-
- private bool HasGISParent(TraversalContext context)
- {
- List vectorLayers = context
- .GetAscendantOfType()
- .Where(obj => obj != context.Current)
- .ToList();
- List rasterLayers = context
- .GetAscendantOfType()
- .Where(obj => obj != context.Current)
- .ToList();
- return vectorLayers.Count + rasterLayers.Count > 0;
- }
-
public HostObjectBuilderResult Build(
Base rootObject,
string projectName,
@@ -116,31 +81,31 @@ CancellationToken cancellationToken
// Prompt the UI conversion started. Progress bar will swoosh.
onOperationProgressed?.Invoke("Converting", null);
- // POC: This is where we will define our receive strategy, or maybe later somewhere else according to some setting pass from UI?
var objectsToConvert = _traverseFunction
.Traverse(rootObject)
+ .Where(ctx => ctx.Current is not Collection || IsGISType(ctx.Current))
.Where(ctx => HasGISParent(ctx) is false)
- .Select(ctx => (GetLayerPath(ctx), ctx.Current, ctx.Parent?.Current.id))
.ToList();
int allCount = objectsToConvert.Count;
int count = 0;
- Dictionary convertedGeometries = new();
- List objectIds = new();
+ Dictionary convertedGeometries = new();
List<(string path, string converted)> convertedGISObjects = new();
// 1. convert everything
List results = new(objectsToConvert.Count);
List bakedObjectIds = new();
- foreach (var item in objectsToConvert)
+ foreach (TraversalContext ctx in objectsToConvert)
{
- (string[] path, Base obj, string? parentId) = item;
+ string[] path = GetLayerPath(ctx);
+ Base obj = ctx.Current;
+
cancellationToken.ThrowIfCancellationRequested();
try
{
- if (obj is VectorLayer or Objects.GIS.RasterLayer)
+ if (IsGISType(obj))
{
- var result = ConvertNativeLayers((Collection)obj, path, objectIds);
+ var result = ConvertNativeLayers((Collection)obj, path);
convertedGISObjects.Add(result);
// NOTE: Dim doesn't really know what is what - is the result.path the id of the obj?
// TODO: is the type in here basically a GIS Layer?
@@ -148,8 +113,8 @@ CancellationToken cancellationToken
}
else
{
- var result = ConvertNonNativeGeometries(obj, path, parentId, objectIds);
- convertedGeometries[obj.id] = result;
+ var result = ConvertNonNativeGeometries(obj, path);
+ convertedGeometries[ctx] = result;
// NOTE: Dim doesn't really know what is what - is the result.path the id of the obj?
results.Add(new(Status.SUCCESS, obj, result.path, result.converted.GetType().ToString())); //POC: what native id?, path may not be unique
@@ -184,4 +149,26 @@ CancellationToken cancellationToken
// TODO: validated a correct set regarding bakedobject ids
return new(bakedObjectIds, results);
}
+
+ [Pure]
+ private static string[] GetLayerPath(TraversalContext context)
+ {
+ string[] collectionBasedPath = context.GetAscendantOfType().Select(c => c.name).ToArray();
+ string[] reverseOrderPath =
+ collectionBasedPath.Length != 0 ? collectionBasedPath : context.GetPropertyPath().ToArray();
+ return reverseOrderPath.Reverse().ToArray();
+ }
+
+ [Pure]
+ private static bool HasGISParent(TraversalContext context)
+ {
+ List gisLayers = context.GetAscendants().Where(IsGISType).Where(obj => obj != context.Current).ToList();
+ return gisLayers.Count > 0;
+ }
+
+ [Pure]
+ private static bool IsGISType(Base obj)
+ {
+ return obj is RasterLayer or VectorLayer;
+ }
}
diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/RhinoHostObjectBuilder.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/RhinoHostObjectBuilder.cs
index 26215d4464..02f653f08a 100644
--- a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/RhinoHostObjectBuilder.cs
+++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/RhinoHostObjectBuilder.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.Contracts;
using Rhino;
using Rhino.DocObjects;
using Rhino.Geometry;
@@ -175,7 +176,8 @@ private int GetAndCreateLayerFromPath(string[] path, string baseLayerName, Dicti
return previousLayer.Index;
}
- private string[] GetLayerPath(TraversalContext context)
+ [Pure]
+ private static string[] GetLayerPath(TraversalContext context)
{
string[] collectionBasedPath = context.GetAscendantOfType().Select(c => c.name).ToArray();
string[] reverseOrderPath =
diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs
index 64cb937987..332a6d077a 100644
--- a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs
+++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/GISAttributeFieldType.cs
@@ -97,11 +97,11 @@ public static FieldType FieldTypeToNative(object fieldType)
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.Single => Convert.ToSingle(value),
+ FieldType.Integer => Convert.ToInt32(value), // need this step because sent "ints" seem to be received as "longs"
+ FieldType.BigInteger => Convert.ToInt64(value),
+ FieldType.SmallInteger => Convert.ToInt16(value),
+ FieldType.Double => Convert.ToDouble(value),
FieldType.Date => DateTime.Parse((string)value, null),
FieldType.DateOnly => DateOnly.Parse((string)value),
FieldType.TimeOnly => TimeOnly.Parse((string)value),
diff --git a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/INonNativeFeaturesUtils.cs b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/INonNativeFeaturesUtils.cs
index 6ab9c32603..1f4295b457 100644
--- a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/INonNativeFeaturesUtils.cs
+++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/INonNativeFeaturesUtils.cs
@@ -1,8 +1,10 @@
+using Speckle.Core.Models.GraphTraversal;
+
namespace Speckle.Converters.ArcGIS3.Utils;
public interface INonNativeFeaturesUtils
{
public List<(string parentPath, string converted)> WriteGeometriesToDatasets(
- Dictionary convertedObjs
+ Dictionary convertedObjs
);
}
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 cdb5226076..621ad260ca 100644
--- a/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/NonNativeFeaturesUtils.cs
+++ b/DUI3-DX/Converters/ArcGIS/Speckle.Converters.ArcGIS3/Utils/NonNativeFeaturesUtils.cs
@@ -5,6 +5,7 @@
using Speckle.Converters.Common;
using FieldDescription = ArcGIS.Core.Data.DDL.FieldDescription;
using Speckle.Core.Logging;
+using Speckle.Core.Models.GraphTraversal;
namespace Speckle.Converters.ArcGIS3.Utils;
@@ -23,24 +24,25 @@ public NonNativeFeaturesUtils(
}
public List<(string parentPath, string converted)> WriteGeometriesToDatasets(
- Dictionary convertedObjs
+ Dictionary convertedObjs
)
{
- List<(string, string)> result = new();
+ List<(string parentPath, string converted)> result = new();
// 1. Sort features into groups by path and geom type
Dictionary geometries, string? parentId)> geometryGroups = new();
foreach (var item in convertedObjs)
{
try
{
- string objId = item.Key;
- (string parentPath, ACG.Geometry geom, string? parentId) = item.Value;
+ TraversalContext context = item.Key;
+ (string parentPath, ACG.Geometry geom) = item.Value;
+ string? parentId = context.Parent?.Current.id;
// add dictionnary item if doesn't exist yet
// 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))
+ if (!geometryGroups.TryGetValue(uniqueKey, out _))
{
geometryGroups[uniqueKey] = (new List(), parentId);
}
@@ -57,26 +59,18 @@ public NonNativeFeaturesUtils(
// 2. for each group create a Dataset and add geometries there as Features
foreach (var item in geometryGroups)
{
+ string uniqueKey = item.Key; // parentId_parentPath
+ string parentPath = uniqueKey.Split('_', 2)[^1];
+ string speckle_type = parentPath.Split('\\')[^1];
+ (List geomList, string? parentId) = item.Value;
try
{
- string uniqueKey = item.Key; // parentId_parentPath
- string parentPath = uniqueKey.Split('_', 2)[^1];
- string speckle_type = parentPath.Split("\\")[^1];
- (List geomList, string? parentId) = item.Value;
- try
- {
- string converted = CreateDatasetInDatabase(speckle_type, geomList, parentId);
- result.Add((parentPath, converted));
- }
- catch (GeodatabaseGeometryException)
- {
- // do nothing if conversion of some geometry groups fails
- }
+ string converted = CreateDatasetInDatabase(speckle_type, geomList, parentId);
+ result.Add((parentPath, converted));
}
- catch (Exception e) when (!e.IsFatal())
+ catch (GeodatabaseGeometryException)
{
- // POC: report, etc.
- Debug.WriteLine("conversion error happened.");
+ // do nothing if writing of some geometry groups fails
}
}
return result;