第1篇. GIS里的图形是咋画到界面上的?

手写地理信息组件系列 第1篇
空间对象的表示和简单绘制
难度指数:★☆☆☆☆

各位看官老爷好!从此一篇起,我将会陆续更新“手写地理信息组件”系列。目的在于和广大GIS爱好者和从业者,讨论关于GIS底层的相关内容。力图用一种轻松的氛围,和最纯粹的代码形式表现GIS软件的底层逻辑。让地理信息从业者不再囿于商业软件的二次开发,更理性的看待商软。愿与诸共同进步,懂点儿底层,不做调包侠。

开发环境介绍

  • 实现语言:C#
  • 开发工具:VisualStudio,版本不限。

C#原生对窗口图形显示支持较好,虽然界面效果十分朴素,但是使用简单,开发难度小,对于图形开发,只要掌握图形理论知识就能很快上手。做到不纠结于具体语言,让GIS相关知识真正沉淀下来。

空间图形的构成

我们知道,不论是一维二维三维空间要素,都可以用点坐标来描述。线可以由两点表示,面可以由三个及以上的平面坐标点表示,空间体可以由四个及以上的空间坐标点表示。由此可见,空间实体可以由最基本的点来表示。

严格的讲,点实体(point)是一种抽象概念,不仅代指点状对象,而是可以被抽象为点的一切空间实体。如市区内的一座楼,楼上向下望的每个小脑袋,甚至这座楼所在的城市,都可以被抽象为一个点实体。
在这里插入图片描述不同于点实体的抽象概念,节点(vertex)是构成如线实体,面实体这样空间实体对象(feature)的实际单元。我们在地图上绘制一个三角形时,可以通过点取三个点坐标就能完成绘制,那么这三个点就可以称为这个面实体的三个节点。事实上,面图形实际存储的也就是这三个节点的有序坐标串。
在这里插入图片描述

空间图形的代码表示

明确了空间图形的组成,下面就以C#代码实现一个简单的地图程序。
首先建立一个C#工程,选择windows窗体程序。命名为MiniGIS,新建类文件GISEntities,在内构建空间实体最基本的属性。

  namespace MiniGIS
  {
    //节点
    class Vertex
    {
     double x;
     double y;
    }
    //点实体
    class Point
    {
      Vertex Location;
    }
    //线实体
    class Line
    {
      List<Vertex> Vertexs;
    }
    //面实体
    class Polygon
    {
      List<Vertex> Vertexs;
    }
  }

OK,空间实体的基本定义已经完成。下面来具体落实,图形怎么画的问题。

图形的绘制和查询

现以最基本的点绘制为例,实现图形和属性的绘制,鼠标点选图形的查询。
首先对实体类进行必要的属性和方法扩展,使其实现绘制方法。

  class Vertex
  {
    public double x;
    public double y;
  
    //扩展构造函数
    public Vertex(double x, double y)
    {
      this.x = x;
     this.y = y;
    }
  
    //扩展计算两点距离方法(点选查询时会用)
    public double Distance(Vertex another)
    {
      //勾股定理求两点距离
      return Math.Sqrt(Math.Pow(x - another.x, 2) + Math.Pow(y - another.y, 2));
    }
  }
 
  //点实体
  class Point
  {
    private Vertex location;
    private String attrbute;
  
    public Point(Vertex vertex, String attr)
    {
      this.location = vertex;
      this.attrbute = attr;
    }
  
    /// <summary>
    /// 绘制图形
    /// </summary>
    /// <param name="graphics">C#绘图类</param>
    public void DrawShape(System.Drawing.Graphics graphics)
    {
      //画笔为实心红色画笔,绘制5*5像素的点
      graphics.FillEllipse(new SolidBrush(Color.Red), new Rectangle((int)location.x, (int)location.y, 5, 5));
    }
    //绘制属性
    public void DrawAttrbute(Graphics graphics)
    {
      graphics.DrawString(attrbute, new Font("宋体", 20), new SolidBrush(Color.Blue), new PointF((int)location.x, (int)location.y));
    }
    //计算两点距离
    public double Distance(Vertex another)
    {
      return location.Distance(another);
    }
  }

设计一个简单界面,主要由三个TextBox和一个Button组成,三个输入框textBox_X、textBox_Y、TextBox_Attr分别代表待显示点的x坐标,y坐标和属性。
在这里插入图片描述现在双击button按钮注册点击事件,在按钮点击事件代码块中书写上述内容的调用逻辑。

  public partial class Form1 : Form
  {
    //已输入点集合
    List<Point> points = new List<Point>();
  
    public Form1()
    {
      InitializeComponent();
    }
  
    private void button1_Click(object sender, EventArgs e)
    {
      //获取输入
      double x = Convert.ToDouble(textBox_X.Text);
      double y = Convert.ToDouble(textBox_Y.Text);
      String attr = textBox_Attr.Text;
  
      //实例化节点
      Vertex vertex = new Vertex(x, y);
  
      //实例化点实体
      Point p = new Point(vertex, attr);
  
      //获取当前Form的Graphics(即窗口背景),并绘制
      Graphics graphics = this.CreateGraphics();
      p.DrawShape(graphics);
      p.DrawAttrbute(graphics);
      points.Add(p);
     }
  }

现在启动项目,输入两组坐标13,45和55.6,80及其属性,现在可以查看效果
在这里插入图片描述当然,绘制和显示并不足以构成一个基本的地图应用,下面加入图形点选的属性查询。

属性查询的实现思路是获取鼠标坐标,匹配图上与之最近的坐标点,如果与最近坐标点的距离小于设定的阈值(Threshold),可以认为该点已被选取,相应地查找该点的Attrbute。
实现鼠标点选,需要事先注册窗体的MouseClick事件。

  //距离容限值 10 像素
  const int ThresholdDistance = 10;
  
  private void Form1_MouseClick(object sender, MouseEventArgs e)
  {
    Vertex vertex = new Vertex(e.X, e.Y);
    double minDistance = Double.MaxValue;
    Point nearest = null;
  
    //筛选与鼠标点最近的坐标点
    foreach (Point p in points)
    {
      double distance = p.Distance(vertex);
      if (distance < minDistance)
      {
        nearest = p;
        minDistance = distance;
      }
    }
  
    //小于阈值,弹出属性
    if (nearest != null && nearest.Distance(vertex) < ThresholdDistance)
      MessageBox.Show(nearest.attrbute);
  }

下面运行看东西:
在这里插入图片描述点选查询已经实现。

OK,我们手写的第一个地图应用就完成了!功能非常纯粹,体积非常小巧(11k…)。虽然这个迷你GIS应用代码量少,逻辑也很简单,但是它暴露了节点(Vertex)这一重要,却在二次开发中并不常用的概念。
我们在产品或项目中更多的调用如Point,Polygon等Geometry层面的包装对象,因其方便的解析调用而更少的使用Vertex概念,因为其更靠近底层。

好了,这一篇的内容就是这些了。如果你自己有动手能力,可以自己手动做一下本篇这个“GIS小玩具”,或者尝试自己增加逻辑,甚至应用啊:),比如这个插入星星,查询星星什么的。。。
在这里插入图片描述当然,点击添加星星后可以注掉一行代码,不显示名称,点选之后再显示。这里为了动图好看就加上了。把星空做背景确实好看些,不过若是使用也确实蛮弱智的。。


看好关注,下期见!
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值