实例六-让Micaps支持新数据类型-BLN地图数据
Micaps本身支持几十类数据,如果将所有类型数据的读写显示都模块都放在一起,那将带来如下问题:
- 代码量太大,代码维护成问题;
- Micaps以后可能还需要支持更多的数据格式,程序的升级扩充问题;
- 各种数据支持代码也不能仅靠Micaps开发团队核心人员编写,团队开发问题;
- 最后,各省还想直接显示他们自己的数据,二次开发问题;
Micaps3.2的策略是利用SharpDevelop的插件框架,将各种数据支持功能都以插件方式提供,一种数据格式一个插件。大家根据Micaps预定义好规则,独立开发插件,扩充性和团队开发问题都得到了解决。二次开发中也可以使用同样方法开发插件让Micaps支持特定的数据格式。
要实现这个功能,插件的代码里首先要解决两个问题:
- 数据识别问题。当用户打开一个文件(链接、字符串等)后,如何让Micaps识别出数据类型,然后根据数据类型来决定怎么读文件、怎么绘图。
对这个问题,Sharpdevelop已经提供了框架,使用一种叫做DisplayBinding(显示绑定)的插件架构。每个显示绑定插件里首先要实现一个对文件类型的识别函数,Sharpdevelop将系统中所有显示绑定插件排成一个列表,如果自己能识别,则打开显示,不能识别则交个下一个数据支持插件处理。
- 数据显示问题。读出了数据,如果是要填到地图上,如何与Micaps地图交互?
要将数据显示在地图上,那必然要和Micaps的地图交互。Micaps已提供大量可供调用类,包括图层、绘制函数等,这也是Micaps二次开发的难点,这些内容可查阅《Micaps3.2二次开发手册》。
这一节,我们编写插件为Micaps增加surfer的bln格式地图数据支持功能。
1、Addin文件:
<Pathname="/Workspace/DisplayBindings"><DisplayBinding id="SurferBln"type="Layer"class="SurferBln.SurferBlnDataBindings"/></Path>
显示绑定的扩展点路径为"/Workspace/DisplayBindings";type="Layer"是Micaps新增的一种类型,它表示该数据将以地图图层方式显示;在显示绑定段里指定绑定的类名为"SurferBln.SurferBlnDataBindings",这个类将完成对数据类型的判断、数据显示等动作。
2、代码:
代码中主要是实现Addin文件里指定的SurferBln.SurferBlnDataBindings,该类必须实现接口ILayerDisplayBinding:
public class SurferBlnDataBindings : ILayerDisplayBinding{#region ILayerDisplayBinding 成员/// <summary>/// 判读传入的文件名是否能被本插件打开/// </summary>/// <param name="filename">文件名</param>/// <returns>可以打开:true,不能打开:false</returns>public bool CanCreateForFile(string filename){//本插件能打开后缀为bln的文件return Path.GetExtension(filename).ToLower() == ".bln";}/// <summary>/// 创建bln图层并加入地图/// </summary>/// <param name="file"></param>/// <returns></returns>public ILayer CreateLayerForFile(OpenedFile file){//取得当前视图,转换为Micaps地图视图var mainRenderView = WorkspaceSingleton.Workspace.ActiveViewContent as IMicapsMainView;//如果当前视图是地图,则加载图层if (mainRenderView != null){//创建bln图层BlnChart Chart = new BlnChart();//用它创建Micaps图层绑定对象MicapsDataLayerWrapper layer = new MicapsDataLayerWrapper(Chart);//给图层的地图对象赋值Chart.Map = mainRenderView.Map;//加载数据layer.Load(file, file.OpenRead());file.CloseIfAllViewsClosed();layer.LayerName = Chart.Description();return layer;}return null; } #endregion}
ILayerDisplayBinding接口包含两个函数:
- bool CanCreateForFile(string filename);用来判断该插件能打开的文件。
- ILayer CreateLayerForFile(OpenedFile file);打开文件,创建图层。
上面的代码里,BlnChart是我们新建的一个类,用来读取和显示bln文件。该类从C_ChartBase类继承,主要实现以下成员:
- string ReadFile(string filename):读取解析bln文件。
- void Draw(GLGraphics g):绘图。
<pre name="code" class="csharp">using System;
using System.Collections.Generic;
using System.Text;
using nmc.micaps3;
using System.Drawing;
using System.IO;namespace SurferBln
{class BlnChart : C_ChartBase{private BlnProperty _Property = new BlnProperty();public override void AddLayer(System.Collections.ArrayList list){LayerZOrder layer_order = new LayerZOrder();layer_order.obj = this;layer_order.zOrder = 31;list.Add(layer_order);}string _Description = "Surfer Bln格式数据";public override string Description(){return _Description;}public override void Draw(GLGraphics g){float x, y;List<PointF> pts;foreach (MapModel mo in MapObjects)//判断地图元素类型,仅显示线状数据if (mo.ObjectType == ShapeType.PolyLine){pts = new List<PointF>();foreach (PointF ptf in mo.Points){map.MemTY(ptf.X, ptf.Y, out x, out y);PointF xy = new PointF(x,y);pts.Add(xy);}g.DrawLines(_Property.LineColor, pts.ToArray());}}//map.MemTY(107, 33, out x, out y);//g.DrawEllipse(_Property.LineColor, x, y, 300, 300);}public override object GetPropertyInstance(){return _Property;}public override DateTime NextDateTime(){throw new NotImplementedException();}public override DateTime PreviousDateTime(){throw new NotImplementedException();}public override string ReadDB(object parameter){throw new NotImplementedException();}protected IList<MapModel> MapObjects = new List<MapModel>();public override string ReadFile(string filename){using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)){using (StreamReader ws = new StreamReader(fs, Encoding.Default)){while (!ws.EndOfStream){//数据行string line = "";//读取地图元素头,并跳过空行do{line = ws.ReadLine();}while (string.IsNullOrEmpty(line));//分隔数据行string[] items = line.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);//点数int n = Convert.ToInt16(items[0]);//地图元素MapModel mo = new MapModel();//地图元素头为两个if (items.Length > 1){int t = Convert.ToInt16(items[1]);if (t == 1){//线mo.ObjectType = ShapeType.PolyLine;}else{//面mo.ObjectType = ShapeType.Polygon;}}else{//线mo.ObjectType = ShapeType.PolyLine;}//地图元素点数mo.Points = new System.Drawing.PointF[n];//读取经纬度for (int i = 0; i < n; i++){PointF ptf = new PointF();line = "";//读入一行,并跳过空行do{line = ws.ReadLine();}while (string.IsNullOrEmpty(line));//分隔数据行items = line.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);//有两项时if (items.Length == 2){//经纬度ptf.X = Convert.ToSingle(items[0]);ptf.Y = Convert.ToSingle(items[1]);mo.Points[i] = ptf;}}//加入到列表MapObjects.Add(mo);}}fs.Close();}_Description = Path.GetFileName(filename);return "Success";}public override string ReadXmlFile(string filename){throw new NotImplementedException();}protected static int z_order = 31;public override int Z_Order{get{return z_order;}set{z_order = value;}}}/// <summary>/// 地图数据实体类/// </summary>public class MapModel //: DrawObject{/// <summary>/// 构造函数/// </summary>public MapModel(){}private ShapeType _ObjectType = 0;/// <summary>/// 对象类型/// </summary>public ShapeType ObjectType{get { return _ObjectType; }set { _ObjectType = value; }}private string _Id;/// <summary>/// 对象ID/// </summary>public string Id{get { return _Id; }set { _Id = value; }}private RectangleF _Rect;/// <summary>/// 对象最大矩形范围/// </summary> public RectangleF Rect{get{return _Rect;}set { _Rect = value; }}private PointF[] _Points;/// <summary>/// 对象点数组/// </summary>public PointF[] Points{get { return _Points; }set { _Points = value; }}}/// <summary>/// 地图元素类型/// </summary>public enum ShapeType : int{/// <summary>/// 空/// </summary>Null = 0,/// <summary>/// 点/// </summary>Point = 1,/// <summary>/// 线/// </summary>PolyLine = 3,/// <summary>/// 面/// </summary>Polygon = 5,};
}
总结一下,显示自定义格式数据时,Addin文件从扩展点路径"/Workspace/DisplayBindings"扩展,指定数据绑定类,代码中,实现数据绑定类接口IlayerDisplayBinding的两个方法,一个方法判断我们的插件是否能识别打开的文件,当文件能打开时,将调用另一个方法打开文件建立新图层,另外,需要新建一个继承C_ChartBase的类,它实现读取文件和绘图功能。
下图为打开bln地图的效果,为了显示更清楚,我隐去了Micaps自带的基础地图:
海南的bln县界与Micaps叠加:
其实,Micaps3.2完全没必要自己再定义一套图层显示绑定,直接使用SharpDevelop的IdisplayBinding接口就行了,甚至写一个适配器直接使用Micaps3.1的原有插件。