关于栅格图层色彩拉伸,有很多例子,网上也可以找到各种资料,但是所有的资料都没有解决一个问题,那就是只说如何做拉伸,但是做色彩拉伸以后的图例更新问题一直没有正解。最早提出这个问题的帖子,在这里:http://forums.esri.com/thread.asp?c=93&f=1170&t=191617#848069,但是答案却一直空缺着,本文就来说明一下如何解决这个问题。
首先说一下如何让做栅格色彩拉伸,请看下面的代码:
IRasterLayer pRasterLayer = wndMap.CustomProperty as IRasterLayer;
IRasterRenderer pRasterRender = pRasterLayer.Renderer;
IRasterRenderer pNewRasterRender = null;
IRgbColor pFromColor = new RgbColorClass();
pFromColor.RGB = 0xffffff;
IRgbColor pToColor = new RgbColorClass();
pToColor.RGB = 0x000000;
IAlgorithmicColorRamp pAlgorithmColorRamp = new AlgorithmicColorRampClass();
pAlgorithmColorRamp.Size = 256;
pAlgorithmColorRamp.FromColor = pFromColor;
pAlgorithmColorRamp.ToColor = pToColor;
bool bColorRampCreated = false;
pAlgorithmColorRamp.CreateRamp(out bColorRampCreated);
IRasterStretchColorRampRenderer pRasterStretchColorRamp = new RasterStretchColorRampRendererClass();
pNewRasterRender = pRasterStretchColorRamp as IRasterRenderer;
pNewRasterRender.Raster = pRasterLayer.Raster;
pRasterStretchColorRamp.BandIndex = 0;
pRasterStretchColorRamp.ColorRamp = pAlgorithmColorRamp;
IRasterStretch pRasterStretch = pNewRasterRender as IRasterStretch;
pRasterStretch.StretchType = esriRasterStretchTypesEnum.esriRasterStretch_MinimumMaximum;
pNewRasterRender.Update();
pRasterLayer.Renderer = pNewRasterRender;
这是基本的处理方式,这里出于使示例整洁完整使用的是一个自定义的色带。更为详尽的示例,请参考这里:
http://www.gisall.com/html/59/26859-2432.html做色彩拉伸时,通常使用的色带是ArcGIS SymbologyControl里的预定义色带,加载的类型为esriSymbologyStyleClass.esriStyleClassColorRamps。可以将SymbologyControl里选中的色带直接转换为IColorRamp,然后作为IRasterStretchColorRampRenderer的ColorRamp。基本流程清除了,但问题也会凸显出来,那就是TocControl里的图例并不会自动显示色带。那么问题在哪里呢?下面来看一下AE附带的示例里相关的例子里是怎么做的,下面是AE的C#例子"SymbologyControlColorRamps"里对矢量图层做分段设色处理的代码:
private void button2_Click(object sender, System.EventArgs e)
{
//Create a new ClassBreaksRenderer and set properties
m_classBreaksRenderer = new ClassBreaksRenderer();
m_classBreaksRenderer.Field = comboBox1.SelectedItem.ToString();
m_classBreaksRenderer.BreakCount = Convert.ToInt32(textBox1.Text);
m_classBreaksRenderer.MinimumBreak = Convert.ToDouble(textBox2.Text);
//Calculate the class interval by a simple mean value
double interval = (Convert.ToDouble(textBox3.Text) - m_classBreaksRenderer.MinimumBreak) / m_classBreaksRenderer.BreakCount;
//Get the color ramp
IColorRamp colorRamp = (IColorRamp) m_styleGalleryItem.Item;
//Set the size of the color ramp and recreate it
colorRamp.Size = Convert.ToInt32(textBox1.Text);
bool createRamp;
colorRamp.CreateRamp(out createRamp);
//Get the enumeration of colors from the color ramp
IEnumColors enumColors = colorRamp.Colors;
enumColors.Reset();
double currentBreak = m_classBreaksRenderer.MinimumBreak;
ISimpleFillSymbol simpleFillSymbol;
//Loop rhough each class break
for (int i = 0; i <= m_classBreaksRenderer.BreakCount-1; i++)
{
//Set class break
m_classBreaksRenderer.set_Break(i,currentBreak);
//Create simple fill symbol and set color
simpleFillSymbol = new SimpleFillSymbolClass();
simpleFillSymbol.Color = enumColors.Next();
//Add symbol to renderer
m_classBreaksRenderer.set_Symbol(i, (ISymbol)simpleFillSymbol);
currentBreak += interval;
}
//Hide the form
this.Hide();
}
可以看到,这里面很重要的一点就是逐一设置了图例的符号。从这里,我们可以受到一点启发,那就是我们的栅格拉伸处理,是不是也需要做这样的一个处理。可以看到RasterStretchColorRampRender实现了ILegendInfo接口,而ILegendInfo接口就是用来管理图层图例的接口。据此,我们就需要详细研究一下ILegendInfo对象在做完上面的栅格色彩拉伸后有什么变化。经过跟踪发现,执行栅格拉伸后,ILegendInfo接口对象始终含有一个LegendGroup,而这个LegendGroup则含有三个LegendItem,查看这三个LegendItem的Label发现正是图层图例里列出的“高值”,"中间值“,”低值“,因此可以推断他们就是解决图例色带问题的核心。研究ILegendCalss接口,发现里面果然有Symbol成员,直觉告诉我们,应该修改这个属性。查找所有实现了ISymbol接口的类,发现IColorRampSymbol最有嫌疑。通过使用ArcMap制作一个彩色拉伸图层,然后保存为MXD文档并用自己的程序加载,对比发现,果然自己做拉伸后所有LegendItem的Symbol都是null,而ArcMap做拉伸后LegendItem的Symbol都有值,并且可以转换为IColorRampSymbol,这样问题似乎已经浮出水面了,于是我们来添加下面的一段处理代码:
ILegendInfo pLengendInfo = m_RasterStretchColorRampRenderer as ILegendInfo;
ILegendGroup pLegendGroup = pLengendInfo.get_LegendGroup(0);
IColorRamp pColorRamp = m_StyleGalleryItem.Item as IColorRamp;
for (int j = 0; j < pLegendGroup.ClassCount; j++)
{
ILegendClass pLegendClass = pLegendGroup.get_Class(j);
pLegendClass.Symbol = new ESRI.ArcGIS.Carto.ColorRampSymbolClass();
ISymbol pSymbol = pLegendClass.Symbol;
IColorRampSymbol pColorRampSymbol = pSymbol as IColorRampSymbol;
pColorRampSymbol.ColorRamp = pColorRamp;
pLegendClass.Symbol = pColorRampSymbol as ISymbol;
}
(m_RasterStretchColorRampRenderer as IRasterRenderer).Update();
很遗憾,图例中的色带出现了,但是并不是我们想看到的那样。继续探索,发现关于IColorRampSymbol接口,AE的帮助里几乎找不到任何有用的说明,于是我们不得不发挥想象力,考虑一下如果你是设计这个接口的人,你会怎么去做。很明显,栅格拉伸后的图例由三个LegendItem组成,每一个都带有一个Symbol,而这个Symbol都是IColorRampSymbol,但是这个色带符号很长,需要横跨三个图例项,唯一的办法就是把它分成3份,于是我们可能需要一个标记来告诉这三个图例项,他们分别应该对应这个色带符号的那一部分。据此,再来研究一下IColorRampSymbol接口,发现里面有两个属性非常可疑,一个是ColorRampInLegendGroup,一个是LegendClassIndex,很明显,作为一个图例项的成员,肯定知道自己的Index,但是这里却要多加这样一个属性,所以问题可能就在这里了。关于这个ColorRampInLegendGroup属性,网上以及AE的帮助里找不到任何有用的信息,所以也只好用猜的了。直白的来说,根据字面意思,这个属性的意思就是”图例组的色带“,所以我们可以猜测到实际处理中,这个图例组实际上使用的是一个色带,每个图例项也都是用这个色带,唯一需要做的就是用一个标记来标注每个图例项对应色带的哪里。下面就再做验证,仍然查看ArcMap下制作的正确的色彩拉伸结果,发现三个图例项果然都使用同一个IColorRamp对象,于是问题就明朗了,毋庸置疑,LegendClassIndex的作用,正是我们所猜测的那样。于是对图例更新代码做一些修改,如下:
IColorRamp pColorRamp = m_StyleGalleryItem.Item as IColorRamp;
m_RasterStretchColorRampRenderer.ColorRamp = pColorRamp;
(m_RasterStretchColorRampRenderer as IRasterRenderer).Update();
ILegendInfo pLengendInfo = m_RasterStretchColorRampRenderer as ILegendInfo;
ILegendGroup pLegendGroup = pLengendInfo.get_LegendGroup(0);
for (int j = 0; j < pLegendGroup.ClassCount; j++)
{
ILegendClass pLegendClass = pLegendGroup.get_Class(j);
pLegendClass.Symbol = new ESRI.ArcGIS.Carto.ColorRampSymbolClass();
ISymbol pSymbol = pLegendClass.Symbol;
IColorRampSymbol pColorRampSymbol = pSymbol as IColorRampSymbol;
pColorRampSymbol.ColorRamp = pColorRamp;
pColorRampSymbol.ColorRampInLegendGroup = pColorRamp;
pColorRampSymbol.LegendClassIndex = j;
pLegendClass.Symbol = pColorRampSymbol as ISymbol;
}
(m_RasterStretchColorRampRenderer as IRasterRenderer).Update();
至此问题解决了。下面画个图,说明一下ColorRampSymbol与图例的关系:
简单说来,就是色彩拉伸后的图例实际上是一个由三个图例项组成的图例组,每个图例项都带有一个ColorRampSymbol,而借助这个ColorRampSymbol也可以指定整个图例组使用的色带,对于这个完整的色带,如何让图例项去自动对应呢,就是靠ColorRampSymbol的LengendClassIndex属性来完成的。