手写地理信息组件系列 第9篇
图层的引入
难度指数:★★☆☆☆
Review:
在之前几篇的内容中,空间实体-点线面和要素(Feature)都已经定义。此一篇将对两种概念进行更高一层级的概括,形成图层(Layer)。
地图的显示是由许多独立的图层构成,每个图层,用以描述地理上的一类空间事物,并加之一系列的地图整饰元素。
其本质是由一系列相同地理实体对象构成的集合,例如界址点图层,宗地图层,核心就是一系列点实体、面实体构成的集合。
图层的核心概念很简单,类比要素的组成就更好理解了。
要素:
Geometry + Attribute = Feature
图层:
Feature + Feature + ··· + n个同类型 Feature = Layer
如此,就可以梳理出Layer对象的类体系。其中空心箭头代表继承,双线箭头代表组合。
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为待绘制要素的属性字段名,如果指定,地图上将会绘制对应字段值。
在引入了图层对象以后,很多之前为了说明概念的蹩脚设计,就可以退出江湖了。在此之前,先整理下之前冗余的界面功能,砍掉测试控件部分,保留已实现了成型概念的功能。
清扫之后,现在集中精力做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做绘制,简洁了不少。
本篇完。
上一篇: 质心计算