WordPress Google Maps Tutorial

Google maps wordpress shortcode tutorial

In this tutorial, learn how to add Google Maps to your WordPress website without using a plugin. We’ll create a custom shortcode that can be used multiple times on any post/page, and it’ll support multiple markers & popup info windows, with fully customisable HTML.

  1. Make sure you’re using a custom child theme so you can edit functions.php.
  2. You’ll need a Google API Key that has access to the Maps API and Places APIs, and is restricted by HTTP Referrer to your website
GMap shortcode. Click on the markers.

Google API Key Restrictions

You should restrict your Google API key so that it only works with the Maps JavaScript API and the Places API. You should also restrict it so that only your website domain (HTTP Referrer) is allowed to use the key.

You can get your API key from the Google Cloud Console

Restrict your Google API Key to specific APIs
Restrict by Google API
Restrict your Google API Key by HTTP Referrer
Restrict by HTTP Referrer

How It’s Going to Work

The Google documentation for the Maps JavaScript API outlines how to get a map into your page, and the process works like this:

  1. Create an HTML element on your page that will act as a “container” element for the maps. This is just a div that can be identified with a CSS selector. We’re going to use a CSS class to do this, so we can select all the maps on a page with a single query.
  2. At the end of the HTML, add a script element that pulls in the Google Maps JS. The URL should contain the name of a function in your page’s JavaScript, which will get called when the Google script has finished loading, e.g. wptgmapInitMaps
  3. When your JavaScript function is called, use the Google JS to initialise your map div element(s).

Here’s a rough outline of how the HTML will look:

<!DOCTYPE html>
<html lang="en-GB">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	
	<script>
	function wptgmapInitMaps() {
		console.log('Maps API loaded. Starting our callback : wptgmapInitMaps()');

		document.querySelectorAll('.map').forEach(function(container) {
			// The container is the DIV element. We want to grab the data-map
			// property and convert the JSON string into a JavaScript object.
			const mapDef = JSON.parse(container.dataset.map);

			// Use the properties in mapDef to initialise this map instance.
			const gmap = new google.maps.Map(container, mapDef.map);

			// Maybe initialise the markers too.
			// ...
		});
	}
	</script>

<body>
	<h1>Google Maps Outline</h1>

	<!-- Map definition one -->
	<div class="map" data-map="{ map: { ... }, markers: [ ... ], ... }"></div>
	
	<!-- Map definition two -->
	<div class="map" data-map="{ map: { ... }, markers: [ ... ]. ... }"></div>

	<!-- Load the Maps JavaScript Code from Google -->
	<script src="https://maps.googleapis.com/maps/api/js?key=_YOUR_API_KEY_HERE_&libraries=places&&callback=wptgmapInitMaps" async></script>
</body>

</html>

See how wptgmapInitMaps is our main entry point, and it’s included in the Google Maps URL as the callback. We’re also passing each maps’ configuration in the div‘s data-map="{ ... }" property as JSON.

Implementing it in WordPress

We’re going to create a shortcode that’ll output a div element, with the data-map="..." property holding the parameters of the map. We’ll put our JS and CSS in separate asset files, and we’ll hook WordPress’ wp_footer action to inject the Maps JavaScript script element at the end of the page.

The only fiddly bit is adding the map markers, as we need to do some to-ing and fro-ing with the Google Places API, but we’ll get the basic maps working first.

Let’s Write the Code

Go to your custom child theme’s main folder and create a new folder called “wpt-google-map”. In here, create two empty files, called “wpt-google-maps.css” and “wpt-google-maps.js”. Back in your child theme’s main folder, create new filed called “wpt-google-map.php” and paste the following into it.

<?php

/**
 * WP Tutorials Google Map (WPTGMAP)
 *
 * https://wp-tutorials.tech/add-functionality/wordpress-google-maps-tutorial/
 *
 * Changes:
 *
 * 2024-10-17 : Added support for custom map marker icons.
 *              URL-encode the API key in wptgmap_page_footer().
 *
 */

// Block direct access.
defined('WPINC') || die();

// Add your Google Maps/Places API Key here
const WPTGMAP_API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; // << Change this!
const WPTGMAP_CLASS = 'wpt-map';

/**
 * Enqueue our front-end assets.
 */
function wptgmap_enqueue_assets() {
	global $wptgmap_have_assets_been_enqueued;

	// Make sure we don't enqueue our assets more than once, even if there
	// are multiple GMap shortcodes on the page.
	if (is_null($wptgmap_have_assets_been_enqueued)) {
		$base_url = get_stylesheet_directory_uri();
		$version = wp_get_theme()->get('Version');

		// Define custom map marker icons (optional). 
		$custom_marker_icons = apply_filters('wptgmap_custom_marker_icons', []);

		// Enqueue our styles.
		wp_enqueue_style('wptgmap', $base_url . '/wpt-google-map/wpt-google-maps.css', null, $version, 'all');

		// Enqueue our frontend JavaScript and set the wptgmapData to hold
		// our global data.
		wp_enqueue_script('wptgmap', $base_url . '/wpt-google-map/wpt-google-maps.js', null, $version);
		wp_localize_script(
			'wptgmap',
			'wptgmapData',
			array(
				'mapSelector' => '.' . WPTGMAP_CLASS,
				'customMarkers' => $custom_marker_icons
			)
		);

		// Enqueue the Google Maps JS in the footer.
		add_action('wp_footer', 'wptgmap_page_footer', 99);

		$wptgmap_have_assets_been_enqueued = true;
	}
}

/**
 * Add the Maps JavaScript script in the footer.
 */
function wptgmap_page_footer() {
	printf(
		'<script src="https://maps.googleapis.com/maps/api/js?key=%s&libraries=places&&callback=wptgmapInitMaps" async></script>',
		urlencode(WPTGMAP_API_KEY)
	);
}

/**
 * Our main GMap shortcode.
 */
function wptgmap_do_shortcode_map($atts) {
	$html = '';

	if (is_admin()) {
		// Don't do anything.
	} elseif (wp_doing_ajax()) {
		// Don't do anything.
	} else {
		wptgmap_enqueue_assets();

		// Decode any parameters that have been passed from the shortcode.
		$args = shortcode_atts(
			array(
				'place_id' => '',
				'zoom' => 1,
				'lat' => 0,
				'lng' => 0,
				'info' => false,
				'caption' => '',
			),
			$atts
		);

		// You can add your own CSS classes for the container DIV element here.
		$classes = array('map', WPTGMAP_CLASS);

		// This goes into the map-data="..." property, to be converted into a
		// JavaS|cript object later in "wpt-google-maps.js"
		$map_data = array(
			'map' => array(
				'zoom' => intval($args['zoom']),
				'center' => array(
					'lat' => floatval($args['lat']),
					'lng' => floatval($args['lng']),
				),
			),
			'markers' => array(),
			'showInfoWindows' => boolval($args['info']),
		);

		// If place_id is specified, split it into an array by commas, so we
		// can spcify multiple Google PlaceIDs (for multiple markers).). If there's
		// only one value in place_id then we'll end up with an array with only
		// one element.
		if (!empty($args['place_id'])) {
			$place_ids = explode(',', $args['place_id']);
			foreach ($place_ids as $place_id) {
				$map_data['markers'][] = array(
					'placeId' => trim($place_id),
				);
			}
		}

		// Write our DIV map container.
		$html .= '<figure class="google-map">';

		$html .= sprintf(
			'<div class="%s" data-map="%s"></div>',
			esc_attr(implode(' ', $classes)),
			esc_attr(json_encode($map_data))
		);

		// Optional caption, below the map.
		if (!empty($args['caption'])) {
			$html .= sprintf(
				'<figcaption>%s</figcaption>',
				esc_html($args['caption'])
			);
		}

		$html .= '</figure>';
	}

	return $html;
}
add_shortcode('gmap', 'wptgmap_do_shortcode_map');

Now open your child theme’s functions.php and add the following couple of lines:

// WP Tutorials : Google Maps
require_once dirname(__FILE__) . '/wpt-google-map.php';

Save everything, reload some content on your site and check nothing’s broken.

Add some Styles

Open wpt-google-map/wpt-google-maps.css and paste the following into it, to get you started.

/**
 * WP Tutorials : Google Maps
 *
 * https://wp-tutorials.tech/add-functionality/wordpress-google-maps-tutorial/
 */

.google-map figcaption {
	text-align: center;
	margin-top:  0.75em;
}

.map {
	min-height: 20em;
}

@media(min-width: 922px) {
	.wp-block-column .map {
		height: 100%;
	}
}

Version One of the JavaScript

The first version of the JS is just enough to get the maps working, but without the markers. So, open wpt-google-map/wpt-google-maps.js and paste the following into it:

/**
 * WP Tutorials : Google Maps
 *
 * https://wp-tutorials.tech/add-functionality/wordpress-google-maps-tutorial/
 */
if (typeof wptgmapData !== 'undefined') {
	// Uncomment this to confirm that the script has laoded and we've
	// picked up wptgmapData from wp_localize_script()
	// console.log('WPTGMAP Init');
	// console.log(wptgmapData);

	/**
	 * This function gets called after the Maps JavaScript has loaded and it's
	 * ready to be used.
	 */
	function wptgmapInitMaps() {
		// Uncomment this to confirm that we've loaded the code from Google.
		// console.log('Maps API loaded. Starting our callback : wptgmapInitMaps()');

		// Select all map elements using the CSS Selector we've passed from
		// the backend.
		document.querySelectorAll(wptgmapData.mapSelector).forEach(function(container) {
			// Extract this map's configuration from the data-map="..." propertyl.
			const mapDef = JSON.parse(container.dataset.map);

			// Create the Google Map instance.
			const gmap = new google.maps.Map(container, mapDef.map);
		});
	}
}

Save all that and try creating a shortcode in some of your content.

WordPress Google Map Shortcode
Example Google Map shortcode
A basic Google Map

That’s the first part working. Here are the parameters you can pass in to the shortcode:

  • zoom : Google Map zoom level integer from zero to about 18.
  • lat : Latitude (float)
  • lng : Longitude (float)
  • caption : Optional text caption to put below the map.
  • place_id: Optional comma-separated list of Google PlaceIds to use as map markers (in Version Two).
  • info: If there are map markers, you can set info=true to show popup info windows (in Version Two).

Version Two: Adding the Markers

To add the markers, we’re going to call the Google Places API. Using a Google PlaceId, we can grab loads of useful information, including the name of the place, its address, user reviews, and more. We’ll make these calls from the browser, after we’ve initialised each map. The logic works like this:

  • For each div with the wpt-map class and a data-map=”…” property…
    • Create a Google Maps PlacesService instance.
    • Extract the array of PlaceIds from the data-map="..." property, split by commas.
    • For each PlaceId…
      • Call getDetails(placeId) on the place service instance and wait for the response.
      • Create the marker, using the result’s location property.
      • Use the result to create the HTML for the InfoWindow and attach it to the marker.

Open wpt-google-map/wpt-google-maps.js and paste the following into it, replacing everything that was in there before:

/**
 * WP Tutorials : Google Maps
 *
 * https://wp-tutorials.tech/add-functionality/wordpress-google-maps-tutorial/
 */
if (typeof wptgmapData !== 'undefined') {
	// Uncomment this to confirm that the script has laoded and we've
	// picked up wptgmapData from wp_localize_script()
	// console.log('WPTGMAP Init');
	// console.log(wptgmapData);

	/**
	 * This function gets called after the Maps JavaScript has loaded and it's
	 * ready to be used.
	 */
	function wptgmapInitMaps() {
		// Uncomment this to confirm that we've loaded the code from Google.
		// console.log('Maps API loaded. Starting our callback : wptgmapInitMaps()');

		// Select all map elements using the CSS Selector we've passed from
		// the backend.
		document.querySelectorAll(wptgmapData.mapSelector).forEach(function(container) {
			// Extract this map's configuration from the data-map="..." propertyl.
			const mapDef = JSON.parse(container.dataset.map);

			// Create the Google Map instance.
			const gmap = new google.maps.Map(container, mapDef.map);

			// Loop through any markers we've defined.
			if (Array.isArray(mapDef.markers) && mapDef.markers.length > 0) {
				const service = new google.maps.places.PlacesService(gmap);
				var infowindow = null;

				mapDef.markers.forEach(function(markerDef) {
					let placeId = markerDef.placeId;
					// If the placeId has a colon in it, split it into
					// palceId and map marker icon name.
					const placeIdComponents = placeId.split(':');
					if (placeIdComponents.length > 1) {
						placeId = placeIdComponents[0];
					}

					const request = {
						placeId: placeId,
					};

					service.getDetails(request, function(result, status) {
						// Dump the result to the JS console, so you can see what
						// comes back from the PlaceId call.
						// console.log(result);

						if (status != google.maps.places.PlacesServiceStatus.OK) {
							// Error querying the PlaceId.
							alert(status);
						} else {
							// Have we specified a custom marker icon?
							let customMarkerIcon = null;
							if (placeIdComponents.length > 1) {
								const customMarkerName = placeIdComponents[1];
								if (typeof wptgmapData.customMarkers[customMarkerName] === 'undefined') {
									console.error(`Custom marker not defined: ${customMarkerName}`);
								} else {
									// Clone the custom marker icon by serialising it and then parsing the JSON string.
									customMarkerIcon = JSON.parse(JSON.stringify(wptgmapData.customMarkers[customMarkerName]));

									// If there's an anchor point specified, convert it from an X:Y array into a Point object.
									if (customMarkerIcon.anchor) {
										customMarkerIcon.anchor = new google.maps.Point(customMarkerIcon.anchor[0], customMarkerIcon.anchor[1]);
									}

									// Diagnostics: Show the custom marker icon definition in the JS console.
									// console.log(customMarkerIcon);
								}
							}

							// Create the marker.
							var marker = new google.maps.Marker({
								map: gmap,
								place: {
									placeId: result.place_id,
									location: result.geometry.location,
								},
								icon: customMarkerIcon,
							});

							// If we've set info=1 in the shortcode...
							if (mapDef.showInfoWindows) {
								if (!infowindow) {
									infowindow = new google.maps.InfoWindow();
								}

								// Create the HTML snippet for the info window.
								content = `
<div style="width:10em;height:3em;margin-bottom:0.5em;margin-left:auto;margin-right:auto;">
   <img src="${result.icon}" style="width:100%;height:100%;object-fit:contain;" />
</div>
<div style="text-align:center">
   <strong>${result.name}</strong><br />
   <span>${result.place_id}</span>
</div>
`;
								// Show the info window when the marker is clicked.
								google.maps.event.addListener(
									marker,
									'click',
									(function(marker, content, infowindow) {
										return function() {
											infowindow.setContent(content);
											infowindow.open(gmap, marker);
										};
									})(marker, content, infowindow),
								);
							}
						}
					});
				});
			}
		});
	}
}

Save everything and create a new test shortcode with one or more PlaceIds in it. If you’re going to add multiple PlaceIds, separate them by commas.

You can use the Google PlcaeId finder to find PlaceIds.

  • San Francisco: ChIJ32vwFWSAhYAR8wx7vbWoSPA
  • London: ChIJ4abvVDwbdkgRDZVFR7A6Bcc
Example GMap shortcode
A GMap shortcode with two map markers
Click on the map markers

You can see in the code where we build the content string for the Info Window HTML, and you can output result to the JS console to see a list of all the interesting stuff you can pick up for the InfoWindow.

Custom marker icons

In our PHP file, we’ve got the following filter…

// Define custom map marker icons (optional).
$custom_marker_icons = apply_filters('wptgmap_custom_marker_icons', []);

This is so we can create custom (legacy) map marker definitions on a per-project basis. These are an associative array that describe Google legacy map markers. So if you add the following to your child theme’s functions.php

function custom_gmap_marker_icons($custom_marker_icons)
{
    $custom_marker_icons = [
        'red'   => [
            'path'         => 'M172.3 501.7C27 291 0 269.4 0 192 0 86 86 0 192 0s192 86 192 192c0 77.4-27 99-172.3 309.7-9.5 13.8-29.9 13.8-39.5 0z',
            'fillColor'    => '#FF0000',
            'fillOpacity'  => 0.6,
            'anchor'       => [192, 512],
            'strokeWeight' => 1.5,
            'strokeColor'  => '#00000088',
            'scale'        => 0.05,
        ],
        'green' => [
            'path'         => 'M172.3 501.7C27 291 0 269.4 0 192 0 86 86 0 192 0s192 86 192 192c0 77.4-27 99-172.3 309.7-9.5 13.8-29.9 13.8-39.5 0z',
            'fillColor'    => '#00FF00',
            'fillOpacity'  => 0.6,
            'anchor'       => [192, 512],
            'strokeWeight' => 1.5,
            'strokeColor'  => '#00000088',
            'scale'        => 0.05,
        ],
        'blue'  => [
            'path'         => 'M172.3 501.7C27 291 0 269.4 0 192 0 86 86 0 192 0s192 86 192 192c0 77.4-27 99-172.3 309.7-9.5 13.8-29.9 13.8-39.5 0z',
            'fillColor'    => '#0000FF',
            'fillOpacity'  => 0.6,
            'anchor'       => [192, 512],
            'strokeWeight' => 1.5,
            'strokeColor'  => '#00000088',
            'scale'        => 0.05,
        ],
    ];

    return $custom_marker_icons;
}
add_filter('wptgmap_custom_marker_icons', 'custom_gmap_marker_icons');

We’ve defined three markers here, called “red”, “green” and “blue. They’re just simple arrays that help Google Maps create small SVG elements on-the-fly. I’ve used the path element from Font Awesome 5 classic map marker.

We can now do this with our shortcodes…

gmap shortcode with custom marker icons
Each place id can have a custom map marker icon

So we specify the place id and custom map marker icon name, separated with a colon. If you don’t specify a custom map marker name, the map will use the default Google marker.

That’s all there is to it. Generate some HTML, call the Google Maps JavaScript with a callback to your own JS function, then initialise each map with at least a center and zoom property.

Happy mapping! 😎 👍

Like This Tutorial?

Let us know

WordPress plugins for developers

Leave a comment