利用現成範本在Google地圖上畫圓圈(新版)

online

一位網友問我如何加工製作〈新加坡為什麼沒有核電廠〉文中所附的google地圖。

文中有三張地圖,應用的標記與圖形套疊技術各不相同,但基本上都利用Google Maps JavaScript API V3。若有JavaScript程式基礎,學這一套不難,可從其基本說明文件()入手,再到Google Code Playground練習,最快不到一小時就可以上手。

沒學過JavaScript怎麼辦?有鑑於此,我在這裡提供一個「套公式」的解決方案,讓使用者只需套用範本,改幾個數字與文字。打個比方;既然釣魚太麻煩,乾脆就從市場把魚帶回家,照食譜烹調吧。

在地圖上標示一個圓形區域,這種呈現方式相當常見,從交通、商業、刑案偵察乃至於軍事都派得上用場。由於我的時間有限,而且不想一開始就弄得太複雜,暫時僅介紹這種技巧。至於我在為那篇〈新加坡…〉製作地圖時使用的一些外掛程式與較麻煩的製圖技巧,這裡就先略過。

閒話休敘,現在就直接上工吧。

第一步,準備工作。您首先需知道圓心地點的經緯座標。這裡介紹兩種方式:

  • 啟用Google Maps選項(網頁右上的齒輪)中的實驗室(Labs)所提供的LatLng Tooltip功能。然後在地圖找到您的目標,按住Shift鍵,用滑鼠點選目標位置。
  • 或利用網站Geohash,它提供的數值精確度較高。在其主網頁上拖曳「#」標誌到所選目標處,再點選Go,地圖上方就會顯現出其經緯度座標。

請記下查到的座標。

如果您要標示數個圓圈,建議您或使用其中一個座標作為全圖的中心點,或另外像我這裡提供的範例那樣另外找個地點(本文範例地圖所選用的是台灣地理中心)。

第二步,下載這個檔案,解壓縮,以純文字編輯器(例如微軟系統的「記事本」)或任何可編輯HTML的軟體開啟。個人建議使用Notepad++

第三步,更改參數。這是整個工作的重頭戲。

1) 基本原則:注意可更改與不可更改。

2) 第一個要改動的地方是網頁標題:


<title>台灣各核電廠周圍三十公里區域</title>

3) 然後改程式碼中的參數。以下加上灰色網底的那幾行皆可改。改什麼?很簡單,等號右邊的部份,若有阿拉伯數字,就只改數字;若無數字,就改文字。行末的那些「//綠色字」則為說明文字(全部改完後可以刪除)請注意參數前後的標點符號,切勿刪除任何一個

    <script type="text/javascript">
	var g = google.maps;
	var map;
	var c = new g.LatLng(23.972583, 120.979361);//地圖中心座標
	var z = 7;//地圖縮放比例(數值越大,細節越清楚)
	var n = 4;//圓圈數
	var p = new Array();//圓心位置
	p[1] = new g.LatLng(25.286747, 121.587753);//圓心座標。以下同類各項視需要增刪
	p[2] = new g.LatLng(25.20267, 121.662555);
	p[3] = new g.LatLng(21.958092, 120.751612);
	p[4] = new g.LatLng(25.03861, 121.92417);
	var l = 30000;//半徑,單位為公尺
	var bc = '#000000';//圓周顏色
	var fc = '#FF0000';//圓內顏色
	var ic = 'img/radiation.png';//標誌圖案,請自訂檔名、路徑;若為'',則恢復預設標誌
	var m = new Array();//標誌物件
	var r = new Array();//圓圈物件
	var t = new Array();//標誌說明文字
	t[1] = "台灣核一廠";//標誌說明文字內容。以下同類各項視需要增刪
	t[2] = "台灣核二廠";
	t[3] = "台灣核三廠";
	t[4] = "台灣核四廠";
	var wo = true;//是否顯示地圖說明視窗。true=是;false=否
	var wc = true;//是否自動關閉地圖說明視窗。true=是;false=否
	var wt = 5000;//自動關閉地圖說明視窗預設時間,計時單位為千分之一秒
	var mt = '台灣各核電廠周圍三十公里區域';//地圖總說明
	var w = new g.InfoWindow({
		content: mt, position: c
        });
	function addCircles() {
    		for (i = 1; i <= n; i++) {
			m[i] = new g.Marker({map: map,position: p[i],icon: ic,zIndex: 80 - i,title: t[i]});
			r[i] = new g.Circle({map: map,radius: l,fillColor: fc,strokeWeight: 1,strokeColor: bc});
			r[i].bindTo('center', m[i], 'position');
		}
	}
	function load() {
  		var mapDiv = document.getElementById('map-canvas');
  		map = new g.Map(mapDiv, {center: c,zoom: z,mapTypeId: g.MapTypeId.TERRAIN});
		x =addCircles();
		if(wo==true){
			w.open(map);
			if(wc==true){
				var o =setTimeout("w.close()",wt);
			}
		}
	}
     </script>
​

4) 我提供的範本有四個圓。這未必符合您的計畫,所以

  • 若您只要畫一個,則請將n設為1,然後刪除p[2]、p[3]、p[4]以及t[2]、t[3]、t[4]那幾行;
  • 或者,若您要畫更多,就請另加 p[5]、p[6]等等,最後記得改變數 n 的值。

5) 在第21行中,變數ic對應的網址就是這個輻射標誌檔案。它八成不符合您的需求。所以:

  • 若您只想使用Google Maps預設的圖釘式標誌圖案,則直接刪除範本中那行程式的網址,讓整行變成 var ic=”;
  • 若要改用其它圖案,可以上這個網站:google-maps-icons找尋,然後使用「複製圖片網址」,來取代我所提供的png圖片網址。
    另,該網站已有新網址Map Icons Collection,新家裡有更多、更漂亮的圖案,但基本上不提供檔案直接外連,而需自行下載、然後上傳到自己的網站(為圖方便,我繼續沿用舊站可供外連的圖案)。

全部改完後,記得儲存檔案。然後直接用瀏覽器打開您的檔案,看看還需要調整什麼地方。

若有疑問或建議,歡迎提出。

關於本文的 33 則留言

  1. 感謝版主的教學文,我也套用您的範例於自己的作品之中。不過可否再回答以下問題,謝謝。
    一、如何呈現沒有「標記」的圖示,例如台北盆地中的那灘水?
    二、如何呈現可以「勾選」四個核電廠的參數?
    三、如何讓每個圈圈可以有不同「大小」、「顏色」或「不規則多邊形」?
    四、「標記」或「圈圈」之內可插入數據或其他內容?

  2. 您好 我想請問一下 如果一開始就要顯示 為地圖形態(不顯示地型圖)要如何修改?
    謝謝

  3. 你好,想請問一下需要不同半徑大小的圓,以及自動縮放到與視窗大小相同要如何修改,
    謝謝。

    1. 若半徑不同,則為半徑變數設定數列。其具體作法為:
      首先,刪除第18列,並在該處插入以下5行(請自行設定等號後的數值):

      	var d = new Array();//半徑,單位為公尺
      	d[1] = 30000;
      	d[2] = 40000;
      	d[3] = 50000;
      	d[4] = 60000;​

      然後把第39列改為:​

      r[i] = new g.Circle({map: map,radius: d[i],fillColor: fc,strokeWeight: 1,strokeColor: bc});​

      若要改為與視窗同寬(或/且)同高,則在第43列至第44列之間插入:​

      document.getElementById("map-canvas").style.width = window.innerWidth + "px";
      document.getElementById("map-canvas").style.height = window.innerHeight + "px";​

      如此一來,第63列的width與height設定就等於無用,可以刪除。

      您可以下載全部改完後的檔案
      (注意:以上所言之列號所對應的是本文原來所附的檔案,不是修改後的這個版本)。

  4. 您好,請問一下如果想將此一圓圈所涵蓋的區加顏色,有辦法辦到嗎?
    ex:核一廠在石門區 他的圓圈會涵蓋到金山&三芝區 那我要如何將金山&三芝加顏色

  5. 請問這個範本可以搭配”我的地圖”的功能使用嗎?
    例如:把50個同學的地址都轉成座標,匯入”我的地圖”後,再看哪些人落在核電場危險範圍內。

    1. 1. 打開所欲使用的那一份「我的地圖」,複製其網址之中等號(=)後的代碼
      例如:
      https://www.google.com/maps/d/u/0/viewer?mid=1CxWP4MA5yYWr9Yr892eP5hGO1J4
      則取:
      1CxWP4MA5yYWr9Yr892eP5hGO1J4

      2. 在本文範例程式碼之第45、46兩行之間插入

      	var myMapsId = '1CxWP4MA5yYWr9Yr892eP5hGO1J4';
      	new google.maps.KmlLayer({
      		map: map,
      		url: 'https://www.google.com/maps/d/kml?mid=' + myMapsId
      	});
      

      將第一行的「1CxW … O1J4」替換成您的地圖網址代碼。

      That’s it!

  6. 不好意思,請問有辦法更改圓內的顏色嗎?或是取消掉只保留圓圈?。因為我圓的中央的顏色深到我看不清楚

    1. 改動第20行的「#FF0000」。

      若改為「#eeeeee」(或#eee),則為近白之灰色。改為「transparent」則為透明無色。

  7. 不好意思!我依您提供的方式已正確繪製出我要的範圍,但是我希望地圖可以秀出停車場的位置,請問該如何修改,麻煩你!

    1. 只要新增幾行,而且每一行都是皆改寫自既有的程式。您既已成功利用原版,必可迅速上手。

      第一步:在第42行與第43行之間插入以下的function,並配合您的需求而改寫之。

      	function moreMarkers(){//加入其它標誌
      		var mm =new Array();//標誌物件
      		var mmn = 2;//標誌數,自訂
      		var mmp = new Array();//標誌座標
      		var mmt = new Array();//標誌說明
      		var mmic = 'p.png';//標誌圖案。注意:請另準備圖檔,並自訂檔案名稱與網址
      		mmp[1] = new g.LatLng(25.047534, 121.505972);//標誌座標。自訂。以下同類各項視需要增刪
      		mmp[2] = new g.LatLng(25.031780, 121.537145);
      		mmt[1] = "洛陽停車場";//標誌說明文字內容。自訂。以下同類各項視需要增刪
      		mmt[2] = "大安森林公園地下停車場";
          	for (i = 1; i <= mmn; i++) {
      			mm[i] = new g.Marker({map: map,position: mmp[i],icon: mmic, zIndex: 900 - i,title: mmt[i]});
      		}
      	}
      

      第二步(=最後一步):在第46行與第47行之間插入

      	moreMarkers();
      

      改寫後的結果 :)

  8. 您好,我想請教
    我是使用 Google 的 My Map。
    我想要建立的是假設我地圖內有30個點
    當我點選某個點時顯示該點半徑1000公尺內有無其他點位。

    請問該如何做呢??

    1. 兩步驟。首先,用以下7行取代原程式的第36-42行:

      function seqAddCircle() {
      	i = r.length;
      	i = i + (1 - Math.sign(i));
      	m[i] = new g.Marker({map: map,position: p[i],icon: ic,zIndex: 80 - i, title: t[i]});
      	r[i] = new g.Circle({map: map,radius: l,fillColor: fc,strokeWeight: 1, strokeColor: bc});
      	r[i].bindTo('center', m[i], 'position');
      }
      

      然後,用以下3行取代原程式的第46行:

      map.addListener('click', function() {
      	seqAddCircle();
      });
      

      改寫後的結果 :)

      2016-10-16 18:07 修正錯誤連結

  9. 謝謝您的解答。但,我沒表達清楚,我真正想請教的是,點擊時,以『點擊點為中心點』畫圓。例如點第一點,以第一點為中心出現圓;點第二點,以第二點為中心又山現一個圓。謝謝!

    1. 不客氣。
      在Google Maps API的架構中,點擊地圖與點擊標示是兩件事。
      實際作法:回到本文範例,改寫其中三個部份
      1. 將原程式之第22、23行改為

      var m; 
      var r; 
      

      2. 以下列程式取代原程式之第32-46行:

      function addMarkers(){
      	for (i = 1; i <= n; i++) {
      		m = new g.Marker({
      			map: map,
      			position: p[i],
      			icon: ic,
      			zIndex: 80 - i,
      			title: t[i]
      		});
      		m.addListener('click', function(event) {
      			clickPoint = event.latLng;
      			redrawCircle(clickPoint);
      		});
      	}	
      }
      function redrawCircle(center) {
      	//if (r){
      	//	r.setMap(null);
      	//}
      	r = new g.Circle({
      		map: map,
      		radius: l,
      		center: center,
      		fillColor: fc,
      		strokeWeight: 1,
      		strokeColor: bc
      	});
      }
      

      (若要在每次點擊時只在圖上秀單一圓圈,則刪除以上第17-19行的所有雙斜線)

      3. 將原程式的第46行改為:

      addMarkers();
      

      改寫後的結果

  10. 謝謝你詳細的解說,讓我受益良多。但程式太深澳了,我仍無法從中改寫真正我想要的東西。我真正想要的東西是,點擊地圖『任意二點(或更多)』出現以該二點為圓心的圓。你的最後版本已預設圓心所在,我本來以為可以從中改寫,但程式碼實在太艱深了,寫不出來。所以,只好硬著頭皮再來麻煩您指點一下迷津。感恩。

    1. 終於有點時間來回應。

      就我所知,Google Maps API讀取點擊時,程序是一個一個來,無法同時讀取觸控螢幕上的同時點擊。

      您想做到的效果不需要太複雜的程式。請先參照這一篇上半部份的介紹,但在第三階段改用以下的程式:

      <script type="text/javascript">
      var g = google.maps;
      var map, clickPoint, clickMarker, clickCircle;
      //地圖中心座標
      var mapCenter = new g.LatLng(23.972583, 120.979361);
      //地圖縮放比例(數值越大,細節越清楚)
      var mapScale = 7;
      //搜尋半徑,單位為公尺
      var rad = 100000;
      
      //圓周顏色
      var circleOutlineColor = '#c0c0c0';
      //圓內顏色
      var circleFillColor = '#e0e0e0';
      
      //顯示點擊處;true=是;false=否
      var clickPointDisplay = true;
      
      //是否顯示地圖說明視窗。true=是;false=否
      var infoWindowDisplay = true;
      //是否自動關閉地圖說明視窗。true=是;false=否
      var infoWindowAutoClose = true;
      //自動關閉地圖說明視窗預設時間;計時單位為千分之一秒
      var infoWindowTimer = 3000;
      //地圖總說明文字
      var infoWindowText = '請地圖上任意點觸';
      var w = new g.InfoWindow({
      	content: infoWindowText, 
      	position: mapCenter
      });
      
      //繪製圓圈
      function updateCircle(){
      	clickCircle = new g.Circle({
      		新圓圈所依附的地圖物件
      		map: map,
      		//園心
      		center: clickPoint,
      		//半徑
      		radius: rad,
      		//內部顏色
      		fillColor: circleFillColor,
      		//圓周線條粗細
      		strokeWeight: 1,
      		//圓周線條顏色
      		strokeColor: circleOutlineColor,
      		//圓在圖層堆疊中的垂直位置
      		zIndex: -100
      	});
      	//讀取發生於圓內的點擊
      	g.event.addListener(clickCircle, 'click', function (e) {
      		//讀取點擊處的經緯度
      		clickPoint = e.latLng;
      		//更新疊層中的圖像
      		updateGraph();
      	});
      }
      //繪製最新點擊處的標記
      function updateClickMarker(){
      	clickMarker = new g.Marker({
      		map: map,
      		position: clickPoint,
      		//圖形,可自訂
      		icon: {
      			//以簡單的小圓圈呈現圓心
      			path: g.SymbolPath.CIRCLE,
      			//顏色
      			strokeColor:'#9932CC',
      			//尺寸
      			scale: 2
      		},
      		//使圓圈無法被拖曳無法
      		draggable: false,
      		//此標記在圖層堆疊中的垂直位置
      		zIndex: -1
      	});
      }
      //繪製疊層
      function updateGraph(){
      	//繪製圓圈
      	updateCircle();
      	//繪製最新點擊處的標記
      	if (clickPointDisplay = true){
      		updateClickMarker();
      	}
      }
      //啟動
      $(document).ready(function(){
      	var mapDiv = document.getElementById('map-canvas');
      	//建立基本地圖
      	map = new g.Map(mapDiv, {
      		center: mapCenter, 
      		zoom: mapScale, 
      		mapTypeId: g.MapTypeId.ROADMAP
      	});
      	//讀取地圖上被點擊的座標
      	map.addListener('click', function(event) {
      		clickPoint = event.latLng;
      		//(重新)繪製疊層
      		updateGraph();
      	});
      	//顯示地圖說明視窗
      	if(infoWindowDisplay==true){
      		w.open(map);
      		if(infoWindowAutoClose==true){
      			var o =setTimeout("w.close()", infoWindowTimer);
      		}
      	}
      });
      </script>
      

      結果

發表迴響