admin 管理员组

文章数量: 887006

[C#] [ArcGIS] [Engine] 近期总结(包括矢量与栅格的分级渲染、要素选择与查找、元素的文本标注和图片标注)

目录

1.Content

<1>矢量与栅格的分级渲染

(1)矢量分级设色(对应ArcGIS中的 矢量图层右键->Symbology->Quantities->Graduated colors)

(2)栅格色带渲染(对应ArcGIS中的 栅格图层右键->Symbology->Stretched)

<2>要素选择与查找

(1)要素查找

(2)要素选择

<3>元素的文本标注和符号标注

(1)文本标注

(2)符号标注

2.Environment

3.Conclusion


1.Content

分享几个近期遇到的问题,直接切入主题:

<1>矢量与栅格的分级渲染

(1)矢量分级设色(对应ArcGIS中的 矢量图层右键->Symbology->Quantities->Graduated colors)

用色带(ColorRamp)对矢量数据进行分级设色,在ArcMap中点两下就能实现的功能,在Engine中实现起来稍微麻烦一点,网上看了不少的示例代码,都是用单一颜色或者自定义颜色进行的简单分级设色,不能满足业务需求,所以自己实现了一下用色带来进行渲染,效果如下

同时,参照CSDN里一个博主的栅格数据色带选择器方法,又实现了一下矢量的。

矢量色带分级设色代码:

        /// <summary>/// 字段数据统计/// </summary>/// <param name="pFeatureLayer">图层</param>/// <param name="field">统计字段</param>/// <returns>返回统计数据</returns>public static Dictionary<string, object> FieldDataStatistics(IFeatureLayer pFeatureLayer, string field){Dictionary<string, object> dict_DataInfo = new Dictionary<string, object>();IFeatureClass pFeatureClass = pFeatureLayer.FeatureClass;IField pField = pFeatureClass.Fields.get_Field(pFeatureClass.Fields.FindField(field));ICursor pCursor = pFeatureClass.Search(null, false) as ICursor;IDataStatistics pDataStatistics = new DataStatisticsClass();pDataStatistics.Field = field;pDataStatistics.Cursor = pCursor;IStatisticsResults pStatisticsResults = pDataStatistics.Statistics;dict_DataInfo["Max"] = pStatisticsResults.Maximum;dict_DataInfo["Min"] = pStatisticsResults.Minimum;dict_DataInfo["Mean"] = pStatisticsResults.Mean;dict_DataInfo["Sum"] = pStatisticsResults.Sum;dict_DataInfo["Count"] = pStatisticsResults.Count;dict_DataInfo["StandardDeviation"] = pStatisticsResults.StandardDeviation;return dict_DataInfo;}        /// <summary>/// 矢量分级符号化渲染(手动分级)/// </summary>/// <param name="pLayer">图层</param>/// <param name="field">字段</param>/// <param name="breakCount">分级数</param>/// <param name="pColorRamp">色带对象</param>/// <param name="isInvert">是否反转</param>public static void GraduatedColorsRender(ILayer pLayer, string field, int breakCount, IColorRamp pColorRampD, bool isInvert){// 统计数据Dictionary<string, object> dict_StasDataInfo = FieldDataStatistics(pLayer as IFeatureLayer, field);double maxBreak = Convert.ToDouble(dict_StasDataInfo["Max"]);double minBreak = Convert.ToDouble(dict_StasDataInfo["Min"]);IGeoFeatureLayer pGeoFeatureLayer = pLayer as IGeoFeatureLayer;// 创建断点分级渲染器,并设置属性IClassBreaksRenderer pClassBreaksRenderer = new ClassBreaksRenderer();pClassBreaksRenderer.Field = field;pClassBreaksRenderer.BreakCount = breakCount;pClassBreaksRenderer.MinimumBreak = minBreak;// 根据中值计算间隔 double interval = (maxBreak - minBreak) / breakCount;// 设置色带的分级数量// 这里必须将原来的色带赋值给新声明的色带对象,否则会无法设置色带宽IColorRamp pColorRamp = pColorRampD;pColorRamp.Size = breakCount;bool bCreateRamp;pColorRamp.CreateRamp(out bCreateRamp);if (!bCreateRamp)throw new Exception("创建自定义分级色带失败!");// 获取色带颜色枚举IEnumColors pEnumColors = pColorRamp.Colors;pEnumColors.Reset();         // 当前断点值double currentBreak = minBreak;// 遍历每个分隔符并进行符号化/** 由于我这里渲染的是线,所以用的LineFillSymbol* 要根据不同需求来选择点、线、面的填充符号*/ILineFillSymbol pLineFillSymbol;// 判断是否要反转色带if (isInvert == true){List<IColor> listColors = new List<IColor>();IColor pColor = pEnumColors.Next();while (pColor != null){listColors.Add(pColor);pColor = pEnumColors.Next();}/** 这里尝试过将 i 赋值为 breakCount - 1 从分级断点的最后一个开始设置* 但是会有问题,所以颜色会变成色带枚举的单一颜色* 所以先存储为一个List,进行逆向取值,实现反转*/for (int i = 0; i < breakCount; i++){// 设置断点pClassBreaksRenderer.set_Break(i, currentBreak);// 创建线填充样式并设置颜色pLineFillSymbol = new LineFillSymbolClass();pLineFillSymbol.Color = listColors[breakCount - i - 1];pLineFillSymbol.LineSymbol.Width = 5;// 添加样式pClassBreaksRenderer.set_Symbol(i, pLineFillSymbol as ISymbol);// 根据间隔递增currentBreak += interval;}}else{IColor pColor = pEnumColors.Next();for (int i = 0; i < breakCount; i++){// 设置断点pClassBreaksRenderer.set_Break(i, currentBreak);// 创建线填充样式并设置颜色pLineFillSymbol = new LineFillSymbolClass();pLineFillSymbol.Color = pColor;pLineFillSymbol.LineSymbol.Width = 5;// 添加样式pClassBreaksRenderer.set_Symbol(i, pLineFillSymbol as ISymbol);// 根据间隔递增currentBreak += interval;pColor = pEnumColors.Next();}}// 设置图层的渲染方式  pGeoFeatureLayer.Renderer = (IFeatureRenderer)pClassBreaksRenderer;  }/// <summary>/// 矢量分级符号化渲染2(自动分级)/// </summary>/// <param name="pLayer">图层</param>/// <param name="fieldName">字段</param>/// <param name="breakCount">分级数</param>/// <param name="pColorRamp">色带对象</param>/// <param name="isInvert">是否反转</param>public static void GraduatedColorsRender2(ILayer pLayer, string fieldName, int breakCount, IColorRamp pColorRampD, bool isInvert){IFeatureLayer pFeatureLayer = (IFeatureLayer)pLayer;IGeoFeatureLayer pGeoFeatLayer = (IGeoFeatureLayer)pLayer;IFeatureClass pFeatureClass = pFeatureLayer.FeatureClass;ITable pTable = (ITable)pFeatureClass;IBasicHistogram pBasicHistogram = new BasicTableHistogramClass();ITableHistogram pTableHistogram = (ITableHistogram)pBasicHistogram;pTableHistogram.Field = fieldName;pTableHistogram.Table = pTable;object dataValues;object dataFrequency;pBasicHistogram.GetHistogram(out dataValues, out dataFrequency);IClassifyGEN pClassifyGEN = new QuantileClass();int numClass = breakCount;pClassifyGEN.Classify(dataValues, dataFrequency, ref numClass);double[] classes = (double[])pClassifyGEN.ClassBreaks;long classesCount = long.Parse(classes.GetUpperBound(0).ToString());IClassBreaksRenderer pClassBreaksRenderer = new ClassBreaksRendererClass();pClassBreaksRenderer.Field = fieldName;pClassBreaksRenderer.MinimumBreak = classes[0];pClassBreaksRenderer.SortClassesAscending = true;// 设置着色对象的分级数目pClassBreaksRenderer.BreakCount = int.Parse(classesCount.ToString());// 设置色带的分级数量IColorRamp pColorRamp = pColorRampD;pColorRamp.Size = breakCount;bool bCreateRamp;pColorRamp.CreateRamp(out bCreateRamp);if (!bCreateRamp)throw new Exception("创建自定义分级色带失败!");// 获取色带颜色枚举IEnumColors pEnumColors = pColorRamp.Colors;pEnumColors.Reset();IClassBreaksUIProperties pUIProperties = (IClassBreaksUIProperties)pClassBreaksRenderer;pUIProperties.ColorRamp = "Custom";ILineFillSymbol pLineFillSymbol;if (isInvert == true){List<IColor> listColors = new List<IColor>();IColor pColor = pEnumColors.Next();while (pColor != null){listColors.Add(pColor);pColor = pEnumColors.Next();}for (int breakIndex = 0; breakIndex < classesCount; breakIndex++){pClassBreaksRenderer.set_Label(breakIndex, classes[breakIndex] + "-" + classes[breakIndex + 1]);pUIProperties.set_LowBreak(breakIndex, classes[breakIndex]);pLineFillSymbol = new LineFillSymbolClass();pColor = pEnumColors.Next();pLineFillSymbol.Color = listColors[Convert.ToInt32(classesCount) - breakIndex - 1];pLineFillSymbol.LineSymbol.Width = 5;pClassBreaksRenderer.set_Symbol(breakIndex, (ISymbol)pLineFillSymbol);pClassBreaksRenderer.set_Break(breakIndex, classes[breakIndex + 1]);}}else{IColor pColor;for (int breakIndex = 0; breakIndex < classesCount; breakIndex++){pClassBreaksRenderer.set_Label(breakIndex, classes[breakIndex] + "-" + classes[breakIndex + 1]);pUIProperties.set_LowBreak(breakIndex, classes[breakIndex]);pLineFillSymbol = new LineFillSymbolClass();pColor = pEnumColors.Next();pLineFillSymbol.Color = pColor;pLineFillSymbol.LineSymbol.Width = 5;pClassBreaksRenderer.set_Symbol(breakIndex, (ISymbol)pLineFillSymbol);pClassBreaksRenderer.set_Break(breakIndex, classes[breakIndex + 1]);}}pGeoFeatLayer.Renderer = (IFeatureRenderer)pClassBreaksRenderer;}

 

(2)栅格色带渲染(对应ArcGIS中的 栅格图层右键->Symbology->Stretched)

用色带(ColorRamp)对栅格数据进行拉伸渲染,实现起来比较简单,不用遍历要素赋值设色,而且Engine直接提供了一个IRasterStretchColorRampRenderer接口,可以直接传入色带进行拉伸,效果如下

同时,参照CSDN里一个博主的栅格数据色带选择器方法,实现了一下。

地址传送:

栅格色带拉伸渲染代码:

        /// <summary>/// 栅格符号化渲染(色带)/// </summary>/// <param name="pRaster">栅格对象</param>/// <param name="pColorRamp">色带对象</param>public static void StretchedRender(IRasterLayer pRasterLayer, IColorRamp pColorRamp){IRasterStretchColorRampRenderer pStretchRenderer = new RasterStretchColorRampRendererClass();IRasterRenderer pRasterRenderer = (IRasterRenderer)pStretchRenderer;// 设置渲染器属性pRasterRenderer.Raster = pRasterLayer.Raster;pRasterRenderer.Update();pStretchRenderer.BandIndex = 0;pStretchRenderer.ColorRamp = pColorRamp;pRasterRenderer.Update();// 设置拉伸类型IRasterStretch pStretchType = (IRasterStretch)pRasterRenderer;pStretchType.StretchType = esriRasterStretchTypesEnum.esriRasterStretch_PercentMinimumMaximum;pStretchType.StandardDeviationsParam = 2;pRasterLayer.Renderer = pStretchRenderer as IRasterRenderer; }

注意:①需要先将传入的图层对象添加到地图控件中,再进行渲染和刷新。

注意:②如果地图控件(axMapControl)和目录控件(TOCControl)有关联,就要刷新一下TOCControl,图层的符号样式才会显示:axTOCControl.Update()


<2>要素选择与查找

(1)要素查找

aliasName是要素类的原始命名,layerName是要素类加载到图层中的图层名。

如果修改了图层名,layerName是会变的,而aliasName不会变, 这里要注意名称判别,否则会找不到数据。

for (int i = 0; i < axMapControl.LayerCount; i++)
{  string aliasName = pFeatureLayer.FeatureClass.AliasName.ToString();string layerName = axMapControl.get_Layer(i).Name;// 注意:aliasName可能不等于layerName
}

这里做一个延伸,通过IFeature对象查找对应的图层名称

/// <summary>
/// 根据要素获得对应的图层名称
/// </summary>
/// <param name="axMapControl">地图对象</param>
/// <param name="pFeature">要素对象</param>
/// <returns>图层名称</returns>
public static string GetLayerNameByFeature(AxMapControl axMapControl, IFeature pFeature)
{int index = -1;IFeatureClass pFeatureClass = pFeature.Class as IFeatureClass;for (int i = 0; i < axMapControl.Map.LayerCount; i++){IFeatureLayer pFeatureLayer = axMapControl.get_Layer(i) as IFeatureLayer;IFeatureClass pFeatureClass2 = pFeatureLayer.FeatureClass;if (pFeatureClass == pFeatureClass2){index = i;break;}}if(index == -1)return "";return axMapControl.Map.get_Layer(index).Name;
}

(2)要素选择

axMapControl图上拉框、点选等等操作的最常用三要素组合:

axMapControl_OnMouseDown事件 + axMapControl.CurrentTool工具 + 自定义操作

①axMapControl_OnMouseDown事件:

string mapOperate = ""; // 判别地图点击事件/// <summary>
/// 地图鼠标单击事件
/// </summary>
private void axMapControl1_OnMouseDown(object sender, IMapControlEvents2_OnMouseDownEvent e)
{if (e.button == 1){switch (mapOperate){case "selection":IPoint point = new PointClass();IGeometry pGeometry = point as IGeometry;axMapControl.Map.SelectByShape(pGeometry, null, false);axMapControl.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeoSelection, null, null);break;}}
}

②axMapControl.CurrentTool工具 (写在自己的方法里):

mapOperate = "selection";
axMapControl.CurrentTool = null;
ControlsSelectFeaturesTool pCSFT = new ControlsSelectFeaturesToolClass();
pCSFT.OnCreate(axMapControl.Object);
axMapControl.CurrentTool = pCSFT as ITool;

③自定义操作(这里的Selectable属性用于控制图层是否可以被选中):

IFeatureLayer pFeatureLayer = pLayer as IFeatureLayer;
pFeatureLayer.Selectable = true;

<3>元素的文本标注和符号标注

这里只列举了文本标注和符号标注,可以自己写一个标注样式管理器,用于设置标注的字体、内容、颜色、大小等属性。

这里所说的标注,其实是Element元素数据,在ArcGIS的视图中显示的数据有两种,一种是地理数据,也就是栅格、矢量等;第二种就是元素,即Element。在地图上,除去地理数据以外的全部数据,都是Element数据。

(1)文本标注

Engine提供了ITextElement接口,可以在地图上标注文本元素,用户能够决定文本元素的位置、大小、内容、字体等,用于临时标注比如某个地块、某个点等的含义或注释。

*这里最好创建一个自定义的Base Tool工具

*然后在自己写的方法里调用

axMapControl.CurrentTool = null;
ICommand tool = new TextLabel();
tool.OnCreate(axMapControl.Object);
axMapControl.CurrentTool = tool as ITool;

*创建文本标注代码如下(写在自定义的Base Tool工具中,当然自己重新写一个方法来调用也可):

public override void OnMouseDown(int Button, int Shift, int X, int Y)
{          if (Button == 1){ITextSymbol pTextSymbol;pTextSymbol = new TextSymbolClass();pTextSymbol.Size = 10;stdole.IFontDisp pFont;pFont = new stdole.StdFontClass() as stdole.IFontDisp;// 字体可以用VS自带的FontDialog来获取pFont.Name = "黑体";pTextSymbol.Font = pFont;IColor pColor;IRgbColor pRgbColor = new RgbColorClass();// 颜色可以用VS自带的ColorDialog来获取pRgbColor.Red = 255;pRgbColor.Green = 255;pRgbColor.Blue = 255;pColor = pRgbColor as IColor;pTextSymbol.Color = pColor;pTextElement.Text = "内容";pTextElement.Symbol = pTextSymbol;// 转为IElement用于添加到视图中IElement pEle;pEle = pTextElement as IElement;IPoint pt = new PointClass();pt = axMapControl.ActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(X, Y);pEle.Geometry = pt;IGraphicsContainer pGraphicsContainer = axMapControl.ActiveView.FocusMap as IGraphicsContainer;pGraphicsContainer.AddElement(pEle, 0);axMapControl.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);}
}

 

(2)符号标注

Engine提供了IPictureMarkerSymbol接口,可以加载bmp位图,用于带有图片信息的标注。

*创建一个自定义的Base Tool工具方法同文本标注。

*创建符号标注代码如下:

IPoint pStopsPoint = new PointClass();
// 这里的X,Y指的是鼠标在空间参考下的坐标位置
pStopsPoint = axMapControl.ActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(X, Y);
IGraphicsContainer pGrap = axMapControl.ActiveView as IGraphicsContainer;IColor pColor;
IRgbColor pRgbColor = new RgbColorClass();
pRgbColor.Red = 255;
pRgbColor.Green = 255;
pRgbColor.Blue = 255;
pColor = pRgbColor as IColor;IPictureMarkerSymbol pms = new PictureMarkerSymbolClass();
pms.BitmapTransparencyColor = pColor;
// 加载自己的bmp文件路径
string bmpPath = "*.bmp";
// 添加自定义图片
pms.CreateMarkerSymbolFromFile(esriIPictureType.esriIPictureBitmap, bmpPath);
pms.Size = 18;IMarkerElement pMarkerEle = new MarkerElementClass();
pMarkerEle.Symbol = pms as IMarkerSymbol;
pStopsPoint.SpatialReference = axMapControl.ActiveView.FocusMap.SpatialReference;
IElement pEle = pMarkerEle as IElement;
pEle.Geometry = pStopsPoint;
pGrap.AddElement(pEle, 1);
axMapControl.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);

 

2.Environment

Environment:Windows 7及以上

Language:C#

IDE:Visual Studio 2012

SDK:ArcGIS Engine 10.2

 

3.Conclusion

最近要转WebGIS了,不知什么时候能再碰Engine。只怕无法再有这种情怀,优美得共你同时在这世界。

 

 

本文标签: C ArcGIS Engine 近期总结(包括矢量与栅格的分级渲染要素选择与查找元素的文本标注和图片标注)