546 lines
21 KiB
C#
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
|
|
}
|
|
} |