Files
SzmediTools/GeologyToolkit/CreatorViewModel.cs
2025-09-16 16:06:41 +08:00

601 lines
26 KiB
C#

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.UI;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Win32;
using Nice3point.Revit.Toolkit.External.Handlers;
namespace GeologyToolkit
{
public partial class CreatorViewModel : ObservableObject
{
const string VbACE = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=";
private readonly ActionEventHandler action;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(CreateGeologyCommand))]
string filePath;
[ObservableProperty]
bool isCreateSurface;
bool CanCreate => FilePath != null && File.Exists(FilePath) && FilePath.EndsWith("mdb");
//const string JET = "Provider=Microsoft.JET.OLEDB.4.0;Data Source=";
public CreatorViewModel()
{
action = new ActionEventHandler();
}
/// <summary>
/// 计算图层在在所有钻孔中出现的次数,单个钻孔,一类土层只计算一次
/// </summary>
/// <param name="boreholes"></param>
/// <returns></returns>
private Dictionary<int, List<SoilLayer>> AppearCount(List<Borehole> boreholes)
{
var layerClasses = LayersClassify(boreholes);
var keyValues = new Dictionary<int, List<SoilLayer>>();
foreach (var item in layerClasses)
{
var currentCount = 0;
var soilLayers = new List<SoilLayer>();
foreach (var borehole in boreholes)
{
var layers = borehole.SoilLayers.Where(l => l.MaterialCode == item.MaterialCode);
if (layers.Count() > 0)
{
soilLayers.AddRange(layers);
currentCount += 1;
break;
}
}
keyValues.Add(currentCount, soilLayers);
}
return keyValues;
}
/// <summary>
/// 创建DirectShape
/// </summary>
/// <param name="doc"></param>
/// <param name="borehole"></param>
private static void CreateDirectShape(Document doc, Borehole borehole)
{
var layers = borehole.SoilLayers;
//var topElev = new XYZ(borehole.X, borehole.Y, borehole.Elev) / 304.8 * 1000;
var solids = new List<Solid>();
var radius = 1000.0;
var lib = DirectShapeLibrary.GetDirectShapeLibrary(doc);
var options = new SolidOptions(ElementId.InvalidElementId, ElementId.InvalidElementId);
foreach (var layer in layers)
{
var curves = new List<Curve>();
var curves1 = new List<Curve>();
var topElev = new XYZ(borehole.X, borehole.Y, layer.TopElev) / 0.3048;
var bottomElev = new XYZ(borehole.X, borehole.Y, layer.BottomElev) / 0.3048;
Curve curve = Arc.Create(topElev, radius / 304.8, 0, Math.PI, XYZ.BasisX, XYZ.BasisY);
Curve curve1 = Arc.Create(topElev, radius / 304.8, Math.PI, Math.PI * 2, XYZ.BasisX, XYZ.BasisY);
//var bottomElev = topElev - XYZ.BasisZ * layer.Thickness / 0.3048;
Curve curve2 = Arc.Create(bottomElev, radius / 304.8, 0, Math.PI, XYZ.BasisX, XYZ.BasisY);
Curve curve3 = Arc.Create(bottomElev, radius / 304.8, Math.PI, Math.PI * 2, XYZ.BasisX, XYZ.BasisY);
curves.Add(curve);
curves.Add(curve1);
curves1.Add(curve2);
curves1.Add(curve3);
var loop = CurveLoop.Create(curves);
var loop1 = CurveLoop.Create(curves1);
var cylinder = GeometryCreationUtilities.CreateBlendGeometry(loop, loop1, null);
var shapeTypes = new FilteredElementCollector(doc)
.OfClass(typeof(DirectShapeType))
.OfCategory(BuiltInCategory.OST_GenericModel)
.WhereElementIsElementType();
var elems = shapeTypes.Where(t => t.Name == layer.Name);
var directShapeType =
elems.Count() == 0
? DirectShapeType.Create(doc, layer.Name, new ElementId(BuiltInCategory.OST_GenericModel))
: elems.FirstOrDefault() as DirectShapeType;
directShapeType.SetShape(new List<GeometryObject> { cylinder });
lib.AddDefinitionType("borehole", directShapeType.Id);
var cateId = Category.GetCategory(doc, BuiltInCategory.OST_Mass).Id;
var ds = DirectShape.CreateElementInstance(
doc,
directShapeType.Id,
cateId,
"borehole",
Transform.Identity
);
ds.SetTypeId(directShapeType.Id);
//ds.ApplicationId = "Application id";
//ds.ApplicationDataId = "Geometry object id";
ds.SetShape(new GeometryObject[] { cylinder });
ds.SetName(layer.Name);
//topElev = bottomElev;
}
}
[RelayCommand]
void SelectMDBFile()
{
var openFileDialog = new OpenFileDialog() { Title = "请选择地层数据库", Filter = "地层数据库(*.mdb)|*.mdb", };
if (openFileDialog.ShowDialog() == true)
{
FilePath = openFileDialog.FileName;
}
}
[RelayCommand(CanExecute = nameof(CanCreate))]
void CreateGeology()
{
action.Raise(
uiapp =>
{
var doc = uiapp.ActiveUIDocument.Document;
List<Borehole> boreholes = null;
boreholes = ProcessMdb(FilePath);
//doc.Invoke(ts =>
//{
// foreach (var borehole in boreholes)
// {
// CreateSolid(doc, borehole);
// }
//}, "建立钻孔");
//doc.Invoke(ts =>
//{
// CreateSurfaceDistinct(uidoc, GetFamily(doc), boreholes);
//}, "创建钻孔和地层面");
using var ts = new Transaction(doc, "创建钻孔");
ts.Start();
var family = GetFamily(doc);
foreach (var borehole in boreholes)
{
var ids = CreateInstances(doc, family, borehole).Select(ins => ins.Id).ToList();
//UiDocument.ShowElements(ids);
}
#region
if (IsCreateSurface)
{
var layerClasses = LayersClassify(boreholes);
//var sb = new StringBuilder();
//for (var i = 0; i < layerClasses.Count; i++)
//{
// var layer = layerClasses[i];
// sb.Append(
// (i + 1).ToString() +
// " " +
// layer.MaterialCode +
// layer.Name +
// layer.DegreeofWeathering +
// "\n\r");
//}
//Debug.Write(sb.ToString());
foreach (var layerClass in layerClasses)
{
var points = new List<XYZ>();
foreach (var hole in boreholes)
{
var layers = hole.SoilLayers
.Where(l => l.MaterialCode == layerClass.MaterialCode);
var p = new XYZ(hole.X, hole.Y, 0) / .3048;
if (layers.Count() == 0)
{
var elev = 100000.0;
SoilLayer soilLayer = null;
foreach (var layer in hole.SoilLayers)
{
//找到距离默认类别layerClass深度最接近的层
var temp = Math.Abs(layer.BottomElev - layerClass.BottomElev);
if (temp < elev)
{
elev = temp;
soilLayer = layer;
}
}
p += XYZ.BasisZ * soilLayer.BottomElev / .3048;
}
else
{
p += XYZ.BasisZ * layers.First().BottomElev / .3048;
}
points.Add(p);
}
var surface = CreateTopoGraphySurface(doc, layerClass.MaterialCode, points);
}
}
#endregion
ts.Commit();
});
}
/// <summary>
/// 创建钻孔实例
/// </summary>
/// <param name="doc"></param>
/// <param name="family"></param>
/// <param name="borehole"></param>
/// <returns></returns>
private List<FamilyInstance> CreateInstances(Document doc, Family family, Borehole borehole)
{
var layers = borehole.SoilLayers;
//var topElev = new XYZ(borehole.X, borehole.Y, borehole.Elev) / 0.3048;
var solids = new List<Solid>();
var radius = 500 / 304.8;
var familyInstances = new List<FamilyInstance>();
var cateId = Category.GetCategory(doc, BuiltInCategory.OST_Mass).Id;
var lib = DirectShapeLibrary.GetDirectShapeLibrary(doc);
var options = new SolidOptions(ElementId.InvalidElementId, ElementId.InvalidElementId);
foreach (var layer in layers)
{
var symbols = family.GetFamilySymbolIds().Select(id => doc.GetElement(id) as FamilySymbol);
FamilySymbol symbol = null;
foreach (var sym in symbols)
{
if (sym.Name == layer.Name)
{
symbol = sym;
break;
}
}
if (symbol == null)
{
var sym = symbols.FirstOrDefault();
symbol = sym.Duplicate(layer.Name) as FamilySymbol;
}
if (!symbol.IsActive)
{
symbol.Activate();
}
//var bottomElev = topElev - XYZ.BasisZ * layer.Thickness / 0.3048;
//土层顶底标高
var topElev = new XYZ(borehole.X, borehole.Y, layer.TopElev) / 0.3048;
var bottomElev = new XYZ(borehole.X, borehole.Y, layer.BottomElev) / 0.3048;
var ins = AdaptiveComponentInstanceUtils.CreateAdaptiveComponentInstance(doc, symbol);
layer.RvInstance = ins;
familyInstances.Add(ins);
//自适应点
var placePointIds = AdaptiveComponentInstanceUtils.GetInstancePlacementPointElementRefIds(ins);
var point = doc.GetElement(placePointIds[0]) as ReferencePoint;
var point1 = doc.GetElement(placePointIds[1]) as ReferencePoint;
point.Position = topElev;
point1.Position = bottomElev;
doc.Regenerate();
//topElev = bottomElev;
//材质
ElementId materialId = null;
var name = $"{layer.MaterialCode}-{layer.DegreeofWeathering}{layer.Name}";
var x = name.Split('-');
var r = Convert.ToByte(Convert.ToInt16(x[0]) * 6);
var g = Convert.ToByte(Convert.ToInt16(x[1]) * 30);
var b = Convert.ToByte(Convert.ToInt16(x[2]) * 60);
var color = new Color(r, g, b);
var materials = new FilteredElementCollector(doc).OfClass(typeof(Material)).Where(m => m.Name == name);
if (materials.Count() > 0)
{
materialId = materials.FirstOrDefault().Id;
}
else
{
materialId = Material.Create(doc, name);
var material = doc.GetElement(materialId) as Material;
material.Color = color;
}
//修改参数值
try
{
ins.LookupParameter("开工日期").Set(borehole.StartWorkDate);
ins.LookupParameter("竣工日期").Set(borehole.EndWorkDate);
ins.LookupParameter("地层编号").Set(layer.SerialNum.ToString());
ins.LookupParameter("钻孔编号").Set(borehole.BoreholeNum);
ins.LookupParameter("钻孔坐标").Set(borehole.Coord);
ins.LookupParameter("地层厚度").Set(layer.Thickness.ToString());
ins.LookupParameter("孔口标高").Set(borehole.Elev.ToString());
ins.LookupParameter("勘探深度").Set(borehole.Depth);
ins.LookupParameter("岩石风化程度").Set(layer.DegreeofWeathering);
ins.LookupParameter("类型").Set(borehole.BoreholeType);
ins.LookupParameter("颜色").Set(layer.Color);
ins.LookupParameter("其他描述信息").Set(layer.OtherDescription);
ins.LookupParameter("初见水位埋深").Set(borehole.MeetWaterLine);
ins.LookupParameter("初见水位观测日期").Set(borehole.MeetWaterLineDate);
ins.LookupParameter("稳定水位埋深").Set(borehole.StableWaterLine);
ins.LookupParameter("稳定水位观测日期").Set(borehole.StableWaterLineDate);
ins.Symbol.LookupParameter("钻孔半径").Set(radius);
ins.LookupParameter("材质").Set(materialId);
}
catch (Exception) { }
}
return familyInstances;
}
/// <summary>
/// 通过去钻孔土层去重后建立钻孔及曲面
/// </summary>
/// <param name="uidoc"></param>
/// <param name="family"></param>
/// <param name="boreholes"></param>
private void CreateSurfaceDistinct(Document doc)
{
var totalLayers = new List<SoilLayer>();
var gros = totalLayers.GroupBy(layer => layer.BoreholeNum).Select(g => g.Count() >= 2);
//TaskDialog.Show("同一个钻孔存在同类型的土层", sb.ToString());
var groups = totalLayers.GroupBy(layer => layer.MaterialCode);
foreach (var group in groups)
{
if (group.Count() <= 2)
{
continue;
}
var points = new List<XYZ>();
foreach (var layer in group)
{
var p = new XYZ(layer.Ower.X, layer.Ower.Y, layer.BottomElev) / 0.3048;
points.Add(p);
}
CreateTopoGraphySurface(doc, group.Key, points);
}
}
/// <summary>
/// 创建地形表面
/// </summary>
/// <param name="document"></param>
/// <param name="name">材质名称</param>
/// <param name="points">点集</param>
private TopographySurface CreateTopoGraphySurface(Document document, string name, List<XYZ> points)
{
var materialId = new FilteredElementCollector(document)
.OfClass(typeof(Material))
.Where(m => m.Name.Contains(name))
.FirstOrDefault()?
.Id;
if (materialId == null)
{
materialId = Material.Create(document, name);
}
var surface = TopographySurface.Create(document, points);
surface.MaterialId = materialId;
return surface;
}
/// <summary>
/// 获取土层族
/// </summary>
/// <param name="doc"></param>
private static Family GetFamily(Document doc)
{
var dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var famdoc = doc.LoadFamily($@"{dir}\钻孔土层.rfa", new LoadFamilyOptions(), out var family);
if (family == null)
{
family = new FilteredElementCollector(doc)
.OfClass(typeof(Family))
.Cast<Family>()
.Where(fam => fam.Name == "钻孔土层")
.FirstOrDefault();
}
return family;
}
/// <summary>
/// 找到当前地质中所有土层分类的默认实例
/// </summary>
/// <param name="boreholes"></param>
/// <returns></returns>
private List<SoilLayer> LayersClassify(List<Borehole> boreholes)
{
var layerClasses = new List<SoilLayer>();
foreach (var borehole in boreholes)
{
foreach (var item in borehole.SoilLayers)
{
var count = layerClasses.Where(l => l.MaterialCode == item.MaterialCode).Count();
if (count == 0)
{
layerClasses.Add(item);
}
}
}
return layerClasses;
}
/// <summary>
/// 数据转实例
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
private List<Borehole> ProcessMdb(string path)
{
var boreholes = new List<Borehole>();
try
{
using var connection = new OleDbConnection($"{VbACE}{path};");
//using (OleDbConnection connection = new OleDbConnection(string.Format("Provider=Microsoft.Jet.OLEDB.4.0; Data Source={0};", path)))
connection.Open();
var allTables = new DataSet();
var schema = connection.GetOleDbSchemaTable(
OleDbSchemaGuid.Tables,
new object[] { null, null, null, "TABLE" }
);
DataTable boreHoleTable = null;
DataTable soilLayerTable = null;
DataTable waterLineTable = null;
foreach (DataRow row in schema.Rows)
{
var table = new DataTable(row["TABLE_NAME"].ToString());
if (table.TableName == "z_ZuanKong")
{
boreHoleTable = table;
}
if (table.TableName == "z_g_TuCeng")
{
soilLayerTable = table;
}
if (table.TableName == "z_g_ShuiWei")
{
waterLineTable = table;
}
//if (table.TableName == "g_STuCeng")
//{
// soilLayerClasses = table;
//}
}
if (null == boreHoleTable || null == soilLayerTable)
{
return null;
}
var boreHoleAdapter = new OleDbDataAdapter($"SELECT * FROM [{boreHoleTable.TableName}]", connection);
boreHoleAdapter.Fill(boreHoleTable);
var soilLayerAdapter = new OleDbDataAdapter($"SELECT * FROM [{soilLayerTable.TableName}]", connection);
soilLayerAdapter.Fill(soilLayerTable);
var waterLineAdapter = new OleDbDataAdapter($"SELECT * FROM [{waterLineTable.TableName}]", connection);
waterLineAdapter.Fill(waterLineTable);
//OleDbDataAdapter layerAdapter = new OleDbDataAdapter($"SELECT * FROM [{soilLayerClasses.TableName}]", connection);
//layerAdapter.Fill(soilLayerClasses);
//int zkbhIndex = boreHoleTable.Columns.IndexOf(boreHoleTable.Columns["ZKBH"]);//钻孔编号的索引
//int zklxIndex = boreHoleTable.Columns.IndexOf(boreHoleTable.Columns["ZKLX"]);//钻孔类型的索引
//int zkxIndex = boreHoleTable.Columns.IndexOf(boreHoleTable.Columns["ZKX"]);//钻孔X坐标的索引
//int zkyIndex = boreHoleTable.Columns.IndexOf(boreHoleTable.Columns["ZKY"]);//钻孔Y坐标的索引
foreach (DataRow drillRow in boreHoleTable.Rows)
{
var borehole = new Borehole
{
BoreholeNum = $"{drillRow["ZKBH"]}",
X = Convert.ToDouble(drillRow["ZKX"]),
Y = Convert.ToDouble(drillRow["ZKY"]),
Elev = Math.Round(Convert.ToDouble(drillRow["ZKBG"]), 3),
StartWorkDate = $"{drillRow["ZKKSRQ"]}",
EndWorkDate = $"{drillRow["ZKZZRQ"]}",
Depth = $"{drillRow["ZKSD"]}",
BoreholeType = $"{drillRow["ZKLX"]}"
};
var zkbh1Index = boreHoleTable.Columns.IndexOf(boreHoleTable.Columns["ZKBH"]); //钻孔编号的索引
var tempLayers = new List<SoilLayer>();
foreach (DataRow layerRow in soilLayerTable.Rows)
{
var num = layerRow["ZKBH"].ToString();
if (borehole.BoreholeNum == num)
{
var soilLayer = new SoilLayer
{
Ower = borehole,
BoreholeNum = $"{layerRow["ZKBH"]}",
Name = $"{layerRow["TCMC"]}",
SerialNum = Convert.ToInt16(layerRow["TCXH"]),
Thickness = Convert.ToDouble(layerRow["TCHD"].ToString()),
Color = $"{layerRow["TCYS"]}",
DegreeofWeathering = $"{layerRow["TCFHCD"]}",
OtherDescription = $"{layerRow["TCMS"]}",
MaterialCode = $"{layerRow["TCZCBH"]}-{layerRow["TCYCBH"]}-{layerRow["TCCYCBH"]}"
};
tempLayers.Add(soilLayer);
}
}
;
foreach (DataRow waterlineRow in waterLineTable.Rows)
{
var num = waterlineRow["ZKBH"].ToString();
if (borehole.BoreholeNum == num)
{
var num1 = waterlineRow["SWLX"].ToString();
if (num1 == "0")
{
borehole.MeetWaterLine = $"{waterlineRow["SWSD"]}";
borehole.MeetWaterLineDate = $"{waterlineRow["SWCSRQ"]}";
}
else if (num1 == "1")
{
borehole.StableWaterLine = $"{waterlineRow["SWSD"]}";
borehole.StableWaterLineDate = $"{waterlineRow["SWCSRQ"]}";
}
}
}
var orderLayers = tempLayers.OrderBy(x => x.SerialNum).ToList();
borehole.SoilLayers = orderLayers;
boreholes.Add(borehole);
//WriteTextForDataTable(newDataTable);
}
//foreach (DataRow layerClass in soilLayerClasses.Rows)
//{
// var layer = new SoilLayer()
// {
// MaterialCode = $"{layerClass["TCZCBH"]}-{layerClass["TCYCBH"]}-{layerClass["TCCYCBH"]}"
// };
// layerClasses.Add(layer);
//}
connection.Close();
}
catch (Exception)
{
throw;
}
return boreholes;
}
private void ShowInstances(UIDocument uidoc, List<FamilyInstance> familyInstances)
{
uidoc.ShowElements(familyInstances.Select(ins => ins.Id).ToList());
}
private static void WriteTextForDataTable(DataTable newDataTable)
{
var tableContents = new StringBuilder();
var rowContents = new StringBuilder();
for (var rowIndex = 0; rowIndex < newDataTable.Rows.Count; rowIndex++)
{
for (var itemIndex = 0; itemIndex < newDataTable.Rows[rowIndex].ItemArray.Length; itemIndex++)
{
rowContents.Append(newDataTable.Rows[rowIndex].ItemArray[itemIndex]);
rowContents.Append(",");
}
tableContents.Append(rowContents);
tableContents.Append(Environment.NewLine);
rowContents.Length = 0;
}
TaskDialog.Show($"{newDataTable.TableName}", tableContents.ToString());
}
}
}