跳转到内容

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 图标

华夏公益教科书