跳轉到內容

XQuery/SMS 跟蹤器

來自華夏公益教科書,開放的書籍,開放的世界

BrightKite 提供一項服務,允許你微部落格你的位置並向服務傳送訊息,以便對地址進行地理編碼、繪製地圖、查詢附近的其他發推者並轉發到其他微部落格。

然而,對於英國使用者來說,這項服務缺乏 SMS 服務的可用性。以下指令碼提供了一個基本的 SMS 跟蹤器服務,允許使用者向 SMS 服務傳送文字地址和訊息,並在生成的地圖上看到該位置。這個簡單的應用程式不提供 BrightKite 的社交功能,僅限於建立簡單的軌跡。

依賴關係

[編輯 | 編輯原始碼]

eXist-db 模組

[編輯 | 編輯原始碼]
  • xmldb - 用於更新軌跡
  • datetime - 用於日期格式化
  • util - 序列化以將 XML 轉換為 CDATA
  • 雙向 SMS 服務
  • Google 地理編碼服務
  • 基於 KML 的地圖繪製,如 Google 地圖或 Google 地球

軌跡結構

[編輯 | 編輯原始碼]

每個軌跡都表示為單個 XML 檔案,包含唯一的名稱、標題、一個或多個手機號碼和事件列表。每個事件都帶時間戳,包含原始地址、地理編碼後的緯度和經度以及訊息。本地名稱空間用於 XML 資料和關聯函式。由於對完整地址和郵政編碼的版權限制,英國不支援完整地址的地理編碼。

<?xml version="1.0" encoding="UTF-8"?>
<track xmlns="http://www.cems.uwe.ac.uk/exist/geo" >
    <name>wiki</name>
    <mobile>44771234578</mobile>
    <title>Demo Track</title>
    <entries>
         <entry date="2008-06-12T09:56:08.593Z">
            <address>bristol parkway station</address>
            <location latitude="53.580320" longitude="-0.683640" ambiguous="true"/>
            <message>Waiting for the paddington train</message>
        </entry>
        <entry date="2008-06-12T10:30:51.454Z">
            <address>swindon</address>
            <location latitude="51.558418" longitude="-1.781985"/>
            <message>Nice empty train</message>
        </entry>
        <entry date="2008-06-12T10:51:12.429Z">
            <address>didcot parkway</address>
            <location latitude="51.610994" longitude="-1.242799"/>
            <message>Grey and its been raining</message>
        </entry>
...

入站訊息

[編輯 | 編輯原始碼]

入站訊息具有以下結構

   geo {address} ! {message}

SMS 訊息傳送到此處所述的 UWE 雙向 SMS 服務。路由器使用第一個單詞將訊息路由到關聯服務,在本例中為 track2sms.xq。此服務透過 HTTP 呼叫,傳遞字首('prefix)、源手機號碼(from)和字首後的訊息文字 (text)。

該指令碼使用源手機號碼查詢關聯的軌跡。如果有一個軌跡,訊息將被解析為地址和訊息文字。地址傳遞給 Google 地理編碼服務。如果地址被識別,將建立一個新的事件並附加到軌跡中的其他事件,並向發件人返回確認資訊(透過雙向 SMS 服務)。


declare namespace  geo =  "http://www.cems.uwe.ac.uk/exist/geo";
declare namespace  kml = "http://earth.google.com/kml/2.0";
declare variable $geo:googleKey :=  "ABQIAAAAVehr0_0wqgw_UOdLv0TYtxSGVrvsBPWDlNZ2fWdNTHNT32FpbBR1ygnaHxJdv-8mkOaL2BJb4V_yOQ";
declare variable $geo:googleUrl := "http://maps.google.com/maps/geo?q=";

declare function geo:geocode($address as xs:string)  as element(geo:location)*  {
   let $address := normalize-space($address)
   let $address := encode-for-uri($address)
   let $url := concat($geo:googleUrl,$address,"&amp;output=xml&amp;key=",$geo:googleKey)
   let $response := doc($url)
   for $placemark in $response//kml:Placemark
       let $point := $placemark/kml:Point/kml:coordinates
       let $latlong := tokenize($point,",")
       return 
              <geo:location latitude="{$latlong[2]}"   longitude="{$latlong[1]}"/>
 };

declare variable $sep   :=   "!";
declare variable $from  :=   request:get-parameter("from",());
declare variable $text    :=   request:get-parameter("text",());
declare variable $track   :=   //geo:track[geo:mobile = $from];
declare variable $now   :=  string(adjust-dateTime-to-timezone(current-dateTime()));

declare option   exist:serialize  "method=text media-type=text/text indent=yes";

if  (exists($track))
then 
   let $address  := 
       if (contains($text,$sep))
       then normalize-space(substring-before($text,$sep))
       else normalize-space($text)
   let $message := substring-after($text,$sep)
   let $location := geo:geocode($address)
   return
       if (exists($location) and count($location)=1)
       then 
             let $update :=
                update 
                   insert 
                      <entry  xmlns="http://www.cems.uwe.ac.uk/exist/geo" date='{$now}' >
                         <address>{$address}</address>
                         {$location}
                         <message>
                              {$message}                           
                          </message>
                       </entry>
                      into $track/geo:entries        
                return
                      concat("Reply:  ",$address, "  is at lat: ", $location/@latitude, " long:.", $location/@longitude)
       else
              concat("Reply:  ",$track/name," address :", $address, "not geocoded or ambiguous", $text,":",$message)
 else 
    ()

生成地圖

[編輯 | 編輯原始碼]

軌跡由名稱標識,並生成軌跡上事件的 KML 檔案。

declare namespace  geo =  "http://www.cems.uwe.ac.uk/exist/geo";
declare namespace kml = "http://earth.google.com/kml/2.1" ;

declare function geo:entry-to-kml($entry  as element(geo:entry)) as element(Placemark) {
   let $location := $entry/geo:location
   let $latlong := concat($location/@latitude," ",$location/@longitude)
   let $dt := datetime:format-dateTime($entry/@date,"yy/MM/dd HH:mm")
   let $popup := 
   <div  xmlns="http://www.w3.org/1999/xhtml">
      <h3>{string($entry/geo:address)}</h3>
       <p> {string($entry/geo:message)} </p>
    </div>
   return 
     <Placemark>
        <name>{$dt} &#160;{string($entry/geo:title)}</name>
         <description>
           {util:serialize($popup,"method=xhtml")}
         </description>
         <Point>
             <coordinates>
                 {string-join(($location/@longitude,$location/@latitude),",")}
             </coordinates>
         </Point>
   </Placemark>
};


declare option exist:serialize  "method=xml indent=yes 
     media-type=application/vnd.google-earth.kml+xml"; 

declare variable  $name := request:get-parameter("name",());
declare variable $track := //geo:track[geo:name=$name];

let $dummy := response:set-header('Content-Disposition',concat('inline;filename=',$name,'.kml;'))
return
<kml xmlns="http://earth.google.com/kml/2.1"  >
<Folder>
   <name>{$name}</name>
   <title>{$track/geo:title}</title>
      { for $entry in $track//geo:entry
        return  geo:entry-to-kml($entry)
     }
</Folder>
</kml>

示例地圖

[編輯 | 編輯原始碼]

Google 地圖

請注意,有一個地址被錯誤編碼,但反饋允許更改地址並重新發送。

待辦事項

[編輯 | 編輯原始碼]
  1. 編輯軌跡以刪除或更正錯誤的地理編碼
  2. 從瀏覽器新增事件
華夏公益教科書