竹形誠司 ブログ
Java+MySQL+Tomcat    »トピック一覧
掲示板へのスパムが多いため、「ご質問」のコーナーはユーザー登録制とさせていただきました。お手数ですが、上の「新規ユーザーの登録」メニューより登録をお願いします。
帳票Web
アプリケーション

受注開発始めました
詳しくは こちら
竹形 誠司 著/ラトルズ刊
JSP帳票アプリケーション実践開発入門
JSP帳票アプリケーション
実践開発入門

JSP業務アプリケーション短期開発入門
JSP業務アプリケーション
短期開発入門

Java+MySQL+Tomcatで始めるWebアプリケーション構築入門
Java+MySQL+Tomcatで始めるWebアプリケーション構築入門

Java+MySQL+Tomcatで作る掲示板とブログ
Java+MySQL+Tomcatで作る
掲示板とブログ
GoogleMapsに写真を貼り付ける
by 竹形 誠司[takegata]
携帯GPSのトラッキングページを公開したら、「携帯から送った写真が貼れたらいぃなぁ〜」という要望がありまして、ざっくりとではありますが実装してみました。

地図のサンプル

方法としてはこんな感じです。

サーバが携帯からのGPSデータを受信。
位置データにIDを振ってデータベースに保存。
位置データIDを含むメールアドレスへのリンク(mailto)を携帯の画面に表示。
このメールアドレスへ携帯から写真付きのメールを送信。
サーバでメールを受信し、位置データを含むアドレス宛であればJamesがmailetを起動
メールの件名、本文、画像ファイル名を位置データIDとともにデータベースに保存。
画像ファイルはファイルとして保存。
地図表示ページにアクセスされたら、メモの登録された場所にマーカーを表示
マーカーがクリックされたら写真付きメモを表示

受信したメールをJamesのmailetで処理する方法については、拙著「Java+MySQL+Tomcatで作る掲示板とブログ」に少し書いたので、参考にしてください(全体的に内容が少し古くなってしまっていますが(すみません))。

デバッグ用のコードなんかがいっぱい入っていてちょっと恥ずかしいけど、とりあえずソースを貼っときます。

携帯用の画面
gps_record.inc
<%@ page pageEncoding="Windows-31J"%>
<%@ page import="java.util.Date"%>
<%@ page import="java.sql.*"%>
<%@ page import="javax.sql.*"%>
<%@ page import="javax.naming.*"%>
<%@ page import="java.text.*"%>
<%
String strId = request.getHeader("X-Up-Subno");
if(strId==null){
    strId="test";
}
String strLatitude = request.getParameter("lat");
String strLongitude = request.getParameter("lon");
String strNickName = null;
String strRequestUrl = request.getRequestURL().toString();
String strPage = strRequestUrl.substring(strRequestUrl.lastIndexOf("/")+1);
int intError = Integer.parseInt(request.getParameter("smaj"));
Date dateNow = new Date();
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/gps");
Connection conn= ds.getConnection();
String strSql1 = "SELECT * FROM user WHERE id = ?";
PreparedStatement pstmt1 = conn.prepareStatement(strSql1);
pstmt1.setString(1,strId);
ResultSet rs = pstmt1.executeQuery();
if(rs.next()){
    strNickName = rs.getString("nick_name");
}else{
    response.sendRedirect("error.jsp");
    return;
}
pstmt1.close();
rs.close();
String strSql2 = "INSERT INTO location SET user_id=?,time=?,lat=?,lon=?,smaj=?";
PreparedStatement pstmt2 = conn.prepareStatement(strSql2);
pstmt2.setString(1,strId);
pstmt2.setLong(2,dateNow.getTime());
pstmt2.setString(3,strLatitude);
pstmt2.setString(4,strLongitude);
pstmt2.setInt(5,intError);
pstmt2.executeUpdate();
rs = pstmt2.executeQuery("SELECT last_insert_id()");
rs.next();
int intId = rs.getInt(1);
pstmt2.close();
conn.close();
String strUrl ="http://www.orquesta.org/gps/" + strPage;
String strPageName=null;
int intInterval=0;
if(strPage.equals("gps0.jsp")){
    strPageName = "手動";
}else if(strPage.equals("gps05.jsp")){
    strPageName = "30秒間隔";
    intInterval = 300;
}else if(strPage.equals("gps1.jsp")){
    strPageName = "1分間隔";
    intInterval = 600;
}else if(strPage.equals("gps5.jsp")){
    strPageName = "5分間隔";
    intInterval = 3000;
}
%>
<%if(intInterval==0){%>
    <wml:card>
<%}else{%>
    <wml:card ontimer="device:gpsone?url=<%=strUrl%>&ver=1&datum=0&unit=1&acry=0&number=0">
    <wml:timer value="<%=intInterval%>" />
<%}%>
<p>GPS位置計測中【<%=strPageName%>】</p>
<%
DateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
String strTime = df.format(dateNow);
%>
日時:<%=strTime%><BR>
緯度:<%=strLatitude%><BR>
経度:<%=strLongitude%><BR>
誤差:<%=intError%><BR>
<a HREF="device:gpsone?url=<%=strUrl%>&ver=1&datum=0&unit=1&acry=0&number=0">【再計測】<BR>
<a HREF="Menu.jsp">【メニュー】<BR>
<a HREF="GoogleMapsImage.jsp?nick_name=<%=strNickName%>&amp;time=<%=(dateNow.getTime())%>">【地図を表示】<BR>
<a HREF="mailto:gps<%=intId%>_<%=(dateNow.getTime())%>@orquesta.org">【写真+メモ】<BR>
</wml:card>
携帯からGPS情報を送信するためにアクセスするURLが<a HREF="device:gpsone?url=...">という特殊な書式のため、gps0.jspは手動、gps05.jspは30秒間隔、などのように、アクセスするページによって記録の間隔が決まるようにしています。あまり美しくありませんね。よく調べれば、他に方法があるかも知れません。
gps0.jsp
<%@ page contentType="text/html; charset=Windows-31J"%>
<%@ include file="gps_record.inc" %>
gps05.jsp
<%@ page contentType="text/html; charset=Windows-31J"%>
<%@ include file="gps_record.inc" %>
gps1.jsp
<%@ page contentType="text/html; charset=Windows-31J"%>
<%@ include file="gps_record.inc" %>
gps5.jsp
<%@ page contentType="text/html; charset=Windows-31J"%>
<%@ include file="gps_record.inc" %>

James 側の Mailet
package gps.mailet;

import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import org.apache.mailet.*;
import java.sql.*;
import java.io.*;
import java.util.Date;

public class PhotoMail extends GenericMailet{

  boolean blDebug=true;
  int intId;
  String strSubject;
  StringBuffer sbBody;
  String strToAddress=null;
  StringBuffer sbImages = new StringBuffer();
  InputStream inputStream=null;

  Connection getConnection() throws Exception{
    Class.forName("com.mysql.jdbc.Driver");
    String strConn="jdbc:mysql://localhost/gps"
        + "?user=Mulder&password=TrustNo1"
        + "&useUnicode=true&characterEncoding=cp932";
    Connection conn =DriverManager.getConnection(strConn);
    return conn;
  }

  public void service(Mail mail) throws MessagingException {
    this.strSubject="無題";
    this.sbBody = new StringBuffer();
    this.sbImages = new StringBuffer();
    if(blDebug){
      writeLog("Receiving message...");
    }
    MimeMessage msg = (MimeMessage)mail.getMessage();
    Address[] recipients = msg.getRecipients(Message.RecipientType.TO);
    for(int i=0;i<recipients.length;i++){
      if(recipients[i].toString().startsWith("gps")){
        this.strToAddress = recipients[i].toString();
        break;
      }
    }
    if(blDebug){
      writeLog("ToAddress=" + this.strToAddress);
    }
    String[] strParam=this.strToAddress.substring(3,this.strToAddress.indexOf("@")).split("_");
    int intLocationId = Integer.parseInt(strParam[0]);
    long lngLocationTime = Long.parseLong(strParam[1]);
    try{                    //【1】
      Connection conn = this.getConnection();
      String strSql1 = "SELECT id FROM location WHERE id = ? AND time = ?";
      PreparedStatement pstmt1 = conn.prepareStatement(strSql1);
      pstmt1.setInt(1,intLocationId);
      pstmt1.setLong(2,lngLocationTime);
      ResultSet rs1=pstmt1.executeQuery();
      if(!rs1.next()){
        throw new Exception("invalid parameter");
      }
      String strSql2 = "INSERT INTO description SET location_id=?";
      PreparedStatement pstmt2 = conn.prepareStatement(strSql2);
      pstmt2.setInt(1,intLocationId);
      pstmt2.executeUpdate();
      ResultSet rs2 = pstmt2.executeQuery("SELECT last_insert_id()");
      rs2.next();
      this.intId = rs2.getInt(1);

      extractPart(msg);

      writeLog("データを保存します"+ (new Date()).toString());

      String strSql3 = "UPDATE description SET title=?,body=?,image=? WHERE id = ?";
      PreparedStatement pstmt3 = conn.prepareStatement(strSql3);
      pstmt3.setString(1,this.strSubject);
      pstmt3.setString(2,this.sbBody.toString());
      if(sbImages.length()!=0){
        sbImages.deleteCharAt(sbImages.length()-1);
      }
      pstmt3.setString(3,this.sbImages.toString());
      pstmt3.setInt(4,this.intId);
      pstmt3.executeUpdate();

      writeLog("データを保存しました"+ (new Date()).toString());

      conn.close();

    }catch(SQLException se){
      writeLog("e1:"+se.getMessage());
    }catch(Exception e){
      writeLog("e2:" + e.getMessage());
    }

  }

  public void extractPart(Part part) throws Exception {
    if(blDebug){
      writeLog("extract part:"+part.getContentType());
    }
    if (part instanceof Message) {  //件名
      Message message = (Message)part;
      String strRawSubject = message.getSubject();
      if(blDebug){
        writeLog("subject: " + strRawSubject);
      }
      if(strRawSubject!=null){
        this.strSubject=correctUnicode(strRawSubject);
      }
    }
    if (part.isMimeType("text/plain")) { // テキストの場合
      if(blDebug){
        writeLog("body:" + part.getContent().toString());
      }
      this.sbBody.append(correctUnicode(part.getContent().toString()));
    }else if(part.isMimeType("multipart/*")) { // マルチパートの場合
      if(blDebug){
        writeLog("multipart...");
      }
      Multipart mpart = (Multipart)part.getContent();
      int intCount = mpart.getCount();
      if(blDebug){
        writeLog("multipart count=" + intCount);
      }
      for(int i = 0; i < intCount; i++) {
        extractPart(mpart.getBodyPart(i));
      }
    }else if(part.isMimeType("image/jpeg")){  //画像の場合
      if(blDebug){
        writeLog("画像の受信中...");
      }
      //AU端末ではファイル名に~(チルダ)の含まれる画像が
      //正しく表示されないことがあるため-(ハイフン)に置換。
      String strFileName = this.intId + "_" + part.getFileName().replaceAll("~","-");
      this.sbImages.append(strFileName).append(',');
      String strFilePath =  "c:/prg/gps/JSP/images/"
        + MimeUtility.decodeText(strFileName);
      try {
        File file = new File(strFilePath);
        OutputStream os
          = new BufferedOutputStream(new FileOutputStream(file));
        InputStream is = part.getInputStream();
        int c;
        while ( (c = is.read()) != -1) {
          os.write(c);
        }
        os.close();
        if(blDebug){
          writeLog(strFilePath + "を保存しました");
        }
      } catch (IOException e) {
        if(blDebug){
          writeLog("ファイルの保存に失敗しました"
            + e.getMessage());
        }
      }
    }
  }

  String correctUnicode(String str){
    StringBuffer sbReturn = new StringBuffer();
    for(int i=0;i<str.length();i++){
      char c = str.charAt(i);
      switch(c){
        case 0x301c: sbReturn.append(Character.toChars(0xff5e));break;//〜
        case 0x2016: sbReturn.append(Character.toChars(0x2225));break;//‖
        case 0x2212: sbReturn.append(Character.toChars(0xff0d));break;//−
        case 0x00a2: sbReturn.append(Character.toChars(0xffe0));break;//¢
        case 0x00a3: sbReturn.append(Character.toChars(0xffe1));break;//£
        case 0x00ac: sbReturn.append(Character.toChars(0xffe2));break;//¬
        default: sbReturn.append(c);
      }
    }
    return sbReturn.toString();
  }

  void writeLog(String strLog){
    try{
      String strFile="c:/prg/gps/PhotoMail.log";
      FileWriter fw = new FileWriter(strFile,true);
      fw.write(strLog);
      fw.write("\n");
      fw.close();
    }catch(IOException e){
      e.printStackTrace();
    }
  }
}

GoogleMaps への表示
<%@ page contentType="text/html; charset=utf-8"%>
<%@ page import="java.io.*"%>
<%@ page import="java.util.*"%>
<%@ page import="java.text.*"%>
<%@ page import="java.sql.*"%>
<%@ page import="javax.sql.*"%>
<%@ page import="javax.naming.*"%>
<%@ page import="java.util.Date"%>
<%@ page import="org.orquesta.forum.*"%>
<%@ page import="java.awt.image.BufferedImage"%>
<%@ page import="javax.imageio.ImageIO"%>
<%!
class LocationData{
  long dateTime;
  float lat;
  float lon;
  String title;
  String body;
  String[] imageFile;
}
%>
<%
request.setCharacterEncoding("utf-8");
String strNickName = request.getParameter("nick_name");
int intYear = Integer.parseInt(request.getParameter("year"));
int intMonth = Integer.parseInt(request.getParameter("month"));
int intDate = Integer.parseInt(request.getParameter("date"));
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(Calendar.YEAR,intYear);
cal.set(Calendar.MONTH,intMonth-1);
cal.set(Calendar.DAY_OF_MONTH,intDate);
Date today = cal.getTime();
cal.add(Calendar.DAY_OF_MONTH,1);
Date tomorrow = cal.getTime();
cal.add(Calendar.DAY_OF_MONTH,-2);
Date yesterday = cal.getTime();;
String strZoom = request.getParameter("zoom");
if(strZoom==null){
  strZoom="13";
}
String strErr = request.getParameter("err");
int intErr=30000;
if(strErr!=null){
  intErr = Integer.parseInt(strErr);
}

ArrayList<LocationData> alPoints = new ArrayList<LocationData>();
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/gps");
Connection conn= ds.getConnection();
String strSql = "SELECT location.*,description.* FROM location"
    + " JOIN user ON location.user_id = user.id"
    + " LEFT JOIN description ON location.id = description.location_id"
    + " WHERE user.nick_name=? AND location.time >=? AND location.time < ? AND smaj <= ?"
    + " ORDER BY location.time";
PreparedStatement pstmt = conn.prepareStatement(strSql);
pstmt.setString(1,strNickName);
pstmt.setLong(2,today.getTime());
pstmt.setLong(3,tomorrow.getTime());
pstmt.setLong(4,intErr);
ResultSet rs = pstmt.executeQuery();
long lngDate=0;
String strPoint="";
while(rs.next()){
  LocationData locationData = new LocationData();
  locationData.dateTime = rs.getLong("time");
  locationData.lat = rs.getFloat("lat");
  locationData.lon = rs.getFloat("lon");
  locationData.title = rs.getString("title");
  locationData.body = rs.getString("body");
  String strImages = rs.getString("image");
  if(strImages != null && strImages.length()!=0){
    locationData.imageFile = strImages.split(",");
  }
  alPoints.add(locationData);
}
rs.close();
pstmt.close();
conn.close();

SimpleDateFormat sdf = new SimpleDateFormat("yyyy年 MM月 dd日 HH時 mm分");
String strColor="";
String strPath = application.getRealPath("/WEB-INF/AccessLog");
DateFormat dfDate = new SimpleDateFormat("yyyyMMdd");
DateFormat dfTime = new SimpleDateFormat("HH:mm");
Date date = new Date();

String strRemoteHost = request.getRemoteHost();
String strDate = dfDate.format(date);
String strTime = dfTime.format(date);
String strReferer = request.getHeader("Referer");
String strUserAgent = request.getHeader("User-Agent");
AccessLog.add(strPath, strDate, strTime, strRemoteHost,strReferer,strUserAgent, "GoogleMapsRouteEx.jsp","-");
%>

<!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>
  <LINK REL="stylesheet" TYPE="text/css" HREF="style.css">
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <title>Google Maps JavaScript API Example</title>
    <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=xxxxxxxxxxxxxxxxxx"
      type="text/javascript"></script>
    <script type="text/javascript">
    //<![CDATA[
    function load() {
      if (GBrowserIsCompatible()) {
        var map = new GMap2(document.getElementById("map"));
    <%if(alPoints.size()!=0){
      LocationData locationData = alPoints.get(0);
      float fltLat = locationData.lat;
      float fltLon = locationData.lon;
      %>
          map.setCenter(new GLatLng(<%=fltLat%>,<%=fltLon%>), <%=strZoom%>);
    <%}%>
    var icon = new GIcon();
        var points = [];
    var markers = [];
    <%for(int i=0;i<alPoints.size();i++){
      LocationData locationData = alPoints.get(i);%>
          points[<%=i%>] = new GLatLng(<%=locationData.lat%>,<%=locationData.lon%>);
      <%if(locationData.title!=null){
        StringBuffer sbHtml = new StringBuffer();
          sbHtml.append(locationData.title)
            .append("<BR>");
          if(locationData.imageFile!=null){
            sbHtml.append("<BR>");
            for(String fileName:locationData.imageFile){
              BufferedImage bImage = ImageIO.read(new File("c:/prg/gps/JSP/images/"+fileName));
              sbHtml.append("<IMG SRC=\'images/").append(fileName).append("\' ")
                .append("width=\'").append(bImage.getWidth()).append("\' ")
                .append("height=\'").append(bImage.getHeight()).append("\' ")
                .append(">");
            }
          }
          sbHtml.append("<BR>").append(locationData.body);
          sbHtml.append("<BR>").append(dfTime.format(new Date(locationData.dateTime)));
        %>
        markers[<%=i%>] = new GMarker(new GLatLng(<%=locationData.lat%>,<%=locationData.lon%>));
        GEvent.addListener(markers[<%=i%>], "click", function() {
          markers[<%=i%>].openInfoWindowHtml("<%=sbHtml.toString()%>");
        });
        map.addOverlay(markers[<%=i%>]);
      <%}%>
    <%}%>

        var polyline = new GPolyline(points, "#F00080",3,1);
        map.addOverlay(polyline);

    map.addControl(new GLargeMapControl());
      }
    }
    //]]>
    </script>
  </head>
  <body onload="load()" onunload="GUnload()">
<DIV>
  ニックネーム:<%=strNickName%>|
  <%
    cal.add(Calendar.DAY_OF_MONTH,+1);
  %>
    <%=cal.get(Calendar.YEAR)%>年<%=cal.get(Calendar.MONTH)+1%>月<%=cal.get(Calendar.DAY_OF_MONTH)%>
  |縮尺:<%=strZoom%>
  |許容誤差:<%=intErr%>m
  |
  <%
    cal.add(Calendar.DAY_OF_MONTH,-1);
  %>
  <a HREF="GoogleMapsRouteEx.jsp?year=<%=cal.get(Calendar.YEAR)%>&month=<%=cal.get(Calendar.MONTH)+1%>&date=<%=cal.get(Calendar.DAY_OF_MONTH)%>&nick_name=<%=strNickName%>&zoom=<%=strZoom%>&err=<%=intErr%>">前日|

  <%
    cal.add(Calendar.DAY_OF_MONTH,+2);
  %>
  <a HREF="GoogleMapsRouteEx.jsp?year=<%=cal.get(Calendar.YEAR)%>&month=<%=cal.get(Calendar.MONTH)+1%>&date=<%=cal.get(Calendar.DAY_OF_MONTH)%>&nick_name=<%=strNickName%>&zoom=<%=strZoom%>&err=<%=intErr%>">翌日
  |<a HREF="SearchPC.jsp">【検索】
  |<a HREF="index.jsp">【説明】
</DIV>
  <%if(alPoints.size()!=0){%>
      <div id="map" style="width:1000px; height:800px;"></div>
  <%}else{%>
    この日のデータはありません。<BR>
  <%}%>
  <BR>
  </body>
</html>

投稿:竹形 誠司[takegata]/2008年 02月 07日 02時 05分 /更新:2009年 04月 06日 07時 38分