🗺️

ACFを使ってカスタム投稿に地図を表示 - WordPress

2020/12/24に公開

WordPressのカスタム投稿に地図を表示する場合のメモ書きです。

  • カスタム投稿タイプ: 店舗情報(shops)
  • カスタムフィールド: Map

(2021.02.12)追記しました

マップを表示するスクリプト(googlemap.js - jQuery)をpure JSに書き換えてみました。

カスタム投稿の設定

参考にさせていただいた記事。
【WordPress】プラグインなしでカスタム投稿タイプにカスタムフィールド(テキスト&画像)を追加する方法
register_post_type() - WordPress Developer Resources

functions.phpに記述。

function create_post_type() {
  register_post_type(
    'shops',
    array(
      'label' => '店舗情報',
      'public' => true,
      'has_archive' => true,
      'menu_position' => 3,
      'supports' => array(
        'title',
        'editor',
        'author',
        'thumbnail',
        'revisions'
      ),
      'show_in_rest' => true
    )
  );
}
add_action('init', 'create_post_type');

ACFのセットアップ

Advanced Custom Fieldsプラグインをインストールして有効にします。
Advanced Custom Fields

フィールドグループを編集

フィールドタイプで Googleマップ を選択。
Edit Field Group
投稿タイプで 店舗情報 を選択。
投稿タイプ

ACF で Google Maps を使うための準備

参考にさせていただいた記事。
WordPress 投稿ページで Google Map を表示する方法
Field Types > Google Map - ACF Documentation

スタイルの設定

style.cssに記述。

.acf-map {
  width: 100%;
  height: 400px;
}
.acf-map img {
  max-width: inherit !important;
}

スクリプトの作成

ACFにあるテンプレートをそのまま利用します。
js/googlemap.jsに記述。

(function( $ ) {

  /**
   * initMap
   *
   * Renders a Google Map onto the selected jQuery element
   *
   * @date    22/10/19
   * @since   5.8.6
   *
   * @param   jQuery $el The jQuery element.
   * @return  object The map instance.
   */
  function initMap( $el ) {
  
      // Find marker elements within map.
      var $markers = $el.find('.marker');
  
      // Create gerenic map.
      var mapArgs = {
          zoom        : $el.data('zoom') || 16,
          mapTypeId   : google.maps.MapTypeId.ROADMAP
      };
      var map = new google.maps.Map( $el[0], mapArgs );
  
      // Add markers.
      map.markers = [];
      $markers.each(function(){
          initMarker( $(this), map );
      });
  
      // Center map based on markers.
      centerMap( map );
  
      // Return map instance.
      return map;
  }
  
  /**
   * initMarker
   *
   * Creates a marker for the given jQuery element and map.
   *
   * @date    22/10/19
   * @since   5.8.6
   *
   * @param   jQuery $el The jQuery element.
   * @param   object The map instance.
   * @return  object The marker instance.
   */
  function initMarker( $marker, map ) {
  
      // Get position from marker.
      var lat = $marker.data('lat');
      var lng = $marker.data('lng');
      var latLng = {
          lat: parseFloat( lat ),
          lng: parseFloat( lng )
      };
  
      // Create marker instance.
      var marker = new google.maps.Marker({
          position : latLng,
          map: map
      });
  
      // Append to reference for later use.
      map.markers.push( marker );
  
      // If marker contains HTML, add it to an infoWindow.
      if( $marker.html() ){
  
          // Create info window.
          var infowindow = new google.maps.InfoWindow({
              content: $marker.html()
          });
  
          // Show info window when marker is clicked.
          google.maps.event.addListener(marker, 'click', function() {
              infowindow.open( map, marker );
          });
      }
  }
  
  /**
   * centerMap
   *
   * Centers the map showing all markers in view.
   *
   * @date    22/10/19
   * @since   5.8.6
   *
   * @param   object The map instance.
   * @return  void
   */
  function centerMap( map ) {
  
      // Create map boundaries from all map markers.
      var bounds = new google.maps.LatLngBounds();
      map.markers.forEach(function( marker ){
          bounds.extend({
              lat: marker.position.lat(),
              lng: marker.position.lng()
          });
      });
  
      // Case: Single marker.
      if( map.markers.length == 1 ){
          map.setCenter( bounds.getCenter() );
  
      // Case: Multiple markers.
      } else{
          map.fitBounds( bounds );
      }
  }
  
  // Render maps on page load.
  $(document).ready(function(){
      $('.acf-map').each(function(){
          var map = initMap( $(this) );
      });
  });
  
})(jQuery);  

Google Maps APIのキーの取得と設定

Get an API Key - Google Maps Platform
上のページの説明にそって、プロジェクトを選択(または作成)して Credentialsページを開く。
Create credentials > API key で Keyを生成。

Application restrictions

HTTP referrers (web sites)

Website restrictions

example.com (地図を設置するドメインを設定)

有効にするAPI

  • Maps JavaScript API
  • Geocoding API(住所から場所を指定)
  • Places API(著名な場所、任意)

スクリプトの読み込み

functions.php に記述。

function my_api_key() {
  return 'MY_API_KEY';
}
function my_acf_google_map_api( $api ) {
  $api['key'] = my_api_key();
  return $api;
}
add_filter('acf/fields/google_map/api', 'my_acf_google_map_api');

function add_my_scripts() {
  wp_enqueue_script( 
    'googlemap-script',
    get_theme_file_uri( '/js/googlemap.js' ),
    array( 'jquery' ),
    filemtime( get_theme_file_path( '/js/googlemap.js' ) ),
    true
  );
  wp_enqueue_script(
    'googlemapsapi',
    '//maps.googleapis.com/maps/api/js?key=' . my_api_key(),
    array(),
    '',
    true
  );
}
add_action('wp_enqueue_scripts', 'add_my_scripts');

[2020-12-29]
ハンバーガーメニュー(レスポンシブ対応)表示が動かない場合があったので jQuery(core, migrate)WordPress の初期状態をそのまま利用するようにしました。(追記していた下記の部分を削除)

  wp_deregister_script( 'jquery' );
  wp_enqueue_script(
    'jquery',
    '//ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js',
    array(),
    '3.5.1',
    true
  );

カスタム投稿(店舗情報)を表示するテンプレート

ファイル構成やスタイル指定など適宜読み替えてください。

single.phpを複製してsingle-shops.phpを作成。
template-parts/content.phpを複製してtemplate-parts/content-shops.phpを作成。
template-parts/content-shops.php(一部)

    the_content( /*...*/ );
    
    $map = get_field('map');
    if( $map ): ?>
        <div class="acf-map" data-zoom="16">
            <div class="marker" data-lat="<?php echo esc_attr($map['lat']); ?>" data-lng="<?php echo esc_attr($map['lng']); ?>"></div>
        </div>
    <?php endif;

複数のマーカーを表示

こちらの記事にサンプルソースが公開されています。
Create Multiple Marker Map from ACF Google Map Location Field from a CPT

ショートコードによる実装 Multiple shortcode [themeprefix_multiple_marker] もあります。post_type を合わせればそのまま使えると思います。

マーカーをカスタマイズ

Customizing a Google Map: Custom Markers - Google Maps Platform
この記事を参考にして js/googlemap.js を編集します。

function initMarker( $marker, map ) {
  
    // Get position from marker.
    var lat = $marker.data('lat');
    var lng = $marker.data('lng');
    var latLng = {
        lat: parseFloat( lat ),
        lng: parseFloat( lng )
    };

    const iconBase = "//developers.google.com/maps/documentation/javascript/examples/full/images/";
    const icons = {
      parking: {
        icon: iconBase + "parking_lot_maps.png",
      },
      library: {
        icon: iconBase + "library_maps.png",
      },
      info: {
        icon: iconBase + "info-i_maps.png",
      },
    };
    // Create marker instance.
    var marker = new google.maps.Marker({
        position : latLng,
        icon: icons["parking"].icon,
        map: map
    });
  
    // Append to reference for later use.
    map.markers.push( marker );
  
    // If marker contains HTML, add it to an infoWindow.
    if( $marker.html() ){

        // Create info window.
        var infowindow = new google.maps.InfoWindow({
            content: $marker.html()
        });
  
        // Show info window when marker is clicked.
        google.maps.event.addListener(marker, 'click', function() {
            infowindow.open( map, marker );
        });
    }
}

pure JSに書き換えてみる

js/googlemap.js

function initMap( acfMap ) {
  var markers = acfMap.querySelectorAll( '.marker' );
  var mapArgs = {
    zoom        : acfMap.dataset.zoom || 16,
    mapTypeId   : google.maps.MapTypeId.ROADMAP
  };
  var map = new google.maps.Map( acfMap, mapArgs );

  // Add markers.
  map.markers = [];
  markers.forEach( function( mkr, index ){
    initMarker( mkr, map );
  });
  centerMap( map );
  return map;
}

function initMarker( mkr, map ) {
  var lat = mkr.dataset.lat;
  var lng = mkr.dataset.lng;
  var latLng = {
    lat: parseFloat( lat ),
    lng: parseFloat( lng )
  };
  const iconBase =
 "//developers.google.com/maps/documentation/javascript/examples/full/images/";
  const icons = {
    parking: {
      icon: iconBase + "parking_lot_maps.png",
    },
    library: {
      icon: iconBase + "library_maps.png",
    },
    info: {
      icon: iconBase + "info-i_maps.png",
    },
  };

  // Create marker instance.
  var marker = new google.maps.Marker({
    position : latLng,
    icon: icons[ "parking" ].icon,
    map: map
  });

  // Append to reference for later use.
  map.markers.push( marker );

  // If marker contains HTML, add it to an infoWindow.
  if( mkr.innerHTML ){
    // Create info window.
    var infowindow = new google.maps.InfoWindow({
      content: mkr.innerHTML
    });

    // Show info window when marker is clicked.
    google.maps.event.addListener(marker, 'click', function() {
      infowindow.open( map, marker );
    });
  }
}
  
function centerMap( map ) {
  // Create map boundaries from all map markers.
  var bounds = new google.maps.LatLngBounds();
  map.markers.forEach(function( marker ){
    bounds.extend({
      lat: marker.position.lat(),
      lng: marker.position.lng()
    });
  });
  
  // Case: Single marker.
  if( map.markers.length == 1 ) {
    map.setCenter( bounds.getCenter() );

  // Case: Multiple markers.
  } else {
    map.fitBounds( bounds );
  }
}

document.addEventListener("DOMContentLoaded", function(event){
  var acfMaps = document.querySelectorAll('.acf-map');
  acfMaps.forEach( function( acfMap, index ){
    var map = initMap( acfMap );
  });
});

上記 js/googlemap.jswp_enqueue_script()で読み込む。

wp_enqueue_script( 
  'googlemap-script',
  get_theme_file_uri( '/js/googlemap.js' ),
  array(),
  filemtime( get_theme_file_path( '/js/googlemap.js' ) ),
  true
);

関数リファレンス/wp enqueue script
これで js/googlemap.jsjQuery に依存しなくなったので、このスクリプトが依存するスクリプトのハンドルの配列 を空( array(), )にしました。

参考記事まとめ

Discussion