跳转到内容

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. 从浏览器添加事件
华夏公益教科书