はてなマップはっく: 一度に表示されるマーカーの数を増やす

今回、greasemonkeyを用いることで、はてなマップに一度に表示されるマーカーの数を増やすことに成功しました。それでは、どのように私がはてなマップをHackしたのかをレポートしたいと思います。

スクリーンショット

まずは結果が気になると思いますので、スクリーンショットを。今回作成したgreasemonkeyスクリプトをインストールして、はてなマップにアクセスすると、こんな感じで表示されるようになります。マーカーがいつもよりも沢山表示されます。マーカー出しすぎですよね。あくまで実験ですので。

The problem

はてなマップに一度に表示されるマーカーの数は、以下のようにコーディングされています(map.jsを参照)。 現在の所、MarkerMaker#getLimit()が常に20個のマーカー情報を取得するように制限をかけられています。コードからわかるように、MarkerMaker#place()内にあるはてなマップのRESTインタフェースの引数を組み立てる所で、MarkerMaker#getLimit()が呼ばれています。

私が実現したいことは、何とかしてMarkerMaker#getLimit()の実装をオーバーライドして、そのメソッドがreturnする数を20からもっと大きな数にすることです。

var MarkerMaker = Class.create();
MarkerMaker.prototype = {
[...]
    getLimit : function() {
        return 20;
    },
    place : function() {
		var request = GXmlHttp.create();
		var b = HatenaMap.getBounds();
		var args = 'minX='+b.minX+'&maxX='+b.maxX+'&minY='+b.minY+'&maxY='+b.maxY;
        args += '&lt=' + this.getLimit();
        var p = map.getCenterLatLng();
        if (MapUtil.japancheck(p.x,p.y)) {
            p = MapUtil.tokyo2wgs(p.y,p.x);
        }
[...]

How did I hack Hatena::Map?

今回のはてなマップのHackは、「JavaScript Shell 1.3」が役に立ちました。これは最近流行のインタラクティブシェルです。id:naoyaさんのはてなブックマーク発見しました。このインタラクティブシェルはブックマークレットとして活用することで真価を発揮します。ブックマークレットは、ここのページにある「shell」をブックマークすることで使えるようになります。

準備は以下のようにして行います。

  1. このブックマークレットをブックマークに登録
  2. はてなマップにアクセス
  3. 登録したブックマークレットをクリック
  4. JavaScript Shell 1.3」のインタラクティブシェルが起動

さて、ここから面白くなります。

インタラクティブシェルを使って、map.jsで定義されているMarkerMakerが持つプロパティーを探ってみます。

props(MarkerMaker)
Methods of prototype of prototype: bind, bindAsEventListener
Fields of prototype of prototype: prototype
Methods of prototype of prototype of prototype: extend

props(MarkerMaker.prototype)
Methods: initialize, update, show, hide, getLimit, place, remove, markForUpdate, complete, openInfoWindow
Methods of prototype: extend

「props(MarkerMaker.prototype)」の結果に注目して下さい。ここでリストアップされているメソッドは見覚えのあるメソッドばかりです。

では、MarkerMaker#getLimit()の実装を確かめてみます。「MarkerMaker.prototype.getLimit」とシェルに入力してEnterキーを押すけで、実装のコードが表示されました。これはなかなか便利ですね。

MarkerMaker.prototype.getLimit
function () {
    return 20;
} 

次にMarkerMaker#getLimit()の実装をオーバーライドできないか試してみました。勉強不足で構文をミスしてしまいました。見事に怒られてしまいました。

MarkerMaker.prototype.getLimit : function() { return 100; }
SyntaxError: invalid label

「それでは、これならどうかな?」と思い、「:」を「=」にすると構文エラーは出なくなりました。希望が見えてきました。ここでちょっとご注意を。このインタラクティブシェルでは既存の実装をオーバーライドできないようです。そんなことができたら最高だったのですが...

MarkerMaker.prototype.getLimit = function() { return 100; }
function () {
    return 100;
}

後は、greasemonkeyで書き直せば絶対うまくいくはずだと考えました。早速greasemonkeyで実装し、このスクリプトをenableにしてから、はてなマップにアクセスすると、いつも以上に多くのマーカー(最高100個)が表示されるようになりました。

[...]
(function() {
	[...]
	// Override the MarkerMaker#getLimit() function
	// to expand the number of markers that will be displayed.
	// The default is 20.
	MarkerMaker.prototype.getLimit = function() {
		return 100;
	};
})();

一応試してみたのですが、今回のgreasemonkeyスクリプトを適用した状態で、インタラクティブシェルでMarkerMaker#getLimit()調べてみると、確かに実装がオーバーライドされているのがわかります。

prototype.jsベースの新はてなマップの可能性

正直、こんなにも簡単に実装をオーバーライドできるとは思っていませんでした。prototype.jsをベースにして作られた新はてなマップは、greasemonkeyによるカスタマイズの可能性を大いに持っていると言えないでしょうか? 後は他のサービスとRemixできればもっと面白いことになっていきそうですね。

やっぱりid:naoyaさんは目の付け所がシャープですね。新はてなマップprototype.jsを採用して正解だったと思います。また、新はてなマップの実装に関わられたid:higeponさんともう一人のエンジニアの方は良い仕事をされたと思います。非常に勉強になりました。

The greasemonkey Script

本当に少しずつですが、Hatena::Map::MyFavoritePlaceHackは着実に進化しています。今回のHackでバージョンは0.03になりました。ここからお試し下さい。

// ==UserScript==
// @name          Hatena::Map::MyFavoritePlaceHack 0.03
// @namespace     http://vaio.redirectme.net/lib/greasemonkey
// @description   Change the default place(hatena,Inc) to my favorite place whenever you load Hatena::Map.
// @include       http://map.hatena.ne.jp/*
// ==/UserScript==

(function() {
	// "favorite" variable below is at Haneda Airport.
	// The coordinate system here is WGS not Tokyo Datum.
	favorite = new GPoint(139.7890, 35.5457);
			
	// The argument order of MapUtil#japancheck() and MapUtil#wgs2tokyo()
	// are litte confusing (lack of consistency). The spec should be improved.
	if (MapUtil.japancheck(favorite.x, favorite.y)) {
		p = MapUtil.wgs2tokyo(favorite.y, favorite.x);
	}

	// "map" is a GMap instance.
	// So, you can use any exported Google Maps API's functions.
	// Here is the example of the centerAndZoom() function.
	map.centerAndZoom(p, 0);
													
	// Tweak the height of the map.
	// The default value is 380px.
	// See the "Developer Notes for prototype.js covers version 1.3.1"
	// for the details of the "$()" utility function.
	$('map').style.height = '500px';
	
	// Override the MarkerMaker#getLimit() function
	// to expand the number of markers that will be displayed on the map.
	// The default is 20.
	MarkerMaker.prototype.getLimit = function() {
		return 100;
	};
})();