Archive

Posts Tagged ‘google map’

Convex hull演算法

November 18th, 2010 No comments

Convex hull演算法為在一個平面上,找出一最小凸多邊形可包含所有的點,目前有幾種比較常見用來計算凸多邊形演算法,如:Incremental 、Jarvis’s March (Gift Wrap)、Divide and Conquer、Quick Hull,而TaiBIF上即採用Quick Hull演算法,來協助我們畫出一個物種的最小凸多邊,因此在這邊,說明此演算法的概念及步驟: Read more…

Categories: TaiBIF Tags: ,

TaiBIF 使用 Google Maps JavaScript 第3版 API 地圖介紹

October 29th, 2010 No comments

TaiBIF(Taiwan Biodiversity Information Facility)使用 Google Maps 做為物種出現記錄的呈現工具,接下來的介紹將以盤古蟾蜍(Bufo bankorensis)的搜尋結果作為介紹所使用的頁面(網址如參考資料1)。

 

Read more…

Categories: TaiBIF Tags: ,

多邊形檢索探討-使用Point in polygon演算法

October 20th, 2010 No comments

很多的朋有再問「究竟TaiBIF上的多邊形檢索是怎麼作」?在這一篇文章中,就來說明其中的關鍵作法。

傳統上多邊形檢索在一般的GIS軟體中,並不是一個特別的功能,幾乎所有的GIS軟體都會提供這樣的功能,大家用久了自然也就覺得好像不是一個特別的技術。但在Google Map中並沒有提供所謂多邊形檢索,因此要達成這樣的目的,必須多花一些功夫,重新探討與瞭解GIS軟體中多邊形檢索的原理。

早期的TaiBIF的空間檢索功能,先利用一個JavaScript的功能,當使用者任意框選Google Map上任一個地區,會將這個矩形的框框的左下與右上(座標的最大值與最小值)傳到資料庫中進行檢索,再回傳符合這個區塊的資料。

Read more…

TaiBIF 地圖服務需求規劃

October 19th, 2009 No comments

TaiBIF 地圖服務需求規劃

接續上篇「TaiBIF 地圖服務建置歷程」,因此TaiBIF的地圖規劃必須包含下面幾項功能:

  • 呈現不同條件的物種分布狀況:地圖服務最為基礎的功能為顯示不同條件下的物種分布狀況,從單一物種分布、甚至某一分類群(從界、門、綱、科、屬)的高階分類的分布情況;或某人過去的採集記錄;甚至大到一整個資料集的分布狀況,均可以透過TaiBIF的地圖服務呈現。
  • 以地圖方式搜尋某一地區物種分布:若有特定需求想查詢某一地點標本的資料,可以利用系統平台上地理空間框選的功能以獲得該地區資訊。步驟如下:

Read more…

Categories: TaiBIF Tags:

TaiBIF 地圖服務建置歷程

October 19th, 2009 No comments

 TaiBIF 地圖服務建置歷程

當時設計系統時,第一個遇到問題與挑戰為如何在地圖上可以依據不同的比例尺以顯示足以代表查詢結果,如一般電子地圖可在不同比例尺中顯示不同的圖面資訊(圖1)。不同比例尺下呈現不同圖層資訊

Read more…

Categories: TaiBIF Tags: ,

TaiBIF 入口網地圖服務

July 17th, 2009 No comments

TaiBIF入口網站中會出現許多標本採集地分布地圖,無論是單一物種或是某一類群的分布,均可透過TaiBIF 入口網地圖服務呈現,若您想要瞭解某一地區物種分布狀況,也可透過地圖服務取得相關資訊。

Read more…

Categories: TaiBIF Tags: ,

Taibif 的 Google Map 應用-2

May 5th, 2009 No comments

http://taibif.org.tw/taibif_search/searchResult.php?op=instit&institution=TAIF 為例,在這個網頁的 html 輸出時就會執行 initialize() 這個 javascript 程式碼,這個程式碼的部分內容如表格一: 

  1. function initialize()
  2. {
  3.    if (GBrowserIsCompatible())
  4.    {
  5.       var map = new GMap2(document.getElementById(“map_canvas”));
  6.       map.setCenter(dataCenter, minZoom);
  7.       map.addControl(new GLargeMapControl());
  8.       map.addControl(new GMapTypeControl());
  9.       map.addControl(new MDControl());
  10.       map.addControl(new GScaleControl());
  11.       //繪圖用的 div 節點不必顯示出來
  12.       var bounds = new GLatLngBounds(dataSW, dataNE);
  13.       var display = new Display(bounds, ‘#FFF’, ‘hidden’);
  14.       map.addOverlay(display);
  15.       GEvent.addListener(map, ‘dragend’, function()
  16.       {
  17.          display.redraw(true);
  18.       });
  19.    }
  20. }

 表格一 – initialize() 部分程式碼

在表格一第12行我們可以看到其實 google map 圖中有一個看不見(顯示設定為 hidden)的 div 區塊,這個區塊的作用是在執行色塊的運算與產生,又因為需要動態顯示色塊,所以第15行到第18行又增加了一個結束拖曳(dragend)的事件(GEvent),當使用者結束拖曳時會呼叫第13行中 Display 類別的 redraw 函式,請注意,如果沒有新增這個拖曳事件,那麼只要一開始沒有出現的色塊,在拖曳完地圖後也不會出現。

  1.          Display.prototype.redraw = function(force)
  2.          {
  3.             if (!force)
  4.                return;
  5.             var zoom = this.map_.getZoom();
  6.             if (zoom < minZoom)
  7.             {
  8.                zoom = minZoom;
  9.                this.map_.setZoom(zoom);
  10.             }
  11.             if (zoom > maxZoom)
  12.             {
  13.                zoom = maxZoom;
  14.                this.map_.setZoom(zoom);
  15.             }
  16.             var c1 = this.map_.fromLatLngToDivPixel(this.bounds_.getSouthWest());
  17.             var c2 = this.map_.fromLatLngToDivPixel(this.bounds_.getNorthEast());
  18.             this.div_.style.width = Math.abs(c2.x – c1.x) + ‘px’;
  19.             this.div_.style.height = Math.abs(c2.y – c1.y) + ‘px’;
  20.             this.div_.style.left = Math.min(c2.x, c1.x) + ‘px’;
  21.             this.div_.style.top = Math.min(c2.y, c1.y) + ‘px’;
  22.             this.div_.style.background = this.bgColor_;
  23.             //將舊的節點刪除
  24.             this.removeAllChildren();
  25.             //新增節點
  26.             this.createChildren();
  27.          }

表格二 – Display 類別的 redraw 函式

網頁的動態效果是由 redraw 函式產生。此外因為我有限制解析度,所以從第5行開始到第15行結束就是在設定當解析度超出最大最小解析度時就強制設定解析度為最大或最小解析度。第16行到第22行都是在畫控制色塊運算的那個看不見的 div 區塊,第16行與第17行是在將單位由原本的 google map 經緯度單位轉換成網頁的 pixel 單位,第18行到第22行在設定區塊的屬性。當 google map 發生改變時(例如使用者拖曳滑鼠改變顯示位置或是解析度改變),地圖中的區塊的大小,顏色,顯示與否都可能會發生變化,發生變化也就是刪除舊節點,產生新節點,也就是24行與26行中這兩個函式的功能。

  1.             this.createChildren = function()
  2.             {
  3.                var startLat = this.bounds_.getSouthWest().lat();
  4.                var stopLat  = this.bounds_.getNorthEast().lat();
  5.                var startLng = this.bounds_.getSouthWest().lng();
  6.                var stopLng  = this.bounds_.getNorthEast().lng();
  7.                var nowZoom = this.map_.getZoom();
  8.                var nowBounds = this.map_.getBounds();
  9.                var nowSW = nowBounds.getSouthWest();
  10.                var nowNE = nowBounds.getNorthEast();
  11.                for (var kiloSpan in data)
  12.                {
  13.                   kiloSpan = parseInt(kiloSpan);
  14.                   if (typeof(showData[kiloSpan] != ‘undefined’) && showData[kiloSpan].in_array(nowZoom))
  15.                   {
  16.                      var span   = parseFloat(kiloSpan / 100);
  17.                      var startM = 0;
  18.                      var stopM  = (stopLat – startLat) / span;
  19.                      var startN = 0;
  20.                      var stopN  = (stopLng – startLng) / span;
  21.                      if (nowSW.lng() > startLng)
  22.                      {
  23.                         startN = Math.floor((nowSW.lng() – startLng) / span);
  24.                      }
  25.                      if (stopLng > nowNE.lng())
  26.                      {
  27.                         stopN = Math.floor((nowNE.lng() – startLng) / span);
  28.                      }
  29.                      if (nowSW.lat() > startLat)
  30.                      {
  31.                         startM = Math.floor((nowSW.lat() – startLat) / span);
  32.                      }
  33.                      if (stopLng > nowNE.lat())
  34.                      {
  35.                         stopM = Math.floor((nowNE.lat() – startLat) / span);
  36.                      }
  37.                      for (var m in data[kiloSpan])
  38.                      {
  39.                         if ((m >= startM) && (m <= stopM))
  40.                         {
  41.                            m = parseInt(m);
  42.                            for (var n in data[kiloSpan][m])
  43.                            {
  44.                               if ((n >= startN) && (n <= stopN))
  45.                               {
  46.                                  n = parseInt(n);
  47.                                  var cnt = data[kiloSpan][m][n];
  48.                                  var index = 0;
  49.                                  for (; (cnt > boundSet[index]) && (index < boundSet.length); index++)
  50.                                  {
  51.                                     ;
  52.                                  }
  53.                                  if (index >= boundSet.length)
  54.                                     index = boundSet.length – 1;
  55.                                  var bgColor   = colorSet[index];
  56.                                  var textColor = textColorSet[index];
  57.                                  var boldColor = textColorSet[index];
  58.                                  if ((typeof(fadeData[kiloSpan]) != ‘undefined’) && fadeData[kiloSpan].in_array(nowZoom))
  59.                                  {
  60.                                     bgColor   = fadeColor;
  61.                                     textColor = fade;
  62.                                  }
  63.                                  var colorSW = new GLatLng(startLat + m * span, startLng + n * span);
  64.                                  var colorNE = new GLatLng(startLat + (m + 1) * span, startLng + (n + 1) * span);
  65.                                  var c1 = this.map_.fromLatLngToDivPixel(colorSW);
  66.                                  var c2 = this.map_.fromLatLngToDivPixel(colorNE);
  67.                                  var lngSpan = (colorNE.lng() – colorSW.lng()) / span;
  68.                                  var latSpan = (colorNE.lat() – colorSW.lat()) / span;
  69.                                  var xSpan = (c2.x – c1.x) / lngSpan;
  70.                                  var ySpan = (c2.y – c1.y) / latSpan;
  71.                                  var color = document.createElement(‘div’);
  72.                                  color.id = ‘color’;
  73.                                  color.style.width = Math.abs(c2.x – c1.x) + ‘px’;
  74.                                  color.style.height = Math.abs(c2.y – c1.y) + ‘px’;
  75.                                  color.style.left = (Math.min(c2.x, c1.x) – weight) + ‘px’;
  76.                                  color.style.top = (Math.min(c2.y, c1.y) – weight) + ‘px’;
  77.                                  color.style.position = ‘absolute’;
  78.                                  color.style.background = bgColor;
  79.                                  color.style.border = weight + “px solid ” + boldColor;
  80.                                  color.innerHTML = ‘<font color=\” + textColor + ‘\’>(SW) | ‘ + (startLat + (m * span)) + ‘, ‘ + (startLng + (n * span)) + ‘ | kiloSpan | ‘ + kiloSpan + ‘ | </font>’;
  81.                                  color.style.opacity = opacityData[kiloSpan];
  82.                                  color.style.filter = ‘alpha(opacity=’ + opacityData[kiloSpan] * 100 + ‘)’;
  83.                                  color.style.overflow = ‘hidden’;
  84.                                  if ((typeof(connectSet) != “undefined”) && connectSet.in_array(kiloSpan))
  85.                                  {
  86.                                     color.style.cursor = ‘pointer’;
  87.                                     color.onclick = function(){connectUrl(this)};
  88.                                  }
  89.                                  this.map_.getPane(G_MAP_MAP_PANE).appendChild(color);
  90.                               }
  91.                            }
  92.                         }
  93.                      }
  94.                   }
  95.                }
  96.             }

表格三 – Display 類別的 createChildren 函式

createChildren 函式是整個 javascript 中最重要的函式,所以我把他從 redraw 函式中獨立出來寫成 Display 的成員函式。表格三中的第3行到第6行分別是取得台灣區塊的西南端與東北端的經度與緯度。第7行是取得地圖的可視範圍,第9行與第10行則是取得地圖可視範圍的西南端與東北端。取得這些資料的目的是在過濾掉一些不必顯示的 div 區塊,節省客戶端電腦的運算時間。接下來就要開始處理資料了,php 會把從資料庫取得的資料以 javascript 中的 data 陣列呈現,所以處理資料也就是在處理 data 陣列的資料,data 陣列的格式解釋請見另外一篇文章。我在一開始的時候有提到區塊有不同的範圍,也不是每個解析度下每種範圍的區塊都要顯示,所以第14行是在判斷某個解析度下色塊是否要顯示,如果不用顯示則跳過下一輪的處理。當區塊在地圖可視範圍外時就一定不會顯示,所以也可以直接排除,而第21行到第36行就是在建立排除的規則。在確定要將 data 陣列內的某筆資料以 div 區塊的方式顯現時,就要開始設定這個區塊的顯示參數了。div 區塊除了顯示顏色之外,還必須在區塊中加入位置的文字資料(第80行的 innerHTML 屬性)作為後續的處理。每個色塊都是一個 id = ‘color’ 的 div 區塊(第72行開始),設定語法從第71行到89行,在第83行的 overflow 屬性如果沒設定為 hidden 的話,那麼使用 firefox 瀏覽器瀏覽時區塊內的文字就會跑出來而出現預期外的畫面(如圖一)。最後,當滑鼠移到有超連結功能(第84行判斷)的區塊的話就會改變滑鼠的游標,點擊這些區塊就會執行 connectUrl 函式。

圖一

圖一

Categories: TaiBIF Tags: , ,

Taibif 的 Google Map 應用 – data 陣列

May 5th, 2009 No comments

本文章將解釋 Taibif 中 google map 應用的 javascript 的 data 陣列的格式。data 陣列請到 http://taibif.org.tw/taibif_search/searchResult.php?op=instit&institution=TAIF 按右鍵檢視原始檔,其中一個範例如表格一,陣列的介紹如圖一。

Read more…

Categories: TaiBIF Tags: , ,

Taibif 的 Google Map 應用-1

May 5th, 2009 No comments

因為 Taibif 有展示資料地理分布的需要,所以我使用 Google Map 完成了這個應用(結果如圖一),實際操作畫面請見 http://taibif.org.tw/?tid=459。這個應用是用不同顏色的 div 區塊表示某個範圍內的資料數,當使用者改變解析度或是拖曳地圖的可視範圍時,地圖會動態的畫出區塊的大小顏色位置,使用者也可以點選 div 區塊,連結到區塊內相關資料的網頁。 Read more…

Categories: TaiBIF Tags: , ,

TaiBIF 地理分布呈現-2

April 14th, 2009 2 comments

上一篇文章中提到GBIF的地理分布呈現方法,在1度與0.1度地圖中利用JavaScript的方式不同的網格大小,但在1度的網格中,台灣(包含離島澎湖、金門等地方)大約14網格左右,若用於呈現台灣的物種出現紀錄,其解析度稍嫌過大。且GBIF的1度與 0.1度的網格中所使用的地圖為一張靜態地圖,相較於使用google map 而言,少了地圖的精確度。

有鑑於此,TaiBIF將發展利用 google map作為地圖呈現的主要工具,分別設計0.1 度(約10公里)與 0.02度(約2公里)的網格。此系統即將完成,敬請期待。

Categories: TaiBIF Tags: , ,