NavisworksTransport/AttributeGrouper.cs
2025-07-18 13:32:50 +08:00

546 lines
21 KiB
C#

using Autodesk.Navisworks.Api;
using System;
using System.Collections.Generic;
using System.Linq;
namespace NavisworksTransport
{
/// <summary>
/// 属性分组器 - 负责根据自定义属性对模型元素进行分组
/// </summary>
public class AttributeGrouper
{
#region
/// <summary>
/// 属性分组结果
/// </summary>
public class AttributeGroup
{
public string GroupName { get; set; }
public string AttributeValue { get; set; }
public ModelItemCollection Items { get; set; }
public Dictionary<string, object> Metadata { get; set; }
public int ItemCount => Items?.Count ?? 0;
public AttributeGroup()
{
Items = new ModelItemCollection();
Metadata = new Dictionary<string, object>();
}
}
/// <summary>
/// 属性统计信息
/// </summary>
public class AttributeStatistics
{
public string AttributeName { get; set; }
public Dictionary<string, int> ValueCounts { get; set; }
public int TotalItems { get; set; }
public int ItemsWithAttribute { get; set; }
public int UniqueValues { get; set; }
public double Coverage => TotalItems > 0 ? (double)ItemsWithAttribute / TotalItems : 0.0;
public AttributeStatistics()
{
ValueCounts = new Dictionary<string, int>();
}
}
#endregion
#region
/// <summary>
/// 根据指定属性对模型元素进行分组
/// </summary>
/// <param name="items">要分组的模型元素集合</param>
/// <param name="attributeName">分组依据的属性名称</param>
/// <returns>分组结果列表</returns>
public List<AttributeGroup> GroupByAttribute(ModelItemCollection items, string attributeName)
{
try
{
LogManager.Info($"[AttributeGrouper] 开始按属性分组,属性: {attributeName}, 元素数量: {items.Count}");
if (items == null || items.Count == 0)
{
LogManager.Warning("[AttributeGrouper] 输入的模型元素集合为空");
return new List<AttributeGroup>();
}
if (string.IsNullOrEmpty(attributeName))
{
throw new ArgumentException("属性名称不能为空", nameof(attributeName));
}
var groups = new Dictionary<string, List<ModelItem>>();
int processedCount = 0;
int itemsWithAttribute = 0;
foreach (ModelItem item in items)
{
string attributeValue = GetAttributeValue(item, attributeName);
if (!string.IsNullOrEmpty(attributeValue))
{
itemsWithAttribute++;
if (!groups.ContainsKey(attributeValue))
{
groups[attributeValue] = new List<ModelItem>();
}
groups[attributeValue].Add(item);
}
else
{
// 处理没有该属性的元素
const string unknownGroup = "未定义";
if (!groups.ContainsKey(unknownGroup))
{
groups[unknownGroup] = new List<ModelItem>();
}
groups[unknownGroup].Add(item);
}
processedCount++;
// 每处理1000个元素记录一次进度
if (processedCount % 1000 == 0)
{
LogManager.Info($"[AttributeGrouper] 已处理 {processedCount}/{items.Count} 个元素");
}
}
// 转换为AttributeGroup列表
var result = new List<AttributeGroup>();
foreach (var kvp in groups.OrderBy(g => g.Key))
{
var group = new AttributeGroup
{
GroupName = SanitizeGroupName(kvp.Key),
AttributeValue = kvp.Key,
Items = new ModelItemCollection(),
Metadata = new Dictionary<string, object>
{
["AttributeName"] = attributeName,
["OriginalValue"] = kvp.Key,
["ItemCount"] = kvp.Value.Count,
["ProcessTime"] = DateTime.Now
}
};
group.Items.AddRange(kvp.Value);
result.Add(group);
}
LogManager.Info($"[AttributeGrouper] 分组完成,共生成 {result.Count} 个组,覆盖率: {(double)itemsWithAttribute / items.Count:P2}");
return result;
}
catch (Exception ex)
{
LogManager.Error($"[AttributeGrouper] 属性分组失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 获取可用的属性列表
/// </summary>
/// <param name="items">模型元素集合</param>
/// <returns>可用属性名称列表</returns>
public List<string> GetAvailableAttributes(ModelItemCollection items)
{
try
{
var attributes = new HashSet<string>();
int sampleSize = Math.Min(100, items.Count); // 采样前100个元素
LogManager.Info($"[AttributeGrouper] 开始获取可用属性,采样数量: {sampleSize}");
foreach (ModelItem item in items.Take(sampleSize))
{
foreach (PropertyCategory category in item.PropertyCategories)
{
foreach (DataProperty property in category.Properties)
{
if (!string.IsNullOrEmpty(property.DisplayName))
{
attributes.Add(property.DisplayName);
}
}
}
}
var result = attributes.OrderBy(attr => attr).ToList();
LogManager.Info($"[AttributeGrouper] 找到 {result.Count} 个可用属性");
return result;
}
catch (Exception ex)
{
LogManager.Error($"[AttributeGrouper] 获取可用属性失败: {ex.Message}");
return new List<string>();
}
}
/// <summary>
/// 获取指定属性的值分布统计
/// </summary>
/// <param name="items">模型元素集合</param>
/// <param name="attributeName">属性名称</param>
/// <returns>属性值计数字典</returns>
public Dictionary<string, int> GetAttributeValueCounts(ModelItemCollection items, string attributeName)
{
try
{
LogManager.Info($"[AttributeGrouper] 开始统计属性值分布: {attributeName}");
var valueCounts = new Dictionary<string, int>();
foreach (ModelItem item in items)
{
string value = GetAttributeValue(item, attributeName);
if (!string.IsNullOrEmpty(value))
{
if (valueCounts.ContainsKey(value))
valueCounts[value] = valueCounts[value] + 1;
else
valueCounts[value] = 1;
}
else
{
if (valueCounts.ContainsKey("未定义"))
valueCounts["未定义"] = valueCounts["未定义"] + 1;
else
valueCounts["未定义"] = 1;
}
}
LogManager.Info($"[AttributeGrouper] 属性值统计完成,共 {valueCounts.Count} 个不同值");
return valueCounts;
}
catch (Exception ex)
{
LogManager.Error($"[AttributeGrouper] 统计属性值分布失败: {ex.Message}");
return new Dictionary<string, int>();
}
}
/// <summary>
/// 获取属性的详细统计信息
/// </summary>
/// <param name="items">模型元素集合</param>
/// <param name="attributeName">属性名称</param>
/// <returns>属性统计信息</returns>
public AttributeStatistics GetAttributeStatistics(ModelItemCollection items, string attributeName)
{
try
{
var statistics = new AttributeStatistics
{
AttributeName = attributeName,
TotalItems = items.Count
};
var valueCounts = GetAttributeValueCounts(items, attributeName);
statistics.ValueCounts = valueCounts;
statistics.UniqueValues = valueCounts.Count;
statistics.ItemsWithAttribute = valueCounts.Sum(kvp => kvp.Value);
// 移除"未定义"项来计算真实的覆盖率
if (valueCounts.ContainsKey("未定义"))
{
statistics.ItemsWithAttribute -= valueCounts["未定义"];
}
LogManager.Info($"[AttributeGrouper] 属性统计完成: {attributeName}, 覆盖率: {statistics.Coverage:P2}");
return statistics;
}
catch (Exception ex)
{
LogManager.Error($"[AttributeGrouper] 获取属性统计失败: {ex.Message}");
return new AttributeStatistics { AttributeName = attributeName };
}
}
/// <summary>
/// 根据多个属性进行复合分组
/// </summary>
/// <param name="items">模型元素集合</param>
/// <param name="attributeNames">属性名称列表</param>
/// <returns>复合分组结果</returns>
public List<AttributeGroup> GroupByMultipleAttributes(ModelItemCollection items, List<string> attributeNames)
{
try
{
LogManager.Info($"[AttributeGrouper] 开始多属性分组,属性数量: {attributeNames.Count}");
if (attributeNames == null || attributeNames.Count == 0)
{
throw new ArgumentException("属性名称列表不能为空", nameof(attributeNames));
}
var groups = new Dictionary<string, List<ModelItem>>();
foreach (ModelItem item in items)
{
var attributeValues = new List<string>();
foreach (string attributeName in attributeNames)
{
string value = GetAttributeValue(item, attributeName) ?? "未定义";
attributeValues.Add(value);
}
string compositeKey = string.Join(" | ", attributeValues);
if (!groups.ContainsKey(compositeKey))
{
groups[compositeKey] = new List<ModelItem>();
}
groups[compositeKey].Add(item);
}
var result = new List<AttributeGroup>();
foreach (var kvp in groups.OrderBy(g => g.Key))
{
var group = new AttributeGroup
{
GroupName = SanitizeGroupName(kvp.Key),
AttributeValue = kvp.Key,
Items = new ModelItemCollection(),
Metadata = new Dictionary<string, object>
{
["AttributeNames"] = attributeNames,
["CompositeKey"] = kvp.Key,
["ItemCount"] = kvp.Value.Count,
["GroupingMethod"] = "MultipleAttributes"
}
};
group.Items.AddRange(kvp.Value);
result.Add(group);
}
LogManager.Info($"[AttributeGrouper] 多属性分组完成,共生成 {result.Count} 个组");
return result;
}
catch (Exception ex)
{
LogManager.Error($"[AttributeGrouper] 多属性分组失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 根据属性值范围进行分组(适用于数值属性)
/// </summary>
/// <param name="items">模型元素集合</param>
/// <param name="attributeName">数值属性名称</param>
/// <param name="rangeSize">范围大小</param>
/// <returns>范围分组结果</returns>
public List<AttributeGroup> GroupByAttributeRange(ModelItemCollection items, string attributeName, double rangeSize)
{
try
{
LogManager.Info($"[AttributeGrouper] 开始按属性范围分组: {attributeName}, 范围大小: {rangeSize}");
var rangeGroups = new Dictionary<string, List<ModelItem>>();
foreach (ModelItem item in items)
{
string attributeValueStr = GetAttributeValue(item, attributeName);
if (double.TryParse(attributeValueStr, out double numericValue))
{
double rangeStart = Math.Floor(numericValue / rangeSize) * rangeSize;
double rangeEnd = rangeStart + rangeSize;
string rangeKey = $"{rangeStart:F1}-{rangeEnd:F1}";
if (!rangeGroups.ContainsKey(rangeKey))
{
rangeGroups[rangeKey] = new List<ModelItem>();
}
rangeGroups[rangeKey].Add(item);
}
else
{
// 非数值的归入"其他"组
const string otherGroup = "其他";
if (!rangeGroups.ContainsKey(otherGroup))
{
rangeGroups[otherGroup] = new List<ModelItem>();
}
rangeGroups[otherGroup].Add(item);
}
}
var result = new List<AttributeGroup>();
foreach (var kvp in rangeGroups.OrderBy(g => g.Key))
{
var group = new AttributeGroup
{
GroupName = $"Range_{kvp.Key}",
AttributeValue = kvp.Key,
Items = new ModelItemCollection(),
Metadata = new Dictionary<string, object>
{
["AttributeName"] = attributeName,
["RangeSize"] = rangeSize,
["RangeKey"] = kvp.Key,
["GroupingMethod"] = "AttributeRange"
}
};
group.Items.AddRange(kvp.Value);
result.Add(group);
}
LogManager.Info($"[AttributeGrouper] 属性范围分组完成,共生成 {result.Count} 个组");
return result;
}
catch (Exception ex)
{
LogManager.Error($"[AttributeGrouper] 属性范围分组失败: {ex.Message}");
throw;
}
}
#endregion
#region
/// <summary>
/// 获取模型元素的指定属性值
/// </summary>
/// <param name="item">模型元素</param>
/// <param name="attributeName">属性名称</param>
/// <returns>属性值字符串</returns>
private string GetAttributeValue(ModelItem item, string attributeName)
{
try
{
foreach (PropertyCategory category in item.PropertyCategories)
{
foreach (DataProperty property in category.Properties)
{
if (string.Equals(property.DisplayName, attributeName, StringComparison.OrdinalIgnoreCase))
{
return property.Value?.ToString()?.Trim();
}
}
}
return null;
}
catch (Exception ex)
{
LogManager.Warning($"[AttributeGrouper] 获取属性值失败: {item.DisplayName}, 属性: {attributeName}, 错误: {ex.Message}");
return null;
}
}
/// <summary>
/// 清理分组名称,移除无效字符
/// </summary>
/// <param name="groupName">原始分组名称</param>
/// <returns>清理后的分组名称</returns>
private string SanitizeGroupName(string groupName)
{
if (string.IsNullOrEmpty(groupName))
return "Unknown_Group";
// 替换空格和特殊字符为下划线
string sanitized = System.Text.RegularExpressions.Regex.Replace(groupName.Trim(), @"\s+", "_");
// 移除或替换其他特殊字符,保留中文字符
sanitized = System.Text.RegularExpressions.Regex.Replace(sanitized, @"[^\w\u4e00-\u9fa5\-\.]", "_");
// 移除多余的下划线
sanitized = System.Text.RegularExpressions.Regex.Replace(sanitized, @"_+", "_");
// 移除首尾下划线
sanitized = sanitized.Trim('_');
// 限制长度
if (sanitized.Length > 50)
{
sanitized = sanitized.Substring(0, 50);
}
return string.IsNullOrEmpty(sanitized) ? "Unknown_Group" : sanitized;
}
/// <summary>
/// 验证属性名称是否有效
/// </summary>
/// <param name="attributeName">属性名称</param>
/// <returns>是否有效</returns>
private bool IsValidAttributeName(string attributeName)
{
return !string.IsNullOrWhiteSpace(attributeName) &&
attributeName.Length <= 100 &&
!attributeName.Contains('\0');
}
/// <summary>
/// 获取属性的数据类型
/// </summary>
/// <param name="items">模型元素集合</param>
/// <param name="attributeName">属性名称</param>
/// <returns>数据类型字符串</returns>
public string GetAttributeDataType(ModelItemCollection items, string attributeName)
{
try
{
var sampleValues = new List<string>();
int sampleCount = 0;
const int maxSamples = 20;
foreach (ModelItem item in items)
{
if (sampleCount >= maxSamples) break;
string value = GetAttributeValue(item, attributeName);
if (!string.IsNullOrEmpty(value))
{
sampleValues.Add(value);
sampleCount++;
}
}
if (sampleValues.Count == 0)
return "Unknown";
// 检查是否为数值类型
bool allNumeric = sampleValues.All(v => double.TryParse(v, out _));
if (allNumeric)
return "Numeric";
// 检查是否为日期类型
bool allDate = sampleValues.All(v => DateTime.TryParse(v, out _));
if (allDate)
return "DateTime";
// 检查是否为布尔类型
bool allBoolean = sampleValues.All(v =>
v.Equals("true", StringComparison.OrdinalIgnoreCase) ||
v.Equals("false", StringComparison.OrdinalIgnoreCase) ||
v.Equals("yes", StringComparison.OrdinalIgnoreCase) ||
v.Equals("no", StringComparison.OrdinalIgnoreCase));
if (allBoolean)
return "Boolean";
return "Text";
}
catch (Exception ex)
{
LogManager.Warning($"[AttributeGrouper] 获取属性数据类型失败: {attributeName}, 错误: {ex.Message}");
return "Unknown";
}
}
#endregion
}
}