[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中实现起来稍微麻烦一点,网上看了不少的示例代码,都是用单一颜色或者自定义颜色进行的简单分级设色,不能满足业务需求,所以自己实现了一下用色带来进行渲染,效果如下[C#] [ArcGIS] [Engine] 近期总结(包括矢量与栅格的分级渲染、要素选择与查找、元素的文本标注和图片标注)_第1张图片

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

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

矢量色带分级设色代码:

        /// 
        /// 字段数据统计
        /// 
        /// 图层
        /// 统计字段
        /// 返回统计数据
        public static Dictionary FieldDataStatistics(IFeatureLayer pFeatureLayer, string field)
        {
            Dictionary dict_DataInfo = new Dictionary();

            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;
        }        
        

        /// 
        /// 矢量分级符号化渲染(手动分级)
        /// 
        /// 图层
        /// 字段
        /// 分级数
        /// 色带对象
        /// 是否反转
        public static void GraduatedColorsRender(ILayer pLayer, string field, int breakCount, IColorRamp pColorRampD, bool isInvert)
        {
            // 统计数据
            Dictionary 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 listColors = new List();
                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;  
        }

        /// 
        /// 矢量分级符号化渲染2(自动分级)
        /// 
        /// 图层
        /// 字段
        /// 分级数
        /// 色带对象
        /// 是否反转
        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 listColors = new List();
                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接口,可以直接传入色带进行拉伸,效果如下

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

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

地址传送:https://blog.csdn.net/u013471015/article/details/80800648

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

栅格色带拉伸渲染代码:

        /// 
        /// 栅格符号化渲染(色带)
        /// 
        /// 栅格对象
        /// 色带对象
        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对象查找对应的图层名称

/// 
/// 根据要素获得对应的图层名称
/// 
/// 地图对象
/// 要素对象
/// 图层名称
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 = ""; // 判别地图点击事件

/// 
/// 地图鼠标单击事件
/// 
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数据。

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

(1)文本标注

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

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

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

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

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

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位图,用于带有图片信息的标注。

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

*创建一个自定义的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。只怕无法再有这种情怀,优美得共你同时在这世界。

 

 

你可能感兴趣的:(ArcGIS,Engine,C#,ArcGIS,Engine,C#)