【Google MAP】緯度・経度情報などをDBから読み込んでMAP表示させる(Ajax)

## はじめに・やりたいこと
- Mapにピンを打ったり、その場所を表示するには、緯度・経度情報が必要になります。
- プログラムの中に、緯度・経度の情報を直接書き込むのではなく、MySQLなどのDBに情報を保持して、そこからデータをSELECTし、処理をしたいなと思いました。
- いろいろ調べ、AjaxでPHPプログラムを呼び出す、という方法にたどり着きました。
- Google Mapのマーカーをまとめて表示する方法 MarkerClusterer を使用してみます。

## 前提
- Google Map API keyの取得

## 使用技術
- JavaScript / Ajax
- PHP
- Google Map / [MarkerClusterer](https://developers.google.com/maps/documentation/javascript/marker-clustering)


## 作成サイトイメージ
世界最大リストを作成する
（【例】世界最大の流量の滝：イグアスの滝　・　世界最大の流域面積の川：アマゾン川　など）

[デモサイト](https://anotherskyjp.site/devapps/googlemapdev/map_uptoworld/map_uptoworld.html)
![pic1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167772/2a0c6f95-53fe-b5b8-756e-a909170ee366.jpeg)


## データベース
- DBには、緯度・経度とともに、下記のような項目を設けます。

　・クラスID/クラス名（分類）
　・テキスト
　・名前
　・ステータス
　・緯度/経度
　・写真

![db.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167772/32288c15-3a85-59a2-fe7e-eabfab5157e5.jpeg)

## コード（JavaScript/PHP）

- 地図の表示部分

~~~HTML
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>Up to World</title>
    <style>
     <!-- 省略 -->
    </style>
  </head>

  <body>
    <div id="header">Google Maps - 世界最大・世界最長リスト</div>
    <table>
      <tr>
        <td><div id="target"></div></td>
        <td><div id="sidebar"></div></td>
      </tr>
    </table>

    <!-- MarkerCluster -->
    <script src="https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/markerclusterer.js"></script>

    <!-- Google MAP API KEY -->
    <script src="https://maps.googleapis.com/maps/api/js?language=ja&region=JP&key=API keキーを指定する&callback=initMap" async defer></script>

    <script src="https://code.jquery.com/jquery-2.1.1.js" integrity="sha256-FA/0OOqu3gRvHOuidXnRbcmAWVcJORhz+pv3TX2+U6w=" crossorigin="anonymous"></script>

    <script>

      function initMap() {

        //マップ初期表示の位置設定
        var target = document.getElementById('target');
        var centerp = {lat: 37.67229496806523, lng: 137.88838989062504};

        //マップ表示
        map = new google.maps.Map(target, {
          center: centerp,
          zoom: 2,
        });

      };

      var markerD = [];

      // DB情報の取得(ajax)
      $(function(){
        $.ajax({
          type: "POST",
          url: "data.php",
          dataType: "json",
          success: function(data){
            markerD = data;
            setMarker(markerD);
          },
          error: function(XMLHttpRequest, textStatus, errorThrown){
            alert('Error : ' + errorThrown);
          }
        });
      });

      var map;
      var marker = [];
      var infoWindow = [];

      function setMarker(markerData) {

        // console.log(markerData);
        // console.log(markerData.length);

        //マーカー生成
        var sidebar_html = "";
        var icon;

        for (var i = 0; i < markerData.length; i++) {

          var latNum = parseFloat(markerData[i]['lat']);
          var lngNum = parseFloat(markerData[i]['lng']);

          // マーカー位置セット
          var markerLatLng = new google.maps.LatLng({
            lat: latNum,
            lng: lngNum
          });
          // マーカーアイコンのセット(行った所はアイコンを変える)
          if (markerData[i]['status'] === 'went'){
            icon = new google.maps.MarkerImage('./icon_color/went' + markerData[i]['classNo'] + '.png');
          } else {
            icon = new google.maps.MarkerImage('./icon_color/list' + markerData[i]['classNo'] + '.png');
          }
          // マーカーのセット
          marker[i] = new google.maps.Marker({
            position: markerLatLng,          // マーカーを立てる位置を指定
            map: map,                        // マーカーを立てる地図を指定
            icon: icon                       // アイコン指定
          });
          // 吹き出しの追加
          infoWindow[i] = new google.maps.InfoWindow({
            content: markerData[i]['class'] + '：' + markerData[i]['name'] + '<br><br>' + markerData[i]['text'] + '<br><br>' + markerData[i]['img']
          });
          // サイドバー
          var wantStar;
          if(markerData[i]['status'] === 'went') {
            wantStar = '●';
          } else if(markerData[i]['status'] === 'want') {
            wantStar = '○';
          } else {
            wantStar = '・';
          }
          sidebar_html += wantStar + '<a href="javascript:myclick(' + i + ')">' + markerData[i]['name'] + '<\/a><br />';
          // マーカーにクリックイベントを追加
          markerEvent(i);
        }

        // Marker clusterの追加
        var markerCluster = new MarkerClusterer(
          map,
          marker,
          {imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'}
        );

        // サイドバー
        document.getElementById("sidebar").innerHTML = sidebar_html;
      }

      var openWindow;

      function markerEvent(i) {
        marker[i].addListener('click', function() {
          myclick(i);
        });
      }

      function myclick(i) {
        if(openWindow){
          openWindow.close();
        }
        infoWindow[i].open(map, marker[i]);
        openWindow = infoWindow[i];
      }

    </script>

  </body>
</html>
~~~

- DBアクセスしデータを返す

~~~PHP
<?php

  //ヘッダー情報の設定
  header("Content-Type: application/json; charset=utf-8");

  $data = array();

  // DB接続情報
  $host = "host情報";
  $dbname = "db name情報";
  $user = "ユーザ情報";
  $pass = "パスワード";

  // DB接続情報設定・SQL準備・接続
  $dbh = new PDO('mysql:host=' . $host . 'dbname=' . $dbname . 'charset=utf8', $user, $pass);

  $sql = "select classNo, class, text, name, status, lat, lng, img from placedata";
  $sth = $dbh -> prepare($sql);
  $sth -> execute();

  //データを取得する
  $data = $sth -> fetchAll(PDO::FETCH_ASSOC);

  //jsonオブジェクト化
  echo json_encode($data);
?>
~~~



## まとめ
- いままでプログラム中に緯度・経度を直接記載していましたが、別だしすることができました。
- 緯度・経度を外出しする方法として、DBでなくても、いろいろな方法があると思います。個人的には、Googleスプレッドシートがとても便利そうだな、と感じています。
- 【参考】[観測史上最速！GoogleスプレッドシートだけでAPIが作れる「Sheetson」](https://entry.anypicks.jp/product-sheetson/)


## 参考URL

https://qiita.com/BRSF/items/e2b64416604f0ad8716a

https://qiita.com/mpyw/items/62e6e415f86eb30a5ff4

https://php-archive.net/php/ajax-json/

http://ftomo100.hatenablog.com/entry/2016/09/24/135132