[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裏一個博主的柵格數據色帶選擇器方法,實現了一下。

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

柵格色帶拉伸渲染代碼:

        /// <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。只怕無法再有這種情懷,優美得共你同時在這世界。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章