Googleスプレッドシートを簡易CMSとしてWebアプリチックなものを作れる

社内の情報共有ツールで話題に出てちょっと悔しかったので書いています。

内容を管理する簡易な仕組み

簡単なWebサービスやアプリみたいなもののデータの管理をどうするかっていうのは結構悩ましい問題だと思います。
例えば僕が作りかけて放置してしまっているうまうまっぷというサイトがあります。
このサイトで必要なデータは店の名前、住所、緯度・経度、コメントくらいです。
それくらいならJSONを直接書いてもいいのですが、後からデータを追加する場合にCMSを使うなりしたほうが何かと便利だと思います。
だからといって既存のCMS、、、WordPressとかMODXとかを使うのは大げさ過ぎます。

そこで、デザイナーリーダーの提案でGoogleスプレッドシートでデータを管理することにしました。(このスプレッドシートを作っていただいた上にデータの取得の仕方までレクチャーしてもっらちゃいました☆)

GoogleスプレッドシートをどうやってCMSとして使うかというと、なんとJSONPとしてデータを受け取ることが出来るのです!

https://spreadsheets.google.com/feeds/list/0AtjU21psYxuFdFBTTFptSjA4SVFNcG04QmZtNVZxd0E/od6/public/values?alt=json-in-script&callback=myFunc

デフォルトではXMLを吐くようですが、アドレスの末尾に以下のパラメータをつけるとJSONで出力されます。

alt=json-in-script&callback=myFunc

ただ、jQueryの.ajax()メソッドを使ってJSONPを読む場合は読み込みURLを以下とし、

https://spreadsheets.google.com/feeds/list/0AtjU21psYxuFdFBTTFptSjA4SVFNcG04QmZtNVZxd0E/od6/public/values?alt=json-in-script&callback=

myFunc(←コールバック関数)はパラメータとして渡してやる必要があります。

読み込みようのコードはこんな感じ。

$.ajax({
	type: 'GET',
	url: 'https://spreadsheets.google.com/feeds/list/0AtjU21psYxuFdFBTTFptSjA4SVFNcG04QmZtNVZxd0E/od6/public/values?alt=json-in-script&callback=?',
	cache: false,
	dataType: 'jsonp',
	jsonp : 'myFunc',
	success: function(json, textStatus) {

	},
	error: function(xhr, textStatus, errorThrown) {

	}
});

個人的にはJSONのオブジェクトの名前が特殊でちょっと気持ち悪いのが難点。
例えば店舗名にアクセスするにはjson.feed.entry.gsx$name.$tという具合です。
最後の$tってなんのためにあるのよ…。

そんなこんなで受け取ったJSONの中身をループでまわしてごにょごにょやってやったらうまうマップみたいなサイトが出来るわけです。

面倒な部分(位置情報入力)は極力手抜き

現在のうまうマップは緯度・経度を受け取ってマーカーを立てていますが、お店を登録する際にいちいち緯度・経度を打ち込むのはめんどくさすぎますよね。
Google Geocoding APIというものを使うと、住所を緯度・経度に、緯度・経度を住所に簡単に変換することが出来るのです!!

使い方は至って簡単!!(※以下のコードはうまうまっぷを作る上で参考にさせていただいたコードです)

function getLatLng(place) {

  // ジオコーダのコンストラクタ
  var geocoder = new google.maps.Geocoder();

  // geocodeリクエストを実行。
  // 第1引数はGeocoderRequest。住所⇒緯度経度座標の変換時はaddressプロパティを入れればOK。
  // 第2引数はコールバック関数。
  geocoder.geocode({
    address: place
  }, function(results, status) {
    if (status == google.maps.GeocoderStatus.OK) {

      // 結果の表示範囲。結果が1つとは限らないので、LatLngBoundsで用意。
      var bounds = new google.maps.LatLngBounds();

      for (var i in results) {
        if (results[i].geometry) {

          // 緯度経度を取得
          var latlng = results[i].geometry.location;

          // 住所を取得(日本の場合だけ「日本, 」を削除)
          var address = results[0].formatted_address.replace(/^日本, /, '');

          // 検索結果地が含まれるように範囲を拡大
          bounds.extend(latlng);

          // あとはご自由に・・・。
          new google.maps.InfoWindow({
            content: address + "
(Lat, Lng) = " + latlng.toString() }).open(map, new google.maps.Marker({ position: latlng, map: map })); } } // 範囲を移動 map.fitBounds(bounds); } else if (status == google.maps.GeocoderStatus.ERROR) { alert("サーバとの通信時に何らかのエラーが発生!"); } else if (status == google.maps.GeocoderStatus.INVALID_REQUEST) { alert("リクエストに問題アリ!geocode()に渡すGeocoderRequestを確認せよ!!"); } else if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) { alert("短時間にクエリを送りすぎ!落ち着いて!!"); } else if (status == google.maps.GeocoderStatus.REQUEST_DENIED) { alert("このページではジオコーダの利用が許可されていない!・・・なぜ!?"); } else if (status == google.maps.GeocoderStatus.UNKNOWN_ERROR) { alert("サーバ側でなんらかのトラブルが発生した模様。再挑戦されたし。"); } else if (status == google.maps.GeocoderStatus.ZERO_RESULTS) { alert("見つかりません"); } else { alert("えぇ~っと・・、バージョンアップ?"); } }); }

出典:http://www.nanchatte.com/map/getLatLngByAddress.html

問題発生

上記のコードを参考に実装しましたが、僕の組み方が悪かったのか、20件くらいのデータをループにかけて一気にgeocodingしたらgoogle.maps.GeocoderStatus.OVER_QUERY_LIMITが出て正常に処理できませんでした。タイマーを用意して1秒ごとに変換とかするとちゃんと処理できていました。

さらに問題が…このAPIにも制限があり2500/日のアクセスを超えるとAPIの利用を一時停止させられます。なので、フロントエンドの仕組みだけでこのAPIを使って運用するのは難しそうです。

解決

僕がGoogle Maps APIで遊んでいるのを見て触発されたデザイナーリーダーが作った入力した住所を緯度・経度に変換するサンプルを使って、現在のスプレッドシートの中に緯度と経度を持たせる仕様に変えました。
CMSならJSでちょちょいとやれば住所を入力したら緯度・経度のフォームに自動的に数字を補完するとかできそうなのですがね。スプレッドシートでもGreasemonkeyでごにょごにょやればできるのかな…またいつか試してみたいと思います。

完成度はこちらのほうが高いですが、似たようなことは我々でもわりとパパっと作れるのでした。