在ArcMap的专题图制作中,我们对Classification并不陌生(如图)。这主要是对应于ClassBreak类型的地图渲染而言的。它可以用来动态设置分类的等级数目,而且可以使用鼠标对代表Break数值大小的蓝色线进行拖动,以调整分级值的大小,甚至可以显示某区间内样本值的分布状况。
AO中没有提供现成的接口用来实现该工具。但可以使用三种方式,实现具有简单分类和动态调节值大小的工具。
第一种思路:修改用于绘图的开源控件,例如ZedGraph等,本来也想按照这个方法实现,但是有点懒的看别人的代码了:(
第二种思路:在MapControl上,添加LineElement,用LineElement代表制的大小,然后用IDisplayFeedback接口完成Element的移动,这样实现的难度最小,但是并不灵活,因为,假如开发工作并没有采用ArcGIS Engine的话,我们总不能为了做出这么个工具,就花上十多万的银子吧?
第三种思路:适用.Net的GDI+直接在窗体上绘制出来。这样的实现方式最灵活,不受所采用的组件的影响。
关键代码:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace HAPPY2007
{
/// <summary>
/// frmGetManualValue 的摘要说明。
/// </summary>
///
//定义两个代理,用于调用HappyMap中的分级渲染和添加图例功能
public delegate ArrayList _deleClassBreakDrawing2(string str,ArrayList arrValues,out ArrayList arrSymbol,out ArrayList arrText);
public delegate void _deleAddLegendClassBreak(ArrayList arrText,ArrayList arrSymbol);
public class frmGetManualValue : System.Windows.Forms.Form
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
public _deleClassBreakDrawing2 _deleCBD;
public _deleAddLegendClassBreak _deleALCB;
private System.ComponentModel.Container components = null;
//首先获得已有的arrValues的最大值和最小值,这是把x坐标值转化为arrValue的必须条件
double dblStartValue;
double dblEndValue;
//横线x坐标的起点
private int intStartX = 30;
//横线x坐标的终点
private int intEndX = 320;
//所有竖线y坐标的起点
private int intStartY = 40;
//所有竖线y坐标的终点
private int intEndY = 230;
//判断鼠标是否按下
private bool blnIsMouseDown = false;
private Graphics dc;
//用于接收传进来的ArrayList变量,该变量内放置的是等级的break值,包括起点值和终点值
private ArrayList arrValues;
//用于保存各条竖线的x坐标值,该值与arrValues里的制具有一定的换算机制
private int[] intXs;
//用于标记渲染哪个字段(面积、缴租、单位租金)
public string strFieldName;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.ComboBox comboBox1;
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
//用于表示选中的线的x值在intXs中的索引位置,初始值为-1
private int intIndex = -1;
public static frmGetManualValue frm;
public static frmGetManualValue CreateForm(ArrayList arr)
{
if(frm==null)
frm = new frmGetManualValue(arr);
return frm;
}
private frmGetManualValue()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint,true);
this.SetStyle(ControlStyles.DoubleBuffer,true);
this.SetStyle(ControlStyles.UserPaint,true);
//必须初始化dc
this.dc = this.CreateGraphics();
//必须先show出来,绘制代码才起作用
this.Show();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}
//重载构造函数,因为往往会传进来一个Values用来初始化
private frmGetManualValue(ArrayList arr)
{
this.InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint,true);
this.SetStyle(ControlStyles.DoubleBuffer,true);
this.SetStyle(ControlStyles.UserPaint,true);
//初始化dc
this.dc = this.CreateGraphics();
this.Show();
this.arrValues = arr;
//利用arr对intXs的个数进行初始化
this.intXs = new int[arr.Count];
this.dblStartValue = Convert.ToDouble(arr[0]);
this.dblEndValue = Convert.ToDouble(arr[arr.Count-1]);
//初始化intXs
this.ConvertToXs(arr);
}
//用于绘制图形的方法
private void Draw(Graphics dc)
{
Pen PenAnixes = new Pen(Color.Blue,2);
Pen PenValue = new Pen(Color.Red,0);
Brush textBrush = new SolidBrush(Color.Black);
Font fnt = new Font("Times New Roman",8);
Point p1 = new Point(intStartX,intEndY);
Point p2 = new Point(intEndX,intEndY);
dc.DrawLine(PenAnixes,p1,p2);
for(int i=0;i<this.intXs.Length;i++)
{
p1 = new Point(this.intXs[i],intEndY);
p2 = new Point(this.intXs[i],intStartY);
if(i==0||i==this.intXs.Length-1)
{
dc.DrawLine(PenAnixes,p1,p2);
}
else
{
dc.DrawLine(PenValue,p1,p2);
}
}
// dc.RotateTransform(90);
for(int i=0;i<this.intXs.Length;i++)
{
// this.dc.RotateTransform(90);
dc.DrawString(Convert.ToInt32(this.arrValues[i]).ToString(),fnt,textBrush,new RectangleF(this.intXs[i],this.intStartY,40,15));
}
}
//用于intXs与arrValues之间的转换。即竖线的x坐标值与实际代表制具有一定的关系
private void ConvertToXs(ArrayList arrList)
{
if(this.intXs!=null)
{
for(int i=0;i<arrList.Count;i++)
{
double dbl = (Convert.ToDouble(arrList[i])-Convert.ToDouble(arrList[0]))*(this.intEndX-this.intStartX)/(Convert.ToDouble(arrList[arrList.Count-1])-Convert.ToDouble(arrList[0])) + this.intStartX;
intXs[i] = Convert.ToInt32(dbl);
}
}
else
{
MessageBox.Show("请首先初始化intXs数组!");
}
}
//用于intXs与arrValues之间的转换。即竖线的x坐标值与实际代表制具有一定的关系
private void ConvertToValues(int[] xs)
{
if(this.arrValues!=null)
{
//首先获得已有的arrValues的最大值和最小值,这是把x坐标值转化为arrValue的必须条件
this.dblStartValue = Convert.ToDouble(this.arrValues[0]);
this.dblEndValue = Convert.ToDouble(this.arrValues[this.arrValues.Count-1]);
for(int i = 0;i<xs.Length;i++)
{
this.arrValues[i] = (xs[i]-this.intStartX)*(dblEndValue-dblStartValue)/(this.intEndX-this.intStartX)+dblStartValue;
}
}
else
{
MessageBox.Show("请首先初始化arrValues!");
}
}
//重写OnPaint方法
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
//调用绘制方法进行重绘
this.Draw(e.Graphics);
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.comboBox1 = new System.Windows.Forms.ComboBox();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.button2 = new System.Windows.Forms.Button();
this.button1 = new System.Windows.Forms.Button();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.SuspendLayout();
//
// groupBox1
//
this.groupBox1.Controls.Add(this.comboBox1);
this.groupBox1.Location = new System.Drawing.Point(352, 32);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(96, 80);
this.groupBox1.TabIndex = 0;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "分级数目";
//
// comboBox1
//
this.comboBox1.Items.AddRange(new object[] {
"3",
"4",
"5",
"6"});
this.comboBox1.Location = new System.Drawing.Point(8, 32);
this.comboBox1.Name = "comboBox1";
this.comboBox1.Size = new System.Drawing.Size(72, 20);
this.comboBox1.TabIndex = 0;
this.comboBox1.Text = "4";
this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
//
// groupBox2
//
this.groupBox2.Controls.Add(this.button2);
this.groupBox2.Controls.Add(this.button1);
this.groupBox2.Location = new System.Drawing.Point(352, 128);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(96, 104);
this.groupBox2.TabIndex = 0;
this.groupBox2.TabStop = false;
//
// button2
//
this.button2.Location = new System.Drawing.Point(16, 64);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(64, 23);
this.button2.TabIndex = 1;
this.button2.Text = "取消";
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// button1
//
this.button1.Location = new System.Drawing.Point(16, 24);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(64, 23);
this.button1.TabIndex = 0;
this.button1.Text = "确定";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// frmGetManualValue
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.BackColor = System.Drawing.SystemColors.HighlightText;
this.ClientSize = new System.Drawing.Size(464, 253);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.groupBox2);
this.MaximizeBox = false;
this.Name = "frmGetManualValue";
this.Text = "动态分级";
this.TopMost = true;
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.frmGetManualValue_MouseDown);
this.Closing += new System.ComponentModel.CancelEventHandler(this.frmGetManualValue_Closing);
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.frmGetManualValue_MouseUp);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.frmGetManualValue_MouseMove);
this.groupBox1.ResumeLayout(false);
this.groupBox2.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private void frmGetManualValue_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if(this.blnIsMouseDown)
{
if(e.X>intXs[this.intIndex-1]&&e.X<intXs[this.intIndex+1])
{
this.intXs[this.intIndex] = e.X;
this.Draw(this.dc);
this.ConvertToValues(intXs);
//实施刷新,为了防止晃动,已经在窗体的构造函数中加入缓存函数.
this.Refresh();
}
}
}
private void frmGetManualValue_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
//i从1开始到Length-1,确保最小值和最大值不能移动!
for(int i=1;i<intXs.Length-1;i++)
{
//缓冲区为5
if(e.X>intXs[i]-5&&e.X<intXs[i]+5)
{
this.Cursor = Cursors.SizeWE;
this.blnIsMouseDown = true;
this.intIndex = i;
return;
}
}
}
private void frmGetManualValue_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
//还原所有相关属性
this.Cursor = Cursors.Default;
this.blnIsMouseDown = false;
this.intIndex = -1;
}
private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
//首先删除已有的所有Labels
// this.DeleteLabels();
//classNum表示分类的级别
int classNum = Convert.ToInt32(this.comboBox1.SelectedItem);
//num表示数组内元素的个数,总是比分类级别大1
int num = classNum+1;
//range为arrValues的极差
int range = Convert.ToInt32(this.arrValues[this.arrValues.Count-1])-Convert.ToInt32(this.arrValues[0]);
//重新定义arrValues,和intXs、lblVs的个数
this.arrValues = new ArrayList();
this.intXs = new int[num];
// this.lblVs = new Label[num];
//定义arrValues的值,从0,开始,逐渐加上每个级别的大小,即平均分级
for(int i=0;i<num;i++)
{
//由于存在误差,为了不显示误差,第一和最后一个值强行设定
if(i==0)
{
arrValues.Add(0);
}
else if(i==num-1)
{
this.arrValues.Add(this.dblEndValue);
}
else
{
arrValues.Add(Convert.ToDouble(arrValues[i-1])+range/classNum);
}
}
//将arrValues里的值转化为x坐标值得
this.ConvertToXs(this.arrValues);
//绘制图形并刷新,一定要刷新
this.Draw(this.dc);
this.Refresh();
}
private void frmGetManualValue_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
frm = null;
}
private void button2_Click(object sender, System.EventArgs e)
{
frm.Close();
}
private void button1_Click(object sender, System.EventArgs e)
{
this.arrValues.RemoveAt(0);
ArrayList arrText;
ArrayList arrSymbol;
this._deleCBD(this.strFieldName,arrValues,out arrSymbol,out arrText);
this._deleALCB(arrText,arrSymbol);
}
}
}
效果: