GWTとGAEとAndroidとGooglMapsの連携
AndroidのGPSを使ったアプリとAndroidの位置情報をデータストアに保存しGoogleMapに表示するWebアプリを作成する
今回はサーバ側を作成します。
サーバはGAE/Jを使います
サーバ側は二つのサーブレットを用意します
■プロジェクト構成
- sample.googlemap.server
- GreetingServiceImpl
- PMF
- GPSPoint
- sample.googlemap.myservlet
- MyServlet
- sample.googlemap.client
- GwtSampleGoogleMap
- GreetingService
- GreetingServiceAsync
- GPSBeans
■作成手順
前回作成したGoogleMapsアプリに以下の修正をします
概ね以下の2つの処理に分けて作成します
サーバ側:データ保存
サーバ側:地図出力
- 座標テキストボックスを削除
- データストアより最新の位置情報を取得する
- マップ表示
サーバ側プログラム概要
■アプリ概要
クラス名:MyServlet
パッケージ名:sample.googlemap.myservlet
サーバ側:位置情報保存サーブレット
■作成・変更クラス
- PMFクラスの作成:PersistenceManagerのヘルパークラス的なもの
- パッケージ名:sample.googlemap.server
- クラス名:PMF
- データストアに保存するデータクラスの作成
- パッケージ名:sample.googlemap.myservlet
- クラス名:GpsPoint
- サーブレットクラスの作成
- パッケージ名:sample.googlemap.myservlet
- クラス名:MyServlet
- web.xmlの修正
- inex.htmlの作成:座標入力フォーム
■PMF
public class PMF { private static final PersistenceManagerFactory pmfInstance = JDOHelper .getPersistenceManagerFactory("transactions-optional"); private PMF() { } public static PersistenceManagerFactory get() { return pmfInstance; } }
■GpsPoint
@PersistenceCapable(identityType = IdentityType.APPLICATION) public class GpsPoint implements IsSerializable{ @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id; @Persistent private double x; @Persistent private double y; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public double getX() { return x; } public void setX(double x) { this.x = x; } public double getY() { return y; } public void setY(double y) { this.y = y; } public GpsPoint( double x, double y) { this.x = x; this.y = y; } public GpsPoint() { } @Override public String toString() { return "id" + this.id + "x:" + this.x + " y:" + this.y; } }
■MyServlet
リクエストより座標情報を取得するdoGetメソッドの実装
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { double point_x, point_y; //リクエスト情報から座標を取得 try{ point_x = Double.parseDouble(request.getParameter("point_x")); point_y = Double.parseDouble(request.getParameter("point_y")); store(point_x, point_y); }catch(NumberFormatException e){ return ; } //レスポンス情報を作成する response.setContentType("text/html; charset=\"utf-8\""); PrintWriter out = response.getWriter(); out.write("<html>"); out.write("<head><title>AndroindGPS</title></head>"); out.write("<body>"); out.write("<h2> x:" + point_x + " y:" + point_y + "</h2>"); out.write("</body>"); out.write("</html>"); } データ保存をするstoreメソッドの実装 >|java| public void store(double x, double y) { GpsPoint gpsPoint = new GpsPoint(x, y); PersistenceManager pm = PMF.get().getPersistenceManager(); try { pm.makePersistent(gpsPoint); } finally { pm.close(); } }
<servlet> <description></description> <display-name>MyServlet</display-name> <servlet-name>MyServlet</servlet-name> <servlet-class>sample.googlemap.myservlet.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/MyServlet</url-pattern> </servlet-mapping> || <span style="font-weight:bold;">■index.html</span> 入力フォームを作成 >|html| <form action="MyServlet" method="GET" > <div>AndroidGPS: x:<input type="text" name="point_x" size=6 value="35.69964" /> y:<input type="text" name="point_y" size=6 value="139.77089" /> </div> <input type="submit" value="送信" /> <input type="reset" value="取り消し" /> </form>
ここまででひとまず実行する
プロジェクト右クリック→Run As→Web Application→index.html
サーバ側:Map表示サーブレット
データストア内のデータを読み込み、地図を表示する
座標データはGpsBeansを使ってやり取りする。
■作成・変更手順
対象パッケージ:sample.googlemap.client
- GwtSampleGoogleMap.html
- ID:error_messageの追加:エラーメッセージ表示用
- Beansの作成:データストア保存データクラスBeans
- クラス名:GpsBeans
- サービスインタフェースの引数・戻り値をBeansにする
- GreetingService
- GreetingServiceAsync
- GreetingServiceImplの修正
- 非同期通信Beans対応
- データストアからデータの読み込み処理
- GwtSampleGoogleMap
- テキストボックス削除
- Beans対応
■GwtSampleGoogleMap.html
エラーメッセージ表示領域の追加
<table align="center"> <tr> <td id="error_message"></td> </tr> <tr> <td id="dockpanel"></td> </tr> </table>
■GpsBeans
データクラスを非同期通信するため、同じ構造のBeansクラスをわざわざ作成。
※GPSPointクラスをそのまま非同期通信しようとしたがなぜかエラーがおきるので同じ構成のBeansクラスを作成した。
メンバ変数、getter/setter と コンストラクタを追加
private Long id; private double x; private double y;
■GreetingService
- 引数なし
- 戻り値:List
List<GpsBeans> greetServer();
■GreetingServiceAsync
- 引数:AsyncCallback
- >
- 戻り値なし
void greetServer(AsyncCallback<List<GpsBeans>> callback);
■GreetingServiceImpl
greetServerメソッドの実装
データストアからデータを取り出し、Listに格納して戻す
@SuppressWarnings("unchecked") @Override public List<GpsBeans> greetServer() { PersistenceManager pm = PMF.get().getPersistenceManager(); String query = "select from " + GpsPoint.class.getName(); List<GpsPoint> list = (List<GpsPoint>) pm.newQuery(query).execute(); List<GpsBeans> result = new ArrayList<GpsBeans>(); for(GpsPoint point : list){ result.add(new GpsBeans(point.getId(), point.getX(), point.getY())); } return result; }
■GwtSampleGoogleMap
GreetingServiceAsync変数の追加
HTML型 error_message変数の追加:サーバエラーなどのエラーメッセージ表示領域
初期化し、RootPanelに追加する
private HTML error_message; private final GreetingServiceAsync greetingService = GWT.create(GreetingService.class); public void onModuleLoad() { error_message = new HTML(); 〜略〜 RootPanel.get("error_message").add(error_message); }
OnClickイベントの非同期通信処理追加
結果データより、最新のものの座標を取得し地図表示
@Override public void onClick(ClickEvent event) { AsyncCallback<List<GpsBeans>> callback = new AsyncCallback<List<GpsBeans>>(){ @Override public void onFailure(Throwable caught) { error_message.setHTML(caught.getMessage()); } @Override public void onSuccess(List<GpsBeans> result) { //debug StringBuilder str = new StringBuilder(); if( result.size() == 0){ str.append("nodata"); } for(GpsBeans gps: result){ str.append(gps.toString()); str.append("<br>"); } if( result.size() != 0){ GpsBeans gps_data = result.get(result.size() - 1); setMap(gps_data.getX(), gps_data.getY()); } error_message.setHTML(str.toString()); } }; greetingService.greetServer(callback); }
マップ表示をルーチン化
public void setMap(double x, double y){ LatLng here = LatLng.newInstance(x, y); //マップサイズ map.setSize("500px", "300px"); // ズームコントローラ map.addControl(new LargeMapControl()); map.setScrollWheelZoomEnabled(true); // マーカー map.addOverlay(new Marker(here)); //真ん中と縮尺 map.setCenter(here, 13); panelMap.add(map, DockPanel.CENTER); }
ソース
■GpsBeans.java
package sample.googlemap.client; import com.google.gwt.user.client.rpc.IsSerializable; public class GpsBeans implements IsSerializable { private Long id; private double x; private double y; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public double getX() { return x; } public void setX(double x) { this.x = x; } public double getY() { return y; } public void setY(double y) { this.y = y; } public GpsBeans( double x, double y) { this.x = x; this.y = y; } public GpsBeans(Long id, double x, double y) { super(); this.id = id; this.x = x; this.y = y; } public GpsBeans() { } @Override public String toString() { return "id" + this.id + "x:" + this.x + " y:" + this.y; } }
■GreetingService
package sample.googlemap.client; import java.util.List; import com.google.gwt.user.client.rpc.RemoteService; import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; /** * The client side stub for the RPC service. */ @RemoteServiceRelativePath("greet") public interface GreetingService extends RemoteService { List<GpsBeans> greetServer(); }
■GreetingServiceAsync
package sample.googlemap.client; import java.util.List; import com.google.gwt.user.client.rpc.AsyncCallback; /** * The async counterpart of <code>GreetingService</code>. */ public interface GreetingServiceAsync { void greetServer(AsyncCallback<List<GpsBeans>> callback); }
■GwtSampleGoogleMap.java
package sample.googlemap.client; import java.util.List; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.maps.client.control.LargeMapControl; import com.google.gwt.maps.client.geom.LatLng; import com.google.gwt.maps.client.overlay.Marker; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DockPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.RootPanel; /** * Entry point classes define <code>onModuleLoad()</code>. */ public class GwtSampleGoogleMap implements EntryPoint { private Button btMap; private DockPanel panelMap; private Panel panelForm; private MapWidget map; private HTML error_message; private final GreetingServiceAsync greetingService = GWT .create(GreetingService.class); /** * This is the entry point method. */ public void onModuleLoad() { error_message = new HTML(); this.btMap = new Button("表示"); this.panelMap = new DockPanel(); this.panelForm = new HorizontalPanel(); this.map = new MapWidget(); this.panelForm.add(btMap); this.panelMap.add(panelForm, DockPanel.NORTH); this.btMap.addClickHandler(new ClickHandler(){ @Override public void onClick(ClickEvent event) { AsyncCallback<List<GpsBeans>> callback = new AsyncCallback<List<GpsBeans>>(){ @Override public void onFailure(Throwable caught) { error_message.setHTML(caught.getMessage()); } @Override public void onSuccess(List<GpsBeans> result) { //debug StringBuilder str = new StringBuilder(); if( result.size() == 0){ str.append("nodata"); } for(GpsBeans gps: result){ str.append(gps.toString()); str.append("<br>"); } if( result.size() != 0){ GpsBeans gps_data = result.get(result.size() - 1); setMap(gps_data.getX(), gps_data.getY()); } error_message.setHTML(str.toString()); } }; greetingService.greetServer(callback); } }); RootPanel.get("error_message").add(error_message); RootPanel.get("dockpanel").add(panelMap); } public void setMap(double x, double y){ LatLng here = LatLng.newInstance(x, y); //マップサイズ map.setSize("500px", "300px"); // ズームコントローラ map.addControl(new LargeMapControl()); map.setScrollWheelZoomEnabled(true); // マーカー map.addOverlay(new Marker(here)); //真ん中と縮尺 map.setCenter(here, 13); panelMap.add(map, DockPanel.CENTER); } }
■MyServlet.java
package sample.googlemap.myservlet; import java.io.IOException; import java.io.PrintWriter; import javax.jdo.PersistenceManager; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import sample.googlemap.server.GpsPoint; import sample.googlemap.server.PMF; /** * Servlet implementation class MyServlet */ public class MyServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public MyServlet() { super(); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { double point_x, point_y; //リクエスト情報から座標を取得 try{ point_x = Double.parseDouble(request.getParameter("point_x")); point_y = Double.parseDouble(request.getParameter("point_y")); store(point_x, point_y); }catch(NumberFormatException e){ return ; } //レスポンス情報を作成する response.setContentType("text/html; charset=\"utf-8\""); PrintWriter out = response.getWriter(); out.write("<html>"); out.write("<head><title>AndroindGPS</title></head>"); out.write("<body>"); out.write("<h2> x:" + point_x + " y:" + point_y + "</h2>"); out.write("</body>"); out.write("</html>"); } public void store(double x, double y) { GpsPoint gpsPoint = new GpsPoint(x, y); PersistenceManager pm = PMF.get().getPersistenceManager(); try { pm.makePersistent(gpsPoint); } finally { pm.close(); } } }
■GpsPoint.java
package sample.googlemap.server; import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.IdentityType; import javax.jdo.annotations.PersistenceCapable; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; import com.google.gwt.user.client.rpc.IsSerializable; @PersistenceCapable(identityType = IdentityType.APPLICATION) public class GpsPoint implements IsSerializable{ @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id; @Persistent private double x; @Persistent private double y; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public double getX() { return x; } public void setX(double x) { this.x = x; } public double getY() { return y; } public void setY(double y) { this.y = y; } public GpsPoint( double x, double y) { this.x = x; this.y = y; } public GpsPoint() { } @Override public String toString() { return "id" + this.id + "x:" + this.x + " y:" + this.y; } }
■GreetingServiceImpl.java
package sample.googlemap.server; import java.util.ArrayList; import java.util.List; import javax.jdo.PersistenceManager; import sample.googlemap.client.GpsBeans; import sample.googlemap.client.GreetingService; import com.google.gwt.user.server.rpc.RemoteServiceServlet; /** * The server side implementation of the RPC service. */ @SuppressWarnings("serial") public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService { @SuppressWarnings("unchecked") @Override public List<GpsBeans> greetServer() { PersistenceManager pm = PMF.get().getPersistenceManager(); String query = "select from " + GpsPoint.class.getName(); List<GpsPoint> list = (List<GpsPoint>) pm.newQuery(query).execute(); List<GpsBeans> result = new ArrayList<GpsBeans>(); for(GpsPoint point : list){ result.add(new GpsBeans(point.getId(), point.getX(), point.getY())); } return result; } }
package sample.googlemap.server; import javax.jdo.JDOHelper; import javax.jdo.PersistenceManagerFactory; public class PMF { private static final PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional"); private PMF() { } public static PersistenceManagerFactory get() { return pmfInstance; } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.4//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.6.4/distro-source/core/src/gwt-module.dtd"> <module rename-to='gwtsamplegooglemap'> <!-- Inherit the core Web Toolkit stuff. --> <inherits name='com.google.gwt.user.User'/> <!-- Inherit the default GWT style sheet. You can change --> <!-- the theme of your GWT application by uncommenting --> <!-- any one of the following lines. --> <inherits name='com.google.gwt.user.theme.standard.Standard'/> <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> --> <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/> --> <!-- Other module inherits --> <inherits name='com.google.gwt.maps.GoogleMaps'/> <script src="http://maps.google.com/maps?gwt=1&file=api&v=2&sensor=false" /> <!-- Specify the app entry point class. --> <entry-point class='sample.googlemap.client.GwtSampleGoogleMap'/> </module>
■web.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <!-- Default page to serve --> <welcome-file-list> <welcome-file>GwtSampleGoogleMap.html</welcome-file> </welcome-file-list> <!-- Servlets --> <servlet> <servlet-name>greetServlet</servlet-name> <servlet-class>sample.googlemap.server.GreetingServiceImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>greetServlet</servlet-name> <url-pattern>/gwtsamplegooglemap/greet</url-pattern> </servlet-mapping> <servlet> <description></description> <display-name>MyServlet</display-name> <servlet-name>MyServlet</servlet-name> <servlet-class>sample.googlemap.myservlet.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/MyServlet</url-pattern> </servlet-mapping> </web-app>
■GwtSampleGoogleMap.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <!-- The HTML 4.01 Transitional DOCTYPE declaration--> <!-- above set at the top of the file will set --> <!-- the browser's rendering engine into --> <!-- "Quirks Mode". Replacing this declaration --> <!-- with a "Standards Mode" doctype is supported, --> <!-- but may lead to some differences in layout. --> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <!-- --> <!-- Consider inlining CSS to reduce the number of requested files --> <!-- --> <link type="text/css" rel="stylesheet" href="GwtSampleGoogleMap.css"> <!-- --> <!-- Any title is fine --> <!-- --> <title>Web Application Starter Project</title> <!-- --> <!-- This script loads your compiled module. --> <!-- If you add any GWT meta tags, they must --> <!-- be added before this line. --> <!-- --> <script type="text/javascript" language="javascript" src="gwtsamplegooglemap/gwtsamplegooglemap.nocache.js"></script> </head> <!-- --> <!-- The body can have arbitrary html, or --> <!-- you can leave the body empty if you want --> <!-- to create a completely dynamic UI. --> <!-- --> <body> <!-- OPTIONAL: include this if you want history support --> <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe> <h1>GWT Google Map Sample</h1> <table align="center"> <tr> <td id="error_message"></td> </tr> <tr> <td id="dockpanel"></td> </tr> </table> </body> </html>
■index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>AndroidGPS</title> </head> <body> <h1>AndroidGPS</h1> <form action="MyServlet" method="GET" > <div>AndroidGPS: x:<input type="text" name="point_x" size=6 value="35.69964" /> y:<input type="text" name="point_y" size=6 value="139.77089" /> </div> <input type="submit" value="送信" /> <input type="reset" value="取り消し" /> </form> <br /> <br /> </body> </html>||<