2023年2月

关于地理信息的开发有好多控件工具可以选择,GIS软件包括三家美国GIS开发商ESRI,Intergraph和MapInfo的软件产品,以及国产软件:MapGIS,GeoStar和Citystar,SuperMap等产品。另外还有一些边缘产品,是Web GIS的领域,不是纯粹的GIS产品,称之为地图应用平台可能较好,如Google公司的Google Maps、微软的Virtual Earth,国内的51地图,搜狗的地图等。

可能用的比较多的是ESRI了,他的Arc IMS或者ArcGIS Server市场占用率好像还比较高,产品系列也做的不错,目前好像版本是9.3;ESRI的产品我没有用过,不做评论,好像也是很不错的;比较喜欢的是MapInfo公司的产品,可能是很早就接触到的原因,如MapX,目前是5.X吧,还有就是他的MapXtreme,目前版本是MapXtreme 2008 (.net 和Java两个版本)。

这些GIS软件,提供了相似的功能集合,不同之处在于其具体的实现方式(如用户界面,操作流程),和操作效率(如速度、数据量)。这种相似性,也正说明了GIS技术的成熟。

什么是ArcIMS

GIS地图、数据和元数据的网络发布。ArcIMS是一个通过中心网络门户来发布GIS地图、数据和元数据的有效解决方案。使用ArcIMS构建的GIS网站允许任意数量的用户通过企业局域网或Internet进行访问。
ArcIMS使网站能够提供GIS数据、交互式地图、元数据目录以及特定的GIS应用。通常,ArcIMS用户通过他们的Web浏览器,借助ArcIMS 内含的HTML或Java应用程序来访问这些GIS服务。除此以外,ArcIMS服务还能够被更多的客户端访问,如ArcGIS Desktop、ArcGIS Engine应用、ArcReader、ArcPad、ArcGIS Server节点、MapObjects for Java应用以及各种使用HTTP和XML进行网络通讯的无线设备。

什么是ArcGIS Server
ArcGIS Server是一个用于构建集中管理、支持多用户的企业级GIS应用的平台。ArcGIS Server提供了丰富的GIS功能,例如地图、定位器和用在中央服务器应用中的软件对象。
开发者使用ArcGIS Server可以构建Web应用、Web服务、以及其它运行在标准的.NET和J2EE Web服务器上的企业应用,如EJB。ArcGIS Server也可以通过桌面应用以C/S(Client/Server)的模式访问。ArcGIS Server的管理由ArcGIS Desktop负责,后者可以通过局域网或Internet来访问ArcGIS Server。

MapInfo MapXtreme 2008

是MapInfo为了支持Microsoft公司的.NET框架,重新设计MapX和MapXtreme for Windows代码库体系结构的新产品。MapXtreme 2008是开发地图和可地图化应用程序的理想开发环境。它与Visual Studio .NET平台无缝结合,支持C#和ASP.NET。利用MapXtreme 2008,用户还能够开发丰富的AJAX Web应用,并且能够充分利用地图技术方面的最新成果,包括半透明层、曲线标记、功能增强的标注功能和抗锯齿等功能。此外,MapXtreme 2008还支持开放地理信息系统联盟(Open GIS Consortium,简称OGC,主要研究和建立开放式地理数据互操作规范并指明了实现该规范的技术手段。)的地理空间标准,加强了互操作性,扩大了用户访问的数据源。

MapInfo MapX

是低价高效、强大的ActiveX 组件,可以很方便地集成到使用标准可视化编程工具开发的商业应用中。MapInfo MapX 支持您将地图功能添加到任何应用中,提供了一种高度可视化的方法,来显示和分析基于位置的数据,从而更好地为客户服务,更好地进行商业决策,更有效地管理资产和运营,与VB、VC、PB、Delphi、.NET等应用开发平台无缝连接,可以很方便地将地图功能集成到各类商业应用中。MapInfo MapX可以说是单机版的GIS开发工具。

Google Maps

Google Maps 是 google 公司推出的一款网上地图工具,通过AJAX技术实现无刷新、可拖动、高性能的网页程序。提供了丰富的API,可以对Google Maps进行开发。
• 普通免费版
– 你的网站服务对用户完全免费
– 在地图上保留Google的标志
– 50000次/天 地址定位查询
– 开发的东西有创意
• 企业版
– 无限量地址定位查询
– 可以运行在收费网站
– 可以运行在防火墙后面或企业局域网
– 获取来自Google的技术支持

Virtual Earth

是微软公司Live服务中的一个地图服务,作为Live提出的服务观点,Virtual Earth提供了非常方便的一套接口,允许我们在Earth上面开发一些自己的应用。
Virtual Earth与微软的产品联系较为紧密,目前提供了Asp.net2.0的系列控件,比较方便做Web方面的开发。目前Virtual Earth和微软本身的SilverLight结合的很好,并且感觉使用.NET来开发更加方便,因为.NET本身就是微软的,集成的力量可见一斑。

本系列文章主要介绍MapX的一些使用资源和心得,在每个阶段做一次总结,与大家分享。

下面提供一些GIS的网络资源,给大家做资料参考

GIS帝国论坛
http://www.gisempire.com/bbs/index.asp

James MapInfo技术论坛
http://www.mygis.com.cn/forum/index.asp

GISVIP社区
http://www.97sky.com/bbs/
中国GIS资讯网—GIS频道
http://www.gissky.com/Gis/
GIS-Smart
http://www.cnblogs.com/fxlcoco
什么是WebGIS?
http://www.97sky.com/bbs/viewthread.php?tid=865&extra=page%3D1
GIS浩淼的天空
http://www.hmgis.cn/catalog.asp?cate=10
ArcGIS Server 体系结构
http://www.cnblogs.com/flyingis/archive/2007/07/17/821174.html
MapXtreme2005二次开发精华文章资料荟萃
http://blog.csdn.net/hornbill/archive/2007/01/26/1495138.aspx
MapX的系列文章
http://www.cnblogs.com/jetz/tag/mapx/
我要地图
http://www.51ditu.com/

下篇开始介绍C#进行MapX二次开发的技术细节。

MapX的主要技术特点
(1)、 以表(Table)的形式组织信息
每一个表都是一组MapInfo文件,这些文件组成了地图文件和数据库文件。为使用MapInfo,就需要有组成表的用户数据和地图文件。这些文件可以来自MapInfo或者由用户创建。用户要想在MapInfo中工作,就必须打开一个或多个表。
MapInfo通过表的形式将数据与地图有机地结合在一起。当用户在MapInfo中打开数据文件时,MapInfo将创建一个表。这个表至少由两个独立的文件组成,一个是包含数据结构的文件,另一个是包含原始数据的文件。一个典型的MapInfo表将主要由*.tab、*.dat、*.wks、*.dbf、*.xls、*.map、*.id、*.ind文件格式组成。
(2)、 图形对象
MapInfo内置的数据库管理系统是一种关系型数据库管理系统,也是用二维表组织数据。与其它关系型数据库不同的是表结构中除可包含常用类型的属性列外,还引入一个图形对象列(OBJ列),用于存储图形对象(如线、区域等)。MapInfo提供许多图形对象的操作接口,利用这些接口可以生成和处理所需要的各种图形。
(3)、 地图图层化
MapInfo是按图层组织计算机地图的。也就是说,将一幅计算机地图加工成多个层层叠加的透明层,这个透明层就称为图层。每个图层包含了整个地图的一个不同方面。例如,第一个图层包含省边界,第二个图层表示省府的符号,第三个图层由标注文本组成,把它们层层叠加就形成一幅完整的地图。在创建每一个图层时,都要为其建立一张表,MapInfo就是通过这种方式使表与地图之间建立了联系。
也就是说,MapInfo是以表的形式来进行管理的,每个表一般包含两部份:地图部分和数据库(属性)部份。
(4)、 专题地图
提供多种数据可视化的专题地图,能将数据库中的信息进行直观的可视化分析。使用专题渲染在地图上显示数据时,可以清楚地看出在数据记录中难以发现的模式或趋势,为用户的决策提供依据。专题地图包括范围值、点密度、柱状图、等级符号、饼图和独立值六种形式。
(5)、 内置ODBC
MapInfo内置ODBC,支持各种关系型数据库,支持SQL查询,从而保证了对原有数据库的沿用和对远程数据库地访问。具备空间查询的功能扩展(如缓冲区、叠加等),采用数据仓库的最新技术OLAP(Online Analytical Processing)的联机事物处理,对于应用程序实现图形查询和表查询提供了强大的手段。
(6)、 支持多种数据格式及其转换
MapInfo既支持数字化仪的图形输入方式,直接生成矢量图,也支持目前市场上流行的图形图象格式。可接受AutoCAD的DWG、DXF标准文件格式,还可通过MIF及MID文件与其它软件建立数据的接口。其中MIF文件内保存有图形信息,MID文件内保存有图形的属性信息。MIF及MID文件通过MapInfo的菜单命令Import和Export来输入和产生。
(7)、 二次开发工具MapBasic
作为一个系统软件,MapInfo提供了可以将其所有的功能用程序来驱动的方法,内置标准的二次开发工具——MapBasic。MapBasic不仅与大众化的Basic语法相一致,具有基本一致的常用函数集(计算、字符串处理、文件I/O、DLL调用等),而且利用MapBasic语言所提供的函数、过程和语句命令可以完成许多有关图形对象管理的复杂操作和运算。它的真正优势在于对MapInfo中的Table及其图形对象的管理所提供的特性和强大功能。采用面向对象及事件驱动编程
(8)、 集成二次开发能力
具有OLE和OLE Automation功能,可以方便地使用Delphi、VB、VC等多种开发工具,保证了新的应用程序与原有的应用界面保持一致,为日后的深层开发提供一个扩展空间。

MapX的组件模型结构

MapX组件的基本组成单元是Object(单个对象)和Collection(集合)。其中集合包括对象,是多个对象的组合。每种对象和集合负责处理地图某一方面的功能。
由图4.4可以看出,位于顶层的是Map对象本身,其它均由Map对象继承。Layers、DataSets、Annotations是Map对象下面的三个重要的分支。其中Layer主要用于操作地图的图层,DataSet用于访问空间数据表,Annotation用于在地图上增加文本或者符号。


每个Map对象主要包括Datasets、Layers、Annotations三个对象集合。
Map对象有一些主要的属性,如Zoom用来设置放大级别(在地图上显示的大小),Rotation控制地图的旋转角度,CenterX和CenterY用于设置x和y的坐标系,这要取决于地图的投影。
Map对象的许多属性本身又是一个对象,比如说一幅地图由多个图层组成,则在一个Map对象中存在一个单独的layers集合,其中包含所有图层的信息。
 Layers
在MapX中,每张单独的地图都被表示成单独的一个图层,所有的图层存储在layers集合中。Layers集合由Layer对象组成,按顺序编号为0到n。Layer对象由features对象组成,features对象又是由Feature对象组成,对应于地图中的点、线、区域或符号。
最上面一层为Layers(1),Layers(2)位于Layers(1)的下面,以次类推。最下面的图层最先绘制,最上面的图层最后绘制。在应用程序中,合理地安排好每层在Layers中的顺序是至关重要的。比如说有两个图层,一层为点,一层为区域,则应将点层放到区域层的上方,否则区域会将点覆盖。
另外,在进行地图选择操作时,根据要求调整图层的顺序也是十分重要的。MapX中的选择工具总是从可选择图层中的最上层开始选择,如果在地图上的同一位置存在多个位于不同层的地图对象,其结果是很难精确地选择到目标对象。因此,最好将被选择图层提到最上层显示。
 GeoSets
GeoSet是在GeoManager中建立好的.GST文件,类似MapInfo中的WorkSpace概念,是图层及其设置的集合,控制程序中显示的地图。也可以在运行阶段设置GeoSet,此时将导致已经加载的所有图层和DataSet被删除而由GeoSet中定义的图层所代替。如果单纯地想删除所有图层,只需给GeoSet赋一个空字符串即可。
可以使用GeoSet Manager程序来管理GeoSet 文件(*.GST)。默认情况下.GST文件存储在…\\mapx\maps目录下,可以调用GeoDictionary Manager程序进行修改,指向用户程序数据所在的位置。
 Datasets
Datasets用于实现地图与数据的绑定。举例说明,有一个关于城市销售情况的MSAccess 数据库和一张该城市的地图,则可以将二者绑定,在地图上形象地显示出各城市销售业绩的趋势,这一点是表格数据无法做到的。
建立地图信息与属性数据之间联系的过程称之为自动绑定或自动匹配(autobinding /automatching)。要实现这一过程,必须首先将地图在GeoDictionary 中注册。
属性数据表示的可视化使得创建专题地图成为可能。
数据绑定"Putting Your Data on the Map"
专题地图"Theme Mapping and Analysis"
 Annotations
Annotations集合提供了操纵地图中文字和符号的简单方法。Annotations位于所有其它图层的上方并且不与任何数据连接,有点儿象MapInfo中的透明图层。
Annotations包括以下主要的属性与方法:AddSymbol在Annotations中增加符号,符号类型使用Map.DefaultStyle定义;AddText在Annotations中增加文本;Remove删除特定的标注.;Type取值为miSymbolAnnotation或miTextAnnotation。
Annotations还有一个非常重要的属性Graphic,其定义为Graphic对象,在该对象中包含了符号或文本的样式、位置等信息,即Graphic的Caption、Position、Style 、X、Y属性。如Annotations的Type属性定义为miTextAnnotation,则可以定义Graphic的Caption属性设置标注的字符串。
 可创建对象
在MapX对象模型中,以下对象是可以被创建的:
AffineTransform、BindLayer、BitmapSymbols、CoordSys、Datum、Feature、Fields、LayerInfo、Map、 ODBCQueryInfo、 Parts、Point、Points、Rectangle、RowValue、RowValues、Style、Variables、NotesQueryInfo、NotesViewInfo。

专题地图

1、 专题地图的概念
MapInfo的一个显著特征就是能将数据库中的信息进行直观的可视化分析。专题地图就是用于分析和表现数据的一种强有力的方式。用户可以通过使用专题地图的方式将数据图形化,使数据以更直观的形式在地图上体现出来。当使用专题渲染在地图上显示数据时,可以清楚地看出在数据记录中难以发现的模式和趋势,为用户的决策支持提供依据。专题地图是MapInfo中的一个重要概念,是用户使用好MapInfo的一种体现。
制作专题地图是根据某个特定专题对地图进行“渲染”的过程。所谓的专题渲染,就是以某种图案或颜色填充来表明地图对象(点、线、区域)的某些信息(例如人口、大小、年降雨量、日期等等),也就是说,这类渲染存在着主题,经过这样渲染的地图就是专题地图。利用MapInfo,可根据数据库表中特定的值来赋给地图对象颜色、图案或符号,从而创建不同的专题地图。

2、 专题图的六种类型
MapInfo为创建专题地图提供了强有力的支持。用户可以使用范围值、等级符号、点密度、独立值、直方图和饼图等多达六种方式来创建不同的专题地图。
(1)、 范围值
按照设置的范围显示数据。这些范围用颜色和图案进行渲染。范围专题地图能够通过点、线和区域来说明数值,在反映数值和地理区域的关系(如销售数字,家庭收入),或显示比率信息如人口密度(人口除以面积)时是很有用的。
(2)、 等级符号
等级符号为表中每条记录显示一个符号,符号大小与数据值成比例。等级符号地图用特定的数值来显示数据点,对于阐明定量信息(如由高到低依次变化)很有用处。符号的大小与该点对应的数值成比例,数值越大点就越大,数值越小点就越小。因此,等级符号最适合数据值数据。
(3)、 点密度
在地图上用点来显示数据,每一点都代表一定数量,某区域中点的总数与该区域数值成比例。每个点代表一定数量的单元,该数乘以区域内总的点数,就等于该区域的数据值。
(4)、 独立值
按独立数值渲染地图,可以表达多个变量。根据独立值绘制地图对象的专题地图有助于强调数据的类型差异而不是显示定量信息(如给定区域内的商店类型、分区类型等等)。
(5)、 直方图
将表中每条记录的专题变量显示为一个直方图。使用直方图可分析地图中每条记录的多个变量。比较每个直方图中各直方条的大小可考察表中某条记录,比较所有直方图中某一条的大小可考察所有记录的某个变量,而比较各直方图的高度可考察整张表。用直方图来表达负值时,该条会沿直方图反方向伸展。在叠加直方图中不显示负值。
(6)、 饼图
以饼图显示表中各记录的专题变量。饼图可包含多个变量。在地图上使用饼图可一次分析多个变量,比较每个图中饼扇的大小可考察表中某条记录,比较所有饼图中某一个饼扇,可考察所有记录中某个变量的变化,比较各饼图的直径可考察整张表。

3、 MapX对专题图的支持
MapX中使用Themes集合与Theme对象来实现对专题图的支持,每个Themes集合中可以包含多个Theme对象,也就是说,针对一个Dataset,可以创建多幅不同的专题地图。
每个Dataset都拥有一个Themes集合,并以其属性的形式存在,即Dataset.Themes。使用Themes的Add、Remove、RemoveAll等方法可以控制专题的添加和删除。
a) Add方法:创建一个专题并将其加入到某个特定的DataSet的Themes集合中,
b) Remove方法:从集合中删除某一特定的专题图
c) RemoveAll方法:从集合中删除所有的的专题图

Theme对象用于设置每个专题图的属性。
比较重要的有
a) Layer:返回一个Layer对象,表示该专题图所在的图层
b) Legend:控制 对专题地图的说明,即图例
c) ComputeTheme 控制是否可以对原始数据进行计算,默认为True
d) Type 即专题图类型,取值范围为ThemeTypeConstants
e) Fields 只读属性,返回该专题图所基于的Dataset中的字段集合
ThemeProperties 复合型属性,对应ThemeProperties对象,包含了专题图详尽的定义信息,如范围定义、显示风格设置等。

4、 专题图的规划
在创建专题图的过程中,有几个关键因素,包括专题图变量的确定、属性数据的获取以及专题图层的显示与控制。
(1)、 确定专题图变量
在专题图中显示的数据就是专题图变量。例如在行政区面积专题图中,表示面积的字段“area”就是这个专题地图的专题地图变量。
一个专题变量可以是一个字段或表达式。取决于专题图的类型,在一张地图上可以显示一个或多个专题图变量。范围值、等级符号、点密度和独立值地图都只检查一个变量。可以利用饼图或直方图一次显示多个专题变量。也可以创建双变量专题地图,其中一个地图对象可代表两个不同的数据,入符号的颜色代表一个专题变量,符号的大小代表另一个专题变量。
(2)、 属性数据的获取
在创建专题地图之前,必须确定需要显示何种信息,信息存储在什么位置。它可以在创建地图时所基于的表中,也可以在ODBC支持的外部数据库中。数据来源于Field对象或Field集合,在Themes.Add方法中通过Fields参数传递。
(3)、 创建专题图
首先应将某个产生专题图数据的dataset引入地图中,之后使用Themes.Add方法创建一个Theme对象。
curmap.Datasets[1].Themes.Add(miThemeRanges,"TotPop","");
语法: Themes.Add([Type], [Field], [Name]);
Type用于定义要创建的专题图的类型,它的取值范围是ThemeTypeConstants,该参数可选,如果没有定义或者定义为miThemeAuto,MapX会根据字段数以及已经存在的专题图类型自动在ThemeTypeConstants中选择一个。如果MapX无法自行确定专题类型,就会产生一个错误。
Field(s)定义在专题图中使用的一个或多个字段,可以通过字段名、字段索引或字段对象来引用。当创建多变量专题图时,可以使用数组表示。该字段是可选的,若不特意指定,MapX会使用DataSet中的第一个数字型字段。
Name,即专题图的名称,String类型参数,若不指定,MapX会自动生成一个名字。
(4)、 专题图类型常量
专题图变量由Theme.Typeproperty取得,其定义如下:
miThemeRanged = 0
miThemeBarChart = 1
miThemePieChart = 2
miTheme GradSymbol = 3
miThemeDotDensity = 4
miThemeIndividualValue = 5
miThemeAuto = 6
miThemeNone = 9

5、 控制专题地图
有两种方法可以控制专题地图。
(1)、 使用Theme.ThemeDlg方法
该方法显示一个对话框,用户可以直接修改专题图特性。语句如下:
curmap.Datasets[1].Themes[1].ThemeDlg;
这种方法虽然简单,但是而且很难与自己的程序风格相一致,用户界面不友好,而且在这个默认的对话框中,用户可以随意改变任何设置,使得程序的控制难度加大。
(2)、 改变ThemeProperties对象属性
通过设置ThemeProperties对象属性,可以使用自己定制的界面,给用户有限的修改能力,实现起来也非常简单,而且对用户的操作有全部的控制权。
ThemeProperties对象是Themes集合中的一员,主要用于定义专题地图的显示,包括颜色、符号等。不同类型的专题图有自己与众不同的一些特性,在ThemeProperties中有其分别的定义。如DotSize专用于设定点密度专题图中点的大小,NumRanges专用于设定范围值专题图中的范围分布,SymbolStyle控制等级符号专题图使用的符号类型,ValuePerDot用于在点密度专题图中每个点所代表的值。
ThemeProperties对象的属性中有许多又属于其他对象,如RangeCategory、IndividualValue、Style 等,可进行更深层次的设定。
6、 自定义图例
专题地图被创建后,MapX会自动生成一个图例来解释颜色、符号或大小所代表的含义。同ThemeDlg一样,可以直接用LegendDlg 方法调用默认的Legend对话框,但更常用的依旧是访问Theme.Legend 属性来进行一些个性化的设置。

基础使用代码介绍

在MapX中,提供了标准的地图工具,可以很容易地利用常用的标准工具开发出方便易用的地理信息系统,而不必针对每一种地图操作都完全靠开发者自已编程。MapX内置了常用的标准地图工具,主要分为两类:一是对象创建工具,用来创建地图图元;另一类是选择工具。在选择工具的使用时,可以配合功能键Shift和Ctrl键。它们包括:
(1)改变地图比例尺和地图导航的工具:放大工具、缩小工具、平移工具和居中工具。
(2)通过单击地图图元进行标注的标注工具。
(3)以不同方式选择地图图元的选择工具集合。具体开发方法是:在程序适当位置激活某一个标准工具,把CurrentTool属性设为某一个常量,或直接用值。例如,在窗体中MapX控件为axMap1对象,当需要放大工具时:
axMap1.CurrentTool = MapXLib.ToolConstants.miZoomInTool;
运行程序就会看到此鼠标光标变为了放大镜。其他工具设定的方法与此相同。在具体实现中经常会为用户提供一些工具条,上面有地图的标准操作工具,以方便用户操作地图,例如提供放大、缩小、平移和选择这4种常用的工具按钮,在按钮的命令中可以编写为:

//
地图放大工具


axMap1.CurrentTool
=
MapXLib.ToolConstants.miZoomInTool;


//
地图缩小工具


axMap1.CurrentTool
=
MapXLib.ToolConstants.miZoomOutTool;


//
缩放到初始大小(全图)、


axMap1.ZoomTo(
this
.MapZoom,
this
.CenterX,
this
.CenterY);


//
地图平移工具


axMap1.CurrentTool
=
MapXLib.ToolConstants.miPanTool;


//
地图选择工具


axMap1.CurrentTool
=
MapXLib.ToolConstants.miSelectTool;


//
矩形选择


axMap1.CurrentTool
=
MapXLib.ToolConstants.miRectSelectTool;


//
圆形选择


axMap1.CurrentTool
=
MapXLib.ToolConstants.miRadiusSelectTool;


//
打开图层对话框


axMap1.Layers.LayersDlg(
null
,
null
);


//
打开ActiveX属性对话框


axMap1.ShowPropertyPages();
axMap1.Refresh();


//
添加符号


axMap1.CurrentTool
=
MapXLib.ToolConstants.miSymbolTool;

为了在地图中创建新的图元,或者修改已有的图元,需要地图编辑功能。MapX的标准工具提供了miAddPoint、ToolmiAddLine、ToolmiAddPolyLineTool、miAddRegionTool四种添加工具,分别属于添加点、添加线、添加折线和添加区域工具。对于添加工具,MapX指定将添加的图元放在Insertionlayer图层中。
因此需要在添加的图层前,先设定Insertionlayer图层,并将其设为可编辑的。方法如下:

axMap1.Layers.LayersDlg(
null
,
null
);
//
(1)


axMap1.Layers.InsertionLayer
=
axMap1.Layers[
1
];
//
(2)

其中(1)表示:打开图层对话框,在图层对话框中将当前图层设为可编辑;(2)表示:将Insertionlayer图层设定为当前层。

上篇介绍了MapX的部分基本使用代码,包括放大、缩小、缩放到初始大小(全图)、平移、矩形选择、圆形选择、箭头、打开图层对话框 、打开ActiveX属性对话框 、添加符号等基本操作代码,本篇继续探讨一些控件的基本操作。

MapX提供的标准工具,不同的工具将会使鼠标能够完成多种任务。例如,如果当前的工具设成
miLabelTool
,那么当单击鼠标时,会在此特指的地图对象上放置标签。鼠标光标将根据正使用的工具更改形状。
MapX
可用的标准工具列表如下所示:



工具

常量

描述


Add Line

MiAddLineTool

向插入图层添加线图元


Add Point

MiAddPointTool

单击从而向插入图层添加点图元。


Add Polyline

MiAddPolyLineTool

添加折线图元到插入图层



Add Region

MiAddRegionTool

添加区域图元到插入图层。



Arrow

MiArrowTool

单击标题或注释。并且,它也可用在可编辑的图层中移动选中的图元或者改变其大小。


Center

MiCenterTool

单击鼠标使地图居中显示。


Label

miLabelTool

单击图元进行标注。


Pan

MiPanTool

拖动地图并重定位地图的中心。


Polygon Select

MiPolygonSelectTool

单击鼠标画多边形;在多边形内的对象被选中。


Radius Select

MiRadiusSelectTool

拖动鼠标并选中在拖动半径内的图元。


Rect Select

MiRectSelectTool

拖动鼠标选中在矩形内的图元。


Select Tool

miSelectTool

单击选择图元。


Symbol

miSymbolTool

放置符号注释。


Text

miTextTool

放置文本注释。


Zoom In

miZoomInTool

放大。


Zoom Out

miZoomOutTool

缩小。


导出地图为图片的操作代码:


if
(axMap1.GeoSet.Length
<

1
)
{
MessageBox.Show(

"
未加载地图,不能导出!
"
);

return
;
}
SaveFileDialog exportFD

=

new
SaveFileDialog();
exportFD.Title

=

"
导出当前地图
"
;
exportFD.Filter

=

"
windows bitmap(*.bmp)|*.bmp|GIF (*.GIF)|*.gif|JPEG (*.JPG;JPEG;JPE)|*.JPG|PNG (*.PNG)|*.PNG|PSD (*.PSD)|*.PSD|TIFF (*.TIF)|*.TIF
"
;

if
(exportFD.ShowDialog()
==
DialogResult.OK
&&
(exportFD.FileName)
!=

null
)
{

try

{
axMap1.ExportSelection

=

true
;

switch
(exportFD.FilterIndex)
{

case

1
:
axMap1.ExportMap(exportFD.FileName, MapXLib.ExportFormatConstants.miFormatBMP, axMap1.MapPaperWidth, axMap1.MapPaperHeight);

break
;

case

2
:
axMap1.ExportMap(exportFD.FileName, MapXLib.ExportFormatConstants.miFormatGIF, axMap1.MapPaperWidth, axMap1.MapPaperHeight);

break
;

case

3
:
axMap1.ExportMap(exportFD.FileName, MapXLib.ExportFormatConstants.miFormatJPEG, axMap1.MapPaperWidth, axMap1.MapPaperHeight);

break
;

case

4
:
axMap1.ExportMap(exportFD.FileName, MapXLib.ExportFormatConstants.miFormatPNG, axMap1.MapPaperWidth, axMap1.MapPaperHeight);

break
;

case

5
:
axMap1.ExportMap(exportFD.FileName, MapXLib.ExportFormatConstants.miFormatPSD, axMap1.MapPaperWidth, axMap1.MapPaperHeight);

break
;

case

6
:
axMap1.ExportMap(exportFD.FileName, MapXLib.ExportFormatConstants.miFormatTIF, axMap1.MapPaperWidth, axMap1.MapPaperHeight);

break
;

case

7
:
axMap1.ExportMap(exportFD.FileName, MapXLib.ExportFormatConstants.miFormatWMF, axMap1.MapPaperWidth, axMap1.MapPaperHeight);

break
;
}
}

catch
(Exception ex)
{
MessageBox.Show(ex.Message);
}
}

添加图层的操作代码

OpenFileDialog openFile
=

new
OpenFileDialog();

if
(openFile.ShowDialog()
==
DialogResult.OK)
{
axMap1.Layers.Add(openFile.FileName,

0
);
//
默认值为0,C#调用不能省略默认值,图层的位置


MapXLib.LayerInfo liinfo;
liinfo

=

new
MapXLib.LayerInfoClass();
liinfo.Type

=
MapXLib.LayerInfoTypeConstants.miLayerInfoTypeTab;

liinfo.AddParameter(

"
AutoCreateDataset
"
,
true
);
}

查找图元信息:

MapXLib.Features mX;
mX

=

this
.axMap1.Layers[
1
].AllFeatures;

this
.listView1.Items.Clear();


foreach
(MapXLib.Feature mY
in
mX)
{
ListViewItem mZ

=

new
ListViewItem(mY._FeatureID.ToString());
mZ.SubItems.Add(mY.Name.ToString());

this
.listView1.Items.Add(mZ);
}

查找图层信息

MapXLib.Layers mX;
mX

=

this
.axMap1.Layers;

this
.listView1.Items.Clear();


foreach
(MapXLib.Layer mY
in
mX)
{
ListViewItem mZ

=

new
ListViewItem(mY._Name);

this
.listView1.Items.Add(mZ);
}

获取鼠标移动的坐标


double
x
=

0
;

double
y
=

0
;

axMap1.ConvertCoord(

ref
e.x,
ref
e.y,
ref
x,
ref
y, MapXLib.ConversionConstants.miScreenToMap);
toolStripStatusLabel1.Text

=

string
.Format(
"
X:{0} Y:{1}
"
, x, y);

放置点工具操作


try

{
MapXLib.Layer mX;
mX

=

this
.axMap1.Layers[
1
];
mX.Editable

=

true
;

this
.axMap1.Layers.InsertionLayer
=
mX;

this
.axMap1.CurrentTool
=
MapXLib.ToolConstants.miAddPointTool;
}

catch
(Exception ex)
{
MessageBox.Show(ex.Message);
}

MapX自定义工具的使用:

如果需要一种
MapX
没有提供的工具栏按钮,可以使用
Map.CreateCustomTool
方法来创建自定义工具。
创建自定义工具时,需要控制创建工具的“类型”,即:就是要选择此工具是否允许用户单击,或是单击并拖动来画线,或是单击并拖动来画矩形等等。也可以选择使用自定义工具时显示的光标。

下面是自定义测量距离和测量面积的操作代码


private

void
Form1_Load(
object
sender, EventArgs e)
{

//
记录地图的比例合中心点



this
.MapZoom
=
axMap1.Zoom;

this
.CenterX
=
axMap1.CenterX;

this
.CenterY
=
axMap1.CenterY;


//
创建测量距离的


axMap1.CreateCustomTool(
99
, MapXLib.ToolTypeConstants.miToolTypePoly, MapXLib.CursorConstants.miCrossCursor,
MapXLib.CursorConstants.miCrossCursor, MapXLib.CursorConstants.miCrossCursor,

false
);


//
创建测量面积的工具


axMap1.CreateCustomTool(
98
, MapXLib.ToolTypeConstants.miToolTypePolygon, MapXLib.CursorConstants.miCrossCursor,
MapXLib.CursorConstants.miCrossCursor, MapXLib.CursorConstants.miCrossCursor,

false
);

}


private

void
axMap1_PolyToolUsed(
object
sender, AxMapXLib.CMapXEvents_PolyToolUsedEvent e)
{

if
(e.toolNum
==

99
)
//
测量距离


{
MapXLib.Points pts

=
(MapXLib.Points)e.points;
MapXLib.Point pt1, pt2;

double
d
=

0.0
;


//
计算顺序两个点距离,累计得到总距离



for
(
int
i
=

1
; i
<
pts.Count; i
++
)
{
pt1

=
pts[i];
pt2

=
pts[i
+

1
];
d

+=
axMap1.Distance(pt1.X, pt1.Y, pt2.X, pt2.Y);
}


this
.Text
=

"
距离:
"

+
d.ToString();
}

else

if
(e.toolNum
==

98
)
//
面积


{
MapXLib.Points pts

=
(MapXLib.Points)e.points;

//
偷懒了但是很正确


MapXLib.FeatureFactory dd
=
axMap1.FeatureFactory;
MapXLib.Style style

=
axMap1.DefaultStyle;

this
.Text
=

"
面积:
"

+
dd.CreateRegion(pts, style).Area.ToString();
}
}


private

void
btnGetDistance_Click(
object
sender, EventArgs e)
{

//
测量距离


axMap1.CurrentTool
=
(MapXLib.ToolConstants)
99
;
}


private

void
btnGetArea_Click(
object
sender, EventArgs e)
{

//
测量面积


axMap1.CurrentTool
=
(MapXLib.ToolConstants)
98
;
}

以上代码有一个是事件操作,如果没有采用事件映射的操作,那么要添加下面的代码:

this.axMap1.PolyToolUsed += new AxMapXLib.CMapXEvents_PolyToolUsedEventHandler(this.axMap1_PolyToolUsed);

自定义工具类型的
ToolTypeConstants
描述当创建一个自定义工具时可使用的工具类型。它们描述工具的行为(例如,
miToolTypeLine
使用户可以画线;
miToolTypeCircle
使用户可以画圆等)。
自定义工具创建以后,需要为该工具实际要做的编写代码。



常量

行为


miToolTypePoint


在指定位置显示点。


miToolTypeLine


画线。


miToolTypeCircle


画圆。


miToolTypeMarquee


画选取框,并选在此框中选择地图对象。


miToolTypePoly


画折线。


miToolTypePolygon


画多边形。

特别说明,本文整理自一篇网络的文章《MapX从数据库读取数据形成新图层(C#)》

在C#中实现MapX从数据库读取数据形成新图层分为两个问题:

1. MapX从数据库读取数据形成新图层;

2. 将DataTable转换为ADO的Recordset。这里的第二个问题是由第一个问题引起的,因为MapX是一个COM控件,而且它只支持ADO的数据访问方式,而C#编程时一般会使用ADO.NET方式,为此需要在两种方式之间做一下转换。(当然也可以在C#中使用ADO方式)

DataTable转换为ADO的Recordset的操作代码如下所示。


///

<summary>


///
在.net中用ADO.NET取代了ADO实现对数据的访问,但一些COM控件只支持ADO并不支持ADO.NET。

///
为了使用这类控件,只能将ADO.NET中的数据对象,比如转换DataTable为ADO中的Recordset

///
(DataSet对象本质上是DataTable的集合,因此本文只讲述DataTable对象的转换)。

///

</summary>



public

sealed

class
ADONETtoADO
{

///

<summary>


///
将DataTable对象转换为Recordeset对象

///

</summary>


///

<param name="table">
DataTable对象
</param>


///

<returns>
转换后得到的Recordeset对象
</returns>



public

static
Recordset ConvertDataTableToRecordset(DataTable table)
{

//
思路:

//
1. 创建Recordset对象后,在其中对应DataTable的Column创建Field,为此需要将ADO.NET的数据类型转换为ADO的数据类型;

//
2. 打开Recordset对象,对应DataTable对象中的每一行,在Recordset对象中新建一条记录,并对每个字段赋值。



Recordset rs

=

new
RecordsetClass();

foreach
(DataColumn dc
in
table.Columns)
{
rs.Fields.Append(dc.ColumnName, GetDataType(dc.DataType),

-
1
, FieldAttributeEnum.adFldIsNullable, Missing.Value);
}

rs.Open(Missing.Value, Missing.Value, CursorTypeEnum.adOpenUnspecified, LockTypeEnum.adLockUnspecified,

-
1
);

foreach
(DataRow dr
in
table.Rows)
{
rs.AddNew(Missing.Value, Missing.Value);

object
o;

for
(
int
i
=

0
; i
<
table.Columns.Count; i
++
)
{
rs.Fields[i].Value

=
dr[i];
o

=
rs.Fields[i].Value;
}
}


return
rs;
}


///

<summary>


///
将ADO.NET的数据类型转换为ADO的数据类型

///

</summary>


///

<param name="dataType">
ADO.NET的数据类型
</param>


///

<returns>
ADO的数据类型
</returns>



private

static
DataTypeEnum GetDataType(Type dataType)
{

switch
(dataType.ToString())
{

case

"
System.Boolean
"
:
return
DataTypeEnum.adBoolean;

case

"
System.Byte
"
:
return
DataTypeEnum.adUnsignedTinyInt;

case

"
System.Char
"
:
return
DataTypeEnum.adChar;

case

"
System.DateTime
"
:
return
DataTypeEnum.adDate;

case

"
System.Decimal
"
:
return
DataTypeEnum.adDecimal;

case

"
System.Double
"
:
return
DataTypeEnum.adDouble;

case

"
System.Int16
"
:
return
DataTypeEnum.adSmallInt;

case

"
System.Int32
"
:
return
DataTypeEnum.adInteger;

case

"
System.Int64
"
:
return
DataTypeEnum.adBigInt;

case

"
System.SByte
"
:
return
DataTypeEnum.adTinyInt;

case

"
System.Single
"
:
return
DataTypeEnum.adSingle;

case

"
System.String
"
:
return
DataTypeEnum.adVarChar;

//
case "TimeSpan":return DataTypeEnum.



case

"
System.UInt16
"
:
return
DataTypeEnum.adUnsignedSmallInt;

case

"
System.UInt32
"
:
return
DataTypeEnum.adUnsignedInt;

case

"
System.UInt64
"
:
return
DataTypeEnum.adUnsignedBigInt;

default
:
throw
(
new
Exception(
"
没有对应的数据类型
"
));
}
}
}

在得到了Recordset对象后,如何解决第一个问题。步骤如下:

1. 创建CMapXFields对象,并对应数据库中字段添加字段;

2. 创建CMapXBindLayer对象,指定其坐标值字段的序号;

3. 向map.DataSets中添加数据集,从而生成新的图层;

4. 指定新图层中要素的显示风格,本文采用显示位图的方式,为此需要将要显示的位图放入MapX安装目录的CUSTSYMB文件夹下。

具体的操作代码如下所示:


///

<summary>


///
删除所有的图层数据

///

</summary>


///

<param name="layerName"></param>



private

void
DeleteLayerByName(
string
layerName)
{

//
Layer的序号是从1开始



int
count
=
axMap1.Layers.Count;

for
(
int
i
=

1
; i
<
count; i
++
)
{

if
(axMap1.Layers[i].Name
==
layerName)
{
axMap1.Layers.Remove(i);
}
}
}


///

<summary>


///
创建新的图层信息

///

</summary>


///

<param name="layerName"></param>


///

<param name="rsNoPass"></param>



private

void
CreatNewLayerfromDB(
string
layerName, ADODB.Recordset rsNoPass)
{
DeleteLayerByName(layerName);

//
将原有层删除


CMapXFields flds
=

new
FieldsClass();


//
Describe the structure of the Unbound dataset


flds.Add(
"
stationid
"
,
"
theid
"
, AggregationFunctionConstants.miAggregationIndividual,
FieldTypeConstants.miTypeString);
flds.Add(

"
address
"
,
"
address
"
, AggregationFunctionConstants.miAggregationIndividual,
FieldTypeConstants.miTypeString);
flds.Add(

"
longitude
"
,
"
longitude
"
, AggregationFunctionConstants.miAggregationSum,
FieldTypeConstants.miTypeNumeric);

//
经度


flds.Add(
"
latitude
"
,
"
latitude
"
, AggregationFunctionConstants.miAggregationSum,
FieldTypeConstants.miTypeNumeric);

//
纬度



CMapXBindLayer bindLayerObject

=

new
BindLayerClass();
bindLayerObject.LayerName

=
layerName;
bindLayerObject.RefColumn1

=

3
;
bindLayerObject.RefColumn2

=

4
;
bindLayerObject.LayerType

=
BindLayerTypeConstants.miBindLayerTypeXY;

CMapXDataset dataSet

=
axMap1.DataSets.Add(DatasetTypeConstants.miDataSetADO, rsNoPass, layerName,
"
stationid
"
,
"
address
"
, bindLayerObject, flds,
false
);
CMapXLayer layer

=
axMap1.Layers._Item(layerName);

layer.OverrideStyle

=

true
;

string
picName
=

"
icon.BMP
"
;

if
(layer.Style.SupportsBitmapSymbols
==

true
)
{
layer.Style.SymbolType

=
SymbolTypeConstants.miSymbolTypeBitmap;
layer.Style.SymbolBitmapSize

=

60
;
layer.Style.SymbolBitmapTransparent

=

true
;
layer.Style.SymbolBitmapName

=
picName;
}
}

我们知道,微软的.NET控件做了大量的工作,用起来还是不错的,一般的数据绑定或者赋值比较简单。如下所示

文本赋值: txtTest.Text = "abc";

控件禁用: txtTest.Enable = false;

复杂的控件,如DataGridView的数据绑定,也是比较简单,只要数据源支持IListDataSource接口就可以了,如下所示

dataGridView1.DataSource=list;//list为DataTable或者ArrayList或者List<T>等

在一般的单线程程序中,我们的数据绑定和UI的线程是一起的,那么绑定很正常,很OK,但是在多线程里面,如果你需要绑定这些数据,那么就要费一点周章了,呵呵。

如果你在不同于UI的另外一个线程里处理数据,当处理完毕,想绑定数据,那么应该如何呢,下面介绍几个方法给大家,一起分享一下,多线程控件中的数据绑定或者赋值等操作。

先介绍一个文本属性的操作:

this
.Invoke(
new
MethodInvoker(
delegate
()
{

this
.Text
=
message;
}));

this
.Invoke(
new
MethodInvoker(
delegate
()
{

this
.Enable
=

false
;
}));

其他的控件也一样了,这样就可以搞定多线程的属性操作了,这种方法机会可以用于各种控件的操作,如DataGridView的数据绑定操作代码如下

dataGridView1.Invoke(
new
MethodInvoker(
delegate
()
{
dataGridView1.DataSource

=
list;
}));

这样,但我们在多线程中使用的时候,如下面的操作中,就可以用这种跨线程的数据绑定了

private

void
btnUpdate_Click(
object
sender, EventArgs e)
{
WaitCallback async

=

new
WaitCallback(AddData);
ThreadPool.QueueUserWorkItem(async,

""
);
}



private

void
AddData(
object
state)
{
dataGridView1.Invoke(

new
MethodInvoker(
delegate
()
{
dataGridView1.DataSource

=
list;
}));
}

另外,我为了方便,封装了一般控件的跨线程访问的公共类,操作控件的代码可以变化为另外一种情况(和上面不同的方式)

CallCtrlWithThreadSafety.SetText(
this
,
"
您要显示的文本
"
,
this
);


//
禁用按钮


CallCtrlWithThreadSafety.SetEnable(
this
.btnUpdate,
false
,
this
);

辅助类的地址为:
https://files.cnblogs.com/wuhuacong/CallCtrlWithThreadSafety.rar
,供下载交流使用。