C#地理信息系统底层开发教程笔记(2)--类库完善与创建要素

该文详细介绍了GIS系统中空间对象体系的重构,包括创建GISFeature类以统一空间和属性特征,以及重构MyGIS类以实现点、线、面等空间对象。文章通过代码展示了GISPoint、GISLine、GISPolygon类的定义,以及GISAttribute类用于存储属性信息。此外,还讨论了如何在GISFeature类中添加方法以描绘几何形状和属性信息。整个设计遵循面向对象编程的原则,利用继承和抽象类提高代码复用性和扩展性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

        矢量数据模型使用点、线和多边形的几何对象表示空间要素。但不管是何种要素,它们都具有空间特征和属性特征。其中空间特征包含要素的几何特征(点、线、多边形等),描述几何特征相关的属性(中心坐标、范围、长度、面积等),以及要素之间的空间关系等。属性特征用于描述要素的非空间特征。
        第一章实现的迷你GIS显然太简单了,所以在本章需要做一些基础性的工作,重新设计和组织一下相关类的定义。通过要素类的定义将它们联系和统一起来。


一、空间对象体系

        一个GIS数据库记录的空间对象,尤其是代表具体空间地物和空间对象的实体:点(GISPoint)、线(GISLine)、多边形(GISPolygon),通常包括空间信息和属性信息(非空间信息)。
        为此,先定义一个这样的对象类,称为GISFeature,Feature的意思是特征,也可以称为要素。这个类用于定义不同要素的空间特征信息和属性特征信息。

1.建立一个新的项目

  1. 打开VS,添加一个新的项目。
  2. 仍然是构建一个新的窗口项目,名称为Lesson_2,修改一下文件路径在这里插入图片描述
    在这里插入图片描述
  3. Lesson_2创建成功后,我们需要对MyGIS这一类库进行重构和修改,即BasicClasses.cs这一文件,复制到Lesson_2的文件夹中来。在这里插入图片描述

2.添加Feature类

  1. 解决方案资源管理器中打开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的实例,这两个类分别指代一个对象(要素)的空间信息属性信息

  1. 类GISAtribute比较简单,它的定义如下。
    class GISAttribute
    {
        // 用特殊的数组存储属性,可存储不同类型的属性作为元素,并且长度可变
        public ArrayList values = new ArrayList();
    }

        类GISAttribute目前仅有一个成员函数,是一个特殊的数组ArrayList, 由C#自带类库提供,它的长度可变,而且数组中每个元素的类型可以不同,用于存储一个对象的不同类型的属性非常合适。如果ArrayList下面出现了红色的波浪线,那么说明用于定义它的类库没有在using中给出,可以利用第1章的方法快速把这个类库引用进来。

  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中给出,而需要在继承它的各个子类中被实现。

  1. 类GISExtent的代码定义如下。
    // 空间范围类
    class GISExtent
    {
        // 矩形范围左下角坐标与右上角坐标的属性,类型为GISVertex,用于确定矩形范围
        public GISVertex bottomleft;
        public GISVertex upright;
        
        // 构造函数方法,用于给属性赋值
        public GISExtent(GISVertex _bottomleft, GISVertex _upright)
        {
            bottomleft = _bottomleft;
            upright = _upright;
        }
    }

在这里插入图片描述
        此类的作用就是定义两个属性,类型为GISVertex,表示此矩形的左下角和右上角坐标,用于描述一个矩形的范围,与描述空间对象的范围具有关系。

二、重构MyGIS类

  1. 基于父类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

  1. 删除资源管理器中的Form1.cs
  2. 将Lesson_1文件夹下的Form1.cs相关三个文件复制粘贴到Lesson_2文件夹下

在这里插入图片描述

在这里插入图片描述

  1. 修改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已经具备了可扩充的能力.而且也具有了较完善的框架。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值