創建地圖
當前的信息爆炸已經促使許多新技術產生了,以幫助消化所有這些上千兆字節的數據。當要盡力吸收太多的信息時,多數人們對圖形方式表示的數據處理得很好。地理數據(在本例中是來自 US Census)能夠借助於 Google Maps 代替傳統的地理信息系統(GIS)而被可視化地表示出來。搜索 “google maps” 時,會出現 6900 萬多條搜索記錄,為什麼還要使用這篇文章呢?這篇文章專注於使用 PHP、Informix、DB2 和 Linux。此外,這裡提供的代碼很清晰,是由年僅 14 歲的程序員提供的。(請參閱 “參考資料”,獲得更多由該作者撰寫的文章。)
文章中的示例基於人口普查的數據。搜索屏幕提供了一個表單用以輸入郵政編碼。結果頁面顯示了所選擇的郵政編碼以及相鄰郵政編碼地區的人口密度,這個相鄰郵政編碼帶有有顏色的 “push pins”。通過左鍵單擊、拖動鼠標動作可以移動地圖,通過地圖左上角的縮放控件能夠改變地圖的比例。
清單 1 顯示了進行一些清理和數據清除之後的一些示例。數據庫模式顯示在 清單 2 中。
清單 1. 示例數據
state AL
zip 35004
longitude -86.502492000000
latitude 33.6063790000000
population 6998
housingunits 2815
sqmeters 49387881
state ME
zip 03901
longitude -70.845590000000
latitude 43.2901600000000
population 6338
housingunits 2406
sqmeters 96091016
state WA
zip 98001
longitude -122.26608100000
latitude 47.3037220000000
population 25771
housingunits 9158
sqmeters 46475168
清單 2. 數據庫模式
Column name Type
state char(2)
zip char(5)
longitude decimal(16)
latitude decimal(16)
population integer
housingunits integer
sqmeters int8
圖 1 展示了搜索頁,它是用戶碰到的第一個頁面。
圖 1. 首先加載的頁面
用戶輸入一個郵政編碼,就會顯示出結果,如 圖 2 所示。這副地圖上,不同的人口密度具有不同的有色指示符。右邊顯示的文本表示地圖上顯示的郵政編碼。左上角的縮放控件能夠改變地圖的比例。信息窗口顯示了 02222 郵政編碼的詳細信息。
圖 2.針對郵政編碼 02222 的搜索結果頁面
架構概述
因此,您如何根據搜索條件從數據庫表中得到結果記錄,並得到圖 1 和圖 2 那樣的顯示屏幕呢?數據源和 Google Map API 結合在一起被稱做一個 “mashup”(Wikipedia.org) 或者 Web 應用程序的混合。圖 3 展示了服務器架構。
這裡真正有趣的概念就是不同數據源在浏覽器上的集成。在客戶浏覽器上能承載的數據大小的限制對於這種方法的成功至關重要。一旦客戶很想得到大量的數據,網絡限制和客戶處理器的速度就會很大地挫傷用戶的積極性。
圖 3. 帶注釋的地圖架構
此架構非常簡單。來自浏覽器的請求傳向 apache Web 服務器。請求的 PHP 頁面包括 Html 和 Javascript。JavaScript 調用 Google Map 服務器後,組合的頁面就會出現(如 圖 1 所顯示)。當向搜索字段輸入某一個郵政編碼後,一個請求就傳向一個使用 PHP ODBC 與後台 Informix 數據庫連接的 Web 服務器。
本文中的代碼示例說明了如何使用 Informix 數據庫。請參閱 “DB2 和 開放源代碼:用 DB2、PHP 和 Linux 實現 Web 投票”(developerWorks,2004 年 8 月),了解知如何配置 DB2 與 PHP 一起工作。
頁面需要的數據從數據庫中檢索出來。push pin 顏色方案基於色譜顯示人口密度。選擇人口范圍以便得到每個地區近似的人口數量。
請參閱有關本文中所提供的代碼和代碼中的注釋的討論,獲得有關架構如何工作的額外信息。
組件
AJax/JavaScript 和 Google API
Google API、Javascript 和 Ajax 構成了大部分應用程序。Google Maps API 需要一個特定於某個 Web 站點的某個目錄的密鑰。為了得到一個密鑰,您必須登錄 www.google.com/apis/maps 並單擊 “Sign up for a Google Maps API key”。這個 API 引入了許多有用的功能,其中包括 Ajax HttpRequest 類和事件處理程序控件。Google 為 api 提供了很好的文檔,這些文檔能夠在 www.google.com/apis/maps/documentation/ 上找到。AJax 僅僅是對 JavaScript 和 XML 組合的富有想像力的縮寫詞。通過調用 AJax,解析由 PHP 返回的 Html,正如您將在下面要看到的,您就不必刷新用戶浏覽器了。
盡管並非所有的浏覽器都會支持 JavaScript 和 Google API,但是,絕大多數都會支持,包括 Mozilla Firefox 和 Internet Explorer 6。此外,一些老的浏覽器可能不會支持當前使用的一些 CSS。
apache、PHP 和 ODBC
中間層服務器提供 Web 頁面、運行時環境和數據庫訪問。Apache 服務器比較有名,根據最新統計數字,有 70% 的 Internet 站點選用 Apache 服務器。apache 支持插件模塊,我們的服務器也使用 mod_php 來提供 PHP 運行時環境。您能夠通過在命令行中使用 php 命令來執行 php 腳本的一些簡單測試。當 php 代碼需要執行並被發送到請求浏覽器時,就有了 apache 和 mod_PHP 的用武之地。
數據庫訪問組件 ODBC 已經遭受到性能上的尴尬。早期發布的 ODBC 比較慢。現在的實現已經大大改善,提供了較好的性能。使用 ODBC 使系統間的可移植性得到大大提高。有許多庫文件能夠支持對 DB2 或 Informix 的本地訪問,而這是特定於數據庫客戶機 API 的。
Informix/DB2
從 IBM Web 站點:
DB2 家族:數據庫管理系統,能夠提供靈活並且有成本效益的數據庫平台,以構建健壯的隨需應變的業務應用程序。
Informix 家族:針對事務集中的環境提供高級應用程序性能。
(我使用 Informix 已經多年了,我可以證明它在數據倉庫/決策支持系統方面做得很好。)
文章 “DB2 和 開放源代碼:用 DB2、PHP 和 Linux 進行 Web 投票” 從 DB2 引用了大量的示例,所以,這裡是來自 Informix 的示例。如果您在用 DB2 完成這個示例時有任何困難,請參考由該作者所寫的其他文章(參閱 “參考資料”)。
代碼
我們的代碼包含 3 個頁面:
主頁面,附帶有地圖,用 Html/JavaScript 編寫而成。
一個 PHP 腳本,返回以 XML 語言表示的郵政編碼的經度/緯度。
另一個 PHP 腳本,返回以 XML 語言表示的地圖邊界內關於郵政編碼的數據。
我們首先來看用戶還從來沒有見過的兩個組件,這兩個組件是 PHP 腳本,由 JavaScript 調用,與數據庫交互,並返回 XML:
清單 3. PHP 代碼,取得給定郵政編碼的經度/緯度
<?PHP
header("Content-Type: text/XML");
echo"<?XML version="1.0"?>";
$newloc=$_GET[location];
//connect to db2/informix database
//connect vars
$dbname="census";
$username="informix";
$passWord="useyourown";
// odbc_pconnect returns 0 if the connection attempt fails
// otherwise it returns a connection ID used by other ODBC functions
// echo ( "attempt connect.....n " );
$conn = odbc_pconnect ( $dbname, $username, $passWord );
if ( $conn == 0 ) {
echo ( "Connection to database failed." );
//If connection failed, show what the error message was:
$sqlerror = odbc_errormsg ( $conn );
dIE($sqlerror);
}
$sql = "select longitude,latitude,zip from census_data where zip = '$newloc'";
$locResult=odbc_exec($conn,$sql);
$locData=odbc_fetch_object($locResult);
echo "n<Results>";
if($locData->latitude !=""){
echo "nt<Zip>";
echo "ntt<Latitude>$locData->latitude</Latitude>";
echo "ntt<Longitude>$locData->longitude</Longitude>";
echo "nt</Zip>";}
echo "n</Results>";
?>
您也許注意到到了,這段代碼連接到一個名為 census 的 ODBC 數據源。該系統是使用 Informix 和 unixODBC 配置的。有關如何設置系統的一個較好的教程能夠在 unixODBC Web 站點上找到(請參閱 “參考資料”)。但是,這個腳本能夠很容易地轉換成 DB2。“Connecting PHP applications to IBM DB2 Universal Database”(developerWorks,2001 年 7 月)討論了如何設置 DB2 與 PHP 交互。
清單 3 中的代碼的輸出將會返回一段格式優美的 XML 源代碼,這段代碼在 Zip 標記中有 Latitude 和 Longitude 標記,這些標記都在 Results 標記中。它會根據 URL 中的值搜索數據庫。如果沒有找到任何結果,Results 標記內就不會有 Zip 標記。您還將會在第二段 PHP 腳本中看到更具有實際作用的 Results 標記。
清單 4 是該腳本的示例輸出,使用 02222(Boston,MA)作為郵政編碼。
清單 4. longitude/latitude 腳本的示例輸出
<?XML version="1.0"?>
<Results>
<Zip>
<Latitude>42.367797</Latitude>
<Longitude>-71.062829</Longitude>
</Zip>
</Results>
這段腳本只會被主頁面的 JavaScript 調用一次。因為它以 XML 語言返回正確的標題(由 header() 函數輸出),這段 XML 代碼能夠被內置在 Google Maps API 中的 XML 解析器所解析。(您將會在後面的 清單 6 中看到這些。)
清單 5. PHP 代碼,檢索關於用戶屏幕中的郵政編碼的數據
<?PHP
header("Content-Type: text/XML");
echo"<?XML version="1.0"?>";
//$newloc=$_GET[location];
//connect to db2/informix database
//connect vars
$dbname="census";
$username="informix";
$passWord="useyourown";
//odbc_pconnect returns 0 if the connection attempt fails
// otherwise it returns a connection ID used by other ODBC functions
// echo ( "attempt connect.....n " );
$conn = odbc_pconnect ( $dbname, $username, $passWord );
if ( $conn == 0 ) {
echo ( "Connection to database failed." );
//If connection failed, show what the error message was:
$sqlerror = odbc_errormsg ( $conn );
dIE($sqlerror);
}
$sql = "select * from census_data where longitude > '$_POST[left]'
and longitude < '$_POST[right]'
and latitude > '$_POST[bottom]'
and latitude <'$_POST[top]'";
$locResult=odbc_exec($conn,$sql);
echo "n<Results>";
while($locData=odbc_fetch_object($locResult))
{
echo "nt<Zip>";
echo "ntt<Code>$locData->zip</Code>";
echo "ntt<State>$locData->state</State>";
echo "ntt<Latitude>$locData->latitude</Latitude>";
echo "ntt<Longitude>$locData->longitude</Longitude>";
echo "ntt<Population>$locData->population</Population>";
echo "ntt<Housing>$locData->housingunits</Housing>";
echo "ntt<Area>$locData->sqmeters</Area>";
echo "nt</Zip>";
}
echo "n</Results>";
?>
這段腳本返回的 XML 代碼大致上基於第一個腳本 —— 它會有個根標記 Results,就像您在第一個腳本中看到的一樣。但是這段腳本通常會返回多個 Zip 標記。對於由該數據源返回的每行數據,while 循環將會在該行做為一個對象被調用,並且輸出一個所有數據都在數據庫中的 Zip 標記(請參閱 清單 1)。
這段代碼最重要的部分就是 SQL 查詢 —— 在 清單 6中,AJax 將會切換為 POST,而不是 GET(正如在 清單 3 中看到的),並且傳送 4 個變量:left、right、bottom 和 top。這些變量以經度/緯度定義為地圖可見部分的邊界。通過查詢數據庫,並請求這些精確邊界內的郵政編碼,您就可以避免使用不必要的帶寬,降低服務器的負荷,且也有助於縮短響應時間。
清單 6 中的代碼由三個部分組成:
CSS
<div> 標記
JavaScript 和很少的 PHP 代碼
第一部分是一個嵌入在 <head> 標記中的級聯樣式表(css)。其邊界由 <style type="text/css"> 和 </style> 標記定義。css 定義了將會在 <div> 標記中使用的類,以創建頁面的外觀。如果沒有 CSS,頁面中就不會有顏色,頁面的布局看起來就會很松散,頁面的組織也很不美觀。
主體標記中的下一部分是:構造塊 <div> 標記,它用於容納布局、信息和搜索工具。由於 JavaScript 的工作方式,<div> 標記對於容易更新的頁面來說工作得非常好。
最後一部分是最重要的:Javascript。正如上文所提到的,JavaScript 中包含很少的幾個部分:
搜索方法
控制 LOADING.. 符號的方法
解析 XML 並將標記加入地圖中的方法
幾個創建圖標的 PHP for 循環
當搜索按鈕被按下或表單被提交時觸發的方法
除了所有這些之外,具有最重要功能的方法就是用來解析 XML 的方法。它們創建一個新的 GXMLHttp.create(); 函數,該函數是一個內置在 Google Maps API 中的 HttpRequest 類。這不僅僅是用於更新地圖的控制中心,也是大多數 Ajax 應用程序的控制中心。(但是,並非所有的 AJax 應用程序都使用 Google Maps HttpRequest 類。)
當該請求發送到 PHP 文件,並且收到 XML 之後,代碼就會搜尋並尋找所有的 Zip 標記。當找到一個結果,程序就會將一個標記添加到地圖裡面。程序也必須添加一個事件監聽器來處理事件 —— 在本例中是某人單擊標記。與 HttpRequest 一樣,這個事件監聽器函數也內置在 Google Maps API 中。
地圖的另一個重要部分是用於監聽地圖移動的所有方法完成之後初始化的事件監聽器。它會查看縮放級別,判斷級別是否超過或低於 6 級(為了避免太多的帶寬和太慢的響應時間),然後將更新地圖和側欄,或者是告訴用戶他/她已經縮小得太多了。
結束語
地理數據顯示的可能性只受您的想像力限制。本文的示例基於 US Census 數據。這裡展示的架構和方法能夠應用於您所選擇的任何數據。我們希望您能夠發覺本文對您很有用,也希望您願意根據自己創建 mashups 的經驗提出反饋意見。