イントロダクション
ブラウザアプリを作成しています。
GoogleMapへのデータ登録と表示を行うシンプルなものです。
設計と思想
今回の実装は、PHPを使用するけどサーバーサイドとクライアントサイドを分断して実装しようと思いましたので以下のようなファイル構成で実装しています。2023-06-16現在では、プログラムのサンプルを起動できない状態になっています。
<クライアント>
- SampleMap.html
- SampleMap.js
クライアントというのはつまるところブラウザ上で動く処理のことを指しています。処理内容としては、GoogleMapを表示してクリックした場所にどんなものがあるか登録する入力フォームを表示、サーバーサイドに送信するプログラムを実装しました。
<サーバー>
- InsertMap.php
- GetMapInfo.php
こちらはサーバーサイドプログラムです。クライアントから送信されたフォームデータを受け取りMySQLのデータベースに情報を登録します。登録された情報はページを開いたときに表示できるようにします。
XmlHttpRequest
クライアントからサーバーへフォームを送信するのに使用するクラスです。JavaScriptのクラスです。
ここで使用するのはXMLHttpReuqstを使用して実装します。
リクエストのタイプは2つあります。
これらは用途により使い分けます。世間巷でよくある$ajaxなどはこれを使用しているのでテクノロジー的には大差ありません。
とりあえずはこんな形で実装します。
しかし
実装したのだけれど、ファイルの送信がうまくいかず。。。以前イメージファイルの送信に以下のようなコードでリクエストを飛ばし、DBへのデータ登録まで確認したのだけれど、またうまくいかなくなりました。
怪しいところ
HTML側のFormに余計な属性をつけた。
しかし、これは関係ないようでした。どこなのだろうか? 絶賛戦闘中です(笑)
実装結果
/**************************************************
* GeoLocationAPIスクリプト *
**************************************************/
// グローバル変数
var id, target;
function getCurrentPos() {
if (navigator.geolocation == false) {
console.log("GEO Locaion is unable.");
return;
}
// オプション・オブジェクト
var optionObj = {
"enableHighAccuracy": false ,
"timeout": 5000 ,
"maximumAge": 0 ,
};
target = {
latitude: 0,
longitufe: 0
};
// Navigator.geolocation
id = navigator.geolocation.watchPosition(
successCurrentPos // 成功時のコールバック
, errorCurrentPos // 失敗時のコールバック
, optionObj); // オプション設定
}
/** GeoLocationの成功時のコールバック */
function successCurrentPos(position) {
var crd = position.coords;
// マップの位置情報オブジェクト
var mapLatLng = new google.maps.LatLng(crd.latitude, crd.longitude);
if (target.latitude === crd.latitude && target.longitude === crd.longitude) {
console.log('Congratulations, you reached the target');
navigator.geolocation.clearWatch(id);
}
// マーカーの追加
var current = new google.maps.Marker({
map: map,
position: mapLatLng,
icon: './mapImg/you.png'
});
console.log("test");
}
/** GeoLocationのエラー時のコールバック */
function errorCurrentPos(error) {
// エラーコード(error.code)の番号
// 0:UNKNOWN_ERROR 原因不明のエラー
// 1:PERMISSION_DENIED 利用者が位置情報の取得を許可しなかった
// 2:POSITION_UNAVAILABLE 電波状況などで位置情報が取得できなかった
// 3:TIMEOUT 位置情報の取得に時間がかかり過ぎた…
// エラー番号に対応したメッセージ
var errorInfo = [
"原因不明のエラーが発生しました…。" ,
"位置情報の取得が許可されませんでした…。" ,
"電波状況などで位置情報が取得できませんでした…。" ,
"位置情報の取得に時間がかかり過ぎてタイムアウトしました…。"
];
alert(errorInfo[error.code]);
id = null;
}
/**************************************************
* ライブハウス照会機能実装スクリプト *
**************************************************/
/** 登録時に選択する位置情報のマーカー */
var selectedMarker;
/** 入力フォーム */
var sideWin;
/** XMLHttpRequest */
var xhr;
/** オーバーレイの数 */
var oblNo = 0;
/** マーカーリスト */
var markerList = [];
/** 画面の初期化 */
function loadView() {
// GeoLocation API
getCurrentPos();
// XMLHttpRequestの生成
xhr = createXmlHttpRequest();
sideWin = document.getElementById('sideWin');
sideWin.hidden = true;
// Load Overlay
var areaData = dstMapData();
}
/** 画面の開閉 */
function sideWinHandle() {
console.log("change");
if (sideWin.style.display === 'block') {
sideWin.style.display = "none";
} else {
sideWin.style.display = "block";
}
sideWin.hidden = !sideWin.hidden;
}
/**
1. PHPでマップデータ(オーバーレイ)の取得
2. 取得したデータをHTMLのDIVタグに出力する
*/
function dstMapData() {
// 初期表示用データの取得
xhr.open('GET', 'https://zenryokuservice.com/tools/maps/GetMapInfo.php', true);
xhr. responseType = 'json';
xhr.onreadystatechange = dstMap;
try {
xhr.send();
} catch(e) {
console.log(e);
}
}
/** 初期データ取得処理のコールバック関数 */
function dstMap(response) {
var json;
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
createMapData(xhr.response);
// カテゴリマスタ取得
getCategories();
} else {
alert('リクエストに問題が発生しました');
}
}
}
/** 取得データ(JSON)からオーバーレイ作成 */
function createMapData(jsonData) {
if (jsonData === "") {
return;
}
var jsonObj = JSON.parse(jsonData);
jsonObj.forEach(function(data) {
createOverLay(data);
});
return jsonObj;
}
/** マーカーとWindowInfoの作成とmapへの登録 */
function createOverLay(json) {
// 位置情報がからの時は飛ばす
if (json.lat == "0" || json.lng == "0") {
return;
}
// マーカー作成
var marker = new google.maps.Marker({
position: {lat: parseFloat(json.lat), lng: parseFloat(json.lng)},
icon: getCategoryImg(json.categoryId),
map: map,
category: json.categoryId,
title: json.name,
});
// WindowInfo作成
var infoWindow = new google.maps.InfoWindow({ maxWidth: 300 });
marker.addListener('click', function() {
infoWindow.setContent(createWindowHTML(json));
infoWindow.open(map, marker);
});
// リストに追加
markerList.push(marker);
}
/** 表示する吹き出しのHTMLを作成する */
function createWindowHTML(json) {
var html = document.createElement("div");
html.setAttribute("width", "300px");
html.innerHTML = '<h4><a href="' + json.url + '">' + json.name + '</a></h4>';
html.innerHTML += '<div width="300px" height="200px"><iframe width="300" height="200" src="https://www.youtube.com/embed/' + json.image + '" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" /></div>';
oblNo++;
// Youtube Player api
return html;
}
/** 位置情報の取得と選択位置表示(Overlay) */
function viewLatLng(mapEvent, map) {
if (selectedMarker != undefined || selectedMarker != null) {
selectedMarker.setMap(null);
}
// 表示部分に設定
document.getElementById('latText').textContent = mapEvent.latLng.lat();
document.getElementById('lngText').textContent = mapEvent.latLng.lng();
// 送信部分に設定
document.getElementById('latValue').value = mapEvent.latLng.lat();
document.getElementById('lngValue').value = mapEvent.latLng.lng();
selectedMarker = new google.maps.Marker(selectedOverlayOpt(mapEvent));
selectedMarker.setMap(map);
}
/** 選択用のオーバーレイOption */
function selectedOverlayOpt(mapEvent) {
return {
position: mapEvent.latLng,
animation: google.maps.Animation.DROP,
icon: {
url: "mapImg/selected.png",
scaledSize: new google.maps.Size(40, 40)
},
};
}
/** セットしたデータをPHPに送信する */
function postData() {
ajaxPost();
}
/** XMLHttpRequest作成 */
function createXmlHttpRequest() {
console.log("init httpRequest");
xhr = new XMLHttpRequest();
return xhr;
}
/** レスポンスを取得するときの実装 */
function recieveResponse() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
alert(xhr.response);
} else {
alert('リクエストに問題が発生しました');
}
}
}
/** Ajax送信処理 */
function ajaxPost() {
xhr.open('POST', 'https://zenryokuservice.com/tools/maps/InsertMapInfo.php', true);
// xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8"');
xhr. responseType = 'text';
xhr.onreadystatechange = recieveResponse;
// POSTデータを設定する
var data = createJSON();
console.log(data);
try {
xhr.send(data);
} catch(e) {
console.log(e);
}
}
// application/x-www-form-urlencoded
// application/json
/** Ajax送信 */
function ajaxGet() {
xhr.open('GET', 'https://zenryokuservice.com/tools/maps/InsertMapInfo.php?param=test', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = recieveResponse;
xhr.send();
}
/** POST送信するデータ(JSON)を作成する */
function createJSON() {
var data = new FormData(document.getElementById("mapData"));
// data.append("name", form.name.value);
// data.append("url", form.url.value);
// data.append("aFile", form.imageFile.files[0]);
// data.append("lat", form.lat.value);
// data.append("lng", form.lng.value);
return data;
}
/** フォーム部分を開く */
function openLoginForm() {
var input = prompt("Login Form");
if ('PGBox' === input) {
var loggedInButton = document.getElementsByName("loggedIn");
loggedInButton.forEach(but => {
but.style = "display: visible";
});
}
// カテゴリマスタ取得
getCategories();
}
/** カテゴリの変更 */
function changeCategory(selectBox) {
markerList.forEach(marker => {
if (selectBox.value === marker.category) {
marker.setVisible(true);
} else {
marker.setVisible(false);
}
});
}
/** Ajax送信 */
function getCategories() {
xhr.open('GET', 'https://zenryokuservice.com/tools/maps/GetCategoryInfo.php', true);
// xhr.setRequestHeader('Content-Type', 'application/json');
xhr. responseType = 'json';
xhr.onreadystatechange = responseMst;
xhr.send();
}
/** マスタ情報取得処理 */
function responseMst() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
var res = JSON.parse(xhr.response);
// セレクトボックス
var selectBox = document.getElementById("categories");
var selectBox1= document.getElementById("categoriesSide");
var arr = [];
for( var i=0; i < res.length; i++) {
var jsn = res[i];
arr[jsn.categoryId] = jsn.name;
}
// Opitonセット
for( var i=0; i < arr.length; i++) {
selectBox.options.add(new Option(arr[i], i));
}
for( var i=0; i < arr.length; i++) {
selectBox1.options.add(new Option(arr[i], i));
}
} else {
alert('リクエストに問題が発生しました');
}
}
}
/**
1. カテゴリのイメージファイルパスを取得
2. 想定外のものは""を返す
*/
function getCategoryImg(categoryId) {
var imagePath = "";
if ("0" === categoryId) {
// 観光資源(その他)
imagePath = "";
} else if ("1" === categoryId) {
// ライブハウス
imagePath = "./mapImg/LiveHouse.png";
} else if ("2" === categoryId) {
// 音楽スタジオ
imagePath = "./mapImg/musicStudio.png";
} else if ("3" === categoryId) {
// 音楽系カフェ
imagePath = "";
}
return imagePath;
}