Google Maps API v3 と jsdo.it を使って、地図上にその位置でつぶやかれたツイートを表示するアプリを作った。
ツイートを地図上に表示(コメント付) – Google Maps JavaScript API v3 – jsdo.it – Share JavaScript, HTML5 and CSS
http://jsdo.it/takatama/zNmW
ソースコードについて解説する。
(1) ツイートの取得
GET search | Twitter Developersでツイートを取得する。JSONPを使ってブラウザ上から取得ができる。jQueryのgetJSONメソッドを使った。
ポイントはurlのquery文字列にcallback=?を加えること。?の部分はjQueryがよきにはからってくれる。
//緯度経度、半径(km)、ツイートに含めたい文字を指定して、ツイートを取得する。
function fetchTweets(latLng, query, radiusKM) {
var url = 'http://search.twitter.com/search.json?callback=?';
var data = {
q: query,
rpp: 100,
geocode: latLng.lat() + ',' + latLng.lng() + ',' + radiusKM
};
$.getJSON(url, data, function (data, status) {
if (status === 'success') {
addTweetsAsMarkers(data);
} else {
alert('twiter error');
}
});
}
(2) ツイートをマーカーとして地図に追加
取得したツイートをマーカーとして追加する。マーカーがクリックされたときに情報ウィンドウを開くためのコールバックを追加しておく。
コールバック関数内ではクリックされたマーカーはthisで参照できるところがポイント。
//マーカーとして未追加で、緯度経度を含むツイートのみ追加する。
function addTweetsAsMarkers(data) {
var i;
for (i = 0; i < data.results.length; i++) {
var id = data.results[i].id_str;
var geo = data.results[i].geo;
if (!markersHash[id] && geo && geo.type === 'Point') {
var marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(parseFloat(geo.coordinates[0]), parseFloat(geo.coordinates[1])),
title: '@' + data.results[i].from_user,
icon: data.results[i].profile_image_url,
result: data.results[i] //情報ウィンドウにツイートを表示するために使う
});
google.maps.event.addListener(marker, 'click', function() {
//クリックされたmarkerはthisで参照できる。
showInfoWindow(this);
});
markersHash[id] = marker;
markersArray.push(marker);
}
}
}
(3) 情報ウィンドウのコンテンツを作成し、表示
情報ウィンドウを開く際、取得したツイートから表示する内容を作る。ツイートのテキストに http:// から始まるリンクが含まれている場合に、クリック可能にする。
JavaScript の replace メソッドにコールバック関数を渡すこともできる。arguments[1] には正規表現でグルーピングした値が入っている。詳しくはjavascriptのreplaceにfunctionを渡す – Webtech Walkerを参照のこと。
//情報ウィンドウを表示する
function showInfoWindow(marker) {
marker.setZIndex(9999); //表示位置を一番上にする
var linkToTheTweet = '<a target="_blank" href="http://twitter.com/' + marker.result.from_user
+ '/status/' + marker.result.id_str + '"> 開く </a></p>';
//ツイート内のリンクをクリック可能にする。
var text = marker.result.text.replace(/(https?:\/\/[\x21-\x7e]+)/gi, function () {
var uri = arguments[1];
return '<a target="_blank" href="' + uri + '">' + decodeURI(uri) + '</a>';
});
infoWindow.content = '<p style="font-size:0.9em;">' + text + '<div style="font-size:0.7em;">'
+ linkToTheTweet + '</div></p>';
infoWindow.open(map, marker);
}
(4) 検索する範囲を計算し、ツイートを取得
位置情報に基づいてツイートを取得する際は、中心となる緯度・経度に加え、検索する半径も指定する必要がある。
表示している地図の北東、南西の2点の位置はMapのgetBounds()メソッドで取得できる。また、2点間の距離はGeometry Libraryのgoogle.maps.geometry.spherical.computeDistanceBetween()関数で計算できる。 ポイントはhtmlのscriptタグでGoogle Maps API を呼び出す際、srcのquery文字列に&libraries=geometryを入れること。
//検索する範囲を表示画面の大きさから計算して、ツイートを取得する。
function fetchTweetsWithRadius() {
var distanceKM = 3.0;
var bounds = map.getBounds();
if(bounds) {
var latlng = new google.maps.LatLng(bounds.getSouthWest().lat(), bounds.getNorthEast().lng());
var distanceM = google.maps.geometry.spherical.computeDistanceBetween(bounds.getNorthEast(), latlng);
distanceKM = Math.floor(distanceM / 2 / 100) / 10;
}
fetchTweets(map.getCenter(), $('#filter').val(), distanceKM + 'km');
}
(5) 地図の操作に対する動作
ここまででツイートの検索からマーカーとしての表示までは説明できた。後は各種操作のための初期化について説明する。
ここではgoogle.maps.event.addListenerを使って、地図上での操作に対する処理を定義している。
また、自動的にツイートの検索と表示を実行するため、fetchTweetsWithRadius と showNextTweet を定期的に実行する。
//ページが読み込み終わったら実行する。
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(35.698619, 139.773288),
zoom: 11,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
infoWindow = new google.maps.InfoWindow();
//地図内をクリックしたら情報ウィンドウを閉じる
google.maps.event.addListener(map, 'click', function () {
infoWindow.close();
});
//ドラッグが終わった位置で、ツイートを検索する。
google.maps.event.addListener(map, 'dragend', function () {
fetchTweetsWithRadius();
});
//ダブルクリックしズームした位置で、ツイートを検索する。
google.maps.event.addListener(map, 'dblclick', function () {
fetchTweetsWithRadius();
});
fetchTweetsWithRadius();
setInterval(fetchTweetsWithRadius, 30000); //30秒ごとにツイートを検索する。
setInterval(showNextTweet, 4000); //4秒ごとに情報ウィンドウを表示する。
geocoder = new google.maps.Geocoder();
}
(6) ツイートの自動表示
あらかじめ取得していたツイートを定期的に表示していく。
マーカーが枠内に含まれている場合にのみ、表示するようにしている。
//追加したマーカーが画面上にあれば情報ウィンドウを表示する。
var currentMarker = null;
function showNextTweet() {
if (currentMarker) {
currentMarker.setMap(null);
infoWindow.close();
currentMarker = null;
}
var marker = markersArray.shift();
while (marker) {
var bounds = map.getBounds();
//はじめに表示したときには、boundsがundefinedになってしまう。
if (!bounds || bounds.contains(marker.getPosition())) {
map.panTo(marker.getPosition());
showInfoWindow(marker);
currentMarker = marker;
return;
} else {
marker.setMap(null);
marker = markersArray.shift();
}
}
}
(7) 地名を入力して移動
地図の上部に設置しているフォームの操作用。入力した地名を Geocoding APIで緯度経度に変換し、地図を移動させた上で、その地点でのツイートを検索する。
//地名から緯度経度を取得して、地図を移動させ、ツイートを検索する。
function codeAddress() {
geocoder.geocode({
'address': $('#address').val()
}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
fetchTweetsWithRadius();
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
(8) 検索したツイートすべてを消去して、再検索
地図の上部に設置しているフォームの操作用。ツイートに含めたい文字を変更したり、クリアした場合に呼び出す。検索したツイートから追加してきたマーカーをすべて消去して、現在地点でのツイートを検索しなおす。
//すべてのマーカーを削除してツイートを検索する。
function reload() {
var i;
for (i = 0; i < markersArray.length; i++) {
markersArray[i].setMap(null);
}
markersArray.length = 0;
markersHash = {};
fetchTweetsWithRadius();
}
(9) フォームをクリアした上で、再読み込み
地図の上部に設置しているフォームの操作用。フォームの文字列をクリアした上で、ツイートの再読み込みを行う。
//ツイートをフィルターする文字列をクリアし、ツイートを検索する。
function clearFilter() {
$('#filter').val('');
reload();
}