google地图测距原码

http://hi.baidu.com/shirdrn/blog/item/a7204afc716641fbfc037f85.html

 

 

Google Maps自带实例多点测距尺实现的功能就是可以根据用户标出的任意多点,从而返回计算得到的各个点之间的距离。也就是说,计算出了一条折线的长度——是在地理上的实际距离。

实例代码如下所示:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Google 地图 API 示例 - 多点测距尺</title>
<script src="http://ditu.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAUbR6g0RGGvD3wc8nEOCNZhQ1-r6OYR-mHk-jCzYLxV7O5V1vkBR9GY27V0NNKkh2ASTbqJMDYJeu2A"
    type="text/javascript"></script>

<!-- 包含自定义控件的源文件。引入GRulerControl控件。 -->
<script src="ruler.js" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[

// Copyright 2007 Google Inc.
// All Rights Reserved.

/**
   * 这个例子演示了 Google 地图 API 的以下功能:
   *   * 使用自定义控件
   *
   * 注意:为了在 IE6 中正常显示折线,必须在网页的 <HTML> 标签中加上:
   *   <html xmlns:v="urn:schemas-microsoft-com:vml">
   *
   * @author haogang
   */

/**
   * 创建地图控件,并添加一些基本的控件
   */

function load() {

    if (GBrowserIsCompatible()) {
      var map = new GMap2(document.getElementById('map'));
     
      // 给地图添加内置的控件,分别为:
      // 平移及缩放控件(左上角)、比例尺控件(左下角)、缩略图控件(右下角)
      map.addControl(new GLargeMapControl());
      map.addControl(new GScaleControl());
      map.addControl(new GOverviewMapControl());
     
      // 添加自定义的控件
      map.addControl(new GRulerControl());

      // 将视图移到自己定义的位置
      map.setCenter(new GLatLng(43.832, 125.322), 14);
    }
}

//]]>
</script>
</head>
<body οnlοad="load()" οnunlοad="GUnload()">
    <div id="intro" style="width: 700px;">
    <p><b>提示: </b>单击地图添加多个标记,设定您的路径。您还可以随意地修改现有路径。试试看在已有的标记上单击,或者拖拽它们!</p>
    </div>
    <div id="map" style="width: 700px; height: 500px;"></div>
</body>
</html>

运行代码后的地图视图如图所示:

 

从代码可以看到似乎实现比较容易,不过就是向创建的地图中添加一系列控件:

var map = new GMap2(document.getElementById('map'));
     
     // 给地图添加内置的控件,分别为:
      // 平移及缩放控件(左上角)、比例尺控件(左下角)、缩略图控件(右下角)
      map.addControl(new GLargeMapControl());
      map.addControl(new GScaleControl());
      map.addControl(new GOverviewMapControl());
     
      // 添加自定义的控件
      map.addControl(new GRulerControl());

      // 将视图移到自己定义的位置
      map.setCenter(new GLatLng(43.832, 125.322), 14);

GLargeMapControl、GScaleControl、GOverviewMapControl、GRulerControl,其中,GRulerControl控件是一个自定义控件。

实际上,这个实例的经典之处在于一个GRulerControl控件的实现,可以从引入的ruler.js文件中看到实现过程,代码如下:

/**
* 这个例子演示了 Google 地图 API 的以下功能:
*   * 可拖拽的标记
*   * 在地图上叠加折线
*   * 计算地理距离
*   * 事件处理(单击、拖拽)
*   * 信息窗口
*   * 利用链表维护各种对象
*   * 自定义控件
*
* 注意:为了在 IE6 中正常显示折线,必须在网页的 <HTML> 标签中加上:
*   <html xmlns:v="urn:schemas-microsoft-com:vml">
*
* @author haogang
*/

/**
* 本示例用一个双向链表维护用户设定的标记,能够容易的实现标记的遍历和删除
* 每个链表结点 m 有如下字段:
*    m.prev      前驱标记
*    m.next      后继标记
*    m.segPrev   连接本标记与前驱标记的线段
*    m.segNext   连接本标记与后继标记的线段
*/

function GRulerControl() {
var me = this;

// 可国际化的字符串
me.RESET_BUTTON_TITLE_ = '清除所有测距标记';
me.ENABLE_BUTTON_TITLE_ = '添加测距标记已启用,单击这里禁用';
me.DISABLE_BUTTON_TITLE_ = '添加测距标记已禁用,单击这里启用';
me.DELETE_BUTTON_TITLE_ = '删除';

me.RESET_BUTTON_IMAGE_ = 'images/ruler_clear.png';
me.ENABLE_BUTTON_IMAGE_ = 'images/ruler_enabled.png';
me.DISABLE_BUTTON_IMAGE_ = 'images/ruler_disabled.png';
me.BACKGROUND_IMAGE_ = 'images/ruler_background.png'

me.KILOMETER_ = '公里';
me.METER_ = '米';
}

GRulerControl.prototype = new GControl();

/**
* 初始化标尺控件
*/

GRulerControl.prototype.initialize = function(map) {
var me = this;
var container = document.createElement('div');
me.setButtonStyle_(container);

// “启用/禁用”按钮
var btnEnable = document.createElement('img');
btnEnable.width = btnEnable.height = 19;
GEvent.addDomListener(btnEnable, 'click',
    function() {
      me.setEnabled(!me.isEnabled());
    }
);
container.appendChild(btnEnable);

// “重置”按钮
var btnReset = document.createElement('img');
btnReset.width = btnReset.height = 19;
btnReset.src = me.RESET_BUTTON_IMAGE_;
btnReset.title = me.RESET_BUTTON_TITLE_;
GEvent.addDomListener(btnReset, 'click',
    function() {
      me.reset();
    }
);
container.appendChild(btnReset);

// 距离标签
var txtInfo = document.createElement('div');
txtInfo.style.font = 'small Arial';
txtInfo.style.fontWeight = 'bold';
txtInfo.style.fontSize = '9pt';
txtInfo.style.width = '82px';
container.appendChild(txtInfo);

// 初始化内部变量
map.rulerControl_ = me;
me.map_ = map;
me.head_ = new Object();
me.tail_ = new Object();
me.head_.next_ = me.tail_;
me.tail_.prev_ = me.head_;
me.btnEnable_ = btnEnable;
me.btnReset_ = btnReset;
me.txtInfo_ = txtInfo;
me.setEnabled(true);

map.getContainer().appendChild(container);
return container;
}


/**
* 设置控件的格式
*/

GRulerControl.prototype.setButtonStyle_ = function(button) {
button.style.backgroundImage = 'url(' + this.BACKGROUND_IMAGE_ + ')';
button.style.font = 'small Arial';
button.style.border = '1px solid #888888';
button.style.padding = '4px';
button.style.textAlign = 'right';
button.style.cursor = 'pointer';
}

/**
* 用恰当的格式表示距离
*/
GRulerControl.prototype.formatDistance_ = function(len) {
var me = this;

len = Math.round(len);
if (len <= 1000) {
    return len + ' ' + me.METER_;
} else if (len <= 1000000) {
    return len / 1000 + ' ' + me.KILOMETER_;
}
return Math.round(len / 1000) + ' ' + me.KILOMETER_;
}

/**
* 格式化角度为字符串
*/

GRulerControl.prototype.formatDegree_ = function(value) {
value = Math.abs(value);
var v1 = Math.floor(value);
var v2 = Math.floor((value - v1) * 60);
var v3 = Math.round((value - v1) * 3600 % 60);
return v1 + '°' + v2 + '/'' + v3 + '"';
}

/**
* 格式化经纬度为字符串
*/

GRulerControl.prototype.formatLatLng_ = function(pt) {
var me = this;

var latName, lngName;
var lat = pt.lat();
var lng = pt.lng();
latName = lat >= 0 ? '北纬' : '南纬';
lngName = lng >= 0 ? '东经' : '西经';

return lngName + me.formatDegree_(lng) + ','
    + latName + me.formatDegree_(lat);
}

/**
* 返回控件的默认位置
*/
GRulerControl.prototype.getDefaultPosition = function() {
return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(8, 8));
}

/**
* 返回控件是否已启用
*/

GRulerControl.prototype.isEnabled = function() {
return this.enabled_;
}

/**
* 设置控件的“启用/禁用"状态
*/

GRulerControl.prototype.setEnabled = function(value) {
var me = this;
if (value == me.enabled_)
    return;
me.enabled_ = value;

if (me.enabled_) {
    me.mapClickHandle_ = GEvent.addListener(me.map_, 'click', me.onMapClick_);
    me.txtInfo_.style.display = 'block';
    me.btnReset_.style.display = 'inline';
    me.btnEnable_.src = me.ENABLE_BUTTON_IMAGE_;
    me.btnEnable_.title = me.ENABLE_BUTTON_TITLE_;
    me.updateDistance_();
} else {
    GEvent.removeListener(me.mapClickHandle_);
    me.txtInfo_.style.display = 'none';
    me.btnReset_.style.display = 'none';
    me.btnEnable_.src = me.DISABLE_BUTTON_IMAGE_;
    me.btnEnable_.title = me.DISABLE_BUTTON_TITLE_;
}
}

/**
* 事件处理函数:当用户单击地图时,要在该位置添加一个标记
*/

GRulerControl.prototype.onMapClick_ = function(marker, latlng) {
var me = this.rulerControl_;

// 如果用户单击的是标记,不再这里处理
if (marker)
    return;

// 创建标记,并添加到链表中
var newMarker = new GMarker(latlng, {draggable: true});

var pos = me.tail_.prev_;
newMarker.prev_ = pos;
newMarker.next_ = pos.next_;
pos.next_.prev_ = newMarker;
pos.next_ = newMarker;

// 为标记添加事件处理函数:拖拽标记时要更新连接线段和距离
GEvent.addListener(newMarker, 'dragend',
    function() {
      me.map_.closeInfoWindow();
      me.updateSegments_(newMarker);
      me.updateDistance_();
    }
);
// 为标记添加事件处理函数:单击标记时要显示信息窗口
GEvent.addListener(newMarker, 'click',
    function() {
      newMarker.openInfoWindow(me.createInfoWindow_(newMarker));
    }
);

// 将创建的标记添加到地图中
me.map_.addOverlay(newMarker);

if (newMarker.prev_ != me.head_) {
    // 如果这不是第一个标记,则创建连接到上一个标记的线段,并显示在地图中
    var segment = [newMarker.prev_.getLatLng(), latlng];
    newMarker.segPrev_ = new GPolyline(segment);
    newMarker.prev_.segNext_ = newMarker.segPrev_;
    me.map_.addOverlay(newMarker.segPrev_);

    // 更新距离显示
    me.updateDistance_();
}
}

/**
* 统计总距离,并显示在网页中
*/

GRulerControl.prototype.updateDistance_ = function() {
var me = this;
var len = me.getDistance();

// 结果显示在网页中
me.txtInfo_.innerHTML = me.formatDistance_(len);
}

/**
* 遍历链表,统计总距离
*/

GRulerControl.prototype.getDistance = function() {
var me = this;
var len = 0;

// 周游链表,累计相邻两个标记间的距离
for (var m = me.head_; m != me.tail_; m = m.next_) {
    if (m.prev_ && m.prev_.getLatLng)
      len += m.prev_.getLatLng().distanceFrom(m.getLatLng());
}
return len;
}

/**
* 清除所有标记,初始化链表
*/

GRulerControl.prototype.reset = function() {
var me = this;

for (var m = me.head_.next_; m != me.tail_; m = m.next_) {
    me.map_.removeOverlay(m);
    if (m.segNext_)
      me.map_.removeOverlay(m.segNext_);
}
me.head_ = new Object();
me.tail_ = new Object();
me.head_.next_ = me.tail_;
me.tail_.prev_ = me.head_;

me.updateDistance_();
}


/**
* 事件处理函数:当用户拖拽标记、标记坐标改变时被调用,这里要更新与该标记连接的线段
* @param {GMarker} marker 被拖拽的标记
*/
GRulerControl.prototype.updateSegments_ = function(marker) {
var me = this;
var segment;

// 更新连接前驱的线段
if (marker.segPrev_ && marker.prev_.getLatLng) {
    // 从地图上删除旧的线段
    me.map_.removeOverlay(marker.segPrev_);
   
    // 根据标记的当前坐标构造新的线段,并更新链表结点的相关字段
    segment = [marker.prev_.getLatLng(), marker.getLatLng()];
    marker.segPrev_ = new GPolyline(segment);
    marker.prev_.segNext_ = marker.segPrev_;
   
    // 将新线段添加到地图中
    me.map_.addOverlay(marker.segPrev_);
}

// 更新连接后继的线段,与上类似
if (marker.segNext_ && marker.next_.getLatLng) {
    me.map_.removeOverlay(marker.segNext_);
    segment = [marker.getLatLng(), marker.next_.getLatLng()];
    marker.segNext_ = new GPolyline(segment);
    marker.next_.segPrev_ = marker.segNext_;
    me.map_.addOverlay(marker.segNext_);
}
}


/**
* 为信息窗口创建 DOM 对象,包括标记的坐标和“删除”按钮
* @param {GMarker} marker 对应的标记
*/

GRulerControl.prototype.createInfoWindow_ = function(marker) {
var me = this;

// 为气泡提示窗口创建动态 DOM 对象,这里我们用 DIV 标签
var div = document.createElement('div');
div.style.fontSize = '10.5pt';
div.style.width = '250px';
div.appendChild(
    document.createTextNode(me.formatLatLng_(marker.getLatLng())));
   
var hr = document.createElement('hr');
hr.style.border = 'solid 1px #cccccc';
div.appendChild(hr);

// 创建“删除”按钮
var lnk = document.createElement('div');
lnk.innerHTML = me.DELETE_BUTTON_TITLE_;
lnk.style.color = '#0000cc';
lnk.style.cursor = 'pointer';
lnk.style.textDecoration = 'underline';

// 为“删除”按钮添加事件处理:调用 removePoint() 并重新计算距离
lnk.onclick =
    function() {
      me.map_.closeInfoWindow();
      me.removePoint_(marker);
      me.updateDistance_();
    };
div.appendChild(lnk);

// 当用户关闭信息窗口时 Google 地图 API 会自动释放该对象
return div;
}


/**
* 事件处理函数:当用户选择删除标记时被调用,这里要删除与该标记连接的线段
* @param {GMarker} marker 要删除的标记
*/
GRulerControl.prototype.removePoint_ = function(marker) {
var me = this;

// 先从地图上删除该标记
me.map_.removeOverlay(marker);

// 对于中间结点,还要把它的前驱和后继用线段连接起来
if (marker.prev_.getLatLng && marker.next_.getLatLng) {
    var segment = [marker.prev_.getLatLng(), marker.next_.getLatLng()];
    var polyline = new GPolyline(segment);
    marker.prev_.segNext_ = polyline;
    marker.next_.segPrev_ = polyline;
    me.map_.addOverlay(polyline);
}
marker.prev_.next_ = marker.next_;
marker.next_.prev_ = marker.prev_;

if (marker.segPrev_)
    me.map_.removeOverlay(marker.segPrev_);
if (marker.segNext_)
    me.map_.removeOverlay(marker.segNext_);
}

我们主要是学实例的实现思想,只要掌握了它的设计思想,才能够编写出我们需要的更好的Google Maps控件。

看一下GRulerControl控件的结构,将GRulerControl控件类的属性和方法拿出来:

   function GRulerControl() {
    var me = this;
   
   // 可国际化的字符串
    me.RESET_BUTTON_TITLE_ = '清除所有测距标记';
    me.ENABLE_BUTTON_TITLE_ = '添加测距标记已启用,单击这里禁用';
    me.DISABLE_BUTTON_TITLE_ = '添加测距标记已禁用,单击这里启用';
    me.DELETE_BUTTON_TITLE_ = '删除';

    me.RESET_BUTTON_IMAGE_ = 'images/ruler_clear.png';
    me.ENABLE_BUTTON_IMAGE_ = 'images/ruler_enabled.png';
    me.DISABLE_BUTTON_IMAGE_ = 'images/ruler_disabled.png';
    me.BACKGROUND_IMAGE_ = 'images/ruler_background.png'

    me.KILOMETER_ = '公里';
    me.METER_ = '米';
}
GRulerControl.prototype = new GControl(); // 编写的任何控件应该实现GControl控件接口(或者说继承自GControl类)。
// GRulerControl控件的可用行为(类的成员方法)
GRulerControl.prototype.initialize = function(map) {...}; // 初始化标尺控件,主要是初始化标尺内具有的其它部件,以及定义链表。
GRulerControl.prototype.setButtonStyle_ = function(button) {...}; // 设置标尺上按钮的样式,按钮为图片按钮。
GRulerControl.prototype.formatDistance_ = function(len) (...); // 格式化数据。因为获取到的经纬度坐标值都是精度很高的浮点数,计算转化为容易读取的常规距离数值,例如单位是公里。
GRulerControl.prototype.formatDegree_ = function(value) {...}; // 格式化角度为字符串。
GRulerControl.prototype.formatLatLng_ = function(pt) {...}; // 格式化经纬度为字符串。
GRulerControl.prototype.getDefaultPosition = function() {...}; // 设置GRulerControl控件的默认位置,并返回GControlPosition对象。
GRulerControl.prototype.isEnabled = function() {...}; // 当前的GRulerControl控件是否可以使用。
GRulerControl.prototype.setEnabled = function(value) {...}; // 设置当前的GRulerControl控件为禁用/启用状态。
GRulerControl.prototype.onMapClick_ = function(marker, latlng) {...}; // 事件处理:当鼠标点击地图,在该点添加一个标记(GMarker)。
GRulerControl.prototype.updateDistance_ = function() {...}; // 即时刷新地图,计算总的距离,并在地图上显示出来。
GRulerControl.prototype.getDistance = function() {...); // 计算折线表示的总距离。
GRulerControl.prototype.reset = function() {...}; // 清除当前地图上显示的所有标记(GMarker),并且初始化(重置)链表。
GRulerControl.prototype.updateSegments_ = function(marker) {...}; // 事件处理函数:当用户拖拽标记、标记坐标改变时被调用,这里要更新与该标记连接的线段。
GRulerControl.prototype.createInfoWindow_ = function(marker) {...}; // 为信息窗口创建 DOM 对象,包括标记的坐标和“删除”按钮。
GRulerControl.prototype.removePoint_ = function(marker) {...}; // 事件处理函数:当用户选择删除标记时被调用,这里要删除与该标记连接的线段

现在来看,就一目了然了,你可以通过运行程序,作为前端用户,体验一下,如图所示:

而且,还可以选择删除指定的已经添加的标记,如图所示:

例如删除指定的那个标记以后,地图变成:

还可以根据需要,拖动已经添加到地图上的任意标记,来适当调整它的所在位置。

根据在地图上的操作来总结该实例的功能,然后对照这些方法名列表,思考一下如果让我们自己来些,你会如何实现。

说一下,该实例中用到的数据结构:双向链表。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值