diff --git a/client/tool/ArcGisProSymbology.cs b/client/tool/ArcGisProSymbology.cs new file mode 100644 index 0000000..94e24a3 --- /dev/null +++ b/client/tool/ArcGisProSymbology.cs @@ -0,0 +1,250 @@ +using ArcGIS.Core.CIM; +using ArcGIS.Core.Data; +using ArcGIS.Desktop.Core; +using ArcGIS.Desktop.Framework.Threading.Tasks; +using ArcGIS.Desktop.Mapping; +using LinkToolAddin.server; +using ModelContextProtocol.Server; +using System; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace LinkToolAddin.client.tool +{ + public class ArcGisProSymbology + { + /// + /// 将 System.Drawing.Color 转换为 CIMColor(支持 RGB 和透明度) + /// + private static CIMColor ColorToCIMColor(int r, int g, int b, double alpha =0) + { + return ColorFactory.Instance.CreateRGBColor(r, g, b); + } + + /// + /// 构造点符号引用(封装 SymbolFactory 调用) + /// + private static CIMSymbolReference GetPointSymbolRef(int r, int g, int b, float size) + { + CIMColor color = ColorToCIMColor(r, g, b); + CIMPointSymbol symbol = SymbolFactory.Instance.ConstructPointSymbol(color, size); + return symbol.MakeSymbolReference(); + } + + /// + /// 构造线符号引用 + /// + private static CIMSymbolReference GetLineSymbolRef(int r, int g, int b, float width) + { + CIMColor color = ColorToCIMColor(r, g, b); + + // 手动创建线符号图层 + CIMSolidStroke stroke = new CIMSolidStroke + { + Color = color, + Width = width, + CapStyle = LineCapStyle.Round, // 端点样式:圆头 + JoinStyle = LineJoinStyle.Round, // 连接处样式:圆角 + Enable = true // 必须启用! + }; + + // 创建线符号并设置图层 + CIMLineSymbol symbol = new CIMLineSymbol + { + SymbolLayers = new CIMSymbolLayer[] { stroke } + }; + + return symbol.MakeSymbolReference(); + } + + /// + /// 构造面符号引用(填充 + 轮廓) + /// + private static CIMSymbolReference GetPolygonSymbolRef( + int fillR, int fillG, int fillB, + int outlineR, int outlineG, int outlineB, + float outlineWidth) + { + // 填充图层 + CIMSolidFill fillLayer = new CIMSolidFill + { + Color = ColorToCIMColor(fillR, fillG, fillB) + }; + + // 轮廓图层 + CIMSolidStroke outlineLayer = new CIMSolidStroke + { + Color = ColorToCIMColor(outlineR, outlineG, outlineB), + Width = outlineWidth, + JoinStyle = LineJoinStyle.Round//圆角 + }; + + CIMPolygonSymbol polygonSymbol = new CIMPolygonSymbol + { + SymbolLayers = new CIMSymbolLayer[] { fillLayer, outlineLayer } + }; + + return polygonSymbol.MakeSymbolReference(); + } + + // ==================== 工具方法 ==================== + + [McpServerTool, Description("可以通过调用 ArcGIS Pro 的符号系统,设置点图层的符号颜色和大小。" + + "此工具将分析要素数据特征与当前地图布局,智能生成符合视觉美学原则的点状符号。" + + "传入参数必须包括红(R)、绿(G)、蓝(B)三种颜色分量(0-255)、符号尺寸(单位:点)以及目标图层名称。" + + "系统在设置过程中综合考虑颜色对比度、符号可辨性、形状匹配度及整体地图协调性等因素," + + "动态调整或创建新的符号配置,避免视觉拥挤或信息弱化问题。" + + "最终输出一个优化后的点符号系统配置,可直接应用于指定的点要素图层,显著提升地图的视觉层次感与信息传达效率。" + + "适用于城市位置、监测站点、兴趣点(POI)等点状地理要素的可视化表达场景。")] + public static async Task SetPointColor(string r, string g, string b, string layerName, string size) + { + return await SetSymbolAsync(layerName, esriGeometryType.esriGeometryPoint, () => + { + return GetPointSymbolRef(int.Parse(r), int.Parse(g), int.Parse(b), float.Parse(size)); + }, "点符号"); + } + + [McpServerTool, Description("可以通过调用 ArcGIS Pro 的符号系统,设置线图层的符号颜色和宽度。" + + "此工具将分析要素数据和当前地图布局,智能选择或生成符合视觉美学原则的线符号。" + + "传入参数必须包括 RGB 三种颜色分量、线符号宽度以及目标图层名称。" + + "系统会综合考虑颜色对比度、线型协调性、地图整体可读性等因素,动态调整或创建新的符号配置。" + + "最终输出一个优化后的线符号系统,可直接应用于指定的线要素图层,显著提升地图的视觉表达效果与信息传达清晰度。" + + "适用于道路、河流、边界等线状要素的符号化场景。")] + public static async Task SetLineColor(string r, string g, string b, string layerName, string width) + { + + return await SetSymbolAsync(layerName, esriGeometryType.esriGeometryPolyline, () => + { + return GetLineSymbolRef(int.Parse(r), int.Parse(g), int.Parse(b), float.Parse(width)); + }, "线符号"); + } + + [McpServerTool, Description("设置面图层的填充颜色、轮廓颜色和轮廓宽度。" + + "此工具将调用 ArcGIS Pro 符号系统,智能生成符合视觉美学的面符号。" + + "传入参数包括:填充颜色的RGB值、轮廓颜色的RGB值、轮廓宽度、图层名称。" + + "最终将优化后的符号应用于指定的面要素图层,提升地图可视化效果。")] + public static async Task SetPolygonColor(string fillR, string fillG, string fillB, + string outlineR, string outlineG, string outlineB, string outlineWidth, string layerName) + { + return await SetSymbolAsync(layerName, esriGeometryType.esriGeometryPolygon, () => + { + return GetPolygonSymbolRef(int.Parse(fillR), int.Parse(fillG), int.Parse(fillB), int.Parse(outlineR), int.Parse(outlineG), int.Parse(outlineB), float.Parse(outlineWidth)); + }, "面符号"); + } + + /// + /// 通用符号设置逻辑(封装重复代码) + /// + private async static Task SetSymbolAsync( + string layerName, + esriGeometryType expectedType, + Func symbolFactory, + string symbolTypeName) + { + try + { + Map map = MapView.Active?.Map; + if (map == null) + { + return new JsonRpcErrorEntity + { + Error = new Error + { + Code = "404", + Message = "当前没有激活的地图" + } + }; + } + + FeatureLayer featureLayer = map.GetLayersAsFlattenedList() + .OfType() + .FirstOrDefault(l => l.Name == layerName); + + if (featureLayer == null) + { + return(new JsonRpcErrorEntity + { + Error = new Error + { + Code = "404", + Message = $"找不到名为 '{layerName}' 的图层" + } + }); + } + + if (featureLayer.ShapeType != expectedType) + { + return(new JsonRpcErrorEntity + { + Error = new Error + { + Code = "400", + Message = $"指定图层 '{layerName}' 不是{symbolTypeName}图层" + } + }); + } + + CIMRenderer renderer = null; + + await QueuedTask.Run(async () => + { + renderer = featureLayer.GetRenderer(); + }); + CIMSimpleRenderer simpleRenderer = renderer as CIMSimpleRenderer; + if (simpleRenderer == null) + { + return(new JsonRpcErrorEntity + { + Error = new Error + { + Code = "400", + Message = $"图层 '{layerName}' 的渲染器不是简单渲染器,无法设置" + } + }); + } + + await QueuedTask.Run(async () => + { + CIMSymbolReference symbolRef = symbolFactory(); + simpleRenderer.Symbol = symbolRef; + featureLayer.SetRenderer(renderer); + }); + + return (new JsonRpcSuccessEntity + { + Result = $"{symbolTypeName}设置成功", + Id = GetIdForType(expectedType) + }); + } + catch (Exception ex) + { + // 捕获 QueuedTask 外层异常(如线程中断等) + return new JsonRpcErrorEntity + { + Error = new Error + { + Code = "500", + Message = $"任务执行失败: {ex.Message}" + } + }; + } + } + + /// + /// 根据几何类型返回对应的 ID(用于兼容原逻辑) + /// + private static int GetIdForType(esriGeometryType type) + { + return type switch + { + esriGeometryType.esriGeometryPoint => 1, + esriGeometryType.esriGeometryPolyline => 2, + esriGeometryType.esriGeometryPolygon => 3, + _ => 0 + }; + } + } +} \ No newline at end of file diff --git a/host/McpServerList.cs b/host/McpServerList.cs index 0d94479..ea97c4d 100644 --- a/host/McpServerList.cs +++ b/host/McpServerList.cs @@ -29,6 +29,13 @@ public class McpServerList Description = "可以调用arcgis的地理处理工具或执行python代码等", IsActive = true }); + servers.Add("ArcGisProSymbology", new InnerMcpServer + { + Name = "ArcGisProSymbology", + Type = "inner", + Description = "可以调用arcgis的符号系统进行简单符号设置", + IsActive = true + }); servers.Add("KnowledgeBase", new InnerMcpServer { Name = "KnowledgeBase", @@ -37,18 +44,18 @@ public class McpServerList // "有地理信息的相关案例步骤参考以及Arcgis Pro的工具详细信息", IsActive = true }); - servers.Add("filesystem", new StdioMcpServer() - { - Name = "filesystem", - Type = "stdio", - Command = "npx", - Args = new List() - { - "-y", - "@modelcontextprotocol/server-filesystem", - "F:\\secondsemester\\linktool\\test\\LinkTool0607\\LinkTool0607.gdb" - } - }); + //servers.Add("filesystem", new StdioMcpServer() + //{ + // Name = "filesystem", + // Type = "stdio", + // Command = "npx", + // Args = new List() + // { + // "-y", + // "@modelcontextprotocol/server-filesystem", + // "F:\\secondsemester\\linktool\\test\\LinkTool0607\\LinkTool0607.gdb" + // } + //}); // servers.Add("fetch", new StdioMcpServer() // { // Name = "fetch", diff --git a/ui/dockpane/TestDockpane.xaml b/ui/dockpane/TestDockpane.xaml index 0306e36..a6e64ed 100644 --- a/ui/dockpane/TestDockpane.xaml +++ b/ui/dockpane/TestDockpane.xaml @@ -26,7 +26,8 @@ - + + @@ -43,5 +44,6 @@ - + + \ No newline at end of file diff --git a/ui/dockpane/TestDockpane.xaml.cs b/ui/dockpane/TestDockpane.xaml.cs index 5985146..873bda2 100644 --- a/ui/dockpane/TestDockpane.xaml.cs +++ b/ui/dockpane/TestDockpane.xaml.cs @@ -320,5 +320,12 @@ namespace LinkToolAddin.ui.dockpane "LandUse_2005_Copy", "30"); log.Info("finish"); } + + private async void TestSymbology_Click(object sender, RoutedEventArgs e) + { + //await ArcGisProSymbology.SetPointColor(115, 174, 82, "population_XYTableToPoint", 10); + //await ArcGisProSymbology.SetLineColor(29,141,213,"testline",50); + //await ArcGisProSymbology.SetPolygonColor(149, 2, 8, 255, 153, 0, 30, "county111_popul"); + } } }