前言
矢量数据模型使用点、线和多边形的几何对象表示空间要素。但不管是何种要素,它们都具有空间特征和属性特征。其中空间特征包含要素的几何特征(点、线、多边形等),描述几何特征相关的属性(中心坐标、范围、长度、面积等),以及要素之间的空间关系等。属性特征用于描述要素的非空间特征。
第一章实现的迷你GIS显然太简单了,所以在本章需要做一些基础性的工作,重新设计和组织一下相关类的定义。通过要素类的定义将它们联系和统一起来。
一、空间对象体系
一个GIS数据库记录的空间对象,尤其是代表具体空间地物和空间对象的实体:点(GISPoint)、线(GISLine)、多边形(GISPolygon),通常包括空间信息和属性信息(非空间信息)。
为此,先定义一个这样的对象类,称为GISFeature,Feature的意思是特征,也可以称为要素。这个类用于定义不同要素的空间特征信息和属性特征信息。
1.建立一个新的项目
- 打开VS,添加一个新的项目。
- 仍然是构建一个新的窗口项目,名称为Lesson_2,修改一下文件路径。


- Lesson_2创建成功后,我们需要对MyGIS这一类库进行重构和修改,即BasicClasses.cs这一文件,复制到Lesson_2的文件夹中来。

2.添加Feature类
- 解决方案资源管理器中打开BasicClasses.cs,在MyGIS类中添加一个新类Feature,其代码如下:
// 要素类
class GISFeature
{
public GISSpatial spatialpart; // 空间特征属性,类型为GISSpatial
public GISAttribute attributepart; //属性特征属性,类型为GISAttribute
// 构造函数方法,为了给属性赋值
public GISFeature(GISSpatial spatial, GISAttribute attribute)
{
spatialpart = spatial;
attributepart = attribute;
}
}
GISFeature类包含一个用于赋值的构造函数,构造函数的名字与类名同名。以及两个成员spatialpart和attributepart,它们在接下来会被定义。它们分别是类GiSSpatial和GIsAtribute的实例,这两个类分别指代一个对象(要素)的空间信息和属性信息
- 类GISAtribute比较简单,它的定义如下。
class GISAttribute
{
// 用特殊的数组存储属性,可存储不同类型的属性作为元素,并且长度可变
public ArrayList values = new ArrayList();
}
类GISAttribute目前仅有一个成员函数,是一个特殊的数组ArrayList, 由C#自带类库提供,它的长度可变,而且数组中每个元素的类型可以不同,用于存储一个对象的不同类型的属性非常合适。如果ArrayList下面出现了红色的波浪线,那么说明用于定义它的类库没有在using中给出,可以利用第1章的方法快速把这个类库引用进来。
- 类GISSpatial的定义代码如下。
// 空间特征类
abstract class GISSpatial
{
// 中心坐标属性,与范围属性是空间对象(点、线、面)都具有的,因此作为父类用于子类的继承
// 实体中心坐标属性,类型为GISVertex,public表示子类可继承此属性,也可被其他类的实例(对象)所引用
public GISVertex centroid;
// 空间范围(最小外接矩形)属性,类型为GISExtent
public GISExtent extent;
// 绘画对象的方法,abstract表抽象方法,每种空间对象不一样,当被子类继承时具体实现
public abstract void draw(Graphics graphics);
}
类GISSpatial的作用是充当一个父类。
因为,针对不同的空间对象实体(点、线、面),它们的成员或方法可能不同,如用来画一个点对象的方法就不能用来画一个面。 所以,GISSpatial 应该是一个比较抽象的类,它不能直接用于声明一个实体对象,它应该是一个父类, 而由继承它的具体的子类来声明对象,为此在类定义前增加了abstract 关键词。
GisSpatial包括两个成员,即centroid和extent,分别表示这个空间实体的中心坐标和空间范围(最小外接矩形),显然,不管什么空间对象都应该具有上述两个特征。
其中,centroid就是GISVertex类的一一个实例,而空间范围extent是一个 新的类GISExtent的实例,稍后定义。这两个成员前面都分别加了public前缀,意味着,它们的值可以被这个类的子类继承及引用,也可以被外部对象引用。
draw 函数用来绘制各种空间实体,它有一个参数就是画图工具graphies,不同类型的空间实体肯定有不同的画法,因此它被定义成一一个抽象的方法,也就是在draw函数前面加了一个abtract 关键词,而其实现部分就不需 要在父类GISSpatial中给出,而需要在继承它的各个子类中被实现。
- 类GISExtent的代码定义如下。
// 空间范围类
class GISExtent
{
// 矩形范围左下角坐标与右上角坐标的属性,类型为GISVertex,用于确定矩形范围
public GISVertex bottomleft;
public GISVertex upright;
// 构造函数方法,用于给属性赋值
public GISExtent(GISVertex _bottomleft, GISVertex _upright)
{
bottomleft = _bottomleft;
upright = _upright;
}
}

此类的作用就是定义两个属性,类型为GISVertex,表示此矩形的左下角和右上角坐标,用于描述一个矩形的范围,与描述空间对象的范围具有关系。
二、重构MyGIS类
- 基于父类GISPatial重新定义三个对象实体类GISPoint、GISLine及GISPolygon,代码如下。
// 定义点类
class GISPoint:GISSpatial //继承空间特征类
{
// 构造函数
public GISPoint(GISVertex onevertex)
{
// 这两个属性在父类已经声明,所以无需声明
centroid = onevertex; //赋值坐标给centroid,表示对象的中心坐标
extent = new GISExtent(onevertex, onevertex); //点范围就是点,左上角和右小角坐标重合
}
// 继承父类画点方法
public override void draw(Graphics graphics)
{
// 具体实现
graphics.FillEllipse(new SolidBrush(Color.Red),
new Rectangle((int)(centroid.x) - 3, (int)(centroid.y) - 3, 6, 6));
}
// 调用距离计算方法
public double Distance(GISVertex anothervertex)
{
// 返回此点与传入的一个参数点之间的距离
return centroid.Distance(anothervertex);
}
}
// 定义线类
class GISLine:GISSpatial
{
List<GISVertex> AllVertexs;
//继承父类的绘画方法,用于画线
public override void draw(Graphics graphics)
{
}
}
// 定义面类
class GISPolygon:GISSpatial
{
List<GISVertex> AllVertexs;
//继承父类的绘画方法,用于画面
public override void draw(Graphics graphics)
{
}
}
与第1章的GISPoint类定义相比,这个新的类是继承自GISSpatial的。
原有的成员Location 实际上就等同于其父类的centoid,因此,这里就可以省略了。在类的构造函数中,主要工作就是为父类的centroid 和extent赋值,因为是点实体,所以赋值非常简单,而extent也是一一个点,即两个角点是重合的。
Distance 函数依然保留,而draw函数是实现父类中同名的抽象函数,因此,前面加了一个前缀override,其函数内容与之前的是一样的。
GISLine 及GISPolygon 两类内容很简单,这里仅是增加了一个来自父类的draw函数,目前还是空的,将在以后章节补充。
“父类子类”机制的价值,它有助于减少一些重复的代码,建立一些统一标准,让子类共享同样的类结构。
此外,发现由于属性信息由GISFeature的attributepart负责了,因此在GISSpatial及其
子类中不再涉及这方面内容。
要素的空间特在已基本构建完毕,接下来要做的便是完善要素的属性特征。
// 属性特征类
class GISAttribute
{
// 用特殊的数组存储属性,可存储不同类型的属性作为元素,并且长度可变
public ArrayList values = new ArrayList();
// 为可变数组增加属性元素的方法
public void AddValue(object o)
{
values.Add(o);
}
// object表示任意类型,此方法用于返回各种类型的数组元素
public object GetValue( int index)
{
return values[index];
}
// 描绘属性的方法
public void draw(Graphics graphics, GISVertex location, int index)
{
graphics.DrawString(values[index].ToString(), new Font("宋体", 20),
new SolidBrush(Color.Green), new PointF((int)(location.x), (int)(location.y)));
}
}
在完善了属性特征类后,我们需要在GISFeature类中添加一些相关的方法,当要素类实例化为要素对象时能够描绘其几何形状和属性信息。
// 要素类
class GISFeature
{
public GISSpatial spatialpart; // 空间特征属性,类型为GISSpatial
public GISAttribute attributepart; //属性特征属性,类型为GISAttribute
// 构造函数方法,为了给属性赋值
public GISFeature(GISSpatial spatial, GISAttribute attribute)
{
spatialpart = spatial;
attributepart = attribute;
}
// 此方法用于画空间信息和画属性信息
public void draw(Graphics graphics, bool DrawAttributeOrNot, int index)
{
// 调用空间特征的绘画方法
spatialpart.draw(graphics);
// 如果传入参数为True,则继续绘画属性信息
if (DrawAttributeOrNot)
{ attributepart.draw(graphics, spatialpart.centroid, index); }
}
// 此方法获得属性数组里对应索引的属性值
public object getAttribute(int index)
{
return attributepart.GetValue(index);
}
}
关系一览图

从图中可以看出,GISFeature 是一一个比较基本的类,它的成员里有空间类GisSpatial及属性类GlsAttribute的实例。CISSpatial 是一个抽象类,也是父类,它有三个子类,分别为GISLine、GISPolygon 及GISPoint。 GISVertex 及GISExtent是独立的类,用来构成其他类的成员。
BasicClasses全代码:
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
namespace MyGIS
{
// 要素类
class GISFeature
{
public GISSpatial spatialpart; // 空间特征属性,类型为GISSpatial
public GISAttribute attributepart; //属性特征属性,类型为GISAttribute
// 构造函数方法,为了给属性赋值
public GISFeature(GISSpatial spatial, GISAttribute attribute)
{
spatialpart = spatial;
attributepart = attribute;
}
// 此方法用于画空间信息和画属性信息
public void draw(Graphics graphics, bool DrawAttributeOrNot, int index)
{
// 调用空间特征的绘画方法
spatialpart.draw(graphics);
// 如果传入参数为True,则继续绘画属性信息
if (DrawAttributeOrNot)
{ attributepart.draw(graphics, spatialpart.centroid, index); }
}
// 此方法获得属性数组里对应索引的属性值
public object getAttribute(int index)
{
return attributepart.GetValue(index);
}
}
// 属性特征类
class GISAttribute
{
// 用特殊的数组存储属性,可存储不同类型的属性作为元素,并且长度可变
public ArrayList values = new ArrayList();
// 为可变数组增加属性元素的方法
public void AddValue(object o)
{
values.Add(o);
}
// object表示任意类型,此方法用于返回各种类型的数组元素
public object GetValue(int index)
{
return values[index];
}
public void draw(Graphics graphics, GISVertex location, int index)
{
graphics.DrawString(values[index].ToString(), new Font("宋体", 20),
new SolidBrush(Color.Green), new PointF((int)(location.x), (int)(location.y)));
}
}
// 空间特征类
abstract class GISSpatial
{
// 中心坐标属性,与范围属性是空间对象(点、线、面)都具有的,因此作为父类用于子类的继承
// 实体中心坐标属性,类型为GISVertex,public表示子类可继承此属性,也可被其他类的实例(对象)所引用
public GISVertex centroid;
// 空间范围(最小外接矩形)属性,类型为GISExtent
public GISExtent extent;
// 绘画对象的方法,abstract表抽象方法,每种空间对象不一样,当被子类继承时具体实现
public abstract void draw(Graphics graphics);
}
// 空间范围类
class GISExtent
{
// 矩形范围左下角坐标与右上角坐标的属性,类型为GISVertex,用于确定矩形范围
public GISVertex bottomleft;
public GISVertex upright;
// 构造函数方法,用于给属性赋值
public GISExtent(GISVertex _bottomleft, GISVertex _upright)
{
bottomleft = _bottomleft;
upright = _upright;
}
}
// 定义坐标类,属性为X,Y
class GISVertex
{
public double x;
public double y;
//赋值操作
public GISVertex(double _x, double _y)
{
x = _x;
y = _y;
}
// 计算距离方法
public double Distance(GISVertex anothervertex)
{
return System.Math.Sqrt((x - anothervertex.x) * (x - anothervertex.x)
+ (y - anothervertex.y) * (y - anothervertex.y));
}
}
// 定义点类
class GISPoint : GISSpatial //继承空间特征类
{
// 构造函数
public GISPoint(GISVertex onevertex)
{
// 这两个属性在父类已经声明,所以无需声明
centroid = onevertex; //赋值坐标给centroid,表示点的中心坐标
extent = new GISExtent(onevertex, onevertex); //点范围就是点,左上角和右小角坐标重合
}
// 继承父类画点方法
public override void draw(Graphics graphics)
{
// 具体实现
graphics.FillEllipse(new SolidBrush(Color.Red),
new Rectangle((int)(centroid.x) - 3, (int)(centroid.y) - 3, 6, 6));
}
// 调用距离计算方法
public double Distance(GISVertex anothervertex)
{
// 返回此点与传入的一个参数点之间的距离
return centroid.Distance(anothervertex);
}
}
// 定义线类
class GISLine : GISSpatial
{
List<GISVertex> AllVertexs;
//继承父类的绘画方法,用于画线
public override void draw(Graphics graphics)
{
}
}
// 定义面类
class GISPolygon : GISSpatial
{
List<GISVertex> AllVertexs;
//继承父类的绘画方法,用于画面
public override void draw(Graphics graphics)
{
}
}
}
三、重新实现迷你GIS
- 删除资源管理器中的Form1.cs
- 将Lesson_1文件夹下的Form1.cs相关三个文件复制粘贴到Lesson_2文件夹下


- 修改Form1.Designer.cs的命名空间为Lesson_2,以及修改Form1.cs的代码

Form1.cs的代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MyGIS; //引用MyGIS库
namespace Lesson_2
{
public partial class Form1 : Form
{
// 用于存储要素的数组
List<GISFeature> features = new List<GISFeature>();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//实例化一个要素
// 获取空间信息
//获取x.y,并转换为double型
double x = Convert.ToDouble(textBox1.Text);
double y = Convert.ToDouble(textBox2.Text);
// x,y作为参数实例化一个坐标类
GISVertex onevertex = new GISVertex(x, y);
// 将坐标onevertex对象作为参数,实例化一个点类
GISPoint onepoint = new GISPoint(onevertex);
// 获取属性信息
string attribute = textBox3.Text;
// 实例化一个属性类
GISAttribute oneattribute = new GISAttribute();
// 调用oneattribute对象的方法添加属性到属性数组中
oneattribute.AddValue(attribute);
// 实例化一个GISFaeture(要素),将空间特征和属性特征作为参数
GISFeature onefeature = new GISFeature(onepoint, oneattribute);
// 将这个要素添加到要素数组中
features.Add(onefeature);
// 调用要素对象的方法,将这个要素和属性画出来
Graphics graphics = this.CreateGraphics();
onefeature.draw(graphics, true, 0);
}
private void Form1_MouseClick_1(object sender, MouseEventArgs e)
{
// 记录点击位置的XY坐标,并作为类实例参数赋予一个对象
GISVertex onevertex = new GISVertex((double)e.X, (double)e.Y);
// 定义一个最小距离,为后面作为判断
double mindistance = Double.MaxValue;
int findid = -1;
for (int i = 0; i < features.Count; i++)
{
double distance = features[i].spatialpart.centroid.Distance(onevertex);
if (distance < mindistance)
{
mindistance = distance;
findid = i;
}
}
if (mindistance > 5 || findid == -1)
{
MessageBox.Show("没有点实体或者鼠标点击位置不准确");
}
else
{
// 得到的属性属于object类,需要转化为string类型
MessageBox.Show(features[findid].getAttribute(0).ToString());
}
}
}
}
若出现问题则根据第一章一样删除相应的事件代码


总结
当类重构后,通过Form1.cs进行调用,为便于解释,在代码中添加了相应的注释。第一个事件处理函数首先分别获得用于构造一个GISFeature实例的空间和属性信息,然后建立这个GISFeature 实例,并把它添加到features数组中,进而把它画出来。其中,给每个GISFeature仅增加了1种属性信息。
本章给出了很多类的定义,并且还使用了面向对象编程思想中的继承概念,涉及抽象类、父类、子类等。虽然,最终的演示程序没有出现更多的功能,但是类库MyGIS已经具备了可扩充的能力.而且也具有了较完善的框架。
该文详细介绍了GIS系统中空间对象体系的重构,包括创建GISFeature类以统一空间和属性特征,以及重构MyGIS类以实现点、线、面等空间对象。文章通过代码展示了GISPoint、GISLine、GISPolygon类的定义,以及GISAttribute类用于存储属性信息。此外,还讨论了如何在GISFeature类中添加方法以描绘几何形状和属性信息。整个设计遵循面向对象编程的原则,利用继承和抽象类提高代码复用性和扩展性。

被折叠的 条评论
为什么被折叠?



