目錄
(1)矢量分級設色(對應ArcGIS中的 矢量圖層右鍵->Symbology->Quantities->Graduated colors)
(2)柵格色帶渲染(對應ArcGIS中的 柵格圖層右鍵->Symbology->Stretched)
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。只怕無法再有這種情懷,優美得共你同時在這世界。