Compare commits

...

9 Commits

Author SHA1 Message Date
Jonathon Broughton 4c42138732 updates category mapping for Civil 3D interoperability
Modifies send settings for better compatibility with Civil 3D mappings.

Enhances logging for category mapping to assist in debugging and monitoring.

Adds additional category mappings to improve conversion accuracy.
2026-01-12 18:10:18 +00:00
Jonathon Broughton 8c84f673df Refactors constructor 2025-12-20 10:25:12 +00:00
Jonathon Broughton 6157cc9d40 Updates method visibility for send settings
Changes the visibility of the send settings method to virtual, enabling overrides in derived classes.
2025-12-19 22:54:44 +00:00
Jonathon Broughton c7e35f6776 Enhance PropertiesExtractor for Revit category mapping 2025-12-19 22:45:04 +00:00
Jonathon Broughton 73c426057a Implement Civil3DBuiltInCategoryExtractor
Update projitems for Civil3DBuiltInCategoryExtractor
2025-12-19 22:45:03 +00:00
Jonathon Broughton 3245bb2be2 Update Civil3dConversionSettings to include Revit category mapping
Update Civil3dConversionSettingsFactory to support Revit category mapping
2025-12-19 22:45:00 +00:00
Jonathon Broughton aeec0976af Implement ToSpeckleSettingsManagerCivil3d
Update projitems for new Civil3D send settings
2025-12-19 22:44:58 +00:00
Jonathon Broughton 18005abc3d Implement Revit category mapping setting for Civil3D 2025-12-19 22:44:57 +00:00
Jonathon Broughton a98e1ccfaa Add new settings and dependencies for Civil3D
Add Civil3D settings to Dependency Injection
2025-12-19 22:44:56 +00:00
11 changed files with 176 additions and 41 deletions
@@ -125,7 +125,7 @@ public abstract class AutocadSendBaseBinding : ISendBinding
public List<ISendFilter> GetSendFilters() => _sendFilters;
public List<ICardSetting> GetSendSettings() => [];
public virtual List<ICardSetting> GetSendSettings() => [];
public async Task Send(string modelCardId) =>
await _threadContext.RunOnMainAsync(async () => await SendInternal(modelCardId));
@@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Autocad.Bindings;
using Speckle.Connectors.Civil3dShared.Operations.Send.Settings;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.Common.Cancellation;
using Speckle.Connectors.Common.Threading;
@@ -7,56 +8,63 @@ using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
using Speckle.Connectors.DUI.Models;
using Speckle.Connectors.DUI.Models.Card.SendFilter;
using Speckle.Connectors.DUI.Settings;
using Speckle.Converters.Autocad;
using Speckle.Converters.Civil3dShared;
using Speckle.Converters.Common;
namespace Speckle.Connectors.Civil3dShared.Bindings;
public sealed class Civil3dSendBinding : AutocadSendBaseBinding
{
private readonly ICivil3dConversionSettingsFactory _civil3dConversionSettingsFactory;
private readonly IAutocadConversionSettingsFactory _autocadConversionSettingsFactory;
public Civil3dSendBinding(
DocumentModelStore store,
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
ICancellationManager cancellationManager,
ISendConversionCache sendConversionCache,
ICivil3dConversionSettingsFactory civil3dConversionSettingsFactory,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IAppIdleManager appIdleManager,
ISendOperationManagerFactory sendOperationManagerFactory
public sealed class Civil3dSendBinding(
DocumentModelStore store,
IBrowserBridge parent,
IEnumerable<ISendFilter> sendFilters,
ICancellationManager cancellationManager,
ISendConversionCache sendConversionCache,
ICivil3dConversionSettingsFactory civil3dConversionSettingsFactory,
IAutocadConversionSettingsFactory autocadConversionSettingsFactory,
IToSpeckleSettingsManagerCivil3d toSpeckleSettingsManagerCivil3d,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler,
IAppIdleManager appIdleManager,
ISendOperationManagerFactory sendOperationManagerFactory
)
: AutocadSendBaseBinding(
store,
parent,
sendFilters,
cancellationManager,
sendConversionCache,
threadContext,
topLevelExceptionHandler,
appIdleManager,
sendOperationManagerFactory
)
: base(
store,
parent,
sendFilters,
cancellationManager,
sendConversionCache,
threadContext,
topLevelExceptionHandler,
appIdleManager,
sendOperationManagerFactory
)
{
_civil3dConversionSettingsFactory = civil3dConversionSettingsFactory;
_autocadConversionSettingsFactory = autocadConversionSettingsFactory;
}
{
private readonly DocumentModelStore _store = store;
public override List<ICardSetting> GetSendSettings() => [new RevitCategoryMappingSetting(true)];
// POC: we're registering the conversion settings for autocad here because we need the autocad conversion settings to be able to use the autocad typed converters.
// POC: We need a separate send binding for civil3d due to using a different unit converter (needed for conversion settings construction)
// POC: We need a separate a send binding for Civil3d due to using a different unit converter (needed for conversion settings construction)
protected override void InitializeSettings(IServiceProvider serviceProvider)
{
// Get the model card from the store to access user settings
var modelCard = _store.GetSenders().FirstOrDefault();
bool mappingToRevitCategories = false;
if (modelCard != null)
{
mappingToRevitCategories = toSpeckleSettingsManagerCivil3d.GetMappingToRevitCategories(modelCard);
}
serviceProvider
.GetRequiredService<IConverterSettingsStore<Civil3dConversionSettings>>()
.Initialize(_civil3dConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument));
.Initialize(
civil3dConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument, mappingToRevitCategories)
);
serviceProvider
.GetRequiredService<IConverterSettingsStore<AutocadConversionSettings>>()
.Initialize(_autocadConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument));
.Initialize(autocadConversionSettingsFactory.Create(Application.DocumentManager.CurrentDocument));
}
}
@@ -6,6 +6,7 @@ using Speckle.Connectors.Civil3dShared.Bindings;
using Speckle.Connectors.Civil3dShared.HostApp;
using Speckle.Connectors.Civil3dShared.Operations.Receive;
using Speckle.Connectors.Civil3dShared.Operations.Send;
using Speckle.Connectors.Civil3dShared.Operations.Send.Settings;
using Speckle.Connectors.Common.Builders;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Converters.Civil3dShared.ToSpeckle;
@@ -24,6 +25,9 @@ public static class Civil3dConnectorModule
serviceCollection.AddScoped<IRootObjectBuilder<AutocadRootObject>, Civil3dRootObjectBuilder>();
serviceCollection.AddSingleton<IBinding, Civil3dSendBinding>();
// send settings
serviceCollection.AddSingleton<ToSpeckleSettingsManagerCivil3d>();
// add receive
serviceCollection.LoadReceive();
serviceCollection.AddScoped<IHostObjectBuilder, Civil3dHostObjectBuilder>();
@@ -0,0 +1,12 @@
using Speckle.Connectors.DUI.Settings;
namespace Speckle.Connectors.Civil3dShared.Operations.Send.Settings;
public class RevitCategoryMappingSetting(bool value) : ICardSetting
{
public string? Id { get; set; } = "mappingToRevitCategories";
public string? Title { get; set; } = "Map to Revit Categories";
public string? Type { get; set; } = "boolean";
public List<string>? Enum { get; set; }
public object? Value { get; set; } = value;
}
@@ -0,0 +1,42 @@
using System.Diagnostics.CodeAnalysis;
using Speckle.Connectors.Common.Caching;
using Speckle.Connectors.DUI.Models.Card;
using Speckle.InterfaceGenerator;
using Speckle.Sdk.Common;
namespace Speckle.Connectors.Civil3dShared.Operations.Send.Settings;
[GenerateAutoInterface]
public class ToSpeckleSettingsManagerCivil3d : IToSpeckleSettingsManagerCivil3d
{
private readonly ISendConversionCache _sendConversionCache;
private readonly Dictionary<string, bool?> _revitCategoryMappingCache = [];
public ToSpeckleSettingsManagerCivil3d(ISendConversionCache sendConversionCache)
{
_sendConversionCache = sendConversionCache;
}
public bool GetMappingToRevitCategories([NotNull] SenderModelCard modelCard)
{
var value = modelCard.Settings?.FirstOrDefault(s => s.Id == "mappingToRevitCategories")?.Value as bool?;
var returnValue = value != null && value.NotNull();
if (_revitCategoryMappingCache.TryGetValue(modelCard.ModelCardId.NotNull(), out var previousValue))
{
if (previousValue != returnValue)
{
EvictCacheForModelCard(modelCard);
}
}
_revitCategoryMappingCache[modelCard.ModelCardId] = returnValue;
return returnValue;
}
private void EvictCacheForModelCard(SenderModelCard modelCard)
{
var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().SelectedObjectIds : [];
_sendConversionCache.EvictObjects(objectIds);
}
}
@@ -14,6 +14,8 @@
<Compile Include="$(MSBuildThisFileDirectory)HostApp\PropertySetBaker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\Civil3dHostObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Civil3dRootObjectBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\RevitCategoryMappingSetting.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManagerCivil3d.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bindings\Civil3dSendBinding.cs" />
</ItemGroup>
<ItemGroup>
@@ -21,5 +23,6 @@
<Folder Include="$(MSBuildThisFileDirectory)HostApp\" />
<Folder Include="$(MSBuildThisFileDirectory)Operations\Receive\" />
<Folder Include="$(MSBuildThisFileDirectory)Operations\Send\" />
<Folder Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\" />
</ItemGroup>
</Project>
</Project>
@@ -1,3 +1,3 @@
namespace Speckle.Converters.Civil3dShared;
public record Civil3dConversionSettings(Document Document, string SpeckleUnits);
public record Civil3dConversionSettings(Document Document, string SpeckleUnits, bool MappingToRevitCategories = false);
@@ -7,8 +7,8 @@ namespace Speckle.Converters.Civil3dShared;
public class Civil3dConversionSettingsFactory(IHostToSpeckleUnitConverter<AAEC.BuiltInUnit> unitsConverter)
: ICivil3dConversionSettingsFactory
{
public Civil3dConversionSettings Create(Document document) =>
new(document, unitsConverter.ConvertOrThrow(GetDocBuiltInUnit(document)));
public Civil3dConversionSettings Create(Document document, bool mappingToRevitCategories = false) =>
new(document, unitsConverter.ConvertOrThrow(GetDocBuiltInUnit(document)), mappingToRevitCategories);
private static AAEC.BuiltInUnit GetDocBuiltInUnit(Document doc)
{
@@ -0,0 +1,47 @@
using Speckle.InterfaceGenerator;
namespace Speckle.Converters.Civil3dShared.Helpers;
[GenerateAutoInterface]
public class Civil3DBuiltInCategoryExtractor : ICivil3DBuiltInCategoryExtractor
{
internal const string DEFAULT_DICT_KEY = "builtInCategory";
public bool TryGetBuiltInCategory(ADB.Entity entity, out string mapped)
{
mapped = string.Empty;
var rx = entity.GetRXClass();
var name = rx?.Name;
if (string.IsNullOrWhiteSpace(name))
{
return false;
}
var builtInCategory = Civil3DClassToRevitBuiltInCategory(name!);
if (string.Equals(builtInCategory, name, StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine($"{name}: {builtInCategory}");
return false; // no mapping
}
mapped = builtInCategory;
Console.WriteLine($"{name}: {builtInCategory} -> {mapped}");
return true;
}
private static readonly Dictionary<string, string> s_civil3dClassMap =
new(StringComparer.OrdinalIgnoreCase)
{
["AeccDbSurfaceTin"] = "OST_Topography",
["AeccDbPipe"] = "OST_PlaceHolderPipes",
["AeccDbStructure"] = "OST_GenericModel"
// ["Structure"] = "OST_PipeFitting"
};
private static string Civil3DClassToRevitBuiltInCategory(string className) =>
s_civil3dClassMap.TryGetValue(className, out var ost) ? ost : className;
}
@@ -15,6 +15,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Civil3dToSpeckleUnitConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SpeckleApplicationIdExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)GlobalUsings.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\Civil3DBuiltInCategoryExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\PropertyHandler.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\CorridorDisplayValueExtractor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\CorridorHandler.cs" />
@@ -1,3 +1,6 @@
using Speckle.Converters.Civil3dShared.Helpers;
using Speckle.Converters.Common;
namespace Speckle.Converters.Civil3dShared.ToSpeckle;
/// <summary>
@@ -9,18 +12,24 @@ public class PropertiesExtractor : Speckle.Converters.AutocadShared.ToSpeckle.IP
private readonly PartDataExtractor _partDataExtractor;
private readonly PropertySetExtractor _propertySetExtractor;
private readonly ExtensionDictionaryExtractor _extensionDictionaryExtractor;
private readonly ICivil3DBuiltInCategoryExtractor _builtInCategoryExtractor;
private readonly IConverterSettingsStore<Civil3dConversionSettings> _settingsStore;
public PropertiesExtractor(
ClassPropertiesExtractor classPropertiesExtractor,
PartDataExtractor partDataExtractor,
PropertySetExtractor propertySetExtractor,
ExtensionDictionaryExtractor extensionDictionaryExtractor
ExtensionDictionaryExtractor extensionDictionaryExtractor,
ICivil3DBuiltInCategoryExtractor builtInCategoryExtractor,
IConverterSettingsStore<Civil3dConversionSettings> settingsStore
)
{
_classPropertiesExtractor = classPropertiesExtractor;
_partDataExtractor = partDataExtractor;
_propertySetExtractor = propertySetExtractor;
_extensionDictionaryExtractor = extensionDictionaryExtractor;
_builtInCategoryExtractor = builtInCategoryExtractor;
_settingsStore = settingsStore;
}
public Dictionary<string, object?> GetProperties(ADB.Entity entity)
@@ -28,6 +37,15 @@ public class PropertiesExtractor : Speckle.Converters.AutocadShared.ToSpeckle.IP
// first get all class properties, which will be at the root level of props dictionary
Dictionary<string, object?> properties = _classPropertiesExtractor.GetClassProperties(entity);
// add built-in category if setting enabled and mapping available
if (
_settingsStore.Current.MappingToRevitCategories
&& _builtInCategoryExtractor.TryGetBuiltInCategory(entity, out string builtInCategory)
)
{
properties[Civil3DBuiltInCategoryExtractor.DEFAULT_DICT_KEY] = builtInCategory;
}
// add part data, property sets, and extension dictionaries to the properties dict
AddDictionaryToPropertyDictionary(_partDataExtractor.GetPartData(entity), "Part Data", properties);
AddDictionaryToPropertyDictionary(_propertySetExtractor.GetPropertySets(entity), "Property Sets", properties);