跳轉到內容

XQuery/National Grid 和 Google 地圖

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

在英國,用於交換時間表資訊的 XML 標準是 TransXchange。例如,公交車站的位置以英國國家網格上的南北向和東西向表示。為了在 Google 地圖上繪製這些位置,需要使用 WGS84 基準將這些座標轉換為經緯度。

TransXChange

[編輯 | 編輯原始碼]

以下是一個典型時間表文件開頭部分的摘錄,其中顯示了一個單獨的 StopPoint 定義

<TransXChange xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:apd="http://www.govtalk.gov.uk/people/AddressAndPersonalDetails" xmlns="http://www.transxchange.org.uk/" xsi:SchemaLocation="http://www.transxchange.org.uk/ TransXChange_general.xsd" CreationDateTime="2006-12-07T14:47:00-00:00" ModificationDateTime="2006-12-07T14:47:00-00:00" Modification="new" RevisionNumber="0" FileName="SVRSGAO070-20051210-5580.xml" SchemaVersion="2.1" RegistrationDocument="false">
    <StopPoints>
        <StopPoint CreationDateTime="2006-12-07T14:47:00-00:00">
            <AtcoCode>0100BRP90340</AtcoCode>
            <NaptanCode>BSTGAJT</NaptanCode>
            <Descriptor>
                <CommonName>Rupert Street (CA)</CommonName>
                <Landmark>NONE</Landmark>
                <Street>Rupert Street</Street>
                <Crossing>Colston Avenue</Crossing>
            </Descriptor>
            <Place>
                <NptgLocalityRef>N0076879</NptgLocalityRef>
                <Location>
                    <Easting>358664</Easting>
                    <Northing>173160</Northing>
                </Location>
            </Place>
            <StopClassification>
                <StopType>BCT</StopType>
                <OnStreet>
                    <Bus>
                        <BusStopType>MKD</BusStopType>
                        <TimingStatus>OTH</TimingStatus>
                        <MarkedPoint>
                            <Bearing>
                                <CompassPoint>N</CompassPoint>
                            </Bearing>
                        </MarkedPoint>
                    </Bus>
                </OnStreet>
            </StopClassification>
            <AdministrativeAreaRef>010</AdministrativeAreaRef>
        </StopPoint>

座標轉換

[編輯 | 編輯原始碼]

從 OS 國家網格座標轉換為 Google 地圖中使用的 WSG84 經緯度需要兩種轉換

  • 在地球橢球模型上的經緯度與 OS 使用的橫軸墨卡託投影之間
  • 基於 OS 座標中使用的不同橢球體的經緯度座標與全球 WGS84 座標之間。

包含這些函式和其他實用函式的 XQuery 模組可在 Github 中找到。

從 TransXChange 轉換

[編輯 | 編輯原始碼]

作為這些函式使用示例,以下指令碼將 TransXChange 檔案中的 StopPoints 轉換為具有經緯度座標的更簡單格式。這裡需要進行區域性校正以獲得更精確的區域性註冊。

(:  Transforms the Stopcodes in a TransXchange file to a simpler format with National grid references converted to latitude and longitude :)
declare namespace tx="http://www.transxchange.org.uk/";

import module namespace geo="http://kitwallace.me/geo" at "/db/lib/geo.xqm";

declare option exist:serialize  "method=xml media-type=text/xml highlight-matches=none";
declare function local:camelCase($s) {
 string-join(
     for $word in tokenize($s,' ')
     return concat(upper-case(substring($word,1,1)), lower-case(substring($word,2))),
     ' ')
};

<StopPointSet> 
    {for $stopCode in distinct-values(//tx:StopPoint/tx:AtcoCode)
     let $stop := (//tx:StopPoint[tx:AtcoCode=$stopCode])[1]
     let $d := $stop/tx:Descriptor
     let $l := $stop/tx:Place/tx:Location
      return 
        <StopPoint>
             <AtcoCode>{string($stop/tx:AtcoCode)}</AtcoCode>
             <CommonName>{string($d/tx:CommonName)}</CommonName>
             {if ($d/tx:Landmark ne 'NONE') 
              then <LandMark>{local:camelCase($d/tx:Landmark)}</LandMark>
              else ()
            }
            <Street>{local:camelCase($d/tx:Street)}</Street>
            <Crossing>{local:camelCase($d/tx:Crossing)}</Crossing>
            {geo:round-LatLong(geo:OS-to-LatLong(geo:Mercator($l/tx:Easting, $l/tx:Northing)),6)}
       </StopPoint>
    }
</StopPointSet>

轉換


這種轉換的輸出包含 StopPoints,例如

    <StopPointSet>
      <StopPoint>
         <AtcoCode>0100BRP90340</AtcoCode>
         <CommonName>Rupert Street (CA)</CommonName>
         <Street>Rupert Street</Street>
         <Crossing>Colston Avenue</Crossing>
         <geo:LatLong xmlns:geo="http://kitwallace.me/geo" latitude="51.455901" longitude="-2.596312" height="49.136187"/>
      </StopPoint>
   </StopPointSet>

對映公交車站

[編輯 | 編輯原始碼]

提取資料的應用之一是在給定位置的範圍內繪製車站。這需要一種足夠好的距離計算方法,適用於短距離

declare function geo:plain-distance ($f, $s as element(geo:LatLong))  as xs:double {
   let $longCorr := math:cos(math:radians(($f/@latitude +$s/@latitude) div 2))
   let $dlat :=  ($f/@latitude - $s/@latitude) * 60
   let $dlong := ($f/@longitude - $s/@longitude) * 60 * $longCorr
   return math:sqrt(($dlat * $dlat) + ($dlong * $dlong))
};
    

生成 kml 檔案

(: return the StopPoints within $range of $latitude and $longitude :)

import module namespace geo="http://kitwallace.me/geo" at "/db/lib/geo.xqm";

declare option exist:serialize  "method=xhtml media-type=application/vnd.google-earth.kml+xml highlight-matches=none"; 

let $latitude := xs:decimal(request:get-parameter("latitude", 51.4771))
let $longitude := xs:decimal(request:get-parameter ("longitude",-2.5886))
let $range := xs:decimal(request:get-parameter("range",0.5))
let $focus := geo:LatLong($latitude,$longitude)
let $x := response:set-header('Content-Disposition','attachment;filename=stops.kml;')

return
<Document>
   <name>Bus Stops  within {$range} miles of   {geo:LatLong-as-string($focus)}</name> 
   <Style id="home">
       <IconStyle>
          <Icon><href>http://maps.google.com/mapfiles/kml/pal2/icon2.png</href>
        </Icon>
       </IconStyle>
    </Style>
   <Style id="stop">
       <IconStyle>
          <Icon><href>http://maps.google.com/mapfiles/kml/pal5/icon13.png</href>
        </Icon>
       </IconStyle>
    </Style>

    <Placemark>
        <name>Home</name>
        <Point>
         <coordinates>{geo:LatLong-as-kml($focus)}</coordinates>
         </Point>
         <styleUrl>#home</styleUrl>
     </Placemark>
    {  for $stop in doc("/db/apps/xqbook/geo/stopPoints.xml")//StopPoint
       let $latlong := geo:LatLong($stop/LatLong/@latitude,$stop/LatLong/@longitude)
       let $dist := geo:plain-distance($focus,$latlong) * 0.868976242 (: distance is in nautical  miles :)
       where $dist < $range  
       return
     <Placemark>
        <name>{string($stop/CommonName)}</name>
       <description>
         {concat($stop/CommonName,' ',$stop/Landmark,' on ', $stop/Street, ' near ', $stop/Crossing)}  is {geo:round($dist,2)} miles away.
       </description>
       <Point> 
        <coordinates>{geo:LatLong-as-kml($latlong)}</coordinates>
       </Point>
       <styleUrl>#stop</styleUrl>
     </Placemark>
   }
</Document>

我家附近半英里內的車站 作為 KML。在 Google 地圖上,車站似乎與公交車站疊加層密切對齊,據推測是從相同的基準位置生成的。

如果您能夠輕鬆瀏覽圖示,那麼選擇 kml 圖示就會變得容易。以下是在 XQuery 中的一個簡單的瀏覽器

declare variable  $base := "http://maps.google.com/mapfiles/kml/";
declare option exist:serialize "method=xhtml media-type=text/html";

<html>
   <h2>Google Earth icons</h2>
   <p>Base url {$base}</p>
    {for $pal in (2 to 5)
     return
     <div>
        <h2>Palette pal{$pal}</h2>
        {for $i in (0 to 63)
         let $icon := concat('pal',$pal,'/icon',$i,'.png')
         return 
            <img src="{$base}{$icon}" title="{$icon}"/>
      } 
     </div>
    }
</html>

瀏覽 kml 圖示

華夏公益教科書