2008年12月9日星期二

使用地图API实现自定义叠加层

与地图上的控件不同,地图上的叠加层(Overlay)指的是和某个经纬度坐标绑定,能够跟随地图的缩放拖拽而相应移动的DOM原始,地图API文档里定义的GMarker、GPolyline、GInfoWindow都属于叠加层。右边地图上显示的交通信息的图标就是一个叠加层。
先看一段Hello World的代码吧:
var MyOverlay = function(latlng_) {
  this.latlng = latlng_;
};

MyOverlay.prototype.initialize = function(map_) {
  this.map = map_;
  var label_ = document.createElement("div");
  var container_ = map_.getPane(G_MAP_FLOAT_PANE);
  container_.appendChild(label_);
  label_.innerHTML = "<span style='color:#FF0000;background-color:#FFFFFF;'>Hello World!</span>";
  label_.style.position = "absolute";
  this.label = label_;
  this.redraw();
}
MyOverlay.prototype.redraw = function() {
  var position = this.map.fromLatLngToDivPixel(this.latlng);
  this.label.style.left = position.x + "px";
  this.label.style.top = position.y + "px";
}
MyOverlay.prototype.remove = function() {
  this.label.parentNode.removeChild(this.label);
}
上面的代码中,MyOverlay就是一个自定义的叠加层,需要的时候可以创建一个MyOverlay的实例,然后使用map.addOverlay()就可以在地图上加上这个叠加层了。这里的叠加层是在给定的位置(地图加载时的中心点)加上一个Hello World的文本,以后,无论你怎么拖动缩放地图,这个文本在地图上的位置会跟着地图变化。
下面详细解释一下代码:
var MyOverlay = function(latlng_) {
  this.latlng = latlng_;
};
声明MyOverlay类的构造函数,创建该类的实例是需要给一个GLatLng类型的坐标对象作为参数。

MyOverlay.prototype = new GOverlay();
声明这个类继承自GOverlay

接下来的initialize、redraw、remove都是实现了在GOverlay接口中定义的抽象方法,这些方法在MyOverlay类的对象初始化、地图拖拽缩放、从地图上删除的时候都会被自动调用。

Overlay也是一个DOM元素,所以在initialize方法中需要做的就是定义这个DOM元素并且把它添加到合适的地方去。使用GMap2对象(作为参数传递进来)的getPane方法取得Overlay需要依附的图层(这个图层按照z-index的不同现在可以分为七种不同的图层,我就不列举了,需要的可以到文档里查查GMapPane这个类),也就是Overlay所要依附的DOM容器(不同于GMap2对象的DOM容器),定义好Overlay的样式和内容,然后作为子元素添加到刚才取到的图层里就可以了。
在initialize方法中需要注意的是,作为叠加层的DOM元素的position样式属性必须设置为absolute,并且需要显式的调用redraw方法,才能保证在叠加层第一次加载时的位置显示正确。

对于redraw方法,就是在地图的位置发生变动以后能够重新计算叠加层的坐标并重新设定叠加层的显示位置,关键点就是使用GMap2.fromLatLngToDivPixel()方法来取得叠加层的DOM绝对坐标,不是很理解这个坐标的话可以看看我以前总结果的地图坐标系统

对于remove方法,地图在调用removeOverlay的时候会自动调用这个方法,把你的叠加层从地图上删除。

这里展示的只是一个添加简单文本的Overlay的例子,如果你需要添加更丰富的文本、图片,或者需要这个Overlay能够监听一些事件,比如单击、双击等等,只需要在initialize()方法里对Overlaly的DOM元素做更多的声明和控制就可以了,和你在网页中其他地方定义一个DOM元素没什么两样。

2008年12月5日星期五

定制GoogleBar --谷歌地图的本地搜索栏

在提到地图上的默认控件GMap2的设置项的时候,我都提过谷歌地图的本地搜索栏这个控件,要在地图上加上这个控件很简单,不需要调用GMap2.addControl(),只需要一句话:
  map.enableGoogleBar();
这样就可以在地图的左下角加上这个搜索栏了,并且替换掉了原来的谷歌Logo的图片。
目前用这个搜索栏可以搜索两种类型的数据:
  1、地名;
  2、谷歌提供的商业数据,比如“餐馆”,默认是在当前显示的地图范围内搜索;
具体的搜索结果是上面的哪种类型由你输入的关键字决定,没有什么特别的方法。搜索返回结果后会自动在地图上加上所有结果的标记,同时会用DOM形式显示这个搜索结果,至于DOM会是什么样的形式展现,接着看就知道了。

定制这个搜索栏对应的类是GGoogleBarOptions,没有构造函数,对应一个对象变量,使用方式类似于使用GMapOptions定制谷歌地图,不过GGoogleBarOptions是作为GMapOptions里的一项在创建GMap2对象的时候使用。如果你了解过Google AJAX Search API的话,你对下面这些设置项会很熟悉,因为,这个本地搜索栏就是对AJAX Search中的GLocalSearchControl的一个封装。
先看几个基本的设置项:
  1、 showOnLoad
  这个是boolean类型的项,决定加载完成后搜索栏的显示状态,默认是false,意思是搜索栏是闭合的,不显示输入框,如果你想提醒用户这里有个输入框可以搜索,可以把这一项设成true,即{showOnLoad:true};
  2、linkTarget
  简单的说,这个就是搜索结果里每一条记录对应link的target属性值,四个可选值,默认是等价于target="blank_"的G_GOOGLEBAR_LINK_TARGET,其他还有parent, self, top这几项,准确的参数我就不列了,开发文档里有。不过这一项基本用不着,不用设置;
  3、resultList
  用来指定搜索结果的DOM显示样式,默认是用列表的方式显示(G_GOOGLEBAR_RESULT_LIST_INLINE),还有一种是添加一对方向箭头让你逐个翻阅,就像右边的地图里显示的那样。如果你的地图空间比较小的话可以用后一种,设定{resultList:G_GOOGLEBAR_RESULT_LIST_SUPPRESS};
  4、suppressInitialResultSelection
  这个用来指定搜索完成后是否自动选中第一个搜索结果并且打开它的信息窗口,默认是false,就是打开第一条记录的信息窗口,不需要的话就指定{suppressInitialResultSelection:true};
  5、suppressZoomToBounds
  按照文档的说法,这个用来设定搜索完成后是否需要地图自动缩放移到来完整显示所有的标记点,默认是false,不缩放的,不过我在试用时发现不管搜索结果如何,地图都不会缩放。所以,暂时忽略这一项。
  6、searchFormHint
  这个在文档里没有列出来,但是也可以使用,用来设定输入框处于非激活状态时的提示语,默认的是“搜索地图”,你也可以设成你自己的提示语,比如:{searchFormHint:"hi,在这里可以直接搜索地图"}

再来看几个用来设定搜索操作时的回调函数,类似于事件监听,这几个可以做很多文章:
  1、onIdleCallback
  这个在搜索结果被清除的时候调用,也就是在你点击搜索结果上的“清除结果”这个链接后,是不是在本次搜索结果返回并且自动清除上一次搜索结果后调用我还不确定,看开发文档似乎有这个意思。
  2、onSearchCompleteCallback
  在已经得到搜索结果但是还没有添加到地图上的时候调用,会传递一个GlocalSearch对象作为参数,GlocalSearch是Google AJAX Search API里定义的一个类,想了解的先去看文档吧,我就不多说了。
  3、onGenerateMarkerHtmlCallback
  在打开一条搜索记录的信息窗口前会被调用,传入marker, html, result三个参数,分别对应这条记录在地图上的标记(GMarker)、填充信息窗口的html字符串、对应的搜索结果(GlocalResult)。这里定义的回调方法必须返回一个dom对象,用在打开的信息窗口中。所以,通过这一项的设置,你就可以更改搜索结果的信息窗口里的默认内容了。右边的地图里就把默认的信息窗口内容换了,去掉了“从这里来”、“到这里去”的链接,加上了一个静态地图,
  4、onMarkersSetCallback
  这个在所有的结果标记都已经创建但还没有添加到地图之前调用,传入的参数是一个搜索结果的数组results,数组里的每一项对应一条搜索记录,包括latLng, marker, result,对应结果的坐标GLatLng、标记GMarker、结果记录GlocalResult。这个时候结果的显示列表已经创建完成了,所以,如果你想修改这个现实结果不妨试试这个设置项。
  
追加一些说明,上面说到的GlocalResult也是在Google AJAX Search API里定义的一个类,没有构造函数,当作对象变量使用,需要了解可以看看文档,这里列一下这个类的一些主要属性:
.title, .titleNoFormatting, .lat, .lng, .streetAddress, .city, .country, .phoneNumbers[], .staticMapUrl 

2008年11月28日星期五

谷歌地图上的快捷键

前面设置地图的博文里面其实漏掉了一个很有意思而又不太引人注目的地图特性,就是设置地图的快捷键。
说实话,之前我也没有注意过这一点,甚至都没有想过地图可以支持快捷键,一个以拖拽成名的应用,谁又能想到可以用快捷键来控制呢。
有一天无聊的上下翻动地图API的文档,突然发现一个很陌生的类,GKeyboardHandler,没见过!
仔细看了一下,居然是用来使地图支持键盘操作的,更有意思的是,这个类在文档顶部的类名列表中没有出现,不知道是整理文档的疏漏,还是因为这个类太小,小的连一个方法或者参数都没有,只有光秃秃一个类充作构造函数。也可能是这个类所支持的键盘操作有限吧,我把支持的键做了个列表:

动作
方向键 向对应的方向连续移动地图,同时按下邻近的两个会向对角移动
Home/End以3/4的幅度左右平移地图 ,动画效果
Page Up/Page Down 以3/4的幅度上下平移地图,动画效果
+ / -
放大/缩小一个级别的地图

 只有地图的移动和缩放操作,特意到地图主页上试了试,支持,没有问题,只不过你需要先点击地图,使地图容器处于激活状态。用起来也不错,感觉地图的移动居然比鼠标拖动时还要平滑。
如果需要在你自己的地图上使用,只需要一句话:

new GKeyboardHandler(map);

够简单吧,呵呵,赶紧扔开你的鼠标到右边的地图上试试吧。

给你的地图加上API提供的默认控件

地图上的控件一般是指悬浮在地图上、不随地图移动的图片、按钮等等DOM元素,可以用来控制地图、帮助用户和地图交互、显示地图的特定信息和状态。
在谷歌地图主页上,有可以控制地图缩放的控件、有切换地图类型的控件、有显示缩略图的控件等等,这些控件在地图API里默认也都是提供的,所以,你可以在你的地图上加上这些控件,并且可以根据你的需要随意调整地图控件的位置、改变控件的外观,就像我在右边的地图上把缩放控件移到右上角而不是默认的左上角、并且使用了一个微型化的外观。下面先看看可以在地图上添加哪些控件吧。

地图API中默认控件的种类
1、地图缩放控件
   缩放控件是可以控制地图移动和放大级别的,默认有三种:
   1) 全功能控件 GLargeMapControl 
        有方向按钮、恢复按钮、缩放按钮和缩放滑块
   2) 微型化控件 GSmallMapControl 
       把全功能控件去掉恢复按钮和缩放滑块后的控件
   3) 微型缩放控件 GSmallZoomControl
       只有放大/缩小两个按钮
   下面从左到右依次是GLargeMapControl,GSmallMapControl,GSmallZoomControl这三个控件,选用哪一个就看你的空间有多大了。不过,我认为通过GMap2提供的设置项允许用户使用滚轮和双击缩放地图比使用一个大大的缩放控件更方便。
            
    这几天谷歌地图主页刚刚换了新的缩放控件,有点类似于谷歌地球上哪个,不过使用API实现的还没有变,仍然是上面显示的这个样子。

2、地图类型选择控件
    地图类型选择控件是用来选定所显示地图的类型的,比如普通地图、卫星地图、地形地图等等,也有三种,唉,突然想起孔乙己:)
    1)按钮式标准控件  GMapTypeControl
    每种地图类型对应一个按钮,点击不同的按钮切换地图类型,比如
   
    2)菜单式控件 GMenuMapTypeControl
    所有的地图类型使用一个下拉菜单来显示,选择菜单中的不同项来切换地图类型,比如
   
    3)阶层式标准控件 GHierarchicalMapTypeControl
    类似标准的按钮控件,不过在某些按钮下可以选择在该类型地图下的嵌套内容,比如卫星地图中还可以选择是否使用标签、即是否使用混合类型(Hybrid)的地图,如下:
   
    这个控件目前在中文地图上没有什么作用,因为中文地图还不支持Hybrid类型的地图,也不能在地图上显示Panoramio的图片和Wikipedia的内容。
3、缩略图控件 GOverviewMapControl
    在地图上显示一个比现有地图范围更大、但是尺寸要小得多的缩略图,默认位置在右下角,在该控件的右下角有一个箭头,点击可以收放这个控件,需要移动地图时你也可以直接拖动缩略图里的那个蓝框,会比你直接拖动地图要快一些。
   
4、比例尺控件  GScaleControl
    显示当前地图的比例尺,所以上面的数值会随地图的缩放变化,这个控件不是特别引人注目
   
5、本地搜索控件  GoogleBar
    上一篇介绍GMap2设置项的文章里把这个包括了进去,还是放在这里比较合适,是什么我就不多说了,看图
   

谷歌地图上常用的默认控件基本就这些了,你在我右边的地图里基本都能找到这些控件,想把这些控件加到你的地图上也不难,接着来看。

向地图上添加控件的方法
上面我已经给出了每个控件对应的类名,添加的步骤(以添加右边地图中的微型缩放控件为例):
1、重置控件的位置,如果你使用控件的默认位置,这一步就不需要了。一般情况下也确实不需要,只有在你的地图空间狭小,控件摆布不开的时候才会有,右边的地图就是这种情况。
    GSmallMapControl.prototype.getDefaultPosition = function(){
        return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10, 30));
    }
    注意G_ANCHOR_TOP_RIGHT和new GSize(10, 30)这两个参数,这个其实就决定了右边地图里微型缩放控件的位置,位于右上角(TOP_RIGHT),距地图右边距10px,上边距30px。
2、创建控件对象
    var mapControl = new GSmallMapControl();
3、调用map.addControl()方法添加控件
    map.addControl(mapControl);

怎么样,还没过瘾?API里提供的默认控件基本就这些了,如果你还想加上你自己设置的控件,比如右边地图上的状态控制,那就看看我之前介绍的自定义地图控件的方法吧。

到这里为止,通过指定GMapOptions设定地图设置项、添加默认控件,我们可以在不需要添加任何叠加层、信息窗口、事件侦听的情况下,就可以创建一个内容比较丰富的地图了。当然,等我介绍完本地搜索控件的定制方法,我们的地图内容会更丰富。

2008年11月26日星期三

GMap2可以有哪些设置项

初始化一个GMap2对象除了可以使用GMapOptions参数设置谷歌地图外,在创建GMap2对象后还可以用一些方法设置地图的一些通用属性,照我的使用习惯和频率,做个分类(如果我在标题后没有添加“默认”,则这个属性在默认状态下地图是不支持的,另外,文中的map指GMap2的实例对象,省得用GMap2来作方法声明的时候有人误解):

1、可以连续缩放
    地图缩放的时候旧图块在新图块加载完成之前不会直接消失,图块的切换比较平滑,在网速较慢的时候这种效果会好一些。地图默认情况下不支持,建议打开这个功能。使用map.enableContinuousZoom() / map.disableContinuousZoom() 方法来切换。另外,需要的时候你可以使用map.continuousZoomEnabled()方法来探测一下地图是否支持连续缩放。

2、支持滚轮缩放

    使用鼠标滚轮来实现地图缩放,默认也是不支持的,建议打开。使用map.enableScrollWheelZoom() / map.disableScrollWheelZoom() 方法来切换,使用map.scrollWheelZoomEnabled()方法来探测。

3、支持双击缩放

    左键双击放大地图,默认也是不支持,建议打开。使用map.enableDoubleClickZoom() / map.disableDoubleClickZoom()方法切换,使用map.doubleClickZoomEnabled()方法探测。另外,右键双击缩小地图默认是支持的,没有发现地图API中有方法可以改变右键双击的属性。

4、支持地图拖动(默认)
    按住左键拖动地图,默认是支持的。使用enableDragging/disableDragging方法切换,使用draggingEnabled()方法探测。这个估计是个比较老的方法,因为在已经有静态地图API的情况下实在想不出来有什么理由不让地图可以拖动,除非你是想临时的禁止地图可拖动。

5、支持信息窗口(默认)

   在需要的时候地图可以弹出一个泡状的东东,在里面显示信息,默认也是支持的。使用enableInfoWindow/disableInfoWindow方法切换,使用infoWindowEnabled()方法探测。一般情况下,这个属性让它保持默认就好了,如果你只需要在地图上标注并且不允许任何信息窗口的弹出,或者想上一个地图拖动的属性一样,需要临时性的禁止。

6、添加搜索栏
   把地图左下角的Logo换成一个带搜索框的控件,这样你就可以在你的地图中直接使用谷歌的数据源来支持用户搜索,定制好的话简直就可以当作一个微型的地图主页来用。这个是中文API中新增的功能,以前的文章中我提到使用GMapOptions中的googleBarOptions属性可以来定制这个控件的。这个在地图上默认是没有添加的,所以,如果你没有使用map.enableGoogleBar()方法来显式的声明加上搜索栏的话,googleBarOptions也是不会起作用的。添加搜索栏反向的禁用操作是map.disableGoogleBar()。

2008年11月25日星期二

使用GMapOptions定制你的谷歌地图

  之前我曾经解释过创建一个地图的详细步骤,但是,真正创建地图的核心步骤也就两行代码:
    var map = new GMap2(document.getElementById("mapContainer"));
    map.setCenter(new GLatLng(33.0, 106.0),  3);
   有这两行代码,你就可以在你的网页上展现你的谷歌地图了。但是,这个是最简单的地图,如果你想要对这个地图做一些小小的变动,更符合你的胃口,可以使用GMapOptions来尝试定制地图。
   简单的说,GMapOptions是你在new一个GMap2对象的时候,可以直接使用对象变量的形式作为可选参数传递给GMap2的构造函数,GMapOptions自己没有构造函数(地图API中类构造函数的可选参数多用这种形式来定义),比如:
   var options = {size:GSize(400, 300), backgroundColor:"#FF0000"};
   var map = new GMap2(document.getElementById("mapContainer"), options);
   这里options就是一个GMapOptions,size、backgroundColor就是他的选项。
   在GMapOptions中,我们可以定义以下这些属性来指定地图的某些特性:

   1)size
   默认情况下,你创建的地图大小就是你给定的地图容器的大小,所以,通常情况下,你需要显式的声明你的地图容器的width和height属性,否则,地图是不能正常显示的。但是,有了size这个可选属性后,你就多了一个选择了。你可以在创建地图的时候直接通过size这个属性指定地图的大小,而不需要听命于地图容器了,即使这个地图容器已经显示的定义了width和height的大小。当然,size属性对应的值是一个GSize类型的数据,比如,如果给定options={size:GSize(400, 300)},那么,你所创建的地图大小就是400×300的一个矩形块,而和你指定的容器大小无关,但是地图的左上角和地图容器的左上角还是重合的。

   2)mapTypes
   创建地图后,默认显示的地图类型是普通地图,如果要加上可以选择的卫星地图、地形地图等等其他类型的地图,可以使用GMap2.setMapType()方法,但这样往往会罗列一堆的setMapType。GMapOptions提供了mapTypes这个可选项,通过一个数组就可以给地图加上多种支持类型,比如使用{mapTypes:[G_NORMAL_MAP,G_SATELLITE_MAP,G_PHYSICAL_MAP]},你的地图就拥有三种普通、卫星、地形三种类型了。mapTypes数组中的第一项是地图加载的默认类型,所以,如果你想默认加载卫星地图,把G_SATELLITE_MAP移到数组的第一项就可以了。

  3)draggableCursor、draggingCursor
  这两个选项是用来定义地图上你的光标类型,我把它们放在一起介绍不等于它们必须一起使用,你可以单独使用任何一个。其中,draggableCursor是地图可拖拽状态(默认就是可拖拽的)下的光标,draggingCursor是拖拽地图时的光标,对应的值和你在JavaScript里面设置其他的光标时使用的值一样,比如,{draggableCursor:"crosshair",draggingCursor:"move"}。当然,你也可以使用url形式加上你自己的图标,看你发挥了!

  4)backgroundColor
  在地图图块的图片还没有传送完成之前,地图的显示区域默认会使用灰色填充,这个就是backgroundColor可以发挥作用的地方了,你可以把灰色换成其他任何符合W3C标准的颜色,比如{backgroundColor:"#FF0000"},不过估计没人会喜欢用这种大红做背景的:)。可惜的是这里只能定义color,要是这个选项是background而不是backgroundColor,千方百计想打个Logo的兄弟就真能找着好地方了。

  5)googleBarOptions
  这个和你在地图上通过GMap2.enableGoogleBar()时有关系,指定你添加GoogleBar时的一些默认属性,在以后说GoogleBar的时候再来专门说吧,定制性还是很强的。
  

2008年11月18日星期二

使用谷歌地图API实现自定义控件

  使用谷歌地图API定义自定义的控件其实非常简单,看看我在右边地图的右上角添加的半透明的状态监控栏,这就是一个自定义的控件。

  闲话少说,先看一段Hello World的代码

  function MyControl(){}

  MyControl.prototype = new GControl();

  MyControl.prototype.initialize = function(map){

    this.map = map;

    var container = map.getContainer();

    var label = document.createElement("div");
    container.appendChild(label);

    lable.innerHTML = "Hello World";

    return label;

  }

  MyControl.prototype.getDefaultPosition = function(){

    return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(50, 10));

  }

  上面这段代码中,MyControl就是一个自定义的控件了,在需要的时候,和添加其他默认控件一样,使用map.addControl(new MyControl())就可以在地图上添加这个自定义的控件了,只不过这个控件现在也没什么实际用处,就是在你的地图容器的顶部显示一个Hello World的文本,与地图没什么交互。

   下面来详细解释一下这段代码:

   第一行定义了一个MyControl类;

   接下来,把这个类定义为GControl类的子类,就是把MyControl的prototype指定为GControl类的实例/对象;

   后面的两个方法initiallize和getDefaultPosition是继承GControl类的时候必须实现的两个方法,API文档里定义GControl是接口类,所以可以把这两个方法认为是抽象方法,就不难理解为什么必须要实现了。需要注意的是,这两个方法我们只负责实现,但是不需要我们去显式的调用,在向地图上添加控件时API类库会自动调用这两个方法。

   方法initialize接受一个参数map,就是你创建地图时new出来的GMap2对象。在这个方法中,你使用map.getContainer()方法取得放置地图的DOM容器,然后,你就可以向这个DOM容器里添加任何你想加入的DOM元素了。在上面的示例中我创建了一个div元素,用来显示Hello World,但是,你可以在这里加入任何你想添加的DOM元素,并且可以定义它们的行为,也就是说,这里就是你可以自由发挥的天地了,无论你是想显示动态的信息,还是来控制地图,比如右边地图里的状态监控栏。initialize方法最后需要返回你所创建的最外层的DOM元素,这个千万别忘了。

   方法getDefaultPosition的作用是定义你的控件在地图容器里的位置,实现起来就更简单了,你可以直接把我这里的代码copy过去,改成你需要定位的地方就可以了。GControlPosition的第一个参数是你的控件的锚点,只能使用G_ANCHOR_TOP_LEFT、G_ANCHOR_TOP_RIGHT、G_ANCHOR_BOTTOM_LEFT、G_ANCHOR_BOTTOM_RIGHT这四个代表上左、上右、下左、下右这四个角的常量,后一个参数使用GSize来定义你的控件距离地图容器边界的偏移量,第一个参数是横向偏移、第二个参数是纵向偏移。
   到这里一个自定义的控件就完成了,没什么复杂的吧?刚才看到论坛里有朋友问怎么能把自己的LOGO和地图左下角Google的LOGO并排放在一起,看了自定义控件,你一定知道怎么做了吧。不过,千万别想用你的LOGO覆盖Google的LOGO哦,基本的版权意识咱们还是应该有的,就像你也希望别人能够尊重你的劳动成果一样!