jQueryとJerseyを使ってGoogle Maps APIをいじる
前回作った無理矢理拡張Jerseyをサーバサイドで、クライアント側はjQueryを利用してGoogle Maps APIをちょっと弄ってみたいと思います。まずはDBに、マップに表示させるコンテンツとカテゴリを保持させます。
CREATE DATABASE mapsample DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci; USE mapsample; CREATE TABLE map_categories ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT 'ID' ,name VARCHAR(255) NOT NULL COMMENT '名前' ,order_by INT NOT NULL COMMENT '順序' ,lock_version INT UNSIGNED NOT NULL COMMENT 'バージョン' ,created_at DATETIME NOT NULL COMMENT '作成日時' ,updated_at DATETIME NOT NULL COMMENT '更新日時' ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='地図カテゴリー'; CREATE TABLE area_categories ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT 'ID' ,name VARCHAR(255) NOT NULL COMMENT '名前' ,order_by INT NOT NULL COMMENT '順序' ,lock_version INT UNSIGNED NOT NULL COMMENT 'バージョン' ,created_at DATETIME NOT NULL COMMENT '作成日時' ,updated_at DATETIME NOT NULL COMMENT '更新日時' ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='エリアカテゴリー'; CREATE TABLE map_contents ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT 'ID' ,map_category_id INT UNSIGNED NOT NULL COMMENT '地図カテゴリーID' ,area_category_id INT UNSIGNED NOT NULL COMMENT 'エリアカテゴリーID' ,title VARCHAR(50) NOT NULL COMMENT 'タイトル' ,lat DECIMAL(18,15) NOT NULL COMMENT '緯度' ,lng DECIMAL(18,15) NOT NULL COMMENT '経度' ,comment VARCHAR(500) COMMENT 'コメント' ,order_by INT NOT NULL COMMENT '順序' ,visible BOOLEAN NOT NULL COMMENT '表示フラグ' ,lock_version INT UNSIGNED NOT NULL COMMENT 'バージョン' ,created_at DATETIME NOT NULL COMMENT '作成日時' ,updated_at DATETIME NOT NULL COMMENT '更新日時' ,FOREIGN KEY (map_category_id) REFERENCES map_categories(id) ,FOREIGN KEY (area_category_id) REFERENCES area_categories(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='地図コンテンツ';
カテゴリに緯度・経度を保持させ、この情報を元にGoogle Mapに表示させるマーカーを作成します。ちなみに緯度経度情報はGoogle Maps APIのGClientGeocoder#getLatLngメソッド等で取得できます。
次は、Google Mapを表示させるHTMLを作成
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" href="css/map.css" /> <script type="text/javascript" src="js/jquery.js"></script> <script type="text/javascript" src="js/util.js"></script> <script type="text/javascript" src="http://maps.google.com/maps?file=api&v=2&key={ここにGoogle Maps APIのKEYを記入}"></script> <script type="text/javascript" src="index.js"></script> <title>Google Maps サンプル</title> </head> <body> <h1>Google Maps サンプル</h1> <div id="map"></div> </body> </html>
利用するJavaScriptファイルの読み込みと、マップを表示させるDIVの定義をしているだけです。マップの大きさはCSSファイルで定義しています。
次は、このHTMLの挙動を設定するJavaScriptファイル
$(function() { if (GBrowserIsCompatible()) { var map = new GMap2(document.getElementById("map")); map.setCenter(new GLatLng({地図中心の緯度を記入}, {地図中心の経度を記入}), 16); map.addControl(new GLargeMapControl()); map.addControl(new GMapTypeControl()); map.addControl(new GOverviewMapControl()); $.getJSON( "resources/mapContents", null, function(data) { var addMarker = function(mc) { var latLng = new GLatLng(mc.lat, mc.lng); var marker = new GMarker(latLng); var html = "<h4>" + xmlEscape(mc.title) + "</h4>"; if (mc.comment) { html += "<p>" + xmlEscape(mc.comment) + "</p>"; } GEvent.addListener(marker, "click", function() { marker.openInfoWindowHtml(html); }); map.addOverlay(marker); }; for (var i = 0; i < data.length; i++) { addMarker(data[i]); } } ); } $(window).unload(GUnload); });
xmlEscapeメソッドは、自作のXMLエスケープメソッドです。
HTMLファイルがロードされたときにAjaxでコンテンツ一覧を取得し、コンテンツの緯度・経度を使ってマーカーを地図に設定します。マーカーをクリックしたときには、タイトルとコメントが表示されるようにイベントを追加します。
最後に、このコンテンツデータを取得するJPAのEntityとJerseyのリソースクラスを作成
package mapsample.entity; import java.io.Serializable; import java.math.BigDecimal; import java.sql.Timestamp; import java.util.Set; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Version; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; @Entity public class Map_contents implements Serializable { /** * */ private static final long serialVersionUID = 838950060562483992L; @Id @GeneratedValue private Integer id; private String title; private BigDecimal lat; private BigDecimal lng; private int order_by; private String comment; @ManyToOne(fetch = FetchType.LAZY) private Area_categories area_category; @ManyToOne(fetch = FetchType.LAZY) private Map_categories map_category; private boolean visible; @Version private Integer lock_version; private Timestamp created_at; private Timestamp updated_at; (setter、getter省略) public JSONObject getJson() throws JSONException { return new JSONObject() .put("id", id) .put("title", title) .put("lat", lat) .put("lng", lng) .put("order_by", order_by) .put("comment", comment) .put("visible", visible) .put("lock_version", lock_version) .put("created_at", created_at) .put("updated_at", updated_at); } }
package mapsample.resource; import java.util.ArrayList; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.ProduceMime; import javax.ws.rs.WebApplicationException; import mapsample.entity.Map_contents; import org.codehaus.jettison.json.JSONArray; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; @Path("/mapContents") public class MapContentsRecource { @PersistenceContext(unitName = "mapsample") private EntityManager em; @SuppressWarnings("unchecked") @GET @ProduceMime("application/json") public JSONArray getList() throws JSONException { List<Map_contents> list = em.createNamedQuery("Map_contents.getMapContents") .getResultList(); List<JSONObject> jsons = new ArrayList<JSONObject>(); for (Map_contents mc : list) { jsons.add(mc.getJson()); } JSONArray array = new JSONArray(jsons); return array; } }
NamedQueryは単純にSELECTしているだけなので省略。Jerseyが利用しているJSONライブラリのjettisonは、どうもBeanからJSONに自動変換するような関数を用意してないみたいなので、Entity側で個別に対応しています。今回は関連Entityについての定義は省略しています。
jQuery側で定義していたURLをJAX-RSの@Pathに定義し、戻り値をJSONArrayにして@ProduceMineでapplication/jsonを定義すれば準備完了です。後は適当にデータを入れて、GlassFish上で実行してみると
こんな感じで画面が表示されます。簡単ですね。