Google地图标注方法
基于SVG的Google用户地图文本标注方法
杨立法
西安邮电大学 计算机学院, 陕西西安, 710121
E-mail: Tel:[1**********]
摘要:提出一种新的Google地图文本标注方法。该方法将HTML5 SVG元素与Google地图自定义叠加层OverlayView类相结合,构成新类SVGMapLabel。该类借助于SVG中TEXT的丰富表现力,可以为Google地图上点、线、面对象建立文本标注。与KML图层结合,可以实现Google用户地图的自动标注,其效果类似于Google基础地图。
1 Google现有标注方法及存在的问题
地图上没有文字是很难理解的,每个地图元素都需要文字注解[1]。传统GIS软件,如ArcGIS、MapInfo等本身具有丰富的文本标注自动生成功能[2,3],在二次开发建立应用系统时无需考虑标注问题。但近年来随着Google地图的广泛应用,越来越多的用户希望将应用建立在Google地图上。Google Maps API V3是建立Google地图应用系统的基本工具,提供了诸多地图绘制功能,但缺少相应的文本标注功能[4]。
针对标注问题,Google提供了几种解决方案,即利用Marker符号或自定义符号[5]、弹出式Info window窗口和自定义叠加层。前两种方案根本无法产生类似Google基础地图那样的标注效果。相比之下,自定义叠加层具有较大灵活性,且Google在其实用程序库(Google Maps Utility Library)中提供了MapLabel[6]作为文本标注类以方便用户在地图上绘制文本。
MapLabel是对OverlayView类[7]的扩展。OverlayView是Google Maps API V3的一个基类,它只为用户提供在创建叠加层时必须实现的若干方法,在用户的自定义叠加层中具体添加什么内容由用户自己决定。MapLabel是将绘有文本的HTML5 Canvas元素加入叠加层,并放置在地图指定位置,从而形成对此处地图对象的标注。
但HTML5 Canvas元素[8]在文本绘制方面有很大的局限性,只能在水平方向自左向右绘制。所以,MapLabel可基本满足场点标注的需要。如果要文字倾斜、旋转,或沿道路、河流、各种管线等线状对象标绘文字,MapLabel就无能为力了。
它在文字输出方面的控HTML5 SVG[9]是W3C推荐的Web页面图形绘制的另一个工具,
制选项远比 Canvas要多。那么,能否将SVG引入Google地图叠加层,以解决文本标注问题呢?本文作者对此进行了尝试,结果发现是可行的,这就为Google用户地图文本标注找到一条新的途径。
2 基于SVG的地图标注类SVGMapLabel
2.1 SVG Text[10]
与HTML5 Canvas绘图方式不同,HTML5 SVG是XML风格的图形标记语言。在文本绘制方面,它利用text元素及其众多样式属性控制文本绘制,如图1上部所示的文档片段。这段文档的作用就是沿路径绘制文本,经浏览器(需支持HTML5)渲染后产生图1下部的输出。
渲染结果 xmlns:xlink=
图1. 用svg沿路径绘制文本
Fig 1. Text on Path in svg
浏览器在渲染svg时,把svg元素输出为页面上的一个图块,其属性(x,y)指其左上角相对页面左上角的位置(以像素计)。text属性(x,y)指文本相对svg左上角的位置。text属性style指文本自身样式属性。其中glyph-orientation-horizontal指字形相对自身水平基线的方位,其值为0或270。0表示正常输出(图1左下图),270表示每个字绕字形左下角顺时针旋转270°(图1右下图)。text子元素textPath指明文字按指定路径输出,其属性href的值”#MyPath”是事先定义的路径path的惟一标识id值。路径path在text之前的defs元素中定义。如果text内只是文本元素,没有textPath元素,输出路径默认为水平线。
路径path是通过属性d来描述的。d=
HTML5 SVG虽然符合W3C XML规则,但其子元素的名称必须处于指定的命名空间,即xmlns = ”http: //www.w3.org/2000/svg”,而链接属性(如href)的名称必须处于另一个命名空间,即xmlns:xlink=
有关SVG更详细的说明,参见SVG规范[9]。
很显然,这样一种文字输出方式与期望的Google地图标注方式是一致的。那么将它引入Google地图产生文本标注,就需要深入研究。
2.2 基于SVG的地图标注类SVGMapLabel
Google提供的地图标注类MapLabel是将HTML5 Canvas元素加入自定义叠加层而形成的,这与Google地图建立在HTML5 Canvas上的总体思路是一致的。现在要用上述svg文档片段替换MapLabel中的Canvas元素,需要建立一个新的JavaScript
类,名曰SVGMapLabel,以描述在Google地图上如何用svg建立文本标注。
按照Google地图自定义叠加层的设置要求,SVGMapLabel类由构造函数和三个原型方法onAdd()、draw()、onRemove()组成,并且将SVGMapLabel原型设为
google.maps.OverlayView的新实例,以继承OverlayView类。SVGMapLabel类的定义如下:
function SVGMapLabel(options) { /*对象构造函数*/}
SVGMapLabel.prototype=new google.maps.OverlayView; /* 子类化*/
SVGMapLabel.prototype.onAdd = function () { /* 添加svg元素 */ }
SVGMapLabel.prototype.draw=function(){ /* 动态计算文本路径及svg位置 */ }
SVGMapLabel.prototype.onRemove=function(){ /* 注销时,删除svg元素 */ }
构造函数SVGMapLabel(options)的作用为创建叠加层对象,并将外部数据转换为对象属性,以利于对象的其他方法共享。外部数据包括标注文本的字体参数、标注路径的经纬
度数据及其惟一性标识等。
当叠加层对象完成首次实例化并处于准备显示的状态时, Google Maps API就会调用叠加层对象的 onAdd() 方法将其添加到Google地图,也就是把2.1节所示的svg树作为一个DOM节点添加到Google地图窗格对象MapPanes中。svg代码的添加有两种方式:一种是将2.1节的svg文档整块加入到窗格层Pane节点上;另一种是按节点对象逐级加入。前者无法进行节点控制,后者可以控制各级节点的属性。因为Google地图随缩放等级而变化,叠加在它上面的svg图块的位置及标注路径也是动态变化的,这种变化需要后续计算才能确定,因此只能采用后一种方式。窗格对象MapPanes有多个窗格层Pane,且有特定的叠置顺序[7],文本标注最好放在最上层MapPanes.floatPane。
完成上述叠加层初始化之后,每当需要在地图上绘制叠加层时(包括首次添加叠加层时),Google Maps API 都会调用 draw() 方法。在此方法中,使用基类方法getProjection() 检索叠加层的 MapCanvasProjection对象以得到当前地图的投影,并据此重新确定文本标注路径及所在SVG图块的位置。按照习惯,标注文本应始终处于路径中间部位,且当路径长度小于某个像素值时不再显示。具体处理过程如下:
(1) 调用getProjection()方法获取当前地图投影;
(2) 将经纬度表示的标注路径转换为当前投影下的像素路径;
(3) 计算像素路径长度,并与标注文本长度比较。如果太短,则不显示svg对象。
(4) 确定标注文本在像素路径的起点位置;
(5) 按文本长度确定标注文本实际占据的像素路径;
(6) 比较确定标注文本像素路径的最小x,y像素值,并将其作为svg对象的左上角坐标;
(7) 计算标注文本相对svg左上角的像素路径;
(8) 将坐标串表示的文本像素路径转换为svg中path元素的路径表示形式,并修改其
d属性。
除了按文本长度控制标注的显示,还可以通过地图缩放级别控制标注的显示。
SVGMapLabel对象注销时,Google Maps API调用onRemove()方法从地图上移除svg对象。利用对象方法setMap(null) 和setMap(map)可从地图上隐去或复显文本标注。
每个待标注地图对象均需建立一个或多个文本标注对象,并利用数组进行管理。 3 应用及效果
SVGMapLabel类是在客户端进行文字标注的一种方法,可以用于交互标注,也可以与Google地图上的KML图层结合起来,利用KML文档提供的属性数据对KML图层中的地图对象进行自动标注。这里以道路标注为例,说明对KML图层自动标注的过程。
道路设有三个图层:路面、边线及中线。标注信息来自中线的KML文档,其基本结构如图2所示。每条道路的标注文本取自文档name元素,标注路径的经纬度坐标
取自coordinates元素。
图2. 道路中线文档结构
Fig 2. Road centerline in KML $.xml2json(kml)可以将获得的KML文档进一步转换为JSON格式以方便读取其中的数据[12]。 一条道路在不同的缩放级别下,其地图
长度是不同的。若要求标注间隔固定,道路地图长度增加时,标注的个数也应该增加;反之,则减少,从而使标注个数适应道路长度的变化。实现这种动态标注的方法如下:
(1) 按缩放级别(或比例尺)估算出道路在地图上的长度(像素);
(2) 按固定像素值(如300像素)的基本长度将道路等分成若干段;
(3) 计算出每段道路的经纬度路径;
(4) 应用SVGMapLabel为每段道路建立文
本标注对象,并设置其对应的缩放级
别。
据此方法,每条道路Road有多个标注
对象label,分别对应于地图map的不同缩
放级别zoom和道路的不同路段pathseg,
每个路段还必须赋予惟一标识pathID。下
面是利用SVGMapLabel类创建一个标注对
象并存入道路标注数组LabelRoad的
JavaScript代码:
var label = new SVGMapLabel({
text: name,
path: pathseg,
pathid: pathID,
map: map,
angle:0,
minZoom: zoom,
maxZoom: zoom
});
LabelRoad.push(label); a. 1:2000道路图 b. 1:5000道路图 图3. 道路标注
Fig 3. Road Labels
由此建立的每个标注只是出现在特定缩放级别的地图的特定位置,彼此不会重叠。图3示出在两个不同缩放级别下三条道路的标注效果。
在建立道路标注时,标注文本是否旋转取决于当前路段的倾斜程度。规定:倾斜超过45°,不管左倾还是右倾,如果是纯汉字,则使文本旋转270°。如果文本夹杂其他字符,则不予旋转。
路径方向就是文本输出方向。考虑到从左到右和从上到下的阅读习惯,那些由下向上和由右到左的文本路径,都要做节点倒序处理。
SVGMapLabel在用于点状标注时,path字段只需提供标注位置。如果要旋转标注,还需提供旋转角度angle值。
4 结论
本文将HTML5 SVG与Google Maps API V3的OverlayView类结合,建立一个
JavaScript类SVGMapLabel,用于在Google地图建立文本标注。文中详细阐述了文本标注生成算法及其适应地图缩放的解决方案。SVGMapLabel可以为Google地图上的点、线、区域对象建立文本标注,可用于交互式标注,也可与KML图层结合,对KML要素进行自动标注,其标注效果类似于Google基础地图,完全可以替代MapLabel。在建立基于Google地图的WebGIS时,SVGMapLabel类可用于用户地图的文本标注。