GIS图层的本质

手写地理信息组件系列 第9篇
图层的引入
难度指数:★★☆☆☆

Review:

在之前几篇的内容中,空间实体-点线面和要素(Feature)都已经定义。此一篇将对两种概念进行更高一层级的概括,形成图层(Layer)

图层的引入

地图的显示是由许多独立的图层构成,每个图层,用以描述地理上的一类空间事物,并加之一系列的地图整饰元素。

其本质是由一系列相同地理实体对象构成的集合,例如界址点图层,宗地图层,核心就是一系列点实体、面实体构成的集合。

 

GIS图层的本质_第1张图片

 

更完善的图层类体系

图层的核心概念很简单,类比要素的组成就更好理解了。


要素:

Geometry + Attribute = Feature

图层:

Feature + Feature + ··· + n个同类型 Feature = Layer

如此,就可以梳理出Layer对象的类体系。其中空心箭头代表继承,双线箭头代表组合。

GIS图层的本质_第2张图片

 

Layer的必要成员是feature列表,而Extent是描述要素空间范围的辅助变量。

 public class Layer
 {
   string layerName;
   Extent extent;
 
   //图层存储的空间实体类型
   ShapeType shapeType;
 
   List features = new List();

   public List Features
   {
     get { return features; }
   }
 
   public string LayerName
   {
     get { return layerName; }
     set { layerName = value; }
   }
 
   public Extent Extent
   {
     get { return extent; }
   }

   public ShapeType ShapeType
   {
     get { return shapeType; }
   }
 
   public Layer(String layerName, Extent extent, ShapeType shapeType)
   {
     this.layerName = layerName;
     this.extent = extent;
     this.shapeType = shapeType;
   }
 
   public void Draw(Graphics graphics, Map map, string fieldName)
   {
     foreach (var f in Features)
     {
       f.Draw(graphics, map, fieldName);
     }
   }
}

类的定义很简单,构造函数、三个属性的访问器和一个绘图方法Draw。

其中绘图方法的fieldName为待绘制要素的属性字段名,如果指定,地图上将会绘制对应字段值。

引入Layer之后

在引入了图层对象以后,很多之前为了说明概念的蹩脚设计,就可以退出江湖了。在此之前,先整理下之前冗余的界面功能,砍掉测试控件部分,保留已实现了成型概念的功能。

GIS图层的本质_第3张图片


清扫之后,现在集中精力做shp文件读取为图层的改造:

 public Layer ReadShapeFile(String filePath)
 {
   FileStream fs = File.Open(filePath, FileMode.Open);
   BinaryReader reader = new BinaryReader(fs);
 
   //读取文件头
   ShapeFileHeader shpHeader = new ShapeFileHeader(reader);
   extent = shpHeader.Extent;
 
   String name = Path.GetFileNameWithoutExtension(filePath);

   Layer layer = new Layer(name, extent, shpHeader.ShapeType);
 
   while (reader.PeekChar() != -1)
   {
     //读取记录头
     ShapeRecordHeader recHeader = new ShapeRecordHeader(reader);

     ShapeType type = shpHeader.ShapeType;
     switch (type)
     {
       case ShapeType.NullShape:
         break;
       case ShapeType.Point:
         {
           Geometry geom = ReadPoint(reader);
           Feature f = new Feature(geom, new Attribute());
           layer.Features.Add(f);
           break;
         }
       case ShapeType.Line:
         {
           List lineList =
             ReadLine(reader).Cast().ToList();
 
           foreach (var g in lineList)
           {
             Feature f = new Feature(g, new Attribute());
             layer.Features.Add(f);
           }
           break;
         }
       case ShapeType.Polygon:
         {
           List polygonList =
             ReadPolygon(reader).Cast().ToList();

           foreach (var g in polygonList)
           {
             Feature f = new Feature(g, new Attribute());
             layer.Features.Add(f);
           }
           break;
         }
       default:
         break;
     }
   }
   reader.Close();
   fs.Close();
   return layer;
}

若留意过之前此方法的定义,你会发现这里的改动是很小的。只是每种Type的读取结果,最后交给图层的Features做存储


但是在交给要素列表之前,给的是一个空的Attribute。不过不要着急,空Attribute不会活过一集,下篇开始专攻shapefile的属性读取,这里留下口子。

最后的改造位于打开图形的按钮上,改动同样很小。

 private void btn_OpenShp_Click(object sender, EventArgs e)
 {
   OpenFileDialog ofd = new OpenFileDialog();
   ofd.Filter = "(*.shp)|*.shp";
   if (ofd.ShowDialog() == DialogResult.OK)
   {
     ShapeFile shp = new ShapeFile();
 
     Layer layer = shp.ReadShapeFile(ofd.FileName);

     if (mapExtent == null || mapExtent.Width < 1)
     {
       //以第一次加载地图的范围为基准
       mapExtent = shp.Extent;
       map.Update(mapExtent, this.ClientRectangle);
     }

     Graphics graphics = this.CreateGraphics();
     layer.Draw(graphics, map, "");
 
     graphics.Dispose();
   }
}

将之前读取到的Geometry列表以遍历的方式绘制,改为直接调取Layer.Draw做绘制,简洁了不少。

绘制图层

 

本篇完。

上一篇: 质心计算


你可能感兴趣的:(GIS图层的本质)