Google地圖開發總結

来源:http://www.cnblogs.com/yincheng/archive/2016/08/21/google-map.html
-Advertisement-
Play Games

我們經常使用地圖查位置、看公交、看街景,同時地圖還開放第三方的API給開發者。利用這些API進行地圖的個性化的展示和控制,例如北京被水淹了,開發一個網頁顯示北京被淹的地圖,地圖上面標誌被水淹的位置、嚴重程度,或者我是交警,想要在地圖上標誌發生車禍、被交通管制的路段,甚至是利用地圖的街景,控制街景的位 ...


我們經常使用地圖查位置、看公交、看街景,同時地圖還開放第三方的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添加到它的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。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Python中的變數不需要聲明,每個變數使用前必須賦值,變數賦值後才會被創建,在Python中變數就是變數,它沒有類型。我們所說的"類型"是變數所指的記憶體中對象的類型。 等號(=)用來給變數賦值,等號(=)運算符左邊是變數名,等號(=)運算符右邊是存儲在變數名中的值。例如: 執行以上程式會輸出以下結 ...
  • 好不容易寫出來一些程式,最後各種錯誤,就是運行不了。最後還得乖乖的 打斷點 ,運用 輸出語句,找出問題具體在什麼地方,進行修改。 1、從本地找cookie。IE-工具-Internet選項-瀏覽歷史紀錄-設置-查看文件。和從C盤找結果是一樣的。 2、簡單設置編碼。 Myeclipse-Windows ...
  • 1.前言 前面講了一些關於python的一些基本的語法及語句,在這個地方繼續講python的方法及其python核心概念的面向對象。我們都知道python被稱為面向對象的語言,那麼在這裡我們將正式的接觸 的python的核心。 2.函數 定義:是可調用的,執行某種行為並返回一個值。判斷一個函數是否可 ...
  • 緩存集群管理 系統化管理資源、節點,統一緩存版本,開發人員無需關心底層基礎設施,簡化 運維複雜度,提供統一的系統化運維監控管理。 自動化運維部署平臺 應用監控 開源項目 總結 用自動代替人工; 用小系統驅動打團隊; 用基礎平臺支撐上層應用。 --------... ...
  • fsbanner是一款自定義功能豐富的響應式網站Banner手風琴特效jQuery插件。該手風琴特效相容性很好,支持點擊和滑鼠滑過等觸發事件,並且可添加標題或描述。 線上實例 使用方法 複製 複製 參數詳解 下載 ...
  • 工程下安裝XTemplate並使用它的方法實例說明: 1.安裝xtpl npm install xtpl xtemplate --save 2.在views目錄添加test.xtpl文件,其內容為 this is {{title}}! 4.集成到Express中,只需要在app.js中,設置模板引擎 ...
  • 第一次寫博,還蠻激動。。。 看到了三題經典題型,經老師講解後,對此類題目有了更深刻的認識 就我目前的認識對此題進行總結。如有錯誤,敬請指正 首先,我們先明確一下JS引擎的工作步驟: js引擎工作分為兩步: 1.將這個js中的變數和函數聲明保存到當前(註意,是當前)執行環境的變數對象中 2.再逐行解析 ...
  • 一、計時器 setInterval ( 函數/名稱 , 毫秒數 )表示每經過一定的毫秒後,執行一次相應的函數(重覆) setTimeout ( 函數/名稱 , 毫秒數 ) 表示經過一定的毫秒後,只執行一次相應的函數(不重覆) 清除計時器: clearInterval( ); clearTimeout ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...