Google地图开发总结

我们经常使用地图查位置、看公交、看街景,同时地图还开放第三方的API给开发者。利用这些API进行地图的个性化的展示和控制,例如北京被水淹了,开发一个网页显示北京被淹的地图,地图上面标志被水淹的位置、严重程度,或者我是交警,想要在地图上标志发生车祸、被交通管制的路段,甚至是利用地图的街景,控制街景的位置变化做一个tour show动画。因为地图本身就是一个比较好玩的东西,再加上一些个性化的控制会更加的有趣。

常有的地图有谷歌、百度、必应等,这些都有提供api,下面以谷歌地图为例做说明。虽然谷歌被墙,但是谷歌有一个中国域名版本的,没有被墙,可以自由访问:http://www.google.cn/maps,估计很多人都不知道。首先来看下谷歌地图是怎么显示在页面的

谷歌地图的组成

只要做一下元素审查,就可以发现谷歌地图的主体部份是用一张张的图片拼成的,只要缩放比例或者位置一改变,就会再去请求新的图片,也就是说地图的渲染是在后端进行的,后端把图片生成好发给前端,之所以没放在前端绘制主要应该是考虑了客户端的性能和兼容性。左上角和右下角的控制也是用div absolute定位上去的。

谷歌地图的使用

引入谷歌地图

首先加载地图的api,你可以指定所用语言,如果没指定,地图将根据浏览器的语言(可通过请求的http头的Accept-Language字段)自动选用语言。还可以指定谷歌地图的版本,现在最新版是ver=3.25,还可以加上一些指定的地图的lib。必填的参数是key,如果没有key去谷歌地图的开发者页面申请一个即可。大陆版的跟正常版的在使用上目测没什么区别

<script src="http://ditu.google.cn/maps/api/js?key=AIzaSyBp&language=zh-CN"></script> <!-- 中国版 -->
<!--正常版,需FQ <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBp"></script> -->

然后在页面写一个div,作为地图的容器,指定地图的宽高

<div id="map" style="width:100%;height:500px"></div>

初始化谷歌地图,最主要的两个参数是传一个中心点和缩放倍数,如果你点地图右下角的+号,就会再放大一倍,这里的放大倍数就指这个

var mapType = google.maps.MapTypeId.ROADMAP;
var lat = 39.915168, lng = 116.403875, zoom = 10;
var mapOptions = {
center: new google.maps.LatLng(lat, lng), //地图的中心点
zoom: zoom,           //地图缩放比例
mapTypeId: mapType,           //指定地图展示类型:卫星图像、普通道路
scrollwheel: true           //是否允许滚轮滑动进行缩放
};
var map = new google.maps.Map(document.getElementById("map"), mapOptions); //创建谷歌地图

这样在你的页面就有一个以天安门为中心的地图了

接下来,给天安门添加一个地理标志,使用谷歌自带的marker

添加一个Marker

通过上面代码的new我们已经有了个map对象,然后再创建一个marker对象,把这个marker绑定到map上

var marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(lat, lng)
});

在地图上就可以看到天安门上标记了一个地理位置图标

接下来希望点一下这个marker的时候就显示这个位置的具体地址,如下所示,首先创建一个InfoWindow,并把它绑定到上面的那个marker的上方展示,同时给marker添加一个点击事件,一点的时候就打开提示框:

var infowindow = new google.maps.InfoWindow({content: "北京市天安门" }); //创建一个InfoWindow
infowindow.open(map, marker); //把这个infoWindow绑定在选定的marker上面
//使用谷歌地图定义的事件,给这个marker添加点击事件
google.maps.event.addListener(marker, "click", function(){
infowindow.open(map,marker);
});

效果如下:

这里所有的样式都是谷歌自带的,假设这个marker的样式跟网站的风格不太一致,我想要自定义一个marker不用谷歌自带的,那怎么办呢?在上面new一个marker的时候可以再传一个icon的参数,自定义icon,同时这个icon需要使用svg的格式。

在PSD里面将UI里面的icon形状导成一个AI文件,然后再用AI导出svg,就有了icon的svg格式。打开svg文件,将里面的path、fill等作为地图icon的参数,如下:

var locationMarker = {
path: 'M22 10.5c0 .895-.13 1.76-.35 2.588C20.025 20.723 13.137 28.032 11 28 9.05 28 3.2 21.28.926 14.71.334 13.42 0 11.997 0 10.5c0-.104.013-.206.017-.31C.014 10.117 0 10.04 0 9.967c-.005-.67.065-1.112.194-1.398C1.144 3.692 5.617 0 11 0c5.416 0 9.906 3.74 10.82 8.657.112.29.18.696.18 1.31 0 .083-.013.167-.015.25.003.095.015.188.015.283zM11 5.833c-2.705 0-4.898 2.09-4.898 4.667S8.295 15.167 11 15.167s4.898-2.09 4.898-4.667c0-2.578-2.193-4.667-4.898-4.667z',
fillColor: '#E84643',
fillOpacity: 1,
strokeColor: '#E84643',
};
var marker = new google.maps.Marker({map: map, icon: locationMarker, position: new google.maps.LatLng(lat, lng)});

就可以将默认的marker样式换掉,如下所示,你也可以换成其它各种各样的形状,像房子、车等icon

检查一下刚刚添加的marker,发现最后被谷歌地图转换成了一个canvas元素:

到这里已经可以解决在地图显示北京哪里被水淹了的问题。就是在被水淹的位置添加一个个的marker,现在我希望点击marker的时候能够显示该处的图文受灾情况。可以使用上面介绍的InfoWindow,只要把参数{content: "北京市天安门" },换成{content: "<div class='detail-info'>...</div>"},然后写detail-info的样式即可。但是这样会有两个问题:

1. 不方便改变InfoWindow那个框的样式,例如没有一个直接的方法可以去掉右上角的x按钮

2. 假设有几百个地方被水淹了,也就是说得添加几百个marker,同时每个marker都得添加一个click事件,因为谷歌的事件没有marker事件委托,每个marker都得一个个加事件,一下子加几百个事件,这样就有点egg pain了。

所以说如果使用了上面的marker的方式,谷歌把它变成了一个canvas,以后所有的操作都得处处受制于谷歌的API,同时谷歌的API并不是十分的丰富和灵活。因此必须得另辟一条路,如果能够用原生的div放到谷歌地图里面那就简单多了,因为地图本身就是用div实现的,所以用原生的应该是可以的。其实只要用谷歌搜一搜就可以找到解决办法

使用原生HTML Marker

谷歌地图还提供了另外一个往地图里面加东西的OverlayView,使用这个的原理就是创建一个OverlayView对象然后给它append一个div,把这个div的position置为absolute(相对于地图的容器container),然后再设置它在这个容器的left/top位置,关键就在于怎样根据当前marker的经纬度转换为在容器的像素位置。而这个对象已经提供了一个转换方法可以调用。将自定义的Marker封装成一个类,例如现在要做一个房源的地图展示,需要把房源标在地图上,自定义一个HouseMarker的类:

function HouseMarker(latlng, map, args) {
  this.latlng = latlng; //for google map
  this.setMap(map); //for google map
  this.args = args; //自定义参数
}

再将这个HouseMarker的原型指向谷歌的OverlayView进行继承

HouseMarker.prototype = new google.maps.OverlayView();

然后实现这个原型的draw函数:

HouseMarker.prototype.draw = function() {
//创建一个div,把marker和详情框写在一起,方便后面的展示和隐藏
$div = $("<div class='marker-container'>" +
        "  <div class="marker"></div>" +
        "  <div class='detail-info'"></div>" +
        "</div>");
//将div添加到它的dom元素里面
var panes = this.getPanes();
var div = $div[0];
panes.overlayImage.appendChild(div);
//根据经纬度计算div的像素位置
var point = this.getProjection().fromLatLngToDivPixel(this.latlng);
div.style.left = (point.x - 20) + 'px'; //减掉marker宽度的一半,居中
div.style.top = (point.y - 20) + 'px'; //减掉marker高度的一半,居中
};

再调new HouseMarker,传进当前的经纬度和map对象,就可以在地图正确的位置上显示这个marker了。接下来就能够使用原生的js事件和css控制这个marker了,这样就很方便灵活了。特别是谷歌的mouse事件,即使是上一个marker盖住了下面的marker,鼠标移到上面那个marker时,仍然会触发下面那个marker的事件,这样就有点恶心了。而使用原生的mouse事件就没有这种情况。其实这个也是可以理解的,因为谷歌地图是用的一个canvas画布展示marker,在这个画布里面只根据鼠标的位置和marker的位置判断鼠标有没有进入marker里面,所以不管上面有没有被盖住,只要算出来的位置是符合的。

如下面所示,鼠标hover的时候就显示详细信息,如果这个详细信息刚好下面有个marker就会出现上面讨论的情况:

详见:Custom HTML Markers with Google Maps

第二步是的鼠标hover的时候展示详情框,最简单的就是用CSS控制即可,使用上面定义的DOM结构,初始化时让detail-info隐藏:

.marker-container .detail-info{
display: none
}

然后再设置:

.marker-container:hover .detail-info{
display: block
}

就可以了,不用一行JS

第二种办法是监听mouse事件,使用事件委托:

$("#map").on("mouseover", ".marker-container", function(){
$(this).find(".detail-info").show();
}); $("#map").on("mouseout", ".marker-container", function(){
$(this).find(".detail-info").hide();
});

用JS的进行显示和隐藏的好处是:可以对展示做一些后续的处理,这也是下面要提到的

我们已经初步解决了marker展示的问题,但其实还有一些问题:展示这些详细信息会出现超出可见区域的情况

边界判断

当这个marker比较靠边的时候,详情的框会超出显示范围:

所以需要做边界判断,不管marker在什么位置,详情框都可以在展示区域内显示,效果如下:

也就是说需要判断当前marker是否超出了地图容器能够正常显示的范围,如果超出了就要做下处理——如果太靠上就把详情展示在下面,如果太靠右详情框就不应该是和marker水平居中了,而是要往左移一移,同时把三角形的位置挪一挪。所以关键是要做一个边界判断,而做边界判断的前提是拿到marker在容器里面的left/top位置。

已经不可以再上使用上面获取位置的方法了,因为那个位置算好之后不会再变,不会跟着地图的拖动而发化变化,谷歌地图是借助transform等设置改变它的位置,而不是用position了。

但是可以拿到当前地图在这个容器里面的边界经纬度,最东、最西、最北、最南,也可以拿到这个容器的像素宽高,所以就可以知道一个像素对应地图多少经纬度,即像素/经纬度的比例ratio。同时marker的纬度是知道的,可以算一下它距离边界的经纬度dx, dy,dx除以ratio就能够换算像素值了。代码如下:

var mapBounds = mapHandler.getBounds();      //调用谷歌的api获取容器经纬度边界并做一些处理
var xRatio = (mapBounds[1] - mapBounds[0]) / mapWidth,
yRatio = (mapBounds[3] - mapBounds[2]) / mapHeight;
//marker的经纬度
var lat = marker.latlng.lat(),
lng = marker.latlng.lng();
//转换marker的像素位置
var pos = {
top: -(+lat - mapBounds[3]) / yRatio,
left: (+lng - mapBounds[0]) / xRatio,
bottom: (+lat - mapBounds[2]) / yRatio,
right: -(+lng - mapBounds[1]) / xRatio
};
var posFlag = 0,
maxLen = 150,
maxLeftLen = 118;
//右边超出
if(pos.right < maxLen) posFlag |= 1;
//上面超出
if(pos.top < maxLen) posFlag |= 2;
//左边超出
if(pos.left < maxLeftLen) posFlag |= 4;
//对超出的情况进行处理,代码略
switch(posFlag){
case 1: //右
case 2: //上
case 3: //右上
case 4: //左
case 6: //左上
}

还有一种情况是如果详情框太长了,超出了容器的一半,不管向上显示还是向下显示,marker刚好在正中间时,详情框都会超出显示范围。这种情况可以借助第二种解决办法,就是将地图移动一下,超出的就可以显示了。需要计算移动后的地图中心点在哪里,再调API提供的panTo就可以了,如下。难点是计算要正确

绘制形状

接下来再简单讨论一个高级话题,就是在谷歌地图上面绘制一个形状,然后获取该形状的地理位置。谷歌已经提供了一个叫DrawingManager的类,只要new一个对象,传些参数,就可以在地图上显示draw tool了,如下:

然后再监听这个manager的complete事件,在complete事件里面获取当前画的图形的范围,例如上面的圆可以获取到它的圆心和半径。详见:Drawing Layer (Library)

然而谷歌提供的这个工具非常的简陋,你无法直接改变上面工具栏的icon,就连画的圆边界也是扭扭曲曲的,如上所示。只提供了完成事件,没有画时候的事件,所以你没办法在画的时候加一个不断变化的、显示所画范围多少公里的提示框。

因此另外一个解决办法是自已实现一个类似的工具,通过鼠标的mousedown、mousemove、mouseup事件搭配组合,结合上面推荐的画marker的方法,插入svg元素,动态改变它的path做到实时变化的效果。这样就很灵活了,想怎么搞就怎么搞,但是代码量应该也是挺大的。

除此之外还有街景、3D控制的API,这里不再讨论,有兴趣自己查查谷歌的API。

 
 

Google地图的更多相关文章

  1. Google地图路线规划

    Google地图路线规划: 需求:给定的两点之间Google地图路径规划和详情. 代码实现: //map定义省略 var directionsDisplay = new google.maps.Dir ...

  2. Google 地图 API V3 使用入门

    Google官方教程: Google 地图 API V3 使用入门 Google 地图 API V3 针对移动设备进行开发 Google 地图 API V3 之事件 Google 地图 API V3 ...

  3. Google 地图 API V3 针对移动设备进行开发

    Google官方教程: Google 地图 API V3 使用入门 Google 地图 API V3 针对移动设备进行开发 Google 地图 API V3 之事件 Google 地图 API V3 ...

  4. Google 地图 API V3 之事件

    Google官方教程: Google 地图 API V3 使用入门 Google 地图 API V3 针对移动设备进行开发 Google 地图 API V3 之事件 Google 地图 API V3 ...

  5. Google 地图 API V3 之控件

    Google官方教程: Google 地图 API V3 使用入门 Google 地图 API V3 针对移动设备进行开发 Google 地图 API V3 之事件 Google 地图 API V3 ...

  6. Google 地图 API V3 之 叠加层

    Google官方教程: Google 地图 API V3 使用入门 Google 地图 API V3 针对移动设备进行开发 Google 地图 API V3 之事件 Google 地图 API V3 ...

  7. 根据google地图抓去全国信息- 抓去全国小区以及新建楼盘信息

    本案例由于google每天每个账户能post20000次所以我们需要相对较长的时间来抓去google的数据信息. 主要思路:通过一定的zoom一个相对较大的zoom.我们尽可能的搜索我们的所有数据. ...

  8. 如何获取google地图、baidu百度地图的坐标

    google:打开google地图-->查找目的地-->右键:此位置居中-->地址栏键入javascript:void(prompt('',gApplication.getMap() ...

  9. GOOGLE地图坐标拾取方法、GOOGLE地图获取坐标方法

    方法一: 打开google地图-->查找目的地-->右键:此位置居中--> 打开IE浏览器(百度浏览器.GOOGLE浏览器.360浏览器均不行,只能在IE中获取),在IE浏览器的地址 ...

  10. Flex加载google地图、百度地图以及天地图作底图

    一  Flex加载Google地图作底图 (1)帮助类GoogleLayer.as /* * 根据输入的地图类型加载Google地图(by chenyuming) */ package Layers ...

随机推荐

  1. Floyd 算法 打印路径模板

    #include <iostream> #include <cstdlib> #include <cstdio> #include <algorithm> ...

  2. Linux 下 的 cc 和 gcc

    在Linux下一会看到cc,另一会又看到gcc,感觉又点混乱的样子.它们是同一个东西么,有啥区别呢? 一分为二地看: 首先,如果讨论范围在Unix和Linux之间,那么cc和gcc不是同一个东西.cc ...

  3. Visual Studio创建跨平台移动应用_02.Cordova Extension

    1简介 本章节是关于Visual Studio Tools for Apache Cordova的,目前此产品只发布了预览版.Visual Studio for Apache Cordova帮助熟悉V ...

  4. 基本 vi 命令

    前言 本文内容翻译 Basic vi Command, Colorado State University, 翻译原文的大部分内容. 本文介绍的是 UNIX 下的 vi 编辑器,和 Linux 下常用 ...

  5. Linux 下最为人熟知的解压缩工具

    很多时候,通过互联网发送或接收大文件和图片是一件令人头疼的事.压缩及解压缩工具正好可以应对这个问题.下面让我们快速浏览一些可以使得我们的工作更加轻松的开源工具. Tar Tar 由 ‘Tape arc ...

  6. Java 程序员必须掌握的 Linux 命令

    作为一个Java开发人员,有些常用的Linux命令必须掌握.即时平时开发过程中不使用Linux(Unix)或者mac系统,也需要熟练掌握Linux命令.因为很多服务器上都是Linux系统.所以,要和服 ...

  7. mybatis3-generator-plugin插件地址

    http://repo1.maven.org/maven2/com/github/oceanc/mybatis3-generator-plugin/0.4.0/

  8. AFNetworking (3.1.0) 源码解析 <二>

    这次讲解AFHTTPSessionManager类,按照顺序还是先看.h文件,注释中写到AFHTTPSessionManager是AFURLSessionManager的子类,并且带有方便的HTTP请 ...

  9. JVM调优之jstack找出最耗cpu的线程并定位代码

    jstack可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多.下面我们来一个实例找出某个Java进程中最耗费CPU的Java线程并定位堆栈信息,用到的命令有 ...

  10. 再探java基础——零碎基础知识整理

    1.java是解释型语言.java虚拟机能实现一次编译多次运行. 2.JDK(java software Development kit 软件开发包),JRE(java Runtime Environ ...