逆ジオコーダを試した

以下 3 つの逆ジオコーダを試した時のメモとテストに使ったグリモンソース。

自分で使うものだけをさっくり試しただけです。作者様方には非常に感謝しております。

逆ジオコーダ

invGeocoder

リクエス
http://nishioka.sakura.ne.jp/google/ws.php?lon=139.7056&lat=35.6723&format=simple

レスポンス

<?xml version='1.0' encoding='UTF-8' ?>
<geometry>
  <version>0.1</version>
  <point>
    <lat>35.673</lat>
    <lon>139.705568</lon>
    <address>東京都渋谷区神宮前一丁目24</address>
    <pref>東京都</pref>
    <city>渋谷区</city>
    <town>神宮前一丁目</town>
    <number>24</number>
    <distance>77.7210722793</distance>
  </point>
</geometry>
rgeocode.php

せっかく? なので jsonp を使用した。XML なども可能。
リクエス
http://refits.cgk.affrc.go.jp/tsrv/jp/rgeocode.php?jsonp=caller&v=2&lat=35.6723&lon=139.7056

レスポンス

caller({
  'status' : true,
  'result' : {
    'prefecture' : {
      'pcode' : 13,
      'pname' : '東京都'
    },
    'municipality' : {
      'mcode' : 13113,
      'mname' : '渋谷区'
    },
    'local' : {
      'section' : '神宮前一丁目',
      'homenumber' : 24
    }
  },
  'argument' : {
    'latitude' : 35.6723,
    'longitude' : 139.7056
  },
  'uri' : '%2Ftsrv%2Fjp%2Frgeocode.php%3Fjsonp%3Dcaller%26v%3D2%26lat%3D35.6723%26lon%3D139.7056',
  'meta' : [
    {
      'name' : 'thanks',
      'content' : 'このサービスは 国土交通省 提供 国土数値情報(行政区域データ) および 街区レベル位置参照情報 を利用しています'
    }
  ]
});
標高 API

本来は標高を取得するもの、だと思わる。日本測地系
リクエス
http://api.alpslab.jp/v1/level?appid=guest&p=35/40/08.7,139/42/31.7

レスポンス

<?xml version="1.0" encoding="UTF-8"?>
<result>
  <address>東京都渋谷区神宮前1丁目</address>
  <level>26</level>
</result>

テストソース

最近 はてなフォトライフ - 無料・大容量、写真を共有できるウェブアルバム では写真に位置情報が含まれる場合に KML ファイルへのリンクが表示されるようになったのだけど、その横に緯度経度と住所を表示するようにしてみた。が、非常にいまいち…。Overview  |  Maps Static API  |  Google Developers とか使って地図を表示させようかとも思ったがやめた。逆ジオコーダを使うことが目的なので。

// ==UserScript==
// @name           TestReverseGeoCoding
// @namespace      http://d.hatena.ne.jp/hetappi
// @include        http://f.hatena.ne.jp/*
// ==/UserScript==

(function() {
  // anchor.href: 'http://f.hatena.ne.jp/xxxxxxx/yyyyyyyyyyyyyy?mode=kml'
  var anchor = document.evaluate(
    'id("breadcrumbs")/a[3]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  if (!anchor)
    return;

  parseKml(anchor.href, function(point) {
    var name = 'invGeocoder';
//    var name = 'rgeocode.php';
//    var name = 'ALPSLABlevel';
    var coder = new GeoCoderFactory().createReverseGeoCoder(name);
    coder.geocode({ lat: point.lat, lon: point.lon }, function(resp) {
      var txt = document.createTextNode(
        '(' + 'lat: ' + resp.lat + ', ' + 'lon: ' + resp.lon + ', ' + 'address: ' + resp.address + ')'
      );
      anchor.appendChild(txt);
    });
  });

  function parseKml(url, callback) {
    var opts = {
      method: 'get',
      url: url,
      onerror: function(resp) {
        callback(null);
      },
      onload: function(resp) {
        var doc = (new DOMParser).parseFromString(resp.responseText, 'text/xml');
        // todo: check <parsererror />
        callback({
          lon: doc.getElementsByTagName('longitude')[0].textContent,
          lat: doc.getElementsByTagName('latitude')[0].textContent
        });
      }
    };
    GM_xmlhttpRequest(opts);
  }
})();

function GeoCoderFactory() {
  return {
    createGeoCoder: function(name) {
      return null;
    },
    createReverseGeoCoder: function(name) {
      if (name == 'invGeocoder')
        return new InvGeocoder();
      else if (name == 'rgeocode.php')
        return new RgeocodePhp();
      else if (name == 'ALPSLABlevel')
        return new AlpsLabLevel();
      return null;
    }
  };
}

// 標高API
// http://www.alpslab.jp/api_altitude.html
// thanks ALPSLAB
function AlpsLabLevel() {
  return {
    geocode: function(point, callback) {
      if (!callback || !point.lat || !point.lon)
        return null;

      var util = new Util;
      var p = util.wgs84ToTokyo({ lat: Number(point.lat), lon: Number(point.lon) });

      var url =
        'http://api.alpslab.jp/v1/level?' +
        'appid=guest&p=' + util.degreeToDms(Number(p.lat), '/') + ',' + util.degreeToDms(Number(p.lon), '/');
      GM_log(url);

      var opts = {
        method: 'get',
        url: url,
        onerror: function(resp) {
          callback(null);
        },
        onload: function(resp) {
          var doc = (new DOMParser).parseFromString(resp.responseText, 'text/xml');
          // todo: check <parsererror />
          callback({
            lat: point.lat,
            lon: point.lon,
            address: doc.getElementsByTagName('address')[0].textContent
          });
        }
      };

      GM_xmlhttpRequest(opts);
    }
  };
}

// rgeocode.php
// http://refits.cgk.affrc.go.jp/tsrv/jp/rgeocode.html
// thanks ReFITS Lab
//
// rgeocode.php は、国土交通省の国土情報の閲覧・提供サービスサイトで公開されている
// 「国土数値情報(行政区域データ)」および「街区レベル位置参照情報」を使用しています。
// 番地情報は、実態と異なる場合があり、またプライバシーにも関わることがありますので、
// 特に慎重に扱って下さい。
function RgeocodePhp() {
  return {
    geocode: function(point, callback) {
      if (!callback || !point.lat || !point.lon)
        return null;

      function caller(resp) {
        if (!resp.status) {
          callback(null);
          return;
        }
        var r = resp.result;
        callback({
          lat: resp.argument.latitude,
          lon: resp.argument.longitude,
          address: r.prefecture.pname + r.municipality.mname + (r.local ? r.local.section + r.local.homenumber : '')
        });
      }

      var url =
        'http://refits.cgk.affrc.go.jp/tsrv/jp/rgeocode.php?' +
        'jsonp=caller&v=2&lat=' + point.lat + '&lon=' + point.lon;
      GM_log(url);

      var opts = {
        method: 'get',
        url:
          'http://refits.cgk.affrc.go.jp/tsrv/jp/rgeocode.php?' +
          'jsonp=caller&v=2&lat=' + point.lat + '&lon=' + point.lon,
        onerror: function(resp) {
          callback(null);
        },
        onload: function(resp) {
          eval(resp.responseText);
        }
      };

      GM_xmlhttpRequest(opts);
    }
  };
}

// invGeocoder
// http://www.knya.net/archives/2005/07/rest.html
// thanks nishioka
function InvGeocoder() {
  return {
    geocode: function(point, callback) {
      if (!callback || !point.lat || !point.lon)
        return null;

      var url =
        'http://nishioka.sakura.ne.jp/google/ws.php?' +
        'lon=' + point.lon + '&lat=' + point.lat + '&format=simple';
      GM_log(url);

      var opts = {
        method: 'get',
        url: url,
        onerror: function(resp) {
          callback(null);
        },
        onload: function(resp) {
          var doc = (new DOMParser).parseFromString(resp.responseText, 'text/xml');
          // todo: check <parsererror />
          var version = doc.getElementsByTagName('version')[0].textContent;
          if (version != '0.1') {
            callback(null);
            return;
          }
          callback({
            lat: doc.getElementsByTagName('lat')[0].textContent,
            lon: doc.getElementsByTagName('lon')[0].textContent,
            address: doc.getElementsByTagName('address')[0].textContent
          });
        }
      };

      GM_xmlhttpRequest(opts);
    }
  };
}

function Util() {
  return {
    // http://homepage3.nifty.com/Nowral/02_DATUM/02_DATUM.html
    // thanks Nowral
    wgs84ToTokyo: function(p) {
      return {
        lat: p.lat + 0.00010696 * p.lat - p.lon * 0.000017467 - 0.0046020,
        lon: p.lon + 0.000046047 * p.lat + p.lon * 0.000083049 - 0.010041
      };
    },

    degreeToDms: function(deg, delim) {
      var h = parseInt(deg);
      var m = parseInt((deg - h) * 60);
      var s = (deg - h - (m / 60)) * 3600;
      var ss = (Math.round(s * 10) / 10);
      if (m < 10)
        m = '0' + m;
      if (ss < 10)
        ss = '0' + ss;
      if (Number(ss).toString().indexOf('.') == -1)
        ss = ss + '.0';
      return [h, m, ss].join(delim ? delim : '.');
    }
  };
}

実行結果

あくまでテストなので…。