歡迎您光臨本站 註冊首頁
  
在這個分為兩部分的系列文章中,您將為房地產經紀公司開發一個應用程序,將所有的公寓列表顯示為 Google Maps 中可點擊的 Placemark 對象。在第 1 部分中,已經創建了應用程序的前半部分,從用戶中收集公寓列表信息,使用 Google Geocoder Web 服務將街道地址轉換為它的地理坐標(經度/緯度),並將地理坐標和地址信息存儲在資料庫中。在第 2 部分中,您將使用這些數據生成 KML 覆蓋圖文檔,並在 Google Maps 和 Google Earth 中顯示。首先,在 MySQL 中使用存儲過程生成 XML 數據,然後利用 XSLT 和一種叫做 Muenchian 分組的技術,將 XML 數據轉化為包含覆蓋圖信息的 KML 文檔 —— 一個 Placemark 對象對應一座公寓大樓。每個 Placemark 對象的彈出氣球顯示那座樓房中的公寓列表。最後,使用 Google Maps API 在 Web 站點嵌套的 Google Map 中顯示 KML 覆蓋圖。

通過 XSLT 獲取用於轉換的源 XML 數據

前面已經在資料庫中存儲了所有的地址信息和對應的經度和緯度坐標。首先,可以用 XML 格式表示這些數據,然後使用 XSLT 樣式表生成最終的 KML 數據。有多種解決方案可供選擇。在本例中,資料庫查詢只返回單個表的行,您也許要編寫一個 PHP 小函數對 XML 標記中的每一列進行打包。對於更複雜的數據,另外一種解決方案是使用 Erik Wetterberg 提出的針對 MySQL 的巧妙存儲過程,可以從 Mysql Forge 下載。它允許直接在 SQL 查詢中構造複雜的分級 XML 元素(參閱 參考資料 獲得鏈接)。

使用存儲過程從 Mysql 返回 XML

Wetterberg 的解決方案使用 3 種存儲過程:xml_tag()、xml_escape() 和 xml_attr()。這 3 種存儲過程從 select 語句的其餘列構造一棵 XML 樹。xml_tag() 包含四個參數:標記名稱、標記值(查詢中資料庫的列)、標記屬性和標記的子元素。標記的屬性和子元素都是單個字元串,因此,可以使用 concat() 函數連接它們。清單 1 說明如何使用這些存儲過程來生成每行的 XML 元素字元串,每個字元串包含每列的一個元素。您也可以很輕鬆地將子元素轉換為行的屬性。


清單 1. 在 PHP 中使用存儲過程以 XML 元素的形式獲取 MySQL 結果
				  function fetchListingsAsXml() {      $sql = <<<SQL          select xml_tag('listing',                null,                null,               concat(xml_tag('id', id, null, null),                  xml_tag('address', address, null, null),                  xml_tag('apt_no', apt_no, null, null),                  xml_tag('city', city, null, null),                  xml_tag('state', state, null, null),                  xml_tag('zipcode', zipcode, null, null),                  xml_tag('longitude', longitude, null, null),                  xml_tag('latitude', latitude, null, null),                  xml_tag('apt_type', apt_type, null, null),                  xml_tag('rent', rent, null, null),                  xml_tag('notes', xml_escape(notes), null, null)))        as xml        from listing;    SQL;     return fetchMysqlXml('listings', $sql);  }    function fetchMysqlXml($tag, $sql) {      if (! $result = mysql_query($sql)) {      die (mysql_error());    }      $xml = '';    while ($row = mysql_fetch_array($result)) {      $xml .= $row[0];    }      return "<$tag>$xml</$tag>";  }  

SQL 查詢使用存儲過程為每行返回數據創建一個 XML 列表元素,同時傳遞 null 作為列以獲取值,因為它的值就是它的子元素。通用,也為參數 attributes 傳遞 null 值。最後,subelements 參數傳入一組子元素標記(每個都由進一步調用 xml_tag() 來生成),其中以一個選定的列作為標記的值,以 null 作為屬性和子元素參數的值。

這樣,在結果集中生成每行的 XML 標記,但是沒有創建頂層元素。為簡化頂層元素的創建過程,fetchMysqlXml() 函數接收頂層聚合元素的名稱和查詢的名稱(它可能使用這些存儲過程來生成 XML 元素字元串)。然後,在查詢結果中,連接這些行中的字元串,以生成包含每行的子元素的聚合 XML 元素。如清單 2 所示。


清單 2. 轉換為 XML 的資料庫數據
				   <listings>  <listing>  <id>10</id>  <address>3 Irving Place</address>  <apt_no>3A</apt_no>  <city>New York</city>  <state>NY</state>  <zipcode>10003</zipcode>  <longitude>-73.988639</longitude>  <latitude>40.734091</latitude>  <apt_type>studio</apt_type>  <rent>2800</rent>  <notes/>  </listing>  <listing>  <id>11</id>  <address>123 E.34th St.</address>  <apt_no>2D</apt_no>  <city>New York</city>  <state>NY</state>  <zipcode>10016</zipcode>  <longitude>-73.980182</longitude>  <latitude>40.746595</latitude>  <apt_type>1br</apt_type>  <rent>2300</rent>  <notes>great views, A/C incl.</notes>  </listing>  </listings>  





將 XML 公寓列錶轉換為 KML 覆蓋圖數據

因為資料庫的內容是以 XML 格式表示的,所以可以使用 XSLT 將數據轉換為 KML 格式,生成的數據可以在地圖上覆蓋,或導入 Google Earth 中,這將在 下一節 討論。

本例中的數據表示的是紐約市曼哈頓區公寓列表的清單,房東可能是個人,也可能是房地產經紀人。預期的效果是在曼哈頓區的地圖上放置書籤,公寓列表的每處房產對應一個書籤。單擊書籤,將會打開一個包含書籤名稱和書籤描述的氣球,書籤描述是想要在氣球中顯示的、有效的 HTML 內容。在本例中,書籤彈出的氣球會顯示該書籤對應的樓房中所有的公寓列表。

您的目的是將清單 2 中的源 XML 數據轉換為清單 3 所示的 KML 數據。


清單 3. KML 輸出示例
				  <?xml version="1.0"?>  <kml xmlns="http://earth.google.com/kml/2.2">      <Document>          <name>Jake's Apartment Rentals</name>          <description>A map of available apartments - click a bookmark to see                                	available apartments there.</description>              <Placemark xmlns="">                  <name>Address 220 E. 22nd St.  (2 listings)</name>                  <description>              <![CDATA[                  <div>APT 1A: $2250,  new kitchen, H/W floors</div>                          <div>                              APT 2S: $2500, loft space, sliding glass doors                          </div>                           ]]>                 </description>                 <Point>                     <coordinates>-73.982488, 40.737675</coordinates>                 </Point>             </Placemark>             <Placemark xmlns="">                 <name>Address 214 E. 73rd St.  (2 listings)</name>                 <description>              <![CDATA[                  <div>APT 2C: $2000, everything brand new</div>                          <div>                            APT 4A: $2400, elevator, A/C incl, gorgeous                          </div>                          ]]>                 </description>                 <Point>                     <coordinates>-73.959088, 40.769987</coordinates>                 </Point>             </Placemark>         </Document>  </kml>  

清單 3 是一個包含名稱元素、描述元素和 Placemark 元素的 KML 文檔,每個 Placemark 元素表示地球上的一個位置。本文檔中的每個 Placemark 元素都包含名稱、描述和 Point 對象,Point 對象用經度、緯度和海拔(可選)指定 Placemark 元素的 3D 坐標。每個 Placemark 元素在 Google Map 中都表示為一個書籤。單擊書籤,就會打開氣球,氣球中的名稱用粗體表示,描述的內容位於名稱下方,如圖 1 所示。


圖 1. 在 Google Map 中顯示的 Placemark

注意,氣球中的每個列表都自成一行。因為 Google Maps 是按照 HTML 格式逐字地表示描述,其中,每一行要求有 <br/> 標記,或者必須打包在自己的 <div> 元素中。可以在描述中包括任何 HTML 標記,但是,必須將描述欄位的內容打包在 CDATA 元素中。

創建 XSLT 樣式表

要將源 XML 數據轉換為 KML,則必須從清單 4 中的樣式表開始。


清單 4. 轉換為 KML 的 XSLT 樣式表框架
				  <xsl:stylesheet version="1.0"    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">      <xsl:key name="listings-by-address" match="listing" use="address" />      <xsl:template match="/">      <kml xmlns="http://earth.google.com/kml/2.2">        <Document>        <name>Jake's Apartment Rentals</name>      <description>A map of available apartments - click a bookmark to see                  available apartments there.</description>                <xsl:apply-templates select="listings"/>          </Document>      </kml>        </xsl:template>    </xsl:stylesheet>  

<xsl:template> 標記匹配源文檔的根節點,並創建包含其名稱和描述的 KML 文檔框架,然後在根節點的子元素 listings 上調用 <xsl:apply-templates>。

<xsl:key> 標記位於文檔的上方,允許根據地址對列表進行分組。因為每個 Placemark 元素都表示一座公寓大樓,其中包括很多公寓,當表示樓房的 Placemark 元素時,需要根據公寓大樓對列表進行分組。因為輸入的 XML 數據包含統一的 <listing> 元素列表,所以在樣式表中可以根據地址對這些元素進行分類。

在 XSLT 中根據公寓大樓地址對公寓列表進行分類

這裡將用到一種稱為 Muenchian 分組(參閱 參考資料 獲得更多信息)的分組技術。該技術通過 XSLT 鍵使分組更加方便。XSLT鍵創建一個索引,通過該索引,可以快速找出一組與單一標準匹配的元素。這個過程創建了一個自然的分組機制,模板可使用它在 KML 中呈現每組的一個 Placemark 元素。

分組行為需要樣式表完成兩件事:

  • 收集一組 group leader,每個 group leader 表示文檔中的惟一的樓房地址,用來創建 Placemark 元素
  • 對於每個惟一的樓房地址,將所有的地址列表傳遞給模板進行處理。在本例中,即在 Placemark 中呈現每個公寓的一個 <div> 元素

收集惟一地址 (group leaders),開始分組過程

下面的 <xsl:template> 元素匹配 listings 標記。現在開始該分組行為,如清單 5 所示。


清單 5. listings 元素的 <xsl:template>
				  <xsl:template match="listings">      <xsl:apply-templates          select="listing[count(. | key('listings-by-address', address)[1]) = 1]"          mode="first-in-group"/>        </xsl:template>   

該模板的內容容可以直接用於第一個模板,但如果將其分開,代碼就很容易理解。該模板調用 <xsl:apply-templates>,並將文檔中惟一樓房地址的列表 —group leader 傳遞給它。選取這些 group leader 元素需要稍微複雜的技巧。Xpath 表達式 listing[count(. | key('listings-by-address', address)[1]) = 1] 會匹配符合括弧內的條件的所有列表元素。表達式 key('listings-by-address', address) 訪問在樣式表上方創建的鍵。該 Xpath 表達式測試文檔中的每個列表元素,使用當前測試的列表的地址調用鍵。鍵返迴文檔中具有該地址值的所有列表。[1] 表示選取這些匹配元素中的第一個元素。管道符 (︱) 創建第一個匹配的元素與當前測試元素(句點 (.) 表示當前元素)的並集。因為並集只包括惟一元素(不允許重複),所以並集的結果將包括一個或兩個元素 — 如果當前測試的元素是包含地址的文檔中的第一個元素(它所在分組的首元素),那麼結果中包括一個元素。如果當前測試的元素不是第一個元素(不是首元素),那麼結果中包括兩個元素。因此,用來測試並集結果的 = 1 表達式只包括一個元素,這意味著當前測試的元素是包含地址的文檔中的第一個元素。最後,Xpath 表達式收集文檔中每個惟一的樓房地址列表,並以 first-in-group 的模式將其傳遞給 listing 模板。

組級別 XSLT 模板(創建 KML Placemark)

如果想要使用相同的標準對不同的環境中元素進行匹配,您可以為 <xsl:template> 指定不同的模式,這相當於定義兩個函數,它們接收相同的參數,但是對參數採用不同的處理方式。在本例中,首先創建一組 Placemark 元素,然後處理組中的每個列表,它們都與 vanilla 列表元素匹配。創建兩個匹配列表元素的 <xsl:templates>。一個將為 group leader(在最後一個模板中選取)處理組級別處理過程,另外一個將處理組中每個列表的處理過程。清單 6 顯示第一個模板。


清單 6. 創建組級別 Placemark 元素的 <xsl:template>
				  <xsl:template match="listing" mode="first-in-group">      <Placemark>        <name>      <xsl:value-of select="address"/>       (<xsl:value-of select="count(key('listings-by-address', address))"/> listings)        </name>        <description>      CDATA-START      <xsl:apply-templates          select="key('listings-by-address', address)"         mode="listing-within-group"/>                  CDATA-END        </description>        <Point>      <coordinates>        <xsl:value-of select="longitude"/>,        <xsl:value-of select="latitude"/>      </coordinates>        </Point>      </Placemark>    </xsl:template>  

同樣,只有模式指定為 first-in-group 時,該模板才匹配列表元素,就像從前一個收集 group leaders 的模板中調用一樣(在 清單 5 中)。該模板為樓房創建 Placemark 元素,這些樓房對應的書籤將出現在地圖上。Placemark 元素的名稱是樓房的地址加上圓括弧內的那座樓房中的列表號。為獲得樓房的列表數量,需要再次用地址鍵對列表中當前地址的公寓列表計數。

<description> 元素是 Placemark 的一部分,在地圖上單擊 Placemark 時,它會出現在彈出的氣球中,位於名稱的下面。這裡包括兩個動作。首先,樣式表將 CDATA-START 和 CDATA-END 兩個標記放在結果 XML 文檔中。因為結果文檔是為 Google Maps 提供的 KML,KML 將接收描述標記的 HTML 內容,並將其逐字地放在 Placemark 元素彈出的氣球中,然後將其作為文本(而不是 XML)處理。因此,需要在 CDATA 結構體中打包 <description> 元素的內容。但是,不能把元素逐字放在樣式表中,否則 XSLT 處理器會忽略它的內容。本文的解決方案是用文本標記代替 CDATA 的開始和結束標記,在調用的 PHP 代碼中,使用正則表達式代替 XSLT 轉換結果中有效的 CDATA 開始和結束標記,如清單 7 所示。

處理單個列表

對描述元素來說,第二件事情是調用 <xsl:apply-templates>。這個行為選取當前 (group leader) 列表中的所有地址清單,其中包括 group leader 本身,然後將它們傳遞給模板,該模板以 listing-within-group 模式(參閱清單 7)匹配清單元素。在描述元素之後,模板創建 point 元素,指定地圖上 Placemark 對象的經度和緯度。如果需要的話,也可以指定海拔。


清單 7. 在 Placemark 的描述中,<xsl:template> 創建每個列表的 <div> 元素
				  <xsl:template match="listing" mode="listing-within-group">      <div>        APT <xsl:value-of select="apt_no"/>:         ___FCKpd___6lt;xsl:value-of select="rent"/>,         <xsl:value-of select="notes"/>      </div>  </xsl:template>  

樣式表的最後一個模板以 listing-within-group 模式對列表進行匹配,從而為公寓號、租金和有關公寓的各種記錄的列表創建 <div> 元素。其中,可加入任意的 HTML 標記,包括 CSS 樣式屬性。

用 PHP 對 XML 數據應用樣式表

最後一步實際上是對 XML 數據應用樣式表。為提供 XSLT 轉換能力,PHP 5 提供構建在 libxslt 上的 XSL 模塊,它是一種 XSL 規範(參閱 參考資料 獲得鏈接)的實現。XSL 模塊可實現對 XML 文檔應用 XSL 文檔,如清單 8 所示。


清單 8. 用 PHP 生成 KML 數據
				  function doXslt($xml, $xsl) {      $xmlDOM = DOMDocument::loadXML($xml);    $xslDOM = DOMDocument::loadXML($xsl);        $processor = new XSLTProcessor();      $processor->importStyleSheet($xslDOM);      return $processor->transformToXML($xmlDOM);  }    $xml = fetchListingsAsXml();    $xsl = file_get_contents ('listings-xml-to-kml.xsl');    $kml = doXslt($xml, $xsl);    $kml = ereg_replace('CDATA-START', '<![CDATA[', $kml);  $kml = ereg_replace('CDATA-END', ']]>', $kml);    header("content-type: application/xml");  echo $kml;  

XSLTProcessor() 函數要求源文檔和樣式表是一個 DOMDocument 對象,而不是一個字元串。在這裡,doXslt() 根據提供的 $xml 和 $xsl 字元串創建 DOMDocuments 對象,並創建一個 XSLTProcessor 對象,然後將提供的樣式表導入處理器,根據樣式錶轉換提供的 XML 文檔。使用 ereg_replace() 函數代替有效的 CDATA 標記,即樣式表中引入的 CDATA-START 和 CDATA-END 標記。有了 xml 格式的內容類型之後,Google Maps 的 KML feed 就準備好了。





在 Google Maps 中查看 KML 覆蓋圖

為了查看覆蓋在 Google Map 中的 KML 數據,將瀏覽器指向 http://maps.google.com/maps?q=http://YOUR-SERVER/listings-as-kml.php?v=2,YOUR-SERVER 是運行 PHP 腳本的伺服器的 URL。Google 緩存了 KML 文件,以避免每次查看 KML 數據時都要訪問伺服器。為執行更新操作,需要指定一個每次都變化的查詢參數,例如 v=2,該參數每次都指定一個不同的數字,使 Google 重新讀取 KML 數據。

該 URL 將產生一個 Google Maps 頁面,該頁面可縮放至地圖上的所有 Placemark 對象都合適的大小。如圖 2 所示。


圖 2. 顯示 KML 數據的 Google Maps 頁面

在地圖的左邊部分,圖 3 顯示所有 Placemark 對象的列表,列表的上方是 KML 文檔的 <name> 和 <description>。


圖 3. Google Map 左部分的 Placemark 對象列表

單擊地圖上的一個 Placemark 對象,這將顯示一個彈出氣球,如圖 4 所示。


圖 4. Placemark 對象的彈出氣球

使用 Google Maps API 顯示 KML 覆蓋圖

您也可以使用 Google Maps API(JavaScript)將 KML 數據載入到地圖上,如清單 9 所示。


清單 9. 使用 Google Maps API 覆蓋 KML 數據
				  function load() {        if (GBrowserIsCompatible()) {            var map = new GMap2(document.getElementById("map"));            map.addControl(new GLargeMapControl());          map.addControl(new GMapTypeControl());            map.setCenter(new GLatLng(40.743884, -73.974666), 13);            var kmlUrl = "http://YOUR-SERVER/listings-as-kml.php?v="   + Math.round(Math.random() * 10000000000);            var kml = new GGeoXml(kmlUrl);           map.addOverlay(kml);        }      }  

在這裡,JavaScript 創建 KML feed URL,並追加一個大的隨機數(大多數情況)以保證 Google 讀取最新的 KML 數據。GGeoXml 對象載入 KML 數據,並提供一些實用函數,然後調用 addOverlay() 以顯示頁面中地圖上的 KML 數據,如圖 5 所示。


圖 5. 使用 Google Maps API 覆蓋 KML 數據





在 Google Earth 中查看 KML 覆蓋圖

您也可以在 Google Earth 中查看 KML 數據。在本地計算機(要以 .kml 為擴展名,否則在 Google Earth 中無法打開)中將 KML 數據存為一個文件。首先,下載並安裝 Google Earth(參見 參考資料),然後通過 File 菜單打開 KML 文件,如圖 6 所示。


圖 6. 載入到 Google Earth 中的 KML 覆蓋圖

在本圖中,黃色圖釘表示 Placemark 對象。單擊其中的一個將顯示與 Google Maps 的彈出氣球相同的 HTML 內容。





結束語

在這個分為兩部分的系列文章中,通過從 PHP 調用 Google Geocoder Web 服務,將街道地址轉換成地理坐標並存儲在資料庫中。然後,使用存儲過程從 MySQL 生成 XML 數據。最後使用 XSLT 樣式表將 XML 數據轉換為 KML 覆蓋圖,這樣就可以在 Google Maps 和 Google Earth 中查看該圖。

這些知識的範圍是十分廣闊的。這兩篇文章只涉及到一些皮毛,特別是您還可以用 KML 創建 3D 多線圖和多角圖,而不僅僅是顯示文本信息的 Placemark。您可以利用 Google Maps、 Google Earth 和 Google Geocoder 在大部分 Web 站點上處理地址信息,並且還可以使用 XSLT 將包含坐標信息的 XML 數據轉換為激動人心的 KML 覆蓋圖。(責任編輯:A6)



[火星人 ] 使用 XSLT、KML 和 Google Maps API 在地圖上覆蓋數據,第 2 部分: 轉換和使用數據已經有1119次圍觀

http://coctec.com/docs/linux/show-post-69058.html