データベースとGoogleMapsAPIの連携は、
 1.PHP(もしくは他の言語)にて接続・取得した情報を所定フォーマット(XMLもしくはJSON形式)に出力
 2.それをJavaScript(GoogleMapsAPI)で受け取り、配列に変換するなどしてマップ出力
という形がスタンダードかと思います。

今回のテストでは、JSON形式でやり取りすることにしました。

1.Accessデータベースの情報を取得しJSON出力(PHP)
 プログラムソース(getPlaceData.php)を以下に記載します。

<?php
  $DB_File = "C:\Users\ひつじかい\Documents\GoogleMapsAPI埋め込みテスト.mdb"; //Accessファイル名(フルパス)
  $Database = mb_convert_encoding($DB_File, "SJIS", "UTF-8");
  $User     = "";             //ログインユーザー名
  $Password = "";             //パスワード

  //Accessに接続
  $DSN = "Driver={Microsoft Access Driver (*.mdb)};Dbq=$Database";
  if (!$con = odbc_connect($DSN, $User, $Password)) {
     echo("err:Accessデータベースへの接続に失敗しました。\n");
     exit();
  }

  $places = array();

  //SELECT文を実行し、結果を取得
  $strSQL = "SELECT PlaceCode, PlaceName, ZIP, Address, TEL, Remarks, Lat, Lng FROM M_Place";
  $strSQL .= " WHERE IsNull(Lat) = false AND IsNull(Lng) = false";
  $strSQL .= " ORDER BY PlaceCode";
  $result = odbc_exec($con, $strSQL);

  while ($row = odbc_fetch_array($result)) {
     $place = array();
     foreach ($row as $key => $value){
        $place[$key] = mb_convert_encoding($value, "UTF-8", "SJIS");
     }
     array_push($places,$place);
  }

  //PHP5.2~
  $jsondata = json_encode($places);

  //PHP5.2未満のバージョンではJSONライブラリが標準搭載されていないため、追加で入手・設置する必要があります
  //以下はZend Framework(http://framework.zend.com/)のJSONを使用する場合のコード例
  //require_once('Zend/Json.php'); 
  //$json = new Services_JSON();
  //$jsondata = $json->encode($places);
  //$json = new Services_JSON();
  //$jsondata = $json->encode($places);

  header("Content-Type: text/javascript; charset=utf-8");
  echo $jsondata;
?>

前半のデータベース接続箇所は前回のテストとほぼ同じです。

  $strSQL .= " WHERE IsNull(Lat) = false AND IsNull(Lng) = false";

MAP出力用なので、緯度もしくは経度が入力されていないデータは除外しています(今回のテストデータには全て入力されていますが)。

  while ($row = odbc_fetch_array($result)) {
     $place = array();
     foreach ($row as $key => $value){
        $place[$key] = mb_convert_encoding($value, "UTF-8", "SJIS");
     }
     array_push($places,$place);
  }

この箇所でJSONエンコード用の連想配列を生成しています。22行目の$rowは既に連想配列になっているのですが、Accessから取得したデータは文字コードがShift_JISなので、そのままでは文字化けしてしまいます。
そこで、$rowを各要素毎にUTF-8に変換したものを新たに準備した$placeに再収納しています(23~26行目)。

そして、

  $jsondata = json_encode($places);

とJSON形式にエンコードします。
なお、PHP5.2未満のバージョンではJSONライブラリが標準搭載されていないため、追加で入手・設置する必要があります。
Zend Framework(http://framework.zend.com/)のJSON等を使用する場合は、上記コマンドとは若干異なりますのでご注意ください。

最後に

  header("Content-Type: text/javascript; charset=utf-8");
  echo $jsondata;

と出力することで、Javascript(GoogleMapsAPI)に結果を渡します。
上記のプログラム(getPlaceData.php)を単独で実行すると、以下のような出力が得られます(実際には改行されません)。

[
	{"PlaceCode":"1001","PlaceName":"\u4e2d\u592e\u56f3\u66f8\u9928","ZIP":"183-0055","Address":"\u6771\u4eac\u90fd\u5e9c\u4e2d\u5e02\u5e9c\u4e2d\u753a2-24","TEL":"042-362-8647","Remarks":"\u30eb\u30df\u30a8\u30fc\u30eb\u5e9c\u4e2d\u5185","Lat":"35.6759578","Lng":"139.4838876"},
	{"PlaceCode":"1002","PlaceName":"\u767d\u7cf8\u53f0\u56f3\u66f8\u9928","ZIP":"183-0011","Address":"\u6771\u4eac\u90fd\u5e9c\u4e2d\u5e02\u767d\u7cf8\u53f01-60","TEL":"042-360-3443","Remarks":"","Lat":"35.667168","Lng":"139.5061246"},
	{"PlaceCode":"1003","PlaceName":"\u897f\u5e9c\u56f3\u66f8\u9928","ZIP":"183-0031","Address":"\u6771\u4eac\u90fd\u5e9c\u4e2d\u5e02\u897f\u5e9c\u753a1-10","TEL":"042-360-8998","Remarks":"\u897f\u5e9c\u6587\u5316\u30bb\u30f3\u30bf\u30fc\u5185","Lat":"35.6709476","Lng":"139.4562688"},
	{"PlaceCode":"1004","PlaceName":"\u6b66\u8535\u53f0\u56f3\u66f8\u9928","ZIP":"183-0042","Address":"\u6771\u4eac\u90fd\u5e9c\u4e2d\u5e02\u6b66\u8535\u53f02-2","TEL":"042-576-6390","Remarks":"\u6b66\u8535\u53f0\u6587\u5316\u30bb\u30f3\u30bf\u30fc\u5185","Lat":"35.6886062","Lng":"139.4614882"},
	{"PlaceCode":"1005","PlaceName":"\u65b0\u753a\u56f3\u66f8\u9928","ZIP":"183-0052","Address":"\u6771\u4eac\u90fd\u5e9c\u4e2d\u5e02\u65b0\u753a1-66","TEL":"042-360-6336","Remarks":"\u65b0\u753a\u6587\u5316\u30bb\u30f3\u30bf\u30fc\u5185","Lat":"35.6871558","Lng":"139.4893015"},
	{"PlaceCode":"1006","PlaceName":"\u4f4f\u5409\u56f3\u66f8\u9928","ZIP":"183-0034","Address":"\u6771\u4eac\u90fd\u5e9c\u4e2d\u5e02\u4f4f\u5409\u753a1-61","TEL":"042-360-5775","Remarks":"\u4f4f\u5409\u6587\u5316\u30bb\u30f3\u30bf\u30fc\u5185","Lat":"35.6603687","Lng":"139.4602852"},
	{"PlaceCode":"1007","PlaceName":"\u662f\u653f\u56f3\u66f8\u9928","ZIP":"183-0014","Address":"\u6771\u4eac\u90fd\u5e9c\u4e2d\u5e02\u662f\u653f2-20","TEL":"042-360-2882","Remarks":"\u662f\u653f\u6587\u5316\u30bb\u30f3\u30bf\u30fc\u5185","Lat":"35.6608908","Lng":"139.4953562"},
	{"PlaceCode":"1008","PlaceName":"\u7d05\u8449\u4e18\u56f3\u66f8\u9928","ZIP":"183-0004","Address":"\u6771\u4eac\u90fd\u5e9c\u4e2d\u5e02\u7d05\u8449\u4e182-1","TEL":"042-360-7227","Remarks":"\u7d05\u8449\u4e18\u6587\u5316\u30bb\u30f3\u30bf\u30fc\u5185","Lat":"35.6766068","Lng":"139.5092064"},
	{"PlaceCode":"1009","PlaceName":"\u62bc\u7acb\u56f3\u66f8\u9928","ZIP":"183-0012","Address":"\u6771\u4eac\u90fd\u5e9c\u4e2d\u5e02\u62bc\u7acb\u753a5-4","TEL":"042-483-4122","Remarks":"\u62bc\u7acb\u6587\u5316\u30bb\u30f3\u30bf\u30fc\u5185","Lat":"35.6537054","Lng":"139.517872"},
	{"PlaceCode":"1010","PlaceName":"\u56db\u8c37\u56f3\u66f8\u9928","ZIP":"183-0035","Address":"\u6771\u4eac\u90fd\u5e9c\u4e2d\u5e02\u56db\u8c372-75","TEL":"042-360-3663","Remarks":"\u56db\u8c37\u6587\u5316\u30bb\u30f3\u30bf\u30fc\u5185","Lat":"35.6653238","Lng":"139.4456242"},
	{"PlaceCode":"1011","PlaceName":"\u7247\u753a\u56f3\u66f8\u9928","ZIP":"183-0021","Address":"\u6771\u4eac\u90fd\u5e9c\u4e2d\u5e02\u7247\u753a2-17","TEL":"042-368-7117","Remarks":"\u7247\u753a\u6587\u5316\u30bb\u30f3\u30bf\u30fc\u5185","Lat":"35.6703378","Lng":"139.4693835"},
	{"PlaceCode":"1012","PlaceName":"\u5bae\u753a\u56f3\u66f8\u9928","ZIP":"183-0023","Address":"\u6771\u4eac\u90fd\u5e9c\u4e2d\u5e02\u5bae\u753a3-1","TEL":"042-364-3613","Remarks":"","Lat":"35.6688235","Lng":"139.4786725"},
	{"PlaceCode":"1013","PlaceName":"\u751f\u6daf\u5b66\u7fd2\u30bb\u30f3\u30bf\u30fc\u56f3\u66f8\u9928","ZIP":"183-0001","Address":"\u6771\u4eac\u90fd\u5e9c\u4e2d\u5e02\u6d45\u9593\u753a1-7","TEL":"042-336-5702","Remarks":"\u751f\u6daf\u5b66\u7fd2\u30bb\u30f3\u30bf\u30fc\u5185","Lat":"35.6791431","Lng":"139.4964433"}
]

日本語テキスト箇所は「\u4e2d・・・」などとUnicodeエスケープされていますが、この形でJavascriptに引き渡してOKです。

2.JSON形式のデータをJavaScript(GoogleMapsAPI)で受け取りマップ出力
 まずはソースを記載します(mapAccessTest.html)。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript">

  var initialLocation = new google.maps.LatLng(35.672154,139.480323); //府中駅
  var initialZoom = 13;                           //ズームレベル
  var markerImage_Normal = 'icon/s_green.png';    //マーカー画像(通常時:緑)
  var markerImage_Selected = 'icon/s_red.png';    //マーカー画像(選択時:赤)
  var currentMarkerNo = -1;                       //選択中のマーカーNo
  var PlaceMarker = new Array();                  //マーカー配列
  var currentInfoWindow = null;                   //情報ウィンドウ

  function initialize() {
     var myOptions = {
        zoom: initialZoom,
        center: initialLocation,
        mapTypeControl: true,
        mapTypeId: google.maps.MapTypeId.ROADMAP
     };
     map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

     PlaceMarker.length = 0;
     getPlaceData();
     //alert(PlaceMarker.length);  //マーカー数
  }

  function getPlaceData() { 
     var objJSON;
     jQuery.ajax({
        url : "getPlaceData.php",
        async : false,
        success: function(res){
           if (res.substring(0,4) == "err:"){
              alert("エラー:"+res.substring(4));
           }else{
              PlaceMarker.length = 0;
              objJSON = eval(res);
             for (var i = 0; i < objJSON.length; i++) {
                  setPlaceMarker(objJSON[i]);
              }
           }
        },
        error: function() {
           alert('データ取得に失敗しました');
        }
     });
  }

  function setPlaceMarker(myPlaceInfo) {
     var markerNo = PlaceMarker.length;
     var myPoint = new google.maps.LatLng(myPlaceInfo.Lat,myPlaceInfo.Lng);
     var myTitle;
     var image = markerImage_Normal;
     PlaceMarker[markerNo] = new google.maps.Marker({
        position: myPoint,
        map: map,
        title: myPlaceInfo.PlaceName,
        icon: image,
        draggable: false
     });
     google.maps.event.addListener(PlaceMarker[markerNo], 'click', function() {
        changeMarkerImages(markerNo);
     });
     PlaceMarker[markerNo].Info = myPlaceInfo;
     google.maps.event.addListener(PlaceMarker[markerNo], 'rightclick', function() {
        setInfoWindow(PlaceMarker[markerNo]);
     });
  }

  function changeMarkerImages(markerNo) {
     if (currentMarkerNo >= 0 && currentMarkerNo !== markerNo) {
        changeMarkerImage(currentMarkerNo,"false");
     }
     currentMarkerNo = markerNo;
     changeMarkerImage(markerNo,"true");
  }

  function changeMarkerImage(markerNo,currentFlag) {
     var dispimage;
     var myMarkerImg;
     if (markerNo >= PlaceMarker.length) {
        return false;
     }
     if (currentFlag == "true") {
        myMarkerImg = markerImage_Selected;
     } else {
        myMarkerImg = markerImage_Normal;
     }
     PlaceMarker[markerNo].setIcon(myMarkerImg);
  }

  function setInfoWindow(myMarker) {
    if (currentInfoWindow) {
       currentInfoWindow.close();
    }
    var contentString =  "<b>"+myMarker.Info["PlaceName"]+"</b><br>"
                        +myMarker.Info["ZIP"]+" "+myMarker.Info["Address"]+"<br>"
                        +"TEL:"+myMarker.Info["TEL"]+"<br>"
                        +myMarker.Info["Remarks"];
    var infoWindow = new google.maps.InfoWindow(
      { content: contentString }
    );
    infoWindow.open(map, myMarker);
    currentInfoWindow = infoWindow;
  }

</script>

</head>

<body onload="initialize()">
  <div id="map_canvas" style="width:100%; height:100%"></div>
</body>
</html>

JSON形式のデータを読み込みは関数getPlaceData(31~51行目)で行っています。
phpで出力したデータの取得には、jQueryの$.ajax関数を利用しています。
非同期通信オプションはasync=falseとして同期通信としました(デフォルトはasync=trueの非同期)。

        async : false,

今回のテストソースでは、どちらでも同じ結果が得られるのですが、参考までに以下の箇所

     //alert(PlaceMarker.length);  //マーカー数

のコメントアウトを外すとasync=false時とtrue時で結果が異なることが分かります。

JSONデータの取り扱いは以下の箇所

              objJSON = eval(res);
             for (var i = 0; i < objJSON.length; i++) {
                  setPlaceMarker(objJSON[i]);
              }

42行目のeval()関数で、オブジェクト(objJSON)に変換し、その要素数回マーカー生成関数(setPlaceMarker)を呼び出しています(43~45行目)。

setPlaceMarker関数(54~73行目)では、マーカーを配列(PlaceMarker)の形で生成させています。
単にMAP表示させるだけであれば配列にする必要はないのですが、配列に収納しておけば複数マーカーに同じ処理を実行したい場合などに利用出来ます。
今回のテストではマーカーのクリック時にアイコンの色を緑色から赤色に変更する(と同時に前回赤色だったマーカーを緑色に戻す)仕組みにしました。
また、JSONデータを正しく取得出来ているかを確認するため、

     PlaceMarker[markerNo].Info = myPlaceInfo;

と、JSONから取得したオブジェクトをPlaceMarker[].Infoに丸ごと収納しておき、右クリック時に情報ウィンドウとして表示させました(setInfoWindow)。

出力イメージは以下のようになります。
MAP出力

図書館の名称や住所等、Unicodeエスケープされていた日本語テキストもきちんと表示出来ています。

 
 
※今回テストで使用したアイコン画像(「icon」ディレクトリに収納)

通常時

s_green.png(通常時)

選択時

s_red.png(選択時)

次回は、このhtmlをAccessに埋め込みます。

コメント   

 2012年7月28日(土)
 地元の府中市美術館の次に多く訪問している私のお気に入りの美術館「東京近代美術館」がリニューアルのため休館になる(3カ月程度ですが)・・ということで、休みに入る直前に行ってきました。開催中の特別展は「吉川霊華展」。

吉川霊華展

 


 吉川霊華・・・失礼ながら全く知りませんでした。細い線を丁寧に重ねていく画法が特徴で、描くというよりも紡ぐといった表現の方が合う気がしました。
 伊勢物語のような古典文学や、中国の説話や詩、あるいは仏画などを描いた作品が多かったですが、平安美人のような長い日本髪の美しさは、この描き方であるからこそ表現出来るのではないかと思いました。先に描きたい作品ありきで線で描くことにしたのか、あるいは先に線で描く画法を生み出し、それに合う画材として古典等を選んだのか?

香具耶姫昇天 竹取物語

香具耶(かぐや)姫昇天 竹取物語

藐姑射之処子

藐姑射之処子(はこやのしょし)

 「神龍」という巨大な作品が大迫力だったのですが、あまりに大きいので(天井画だとか)展示室の高さが足りず、斜めに寝かされる形で展示されていたのがちょっと残念。
 他の作品はどれも繊細なのですが、その分インパクトは弱いので、他の画家の作品の中で展示されていても、あまり印象に残らない気がします。実際に「過去に見たことはあるけれど、記憶に残っていない」という作品もあるのかもしれません。それだけに、吉川霊華の作品ばかりを集めた今回の企画は、その作品の素晴らしさを発見する機会を与えてくれる良い試みだと思いました。

コメント   

 第65回富士登山競走に参加してきました。
 5年前に初出走して以来、毎年欠かさずに参加している私にとっての夏の恒例行事です。
 富士吉田市役所を朝の7時にスタートし、11時30分迄に山頂のゴールにたどり着かないといけません。例年の完走率が50%前後と厳しいこのレースを走破し完走賞のTシャツをもらうことが、日頃の鍛錬の1つのモチベーションとなっています。

 クライマー属性の私にとって、本格的な登りの部分に不安はないのですが、問題は馬返し(登山口)までのロード部分。ここまでにそれなりの位置をキープしておかないと山道は隊列が形成されてしまうので抜かすポイントが少なく、またコース幅が広い箇所でも抜くには悪路を選択しなければなりません。馬返し到着が遅くなればなるほど、当然ながら周りのペースも遅くなるので、いくら登りが得意でも中々自分のペースでは進めず、結果タイムオーバーということになりかねません。

 ロード部分といっても馬返しまで11km弱で680m登ります。勾配は箱根駅伝5区の一番傾斜のきつい部分よりも上・・・特に中の茶屋から馬返しまでの未舗装箇所(3.5kmくらい)はかなりきついのですが、とにかく歩かないよう必死でした。ここまで1時間5分弱と、自己ベストで完走した昨年よりは1分半くらい遅いものの、私にしてはまずまずのタイム。
 しかし、ここまでかなり無理をしたので、登りで思うような「追い抜きモード」には入れず、5合目通過が2時間5分。昨年は2時間切ってたのに・・・。
 こうなってしまうと、周りのペースが遅いので自分のペースもそれに合わせて・・・という状態。岩場の急斜面は結構得意なのですが、富士山の特徴は砂利道が多いこと。スピードアップしたくてもズルズル滑る感じで上手くいきません。車のタイヤが空回りするようなイメージ・・・何かコツがあるのでしょうかね?

 何とか時間内完走は出来ましたが、4時間15分弱と昨年よりも20分も遅いタイムとなってしまいました(一昨年よりは10分ほど良いのですが、当時は重度の貧血だったため参考外)。暑かった気候の影響もあるんでしょうが、UTMF挑戦の関係でスタミナをつけるための練習ばかりしていてスピード練習が絶対的に不足していたことが原因?
 それとも年齢からくる衰えは隠せないのか・・・「果たしてあと何年完走できるのだろうか?今年が最後?」と考えてしまいます。

ゴール手前の鳥居付近

ゴール手前の鳥居付近

コメント   

今回は、GoogleMapsAPIとは少し離れますが、Accessテーブルの情報をWeb出力するテストです。
AccessのテーブルをPHPでブラウザ表示させるためには、ODBC経由で接続します。
ODBC接続は、コントロールパネルの「管理ツール」→「データソース(ODBC)」にてDSNを登録する方法でも良いのですが、それだと複数のPCで使用する場合に都度設定するのが面倒なので、今回はDSN構文を直接記述する方法を選択しました(これならば、対象ファイルをネットワークドライブに置いておけば各PCへのDSN登録が不要)。
Accessへの接続文字列は次のようになります。
 odbc:Driver={Microsoft Access Driver (*.mdb)};Dbq=ファイル名

接続にはodbc_connect関数を使用します。

今回、接続・出力対象とするのは前回作成した府中市の図書館情報テーブル「M_Place」です。当テーブルは「C:\Users\ひつじかい\Documents\GoogleMapsAPI埋め込みテスト.mdbに収納と仮定します(テストなのでローカル上で・・・)。
以下に全体のソースを記載します。

<?php
  $DB_File = "C:\Users\ひつじかい\Documents\GoogleMapsAPI埋め込みテスト.mdb"; //Accessファイル名(フルパス)
  $DATABASE = mb_convert_encoding($DB_File, "SJIS", "UTF-8");
  $DBUSER     = "";             //ログインユーザー名
  $DBPASSWORD = "";             //パスワード

  //Accessに接続
  $DSN = "Driver={Microsoft Access Driver (*.mdb)};Dbq=$DATABASE";
  if (!$con = odbc_connect($DSN, $DBUSER, $DBPASSWORD)) {
     exit("Accessデータベースに接続できませんでした!");
  }

  $table = "";

  //SELECT文を実行し、結果を取得
  $strSQL = "SELECT PlaceCode, PlaceName, ZIP, Address, TEL, Remarks, Lat, Lng FROM M_Place";
  $result = odbc_exec($con, $strSQL);
  //取得したレコード数(行)分ループ
  while (odbc_fetch_row($result)) {
    $table .= "<TR>";
    $table .= "<TD>" . odbc_result($result, "PlaceCode") . "</TD>";
    $table .= "<TD>" . mb_convert_encoding(odbc_result($result, "PlaceName"), "UTF-8", "SJIS") . "</TD>";
    $table .= "<TD>" . mb_convert_encoding(odbc_result($result, "ZIP"), "UTF-8", "SJIS") . "</TD>";
    $table .= "<TD>" . mb_convert_encoding(odbc_result($result, "Address"), "UTF-8", "SJIS") . "</TD>";
    $table .= "<TD>" . mb_convert_encoding(odbc_result($result, "TEL"), "UTF-8", "SJIS") . "</TD>";
    $table .= "<TD>" . mb_convert_encoding(odbc_result($result, "Remarks"), "UTF-8", "SJIS") . "</TD>";
    $table .= "<TD>" . "(" . odbc_result($result, "Lat") . "," . odbc_result($result, "Lng") . ")</TD>";
    $table .= "</TR>";
  }

  //ODBC接続を閉じる
  odbc_close($con);
?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=utf-8">
<META http-equiv="Content-Style-Type" content="text/css">
</HEAD>
<BODY>
<TABLE border="1" cellpadding="2">
  <TR>
    <TH>PlaceCode</TH>
    <TH>PlaceName</TH>
    <TH>ZIP</TH>
    <TH>Address</TH>
    <TH>TEL</TH>
    <TH>Remarks</TH>
    <TH>LatLng</TH>
  </TR>
<?php echo $table; ?>
</TABLE>
<BR>
</BODY>
</HTML>
?>

まずポイントとなるのは次の箇所

<META http-equiv="Content-Type" content="text/html; charset=utf-8">

文字コードをUTF-8にしています。
Accessを含めWindowsで扱われる文字コードはShift_JIS(厳密にはCP932)ですので、今回の接続テストだけを考えればShift_JISで出力する方がシンプルなのですが、最終的な目的はGoogleMapsAPIとの連携です。そのGoogleMapsAPIはUTF-8でないと利用出来ないので、それに合わせてUTF-8で出力するというわけです。

UTF-8環境ではWindowsのファイル名は正しく認識しませんので

  $DB_File = "C:\Users\ひつじかい\Documents\GoogleMapsAPI埋め込みテスト.mdb"; //Accessファイル名(フルパス)
  $Database = mb_convert_encoding($DB_File, "SJIS", "UTF-8");

と、mb_convert_encoding関数でファイル名をShift_JISに変換しています(フォルダ名を含めファイル名が全て半角英数字で構成されている場合は変換不要)。
そして、

  //Accessに接続
  $DSN = "Driver={Microsoft Access Driver (*.mdb)};Dbq=$Database";
  if (!$con = odbc_connect($DSN, $User, $Password)) {
     exit("Accessデータベースに接続できませんでした!");
  }

でAccessに接続。今回使用のmdbファイルにはユーザーやパスワードの設定をしていませんので、9行目の$Userおよび$Passwordは空文字列のままで接続出来ます。

データの取得・出力は以下の部分

  //SELECT文を実行し、結果を取得
  $strSQL = "SELECT PlaceCode, PlaceName, ZIP, Address, TEL, Remarks, Lat, Lng FROM M_Place";
  $result = odbc_exec($con, $strSQL);
  //取得したレコード数(行)分ループ
  while (odbc_fetch_row($result)) {
    $table .= "<TR>";
    $table .= "<TD>" . odbc_result($result, "PlaceCode") . "</TD>";
    $table .= "<TD>" . mb_convert_encoding(odbc_result($result, "PlaceName"), "UTF-8", "SJIS") . "</TD>";
    $table .= "<TD>" . mb_convert_encoding(odbc_result($result, "ZIP"), "UTF-8", "SJIS") . "</TD>";
    $table .= "<TD>" . mb_convert_encoding(odbc_result($result, "Address"), "UTF-8", "SJIS") . "</TD>";
    $table .= "<TD>" . mb_convert_encoding(odbc_result($result, "TEL"), "UTF-8", "SJIS") . "</TD>";
    $table .= "<TD>" . mb_convert_encoding(odbc_result($result, "Remarks"), "UTF-8", "SJIS") . "</TD>";
    $table .= "<TD>" . "(" . odbc_result($result, "Lat") . "," . odbc_result($result, "Lng") . ")</TD>";
    $table .= "</TR>";
  }

先ほどのファイル名の処理時とは逆にAccessの文字コード(Shift_JIS)を出力用(UTF-8)に変換しています。
なお、今回のテストでは該当しませんが、テーブル名やフィールド名に日本語が含まれる場合は、16行目のSQL文や21~27行目のフィールド名もShift_JISへの変換をする必要があります。
 mb_convert_encoding(odbc_result($result, mb_convert_encoding(“日本語フィールド”, “SJIS”, “UTF-8”)), “UTF-8”, “SJIS”)
こんな感じで・・・読み込みのために一旦Shift_JIS変換したものを、出力用にUTF-8に戻すというややこしい処理になります。

掲載したコードを実行すると、以下のような出力結果を得られます。
出力結果

次回は、今回出力したデータをGoogleMapsAPIに反映(アイコン表示)させてみます。

コメント   

Microsof Office Accessは、とても便利で使い易いデータベースソフトですが、スタンドアロンもしくは小規模オフィスでのリレーショナルDBとしての利用を想定して設計されたものなので、Web連携機能はあまり充実しているとはいえません。
そのため、Web上で動作するGoogleMapsAPIとの相性は良いとは言えず、Accessアプリケーションにてこの機能を利用する例をインターネット上で探してみてもなかなか見つかりません(きちんと探せば沢山あるのでしょうが・・・)。
しかし、GoogleMapsAPIはインターネット接続環境さえあればローカル上でも動作するのでAccessにも埋め込めるはず・・・ということで実際に試してみることにしました。

今回のテストで実装さえたい機能は、以下のようなもの
・AccessのフォームにGoogleMapsAPIを埋め込む(ActiveX:Microsoft Web Browser使用)
・Accessテーブルに登録してある位置データをGoogleMapsAPI上にアイコン表示
・同データをAccessフォームにリスト表示(サブフォームにて)
・サブフォームのデータ選択時に、GoogleMapsAPI上の該当アイコンの色を変更する
・GoogleMapsAPIのアイコンをダブルクリックすると、該当する箇所の詳細フォームを別窓で開く

1.Web環境の準備
以下の環境でテスト
・データベース:Access2003
・Webサーバ:Apache
・Webプログラム言語:PHP

ApacheとPHPは別々に導入しても良いのですが、XAMPPにてまとめてインストールしました。
XAMPPには今回の実験では使用しないMySQL等も含まれていますが、別件でいろいろと使用機会がありますので。

XAMPP for Windows(日本語版): http://www.apachefriends.org/jp/xampp-windows.html

※XAMPPのインストールに関しては以下のサイトに丁寧な解説があります。

 金子邦彦研究室  http://www.kkaneko.com/rinkou/mysql/xamppinstall.html
 
2.データテーブルの準備
 テスト用サンプルとして、以下のような府中市の図書館のテーブルを作成しました。

 <仕様>
 ・テーブル名:M_Place
 ・フィールド構成
   PlaceCode:長整数型(PrimaryKey)・・・コード
   PlaceName:テキスト型・・・名称
   ZIP:テキスト型・・・郵便番号
   Address:テキスト型・・・住所
   TEL:テキスト型・・・電話番号
   Remarks:テキスト型・・・備考
   Lat:倍精度浮動小数点型・・・緯度
   Lng:倍精度浮動小数点型・・・経度

 図書館名称や住所、電話番号などの情報は府中市のHPから入手しました。
 緯度・経度は1件ずつ調べても良いのですが、せっかくなのでGoogleGeocodingAPIを利用して取得しました。
 以下にそのコードを記載します。

 緯度・経度の取得モジュール(関数:Get_LatLng)

'
'  GoogleMapAPIのジオコーディングサービスを利用して緯度・経度を取得
'
'  <引数>
'     myAddress:緯度・経度を取得したい地点の住所(○○駅、△△市役所など、名称でも取得出来る)
'     myLat    :緯度(参照渡し:戻り値)
'     myLng    :経度(参照渡し:戻り値)
'
Public Function Get_LatLng(ByVal myAddress As String, ByRef myLat As Double, ByRef myLng As Double) As Boolean

  Dim strURL As String, strURL_Base As String
  Dim objHttp As WinHttp.WinHttpRequest
  Dim resXML As String
  Dim strLocationS As String
  Dim strLocationE As String
  Dim strLocation As String
  Dim strLatS As String, strLatE As String
  Dim strLngS As String, strLngE As String
  Dim strLat As String, strLng As String
  Dim intS As Integer, intE As Integer
  Dim intLatS As Integer, intLatE As Integer
  Dim intLngS As Integer, intLngE As Integer
  Dim resArray
     
On Error GoTo Get_Html_Err
  
    Get_LatLng = False
  
    strLocationS = "<location>"
    strLocationE = "</location>"
    strLatS = "<lat>"
    strLatE = "</lat>"
    strLngS = "<lng>"
    strLngE = "</lng>"
  
    myLat = 0
    myLng = 0
    
    Set objHttp = CreateObject("WinHttp.WinHttpRequest.5.1")
           
    strURL_Base = "http://maps.google.com/maps/api/geocode/xml?sensor=false"
    strURL = strURL_Base & "&address=" & UrlEncode(myAddress)
    
    objHttp.Open "GET", strURL
    objHttp.Send
    If objHttp.Status <> 200 Then
       'エラー
    Else
       resXML = objHttp.ResponseText
       intS = InStr(resXML, strLocationS)
       intE = InStr(resXML, strLocationE)
       If (intS > 0 And intE > 0 And intE > intS) Then
          strLocation = Mid(resXML, intS + Len(strLocationS), intE - intS - Len(strLocationS))
          intLatS = InStr(strLocation, strLatS)
          intLatE = InStr(strLocation, strLatE)
          intLngS = InStr(strLocation, strLngS)
          intLngE = InStr(strLocation, strLngE)
          If intLatS > 0 And intLatE And intLatE > intLatS Then
             strLat = Mid(strLocation, intLatS + Len(strLatS), intLatE - (intLatS + Len(strLatS)))
          End If
          If intLngS > 0 And intLngE And intLngE > intLngS Then
             strLng = Mid(strLocation, intLngS + Len(strLngS), intLngE - (intLngS + Len(strLngS)))
          End If
          If IsNumeric(strLat) = True And IsNumeric(strLng) = True Then
             Get_LatLng = True
             myLat = CDbl(strLat)
             myLng = CDbl(strLng)
          End If
       End If
    End If

Get_Html_Exit:
   
    Set objHttp = Nothing
    Exit Function

Get_Html_Err:
    
    MsgBox Err.Number & ":" & Err.Description
    Resume Get_Html_Exit

End Function

'
'  文字列をURLエンコード
'
Public Function UrlEncode(ByVal strTxt As String) As String

Dim objScrCtl As Object

   Set objScrCtl = CreateObject("ScriptControl")

   objScrCtl.Language = "Jscript"
   UrlEncode = objScrCtl.CodeObject.encodeURIComponent(strTxt)

   Set objScrCtl = Nothing

End Function

※上記関数ではWinHTTPを利用してHTTP通信していますので、「ツール」→「参照設定」にて「Microsoft WinHTTP Services」にチェックを入れる必要があります(下画像参照)。

下の例ではフォーム上のコマンドボタン(Cmd_GetLatLng)クリック時に上記関数を使用して、テーブル「M_Place」の緯度・経度情報を更新

Private Sub Cmd_GetLatLng_Click()

  Dim db As DAO.Database
  Dim rs As DAO.Recordset
  Dim dblLat As Double
  Dim dblLng As Double
  
  Set db = CurrentDb
  
  strSQL = "SELECT * FROM M_Place"
  
  Set rs = db.OpenRecordset(strSQL, dbOpenDynaset)
  
  Do Until rs.EOF
     If Get_LatLng(rs!Address & " 府中市立" & rs!PlaceName, dblLat, dblLng) = True Then
        Debug.Print rs!PlaceName & ":" & rs!Address & ">>" & dblLat & "," & dblLng & "  " & rs!Lat & "," & rs!Lng

        rs.Edit
           rs!Lat = dblLat
           rs!Lng = dblLng
        rs.Update
     Else
        Debug.Print "error:" & rs!PlaceName
     End If
     rs.MoveNext
  Loop

  Set rs = Nothing
  Set db = Nothing
  
End Sub

Google Geocoding APIでは、必ずしも正確な緯度・経度を取得出来るとは限りません。
今回の府中市内の図書館のケースでも、住所が番地までしかないせいか(号がない)、取得位置にズレが生じました。次に住所+図書館名で試してもズレる箇所がありましたが、図書館の名称の頭に「府中市立」を付加したところ上手くいきました(上記コード)。

なおGoogle Geocoding APIの利用は1日あたり2,500件までに制限されているようなので、大量のデータを扱う場合には注意が必要です(有料サービスのGoogle Maps API Premierを利用すれば1日あたり100,000件までリクエスト可能とのことですが・・・)。
https://developers.google.com/maps/documentation/geocoding/?hl=ja

次回は、Accessテーブルの内容をPHPにてWeb出力するテストです。

コメント   

 東京都の最高峰、雲取山へ。トレーニングには最適なので、もう5回位は登ってます。
 都内とはいえ、鹿や猿が普通に生息しています。今日も鹿と遭遇しました。熊もいるらしいですが、未だ遭ったことはありません。

雲取山山頂

雲取山山頂

 下山後は、多分東京都最西端(島しょ部を除く)の食堂と思われる「鳥勝」で昼食。

とろろめし定食

とろろめし定食(\850)


 とろろ(卵入り)、なす田楽、にんじんの和え物、刺身こんにゃく、野菜とさつまあげの煮物、豆腐の味噌汁、お新香・・・菜食志向の私には、かなり嬉しいおかず構成でした。これで850円というのはかなりお得だと思います。

コメント   

 2012年7月4日(水)
 一昨年に速水御舟の「炎舞」目的で行って以来、2度目の山種美術館訪問です。
 現在開催中の特別展は「福田平八郎と日本画モダン」

福田平八郎と日本画モダン

 


 「日本画モダン」というのは、本特別展の開催に当たって作られた造語だそうですが、この言葉がぴったりくる作品の数々でした。
 今回の目玉作品、雨粒で濡れた瓦がパターン図のように描かれている「雨」は、日本画でありながらポップアートのような趣き・・・。「筍」も同様にポップ。福田平八郎は色彩を第一に考えていたそうで、特に緑色が鮮やかな印象を受けました。
 平八郎作品は20点ほど。個人的には前述の「雨」「筍」も良いのですが、淡い色遣いの繊細な作品である「鮎」や屏風に描かれた「牡丹」に惹かれました。

 他の画家の作品も「日本画モダン」なもので統一されている感じで、山口蓬春「夏の印象」、前田 青邨「鶺鴒」、杉山寧「榕」、奥村土牛「啄木鳥」などが特に印象に残りました(惹かれた作品に、鳥が描かれたものが多かったのは偶々?)
 今日の私的No.1は中村岳陵「緑影」・・・色遣いが素晴らしい作品です。

コメント