337 lines
14 KiB
C#
337 lines
14 KiB
C#
|
|
|
|||
|
|
using System.ComponentModel;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Text.RegularExpressions;
|
|||
|
|
using System.Windows;
|
|||
|
|
using System.Windows.Markup;
|
|||
|
|
|
|||
|
|
using Autodesk.Revit.Attributes;
|
|||
|
|
using Autodesk.Revit.DB;
|
|||
|
|
using Autodesk.Revit.DB.Structure;
|
|||
|
|
|
|||
|
|
using FuzzySharp;
|
|||
|
|
|
|||
|
|
using JiebaNet.Segmenter;
|
|||
|
|
|
|||
|
|
using Microsoft.Win32;
|
|||
|
|
|
|||
|
|
using Nice3point.Revit.Toolkit.External;
|
|||
|
|
|
|||
|
|
using Szmedi.RevitToolkit.Approval.Controls.ProgressWrapper;
|
|||
|
|
using Szmedi.RevitToolkit.Approval.Models;
|
|||
|
|
|
|||
|
|
namespace Szmedi.RevitToolkit.Approval.Commands
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 建筑标识预处理
|
|||
|
|
/// </summary>
|
|||
|
|
[Transaction(TransactionMode.Manual)]
|
|||
|
|
[Regeneration(RegenerationOption.Manual)]
|
|||
|
|
public class ProcessArchiSignCmd : ExternalCommand
|
|||
|
|
{
|
|||
|
|
private Major major;
|
|||
|
|
private string majorName;
|
|||
|
|
public override void Execute()
|
|||
|
|
{
|
|||
|
|
StringBuilder sb = new StringBuilder();
|
|||
|
|
const string signParam = "深圳构件标识";
|
|||
|
|
var dir = Path.Combine(GlobalAssists.DirAssembly, "mvdlite");
|
|||
|
|
major = Document.GetMajor(out majorName);
|
|||
|
|
var result = MessageBox.Show(
|
|||
|
|
$"""
|
|||
|
|
通过各专业专业独有的构件
|
|||
|
|
(建筑-门窗,结构-梁,暖通-风管,电气-桥架,给排水-无风管有水管)
|
|||
|
|
|
|||
|
|
当前专业为:{majorName},
|
|||
|
|
是 - 继续,否 - 手动选择mvdlite文件
|
|||
|
|
""",
|
|||
|
|
"提示",
|
|||
|
|
MessageBoxButton.YesNoCancel,
|
|||
|
|
MessageBoxImage.Question);
|
|||
|
|
string fileContent;
|
|||
|
|
if (result == MessageBoxResult.Yes)
|
|||
|
|
{
|
|||
|
|
fileContent = Directory.GetFiles(dir, "*.mvdlite")
|
|||
|
|
.FirstOrDefault(f => f.Contains(majorName)) switch
|
|||
|
|
{
|
|||
|
|
string file when !string.IsNullOrEmpty(file) => File.ReadAllText(file),
|
|||
|
|
_ => throw new FileNotFoundException("未找到对应的MVDLite文件,请检查目录或文件名。")
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
else if (result == MessageBoxResult.No)
|
|||
|
|
{
|
|||
|
|
OpenFileDialog dialog = new OpenFileDialog()
|
|||
|
|
{
|
|||
|
|
Filter = "MVDLite 文件 (*.mvdlite)|*.mvdlite",
|
|||
|
|
Title = "选择 MVDLite 文件",
|
|||
|
|
InitialDirectory = dir,
|
|||
|
|
RestoreDirectory = true
|
|||
|
|
};
|
|||
|
|
if (dialog.ShowDialog() != true)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
fileContent = File.ReadAllText(dialog.FileName);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
Document.InvokeGroup(
|
|||
|
|
_ =>
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
if (!NeedRemoveParameter(signParam))
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var componentSignSet = UiApplication.Application.Create.NewCategorySet();
|
|||
|
|
var builtInCategories = Enum.GetValues(typeof(BuiltInCategory));
|
|||
|
|
foreach (BuiltInCategory cate in builtInCategories)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var category = Category.GetCategory(Document, cate);
|
|||
|
|
if (category is { CategoryType: CategoryType.Model, AllowsBoundParameters: true })
|
|||
|
|
{
|
|||
|
|
if (cate == BuiltInCategory.OST_Rooms || cate == BuiltInCategory.OST_Areas)
|
|||
|
|
{
|
|||
|
|
continue; //空间和区域不需要标识
|
|||
|
|
}
|
|||
|
|
componentSignSet.Insert(category);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//有的类别不支持这个方法获取
|
|||
|
|
catch (Exception)
|
|||
|
|
{
|
|||
|
|
//sb.AppendLine($"{ex.Message}:{cate}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Document.AddSharedParameter(signParam, componentSignSet, BuiltInParameterGroup.PG_IFC);
|
|||
|
|
|
|||
|
|
var allElements = Document.OfParentModelCollector().ToElements();
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
//sb.AppendLine($"标识,优化标识,匹配,比率,");
|
|||
|
|
MvdData data = MvdLiteAssist.Parse(fileContent);
|
|||
|
|
var groups = allElements.GroupBy(e => e.GetTypeId());
|
|||
|
|
Document.Invoke(
|
|||
|
|
_ =>
|
|||
|
|
{
|
|||
|
|
foreach (var g in groups)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
string mapperName = GetMapperSign(g, data);
|
|||
|
|
|
|||
|
|
//if (elementGroup.Key == ElementId.InvalidElementId)//可能是地形
|
|||
|
|
//{
|
|||
|
|
// string name = string.Empty;
|
|||
|
|
// if (elementGroup.FirstOrDefault().Category != null)
|
|||
|
|
// {
|
|||
|
|
// name = elementGroup.FirstOrDefault().Category.name;
|
|||
|
|
// }
|
|||
|
|
// else
|
|||
|
|
// {
|
|||
|
|
// name = elementGroup.FirstOrDefault().name;
|
|||
|
|
// }
|
|||
|
|
// mapperName = GetMapperSign(name, data);
|
|||
|
|
//}
|
|||
|
|
//else
|
|||
|
|
//{
|
|||
|
|
// mapperName = GetMapperSign(type.name, data);
|
|||
|
|
|
|||
|
|
// if (string.IsNullOrEmpty(mapperName))//如果类型名找不到标识,使用族名称
|
|||
|
|
// {
|
|||
|
|
// mapperName = GetMapperSign(type.FamilyName, data);
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
foreach (var elem in g)
|
|||
|
|
{
|
|||
|
|
var param = elem.LookupParameter(signParam);
|
|||
|
|
param.SetValue(mapperName);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}, $"填写标识");
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
MessageBox.Show(ex.Message);
|
|||
|
|
}
|
|||
|
|
}, $"填写 {majorName} 构件标识");
|
|||
|
|
MessageBox.Show("处理完成", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
|||
|
|
//File.WriteAllText(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\匹配结果" + ".csv",
|
|||
|
|
// sb.ToString(), Encoding.UTF8);
|
|||
|
|
}
|
|||
|
|
class Mapper(string name, string sign, int ratio)
|
|||
|
|
{
|
|||
|
|
public string Name { get; } = name;
|
|||
|
|
public string Sign { get; } = sign;
|
|||
|
|
public int Ratio { get; } = ratio;
|
|||
|
|
public override string ToString()
|
|||
|
|
{
|
|||
|
|
return $"{Name} - {Sign} ({Ratio})";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//private StringBuilder sb = new StringBuilder();
|
|||
|
|
//private List<Mapper> Mappers { get; componentSignSet; } = new List<Mapper>();
|
|||
|
|
private string GetMapperSign(IGrouping<ElementId, Element> elementGroup, MvdData data)
|
|||
|
|
{
|
|||
|
|
if (elementGroup.Key == ElementId.InvalidElementId)
|
|||
|
|
{
|
|||
|
|
return string.Empty;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var definitions = MvdLiteAssist.GetElementSigns(data);
|
|||
|
|
var categoryName = elementGroup.FirstOrDefault().Category?.Name;
|
|||
|
|
|
|||
|
|
//如果没有匹配到,使用类型名进行匹配
|
|||
|
|
var type = Document.GetElement(elementGroup.Key) as ElementType;
|
|||
|
|
var famName = type.FamilyName;
|
|||
|
|
ConfigManager.ConfigFileBaseDir = Path.Combine(RvApp.AddInDirectory, "configs");
|
|||
|
|
var segmenter = new JiebaSegmenter();
|
|||
|
|
var subText = $"{type.Name} {famName} {categoryName}";
|
|||
|
|
var replaceText = subText.ExtractChinese();
|
|||
|
|
var segments = segmenter.Cut(replaceText).Distinct();
|
|||
|
|
var combinedText = string.Join(" ", segments);
|
|||
|
|
string resultName = string.Empty;
|
|||
|
|
double minRatio = 30;
|
|||
|
|
|
|||
|
|
foreach (var defin in definitions)
|
|||
|
|
{
|
|||
|
|
var defText = defin.Replace(majorName, "");
|
|||
|
|
var segmentsDef = segmenter.Cut(defText);
|
|||
|
|
defText = string.Join(" ", segmentsDef);
|
|||
|
|
var tempRatio = Fuzz.TokenSetRatio(defText, combinedText);
|
|||
|
|
//var tempRatio = Fuzz.Ratio(defin, segment);
|
|||
|
|
if (tempRatio >= minRatio)
|
|||
|
|
{
|
|||
|
|
//sb.AppendLine($"{defin},{defText},{combinedText},{tempRatio},");
|
|||
|
|
minRatio = tempRatio;
|
|||
|
|
resultName = defin;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return resultName;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//private static string ExtractChinese(string text)
|
|||
|
|
//{
|
|||
|
|
// if (string.IsNullOrEmpty(text)) return string.Empty;
|
|||
|
|
|
|||
|
|
// // 方法1:正则(简单常用)
|
|||
|
|
// return Regex.Replace(text, @"[^\u4e00-\u9fa5]", "");
|
|||
|
|
|
|||
|
|
// // 方法2:遍历(可扩展)
|
|||
|
|
// // var sb = new StringBuilder();
|
|||
|
|
// // foreach (char c in text)
|
|||
|
|
// // if (c >= 0x4e00 && c <= 0x9fa5)
|
|||
|
|
// // sb.Append(c);
|
|||
|
|
// // return sb.ToString();
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 清理字符串中的括号、下划线、减号、加号等符号
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="input">原始字符串</param>
|
|||
|
|
/// <returns>清理后的字符串</returns>
|
|||
|
|
public static string CleanSpecialChars(string input, string replacestr = " ")
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrEmpty(input))
|
|||
|
|
return input;
|
|||
|
|
|
|||
|
|
// 正则模式说明:
|
|||
|
|
// [()\[\]{}<>_-+] 匹配以下符号:
|
|||
|
|
// 圆括号 ()、方括号 []、花括号 {}、尖括号 <>、下划线 _、减号 -、加号 +
|
|||
|
|
// 注意:方括号内的特殊字符(如 [ ] - )需要转义或放在安全位置(如末尾)
|
|||
|
|
string pattern = @"[()\[\]{}<>_+-]";
|
|||
|
|
return Regex.Replace(input, pattern, replacestr);
|
|||
|
|
}
|
|||
|
|
private string GetBestMatchingSign(IGrouping<ElementId, Element> g, HashSet<string> definitions, string categoryName, string famName, ref string resultName)
|
|||
|
|
{
|
|||
|
|
//初步匹配
|
|||
|
|
List<string> strings = [];
|
|||
|
|
if (major == Major.Architecture || major == Major.Structure || major == Major.MasterPlan)
|
|||
|
|
{
|
|||
|
|
int mapperRatio = 30;
|
|||
|
|
foreach (var mapper in definitions)
|
|||
|
|
{
|
|||
|
|
//var sourceIndex = Levenshtein.GetMatchingBlocks(mapper, categoryName).FirstOrDefault().SourcePos;
|
|||
|
|
//var pos = mapper.Length - sourceIndex;
|
|||
|
|
|
|||
|
|
var tempRatio = Fuzz.WeightedRatio(mapper, $"{categoryName}");
|
|||
|
|
if (tempRatio > 30)
|
|||
|
|
{
|
|||
|
|
if (tempRatio > mapperRatio)
|
|||
|
|
{
|
|||
|
|
mapperRatio = tempRatio;
|
|||
|
|
resultName = mapper;
|
|||
|
|
}
|
|||
|
|
strings.Add(mapper);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
strings = [.. definitions];
|
|||
|
|
}
|
|||
|
|
var subText = g.FirstOrDefault().Name;
|
|||
|
|
|
|||
|
|
var replaceText = subText.Replace('-', ' ').Replace('_', ' ').Replace('—', ' ');
|
|||
|
|
var combinedText = $"{famName} {replaceText}";
|
|||
|
|
int minRatio = 20;
|
|||
|
|
foreach (var defin in strings)
|
|||
|
|
{
|
|||
|
|
//var tempRatio = Fuzz.TokenSortRatio(defin, replaceText);
|
|||
|
|
var tempRatio = Fuzz.WeightedRatio(defin, combinedText);
|
|||
|
|
if (tempRatio > minRatio)
|
|||
|
|
{
|
|||
|
|
//Mappers.Add(new(replaceText, defin, tempRatio));
|
|||
|
|
minRatio = tempRatio;
|
|||
|
|
resultName = defin;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//if (string.IsNullOrEmpty(resultName))
|
|||
|
|
//{
|
|||
|
|
// resultName = "无标识";
|
|||
|
|
//}
|
|||
|
|
return resultName;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 需要移除现有的标识参数
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="paramName"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
private bool NeedRemoveParameter(string paramName)
|
|||
|
|
{
|
|||
|
|
var dict = SharedParameterAssists.GetParameterElementBindings(Document)
|
|||
|
|
.Where(d => d.Key.Name == paramName);
|
|||
|
|
if (dict.Any())
|
|||
|
|
{
|
|||
|
|
var result = MessageBox.Show(
|
|||
|
|
$"已存在一个或多个{paramName}的参数定义,将全部进行删除,并重新添加,是否继续?",
|
|||
|
|
"警告",
|
|||
|
|
MessageBoxButton.YesNo,
|
|||
|
|
MessageBoxImage.Warning);
|
|||
|
|
if (result == MessageBoxResult.Yes)
|
|||
|
|
{
|
|||
|
|
Document.Invoke(
|
|||
|
|
_ =>
|
|||
|
|
{
|
|||
|
|
Document.Delete(dict.Select(d => d.Key).Select(p => p.Id).ToList());
|
|||
|
|
},
|
|||
|
|
$"删除现有{paramName}参数");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|