30% OFF FLASH SALE on AVF Pro – HURRY! Sale Ends in:

0

Days

:

0

Hrs

:

0

Mins

:

0

Secs

Shop now!
WPLake > Learning Hub > ACF Google and Open Street Map Fields: Complete Guide with Code Snippets
  • Deutsch
  • Español
  • Français
  • Italiano

ACF Google and Open Street Map Fields: Complete Guide with Code Snippets

Learn how to use ACF's Google and OSM map fields, manage API keys, display maps, handle multiple markers, and set custom marker icons.
Google maps offer context

Key Points at a Glance

  1. ACF Google Map Field: Allows adding a single marker on Google Maps with coordinates and zoom level stored in metadata.
  2. API Key Setup: Google Maps fields in ACF require an API key, which can be set via custom code or ACF Options page for easier management.
  3. Multiple Markers Field: ACF Multiple Google Markers field allows multiple markers on a single map, unlike the default Google Map field.
  4. OSM Field Add-On: The ACF OpenStreetMap field supports multiple markers, requires no API key, and integrates with Leaflet.js.
  5. ACF Map Display: Displaying Google Maps requires enqueuing and setting up the Google Maps JS library or can be simplified by using the Advanced Views Framework.
  6. Querying Map Data: ACF map fields store data as serialized arrays, making direct querying limited.

Table of Contents

The ACF Google Map is a multi-part field type within the Advanced Custom Fields plugin, categorized under the "Advanced" group. It allows editors to locate a specific area using the map widget, place a marker, save the marker's coordinates, and zoom level into metadata.

However, the ACF plugin doesn't provide a dedicated field type for OpenStreetMap (OSM) by default, and the Google Map field is limited to storing only a single marker.

Fortunately, the active ACF community has developed several add-ons that introduce new field types, such as the ACF OSM Field and ACF Multiple Google Markers Field.

These field types function similarly to the default Google Map field, allowing editors to interact with a map, add markers, and store multiple coordinates in object metadata.

Iframe oEmbed alternative

Aside from using a Map field in plugins like ACF, there's a simpler alternative for embedding maps in WordPress: using iframes. Both Google Maps and OSM offer an iframe oEmbed option, allowing you to visit their websites, select a location, and generate a code to embed directly on your site.

You can also set up a marker to highlight a specific spot on the map. For a step-by-step guide, read our on How to Embed a Map in WordPress using Iframe in our dedicated article.

This method is straightforward and doesn’t require any plugins, making it perfect for one-time map embeds, like on a contact page. However, it’s not ideal for Custom Post Types (CPT) or scenarios requiring multiple embeds, as it lacks a built-in map selection widget for editors.

In case you've opted for the Map field usage, let's go on with the ACF Map fields review.

ACF Google Map field UI

google map field settings
General settings of the ACF Google Map field type.
Editors define a marker, and the field stores its coordinates along with the zoom level in the metadata.

The Google Map widget allows you to define an address not only by drag-and-drop scrolling but also by entering the desired address into the autocomplete input field.

Note: Keep in mind that the 'autocomplete' address feature works only for editors, so if you need, for example, the autocomplete address for WooCommerce checkout, this field doesn't fit directly.

ACF Map field-related addons

ACF addons can be an important part of the workflow, and there are several addons especially useful for the ACF Map field:

  • ACF Google Map Multi Markers plugin provides an ACF field type that allows placing multiple markers on a single Google Map.
  • ACF OSM add-on brings Open Street map integration. This plugin adds the new OSM field type, which allows you to embed customizable maps in your WordPress site, leveraging OpenStreetMap and Leaflet.
  • ACF Yandex map addon allows you to integrate Yandex Maps into Advanced Custom Fields.
  • ACF City Selector adds a new field type to Advanced Custom Fields, allowing users to select cities based on country and province/state.
  • ACF Tooltip enhances the user experience by managing lengthy instruction texts in ACF fields. Instead of cluttering the edit screen, this plugin allows you to add tooltips to field labels, keeping the interface clean and space-efficient.
  • WPGraphQL for ACF allows you to expose ACF fields and field groups to GraphQL, enabling seamless querying of ACF data within your GraphQL queries.

Map fields in other meta-field plugins

If you're exploring alternatives to ACF, similar Map fields can be found in the Meta Box plugin, which supports both Google Maps and OpenStreetMap fields. On the other hand, the Pods plugin does not provide a direct alternative for map fields.

For a deeper look at the differences between ACF and Meta Box plugins, check out our detailed comparison of the best meta field plugins to understand which one suits your needs best.

1. Google vs. OpenStreetMap

The primary decision to make when using ACF Map fields is choosing which map vendor to use.

Google Maps offers a familiar and feature-rich interface, but it requires an API key and follows a freemium pricing model. This means that while Google provides some free usage credits each month, any usage beyond this limit incurs charges, which can add up quickly, especially for high-traffic sites.

OpenStreetMap, on the other hand, is an open-source, completely free option with no usage limits. It’s an ideal choice if you have no specific requirements tied to Google’s ecosystem. OSM is also a privacy-friendly alternative, as it doesn’t collect user data like Google Maps.

For more insights into the differences between these vendors, check out our comprehensive guide on Embedding Maps in WordPress.

2. ACF Google Map field essentials

2.1) Field settings

  1. Center Lat/Lng
    This setting allows you to set the default center of the map by entering latitude and longitude coordinates. This is particularly useful for setting a specific starting point on the map.
  2. Zoom Level
    This setting controls the initial zoom level of the map when it's loaded. Higher values mean closer zoom, while lower values provide a wider view of the area.
  3. Height
    The height setting defines the vertical size of the map widget in the editor. This is purely for the backend and doesn't affect the frontend display.

The Google Map field settings in ACF do not provide an option to enter an API key directly. The reason for this is that the API key should be consistent and shared across all Google Map fields on the site to ensure seamless functionality and easy management.

To set up the API key, you'll need to add a custom code snippet. We'll cover it separately in a short time.

2.2) Storage format and supported field locations

The data from the ACF Google Map field is stored as a serialized array in the WordPress metadata. This array contains detailed information about the chosen location, including coordinates, address, and additional geographical data. Here’s an example of what the stored data looks like:

a:9:{
  s:7:"address";s:32:"XVJP+GV Andasibefasy, Madagascar";
  s:3:"lat";d:-20.0186875;
  s:3:"lng";d:45.8871875;
  s:4:"zoom";i:5;
  s:8:"place_id";s:100:"EiFYVkpQK0dWLCBBbmRhc2liZWZhc3ksIE1hZGFnYXNjYXIiJjokCgoNBWQR9BVD1FkbEAoaFAoSCeXzmYlI6uMhEW5ofOqrJsUx";
  s:4:"city";s:12:"Andasibefasy";
  s:5:"state";s:14:"Amoron'i Mania";
  s:7:"country";s:10:"Madagascar";
  s:13:"country_short";s:2:"MG";
}

Supported field locations

The ACF Map field can be applied across various locations in WordPress, allowing you to store geographical data consistently, regardless of where the field is used:

  1. Post Objects (Posts, Pages, Media Attachments, Custom Post Types, WooCommerce Products):
    The ACF Map field can be attached to any post object, including standard posts, pages, media attachments, and custom post types (CPTs). The map data, including coordinates, address, and other location details, are stored in the wp_postmeta table, linked to the respective post or custom post type item.
  2. Options Page:
    The Map field can also be used on ACF Options Pages, storing location data in the wp_options table. This makes the map settings accessible globally, which is particularly useful for site-wide features such as a store locator or a central business address.
  3. User Profiles:
    Adding the Map field to user profiles allows you to capture location data specific to each user. The map coordinates and other location details are stored in the wp_usermeta table, connected to the relevant user. This setup can be useful for user-specific location data, like user-reported locations or delivery addresses.
  4. Terms (e.g., Post Categories, Tags, Custom Taxonomies):
    The Map field can be attached to terms such as post categories, tags, or custom taxonomies. The stored map data is saved in the wp_termmeta table, linked to the specific term, making it a great option for location-based categorization, such as event types, regions, or areas.
  5. ACF Gutenberg Block:
    The ACF Map field can be integrated into custom ACF Gutenberg blocks. When used in a block, the map data is stored within the post_content as part of the block's JSON data structure in the wp_posts table. This makes it an ideal choice for dynamic content placement, such as embedding maps directly within block layouts.

2.3) Return value format

The ACF Map field returns an array containing all the available location data stored in the database. This array provides comprehensive information about the selected place, which includes coordinates, address details, and other metadata. Below is an example of the returned array with all available keys:

$location_info = [
    'address'       => 'XVJP+GV Andasibefasy, Madagascar',
    'lat'           => -20.0186875,
    'lng'           => 45.8871875,
    'zoom'          => 5,
    'place_id'      => 'EiFYVkpQK0dWLCBBbmRhc2liZWZhc3ksIE1hZGFnYXNjYXIiJjokCgoNBWQR9BVD1FkbEAoaFAoSCeXzmYlI6uMhEW5ofOqrJsUx',
    'city'          => 'Andasibefasy',
    'state'         => 'Amoron\'i Mania',
    'country'       => 'Madagascar',
    'country_short' => 'MG'
];

2.4) Google API key

To use the Google Map field in ACF, you need to register a Google Maps API key. Follow the official Google tutorial to get your API key. Once you have the key, you can register it with ACF in one of the following ways:

Using a common map field filter

add_filter('acf/fields/google_map/api', function ( array $api ): array {
    $api['key'] = 'YOUR_API_KEY'; // Replace 'YOUR_API_KEY' with your actual key
    return $api;
});

Using a global setting

add_action('acf/init', function() {
    acf_update_setting('google_api_key', 'YOUR_API_KEY'); // Replace 'YOUR_API_KEY' with your actual key
});

In both cases, adding the code to the functions.php file will hardcode the API key, which is quick but not easily editable.

Recommended approach: adding the API Key via the ACF Options page

For greater flexibility, it's recommended to add a text field to the ACF Options page. This approach allows site administrators or editors to update the API key directly from the WordPress admin panel without editing code. If you opt for this setup, your code snippet will look like this:

add_filter('acf/fields/google_map/api', function ( array $api ): array {
    $api['key'] = get_field('my_text_api_key_field', 'option'); // Fetches the API key from a text field on the Options page
    return $api;
});

3. ACF Multiple Google Map Markers field

The ACF Multiple Google Markers addon introduces a new field type specifically designed for handling multiple markers on a single Google map. This field type comes with the same settings as the standard ACF Google Map field, such as center latitude/longitude, zoom level, and height.

However, it adds an important additional setting: the 'Max Pins' option, which allows you to define the maximum number of markers that can be placed on the map.

Note: This field type doesn't automatically appear within your usual field groups. To select it in the field type browser popup, type "map" in the search box, and choose the Google Maps (Multiple Markers) from the search results.

ACF Multiple Google Map Markers field settings
The multiple google map markers field type comes with the same settings as the standard ACF Google Map field.
ACF multiple google map markers field ui
Using this field you can add multiple markers to the google map.

Return value format

The ACF Multiple Google Markers field returns an array of location data. Each entry in the array corresponds to a single marker and includes all relevant location details. Here’s an example of what the returned data looks like:

$locations = [
    [
        'address'       => '9QCW+J5 Zlynets, Rivne Oblast, Ukraine',
        'lat'           => 50.371516139562,
        'lng'           => 25.79547825224,
        'zoom'          => 7,
        'place_id'      => 'GhIJ_7ft1o0vSUARfC1Dd6TLOUA',
        'city'          => 'Zlynets',
        'state'         => 'Rivne Oblast',
        'post_code'     => '35652',
        'country'       => 'Ukraine',
        'country_short' => 'UA'
    ],
    [
        'address'       => 'XVG6+V9 Polonycheve, Zhytomyr Oblast, Ukraine',
        'lat'           => 50.977227809162,
        'lng'           => 27.86090793974,
        'zoom'          => 7,
        'place_id'      => 'GhIJ-t7wzBV9SUARN17LdWTcO0A',
        'city'          => 'Polonycheve',
        'state'         => 'Zhytomyr Oblast',
        'post_code'     => '11210',
        'country'       => 'Ukraine',
        'country_short' => 'UA'
    ]
];

4. ACF Open Street Map field

The ACF OpenStreetMap addon introduces a new field type specifically for OpenStreetMap. This field is highly flexible and supports multiple markers by default.

Key Benefits:

  1. No API keys required:
    Unlike Google Maps, the OSM field does not require API keys, simplifying the setup process and eliminating associated costs.
  2. Built-in leaflet support:
    The addon comes with built-in support for Leaflet, a popular open-source mapping library. This integration allows you to display the map on the front end without needing to write custom JavaScript code.
ACF OSM map field addon
Similar to the Google map field, editors define the locations using the OSM map widget.

Storage format

Unlike Google Maps, the OSM response provides much richer location data. Below is an example of the data stored in the metadata for OSM field:

$result = [
    'lat'           => 53.4095319,
    'lng'           => 9.041748,
    'zoom'          => 9,
    'markers'       => [
        [
            'label'          => 'Friedrichsdorf, 27442 Gnarrenburg Friedrichsdorf, Lower Saxony Germany',
            'default_label'  => 'Friedrichsdorf, 27442 Gnarrenburg Friedrichsdorf, Lower Saxony Germany',
            'lat'            => 53.4095148,
            'lng'            => 9.0394558,
            'geocode'        => [
                [
                    'name'        => 'Friedrichsdorf, Gnarrenburg, Landkreis Rotenburg (Wümme), Lower Saxony, 27442, Germany',
                    'html'        => 'Friedrichsdorf, 27442 Gnarrenburg Friedrichsdorf, Lower Saxony Germany',
                    'center'      => [
                        'lat' => 53.408440031458,
                        'lng' => 9.0407555352178
                    ],
                    'bbox'        => [
                        '_southWest' => [
                            'lat' => 53.408440031458,
                            'lng' => 9.0407555352178
                        ],
                        '_northEast' => [
                            'lat' => 53.408440031458,
                            'lng' => 9.0407555352178
                        ]
                    ],
                    'properties'  => [
                        'place_id'      => 164558606,
                        'licence'       => 'Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright',
                        'osm_type'      => 'way',
                        'osm_id'        => 29346103,
                        'lat'           => 53.4084400314582,
                        'lon'           => 9.040755535217812,
                        'class'         => 'highway',
                        'type'          => 'residential',
                        'place_rank'    => 26,
                        'importance'    => 0.10001,
                        'addresstype'   => 'road',
                        'name'          => 'Friedrichsdorf',
                        'display_name'  => 'Friedrichsdorf, Gnarrenburg, Landkreis Rotenburg (Wümme), Lower Saxony, 27442, Germany',
                        'address'       => [
                            'road'        => 'Friedrichsdorf',
                            'hamlet'      => 'Friedrichsdorf',
                            'town'        => 'Gnarrenburg',
                            'county'      => 'Landkreis Rotenburg (Wümme)',
                            'state'       => 'Lower Saxony',
                            'ISO3166-2-lvl4' => 'DE-NI',
                            'postcode'    => '27442',
                            'country'     => 'Germany',
                            'country_code' => 'de'
                        ],
                        'boundingbox'  => [
                            0 => 53.4058577,
                            1 => 53.4149309,
                            2 => 9.0386775,
                            3 => 9.0460674
                        ]
                    ]
                ]
            ],
            'uuid'           => 'marker_6540e48dce3e0'
        ]
    ],
    'address'        => 'Friedrichsdorf, 27442 Gnarrenburg Friedrichsdorf, Lower Saxony Germany',
    'layers'         => [
        'OpenStreetMap.Mapnik'
    ],
    'version'        => '1.5.6',
    'center_lat'     => 53.4095319,
    'center_lng'     => 9.041748
];

We'll review how to display the OSM map on the front in detail in the Code snippets chapter below.

5. Use cases of the ACF Map field

ACF Map is a versatile field type and can be used in various use cases, including:

  1. Contact page
    Adding a map to your contact page is one of the most common uses of the ACF Map field.
  2. CPT item location (e.g., Events or Real estate listings)
    For Custom Post Types like Events, the ACF Map field can be used to display the event location on the map.
  3. Taxonomy Terms (e.g., Cities)
    The ACF Map field can also be used with taxonomy terms to display locations related to categories or tags. This is useful for sites with geographical content or when categorizing posts based on locations.
  4. User Profiles
    Integrate the ACF Map field into user profiles to allow users to specify their locations. This can be particularly useful for sites where user location is relevant, such as in directories, networking platforms, or service provider listings.

6. ACF Map fields support in Themes and Builders

ACF is one of the most popular WordPress plugins, and it's supported by many page builders right out of the box. However, most of these integrations are basic, providing limited control over layout or lacking support for advanced field types like Gallery or Repeater.

To achieve universal compatibility with any theme and page builder while gaining advanced control over your layout, consider using the Advanced Views Framework or custom code snippets as discussed in the following chapter.

Here's a snapshot of the built-in features offered by some of the most popular WordPress themes and page builders for displaying ACF Map fields:

Theme/BuilderACF-related featureACF Map type support
Astra-no
AvadaDynamic Contentno (not declared)
BeaverACF Modulepartial (only Google)
BricksDynamic datano (not declared)
DiviACF Modulepartial (only Google)
ElementorDynamic Tagsno (not declared)
GeneratePress-no
Gutenberg-no
KadenceDynamic Contentpartial (only Google)
OceanWP-no
Visual ComposerDynamic Contentno (not declared)
WPBackery-no

While this list may seem brief, many themes come with their own page builders. Check your theme’s documentation for guidance on displaying ACF Map fields, or refer to the universal methods outlined below for a more flexible approach.

7. All WordPress CPT items on a single map

The straightforward use of Map fields typically involves displaying a location map on a single template (such as a page or CPT). However, you may also want to show all items of a specific CPT on a single map page.

Both Google Maps and OpenStreetMap (OSM) fields lack a built-in feature to display multiple items on one map. Thankfully, because these map fields store location data in the metadata, you can achieve this with some custom development.

To do this, you'll need to query the CPT items, gather their location data, and pass it to the frontend. You will then use custom JavaScript to initialize the map and add the locations as markers. This approach allows you not only to display all items on a single map but also to show a custom popup with details when clicking on a marker.

While it may seem complex, the process is quite manageable. For a detailed step-by-step guide, refer to the How to display all WordPress CPT items on a single map section in our Embed map in WordPress article.

8. Code snippets for the ACF Map Field

ACF functions and their responses are essentially wrappers around built-in WordPress features. This means that to load and display field values, you need to be familiar not only with ACF but also with core WordPress classes and functions. Additionally, writing markup from scratch and manually escaping data can be time-consuming.

To streamline development, you can use the Advanced Views Framework. This WordPress framework offers smart templates for the front end, simplifying post queries and template creation. It generates default markup and automatically loads the escaped data into the template, allowing you to focus on the layout itself.

Unlike drag-and-drop page builders, smart templates in Advanced Views are modular and based on a template engine (Twig or Blade), providing full control over the markup and helping you avoid the pitfalls of global CSS and JavaScript files. The framework natively supports all ACF field types, along with other data sources.

Below are examples for both Advanced Views and custom code:

8.1) Setting up the Google map API key

Here is a copy of the snippet mentioned in the Essentials chapter for quick reference. You can add this snippet to the functions.php file of your theme:

add_filter('acf/fields/google_map/api', function ( array $api ): array {
    $api['key'] = 'YOUR_API_KEY'; // Replace 'YOUR_API_KEY' with your actual key
    return $api;
});

8.2) Loading and displaying the Google map

Unlike the OSM map field addon, the built-in Google Map field does not assist with front-end display. This means you need to enqueue the Google Maps library and initialize the map instance yourself. A simpler approach is to use the Advanced Views Framework, which handles these tasks for you.

Below are examples for both Advanced Views and custom code:

Using Advanced Views Framework

Complete the following steps:

  1. Install the Advanced Views Framework:
    Begin by installing and activating the AVF plugin on your WordPress site.
  2. Create a View:
    Set up a new View within AVF and select the target Google Map field that you want to display.
  3. Publish the View:
    Click the "Publish" button to create the View.
  4. Copy the Shortcode:
    From the right sidebar, copy the generated shortcode.
  5. Paste the Shortcode:
    Paste this shortcode into the desired location on your page or post.

That's it! AVF will automatically handle retrieving the field’s data, enqueuing the Google Maps script, and initializing the map instance for you. If needed, you can customize the generated View template to add headings or other elements.

Loading from different locations: To load a field from different locations (e.g., user profile), use the object-id shortcode argument.

In the Pro version, you can also define a custom map marker, display the location data as text, and turn any View into a custom Gutenberg block!

Using custom code

To display the Google Map manually, you'll need to load the marker data using the ACF get_field function, pass the location data to the front end, and initialize the map instance yourself. Don't forget about security, and escape the output as shown below.

For the markup creation, you can use the following code:

<?php
// todo
// Retrieve the Google Map field value from the current post
$mapInfo = get_field('your_map_field_name');
// Retrieve the Google Map field value from a specific post by ID
$mapInfo = get_field('your_map_field_name', 10);
// Retrieve the Google Map field value from the options page
$mapInfo = get_field('your_map_field_name', 'option');
// Retrieve the Google Map field value from a user by ID
$mapInfo = get_field('your_map_field_name', 'user_1');
// Retrieve the Google Map field value from the category term with ID 2
$mapInfo = get_field('your_map_field_name', 'category_2');
// Retrieve the Google Map field value from the custom genre term with ID 3
$mapInfo = get_field('your_map_field_name', 'genre_3');

$zoom = $mapInfo['zoom'] ?? '16';
$lat = $mapInfo['lat'] ?? '';
$lng = $mapInfo['lng'] ?? '';

// zoom level - gets from every specific map (when admins zoom out and saves a page, the zoom is also saved)
printf(
    '<div class="my-map" style="width:100%%;height:400px;" data-zoom="%s">',
    $zoom
);

printf(
    '<div class="my-map__marker" data-lat="%s" data-lng="%s"></div>',
    esc_attr($lat),
    esc_attr($lng)
);

echo "</div>";

Next, enqueue the Google Maps script by adding the following code to your theme's functions.php file. This script will retrieve the registered API key (as described previously) and enqueue the Google Maps API script:

<?php

add_action('wp_footer', function () {
    $apiData = apply_filters('acf/fields/google_map/api', []);

    $key = $apiData['key'] ?? '';

    $key = !$key ?
        acf_get_setting('google_api_key') :
        $key;

    if (!$key) {
        return;
    }

    wp_enqueue_script(
        'google-maps',
        sprintf('https://maps.googleapis.com/maps/api/js?key=%s&callback=googleMapsCallback', $key),
        null,
        true
    );
});

Pro tip: For better performance, we recommend adding some checks, to make sure this only loads on pages where the map exists. E.g. Limit it to a single page by URL with is_page, or to the CPT type with is_singular.

Now it's time to create the JS code which will initialize our map. You can place the code into the script.js file of your theme. Note that we define the googleMapsCallback window property, which is used in the script enqueue as well. This is how we connect the script with the callback function for initializing the map.

class Map {
    constructor(element) {
        this.element = element
        this.map = null
        this.mapMarkers = []
    }

    readMarkers() {
        // TODO replace the selector if you've changed it in the markup
        this.element.querySelectorAll('.my-map__marker').forEach((markerElement) => {
            let lat = markerElement.dataset.hasOwnProperty('lat') ?
                markerElement.dataset['lat'] :
                0
            let lng = markerElement.dataset.hasOwnProperty('lng') ?
                markerElement.dataset['lng'] :
                0

            this.mapMarkers.push({
                lat: parseFloat(lat),
                lng: parseFloat(lng),
            })

            markerElement.remove()
        })
    }

    createMap() {
        let mapArgs = {
            zoom: parseInt(this.element.dataset.hasOwnProperty('zoom') ?
                this.element.dataset['zoom'] :
                16),
            mapTypeId: window.google.maps.MapTypeId.ROADMAP,
        }
        this.map = new window.google.maps.Map(this.element, mapArgs)
    }

    createMarkers() {
        this.mapMarkers.forEach((marker) => {
            new window.google.maps.Marker({
                position: marker,
                map: this.map,
            })
        })
    }

    centerMap() {
        // Create map boundaries from all map markers.
        let bounds = new window.google.maps.LatLngBounds()

        this.mapMarkers.forEach((marker) => {
            bounds.extend({
                lat: marker.lat,
                lng: marker.lng,
            })
        })

        if (1 === this.mapMarkers.length) {
            this.map.setCenter(bounds.getCenter())
        } else {
            this.map.fitBounds(bounds)
        }
    }

    init() {
        if (!window.hasOwnProperty('google') ||
            !window.google.hasOwnProperty('maps') ||
            !window.google.maps.hasOwnProperty('Map') ||
            !window.google.maps.hasOwnProperty('Marker') ||
            !window.google.maps.hasOwnProperty('LatLngBounds') ||
            !window.google.maps.hasOwnProperty('MapTypeId') ||
            !window.google.maps.MapTypeId.hasOwnProperty('ROADMAP')) {
            console.log('Google maps isn\'t available')
            return
        }

        // before the map initialization, because during creation HTML is changed
        this.readMarkers()
        this.createMap()
        this.createMarkers()
        this.centerMap()
    }
}

class Maps {
    constructor() {
        this.isMapsLoaded = false
        this.mapsToInit = []

        // TODO change to yours if you've defined own callback (for https://maps.googleapis.com/maps/api...)
        window.googleMapsCallback = this.mapsLoadedCallback.bind(this)

        'loading' !== document.readyState ?
            this.setup() :
            window.addEventListener('DOMContentLoaded', this.setup.bind(this))
    }

    setup() {
        const observer = new MutationObserver((records, observer) => {
            for (let record of records) {
                record.addedNodes.forEach((addedNode) => {
                    this.addListeners(addedNode)
                })
            }
        })
        observer.observe(document.body, {
            childList: true,
            subtree: true,
        })

        this.addListeners(document.body)
    }

    mapsLoadedCallback() {
        this.isMapsLoaded = true

        this.mapsToInit.forEach((map) => {
            map.init()
        })

        this.mapsToInit = []
    }

    addListeners(element) {
        if (Node.ELEMENT_NODE !== element.nodeType) {
            return
        }

        // TODO replace the selector if you've changed it in the markup

        element.querySelectorAll('.my-map').forEach((mapElement) => {
            let map = new Map(mapElement)

            if (!this.isMapsLoaded) {
                this.mapsToInit.push(map)

                return
            }

            map.init()
        })
    }

}

new Maps()

That's it. Now you can visit the page and see the Google map as a part of your page.

8.3) Loading and displaying the OpenStreetMap

Using Advanced Views Framework

AVF supports the OSM map field type out of the box, so the steps will be exactly the same, as with the Google Map case:

  1. Install the Advanced Views Framework:
    Begin by installing and activating the AVF plugin on your WordPress site.
  2. Create a View:
    Set up a new View within AVF and select the target OSM field that you want to display.
  3. Publish the View:
    Click the "Publish" button to create the View.
  4. Copy the Shortcode:
    From the right sidebar, copy the generated shortcode.
  5. Paste the Shortcode:
    Paste this shortcode into the desired location on your page or post.

That's it! AVF will automatically handle retrieving the field’s data, enqueuing the Leaflet script, and initializing the map instance for you. If needed, you can customize the generated View template to add headings or other elements.

Using custom code

The field has 3 Return formats, which control the ACF get_field function response. You should choose the Return format, which closer fits your goals. Keep in mind that, regardless of the return format setting, the location data is always stored as a serialized array in the database.

Below, we review displaying depending on the return format:

Leaflet JS return format

When this format is chosen, to display the map on the front end, you simply call:

// Echo the OSM field value from the current post
echo get_field('your_map_field_name');
// Echo the OSM field value from a specific post by ID
echo get_field('your_map_field_name', 10);
// Echo the OSM field value from the options page
echo get_field('your_map_field_name', 'option');
// Echo the OSM field value from a user by ID
echo get_field('your_map_field_name', 'user_1');
// Echo the OSM field value from the category term with ID 2
echo get_field('your_map_field_name', 'category_2');
// Echo the OSM field value from the custom genre term with ID 3
echo get_field('your_map_field_name', 'genre_3');

It'll print the necessary markup, and the addon will enqueue leaflet.js and render the map without needing additional JavaScript code.

Iframe return format

This option changes the get_field function response and returns the iframe HTML. To display the map, use:

// Echo the OSM field value from the current post
echo get_field('your_map_field_name');
// Echo the OSM field value from a specific post by ID
echo get_field('your_map_field_name', 10);
// Echo the OSM field value from the options page
echo get_field('your_map_field_name', 'option');
// Echo the OSM field value from a user by ID
echo get_field('your_map_field_name', 'user_1');
// Echo the OSM field value from the category term with ID 2
echo get_field('your_map_field_name', 'category_2');
// Echo the OSM field value from the custom genre term with ID 3
echo get_field('your_map_field_name', 'genre_3');

Raw data return format

In this case, you’ll need to manually enqueue the Leaflet or another mapping library and initialize the instance according to the library's documentation.

8.4) Setting a custom Google marker icon

By default, the Google Map displays a standard red marker icon. In some cases, it may be useful to use a custom marker icon instead.

Custom markers can vary in color and shape, adding a unique touch to your website.

Using Advanced Views Framework

The AVF: Pro edition allows you to choose a custom marker icon directly in the field settings. For more details, visit the AVF Google Map documentation.

Using custom theme code

In this case, we need to amend the map script. In the createMarkers method, while the marker creation we can pass the icon property (image URL), which will be used instead of the default markup:

    createMarkers() {
        this.mapMarkers.forEach((marker) => {
            new window.google.maps.Marker({
                position: marker,
                map: this.map,
                // todo replace with the path to your marker icon
                icon: 'https://yoursite/your-icon.png',
            })
        })
    }

8.5) Setting up a custom OSM marker icon

In case you're using a map field from the OSM addon, we've no control over the JS script, so we'll need to replace a marker icon on the server side.

Using Advanced Views Framework

OpenStreetMap field is supported by AVF, and the Pro version allows to set a custom marker icon right in the field settings. For more details, visit the AVF OSM field documentation.

Using custom theme code

While the addon doesn't provide a separate setting for customizing the icon, we can achieve it by using the following trick:

add_action( 'wp_footer', function () {
	// todo put your source here
	$icon = '<img class="custom-marker-icon" src="your_icon_source.png">';

	// addslashed to avoid breaking the JSON code.
	$new_part = sprintf( '"marker":{"html":"%s",', addslashes( $icon ) );

	global $wp_scripts;
	$data = $wp_scripts->get_data( 'acf-osm-frontend', 'data' );

	$data = str_replace( '"marker":{"html":false,', $new_part, $data );
	$wp_scripts->add_data( 'acf-osm-frontend', 'data', $data );

	// it can be included as inline 'style=' for the icon, but for some reason it ignores the 'transform' property
	// maybe Leaflet.js overrides or removes it.
	echo '<style>.custom-marker-icon{position:absolute;bottom:50%;left:50%;transform: translateX(-50%);}</style>';
} );

This code can be added to the functions.php of your theme. It replaces the default icon with a custom one and prints the basic CSS to style it.

8.6) Updating the Google map field programmatically

To update the Google map field programmatically, you can use the ACF update_field function. Since this field stores items in an array, the data you pass must be an array with specific keys.

add_action('acf/init', function() {
    // Define the map data
    $map_data = array(
        'address'      => 'XVJP+GV Andasibefasy, Madagascar',
        'lat'          => -20.0186875,
        'lng'          => 45.8871875,
        'zoom'         => 5,
        'place_id'     => 'EiFYVkpQK0dWLCBBbmRhc2liZWZhc3ksIE1hZGFnYXNjYXIiJjokCgoNBWQR9BVD1FkbEAoaFAoSCeXzmYlI6uMhEW5ofOqrJsUx',
        'city'         => 'Andasibefasy',
        'state'        => "Amoron'i Mania",
        'country'      => 'Madagascar',
        'country_short'=> 'MG'
    );

    // Update the Google Map field
    update_field('my_map_field', $map_data, 1); // Replace 'my_map_field' with your Google Map field key, and 1 with the target post ID
});

If you need to update the Map field on a user or term object, you must add the appropriate prefix, just as shown in the "loading" field value section.

Note: You should call the update_field function inside the acf/init action, or in any hooks that happen later.

8.7) Updating the OSM map field programmatically

To update the OpenStreetMap field programmatically, you can use the ACF update_field function. Since this field stores items in an array, the data you pass must be an array with specific keys.

add_action('acf/init', function() {
    // Define the OSM map data
    $osm_map_data = [
        'lat'           => 53.4095319,
        'lng'           => 9.041748,
        'zoom'          => 9,
        'markers'       => [
            [
                'label'          => 'Friedrichsdorf, 27442 Gnarrenburg Friedrichsdorf, Lower Saxony Germany',
                'default_label'  => 'Friedrichsdorf, 27442 Gnarrenburg Friedrichsdorf, Lower Saxony Germany',
                'lat'            => 53.4095148,
                'lng'            => 9.0394558,
                'geocode'        => [
                    [
                        'name'        => 'Friedrichsdorf, Gnarrenburg, Landkreis Rotenburg (Wümme), Lower Saxony, 27442, Germany',
                        'html'        => 'Friedrichsdorf, 27442 Gnarrenburg Friedrichsdorf, Lower Saxony Germany',
                        'center'      => [
                            'lat' => 53.408440031458,
                            'lng' => 9.0407555352178
                        ],
                        'bbox'        => [
                            '_southWest' => [
                                'lat' => 53.408440031458,
                                'lng' => 9.0407555352178
                            ],
                            '_northEast' => [
                                'lat' => 53.408440031458,
                                'lng' => 9.0407555352178
                            ]
                        ],
                        'properties'  => [
                            'place_id'      => 164558606,
                            'licence'       => 'Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright',
                            'osm_type'      => 'way',
                            'osm_id'        => 29346103,
                            'lat'           => 53.4084400314582,
                            'lon'           => 9.040755535217812,
                            'class'         => 'highway',
                            'type'          => 'residential',
                            'place_rank'    => 26,
                            'importance'    => 0.10001,
                            'addresstype'   => 'road',
                            'name'          => 'Friedrichsdorf',
                            'display_name'  => 'Friedrichsdorf, Gnarrenburg, Landkreis Rotenburg (Wümme), Lower Saxony, 27442, Germany',
                            'address'       => [
                                'road'        => 'Friedrichsdorf',
                                'hamlet'      => 'Friedrichsdorf',
                                'town'        => 'Gnarrenburg',
                                'county'      => 'Landkreis Rotenburg (Wümme)',
                                'state'       => 'Lower Saxony',
                                'ISO3166-2-lvl4' => 'DE-NI',
                                'postcode'    => '27442',
                                'country'     => 'Germany',
                                'country_code' => 'de'
                            ],
                            'boundingbox'  => [
                                0 => 53.4058577,
                                1 => 53.4149309,
                                2 => 9.0386775,
                                3 => 9.0460674
                            ]
                        ]
                    ]
                ],
                'uuid'           => 'marker_6540e48dce3e0'
            ]
        ],
        'address'        => 'Friedrichsdorf, 27442 Gnarrenburg Friedrichsdorf, Lower Saxony Germany',
        'layers'         => [
            'OpenStreetMap.Mapnik'
        ],
        'version'        => '1.5.6',
        'center_lat'     => 53.4095319,
        'center_lng'     => 9.041748
    ];

    // Update the OSM map field
    update_field('my_osm_map_field', $osm_map_data, 1); // Replace 'my_osm_map_field' with your OSM map field key, and 1 with the target post ID
});

If you need to update the Map field on a user or term object, you must add the appropriate prefix, just as shown in the "loading" field value section.

Note: You should call the update_field function inside the acf/init action, or in any hooks that happen later.

9. Querying by the ACF Map fields

ACF stores location data as a serialized array in the metadata, which means direct queries by specific fields like 'street' or 'lat' aren't possible. However, you can use the LIKE comparison to match items based on strings inside the array. For example, using LIKE '%Germany%' can help you find all locations in Germany.

However, this way, we can't find a location within some area by coordinates.

Unlike displaying, querying map fields can be more complex because ACF fields store data in various locations depending on the context (posts, users, options pages, etc.). Here's how to handle queries based on where the Map field values are stored:

9.1) By postmeta (Post, Page, Any CPT)

Using Advanced Views Framework:

If you're using the AVF: Pro edition, querying by ACF Map field values is straightforward with Meta Queries. You need to create a Card, choose the target Map field in the Meta Fields tab, and define the desired value. It can be a static id or pointer to another field. Make sure the comparison is equal to Contains and save the Card.

That's it! The framework will take care of the rest.

Using WP_Query

For custom queries, you can use the built-in WP_Query class:

$args = array(
    'post_type' => 'post', // Replace with your custom post type if needed
    'post_status' => 'publish',
    'posts_per_page' => -1, // Retrieve all posts
    'meta_query' => array(
        array(
            'key'   => 'your_map_field', // Replace with your ACF Map field key
            'value' => 'Germany', // put the target city or country here
            'compare' => 'LIKE', // Use LIKE to search within the serialized array
        )
    )
);

// Execute the query
$query = new WP_Query($args);

// Loop through the results
foreach($query->get_posts() as $post){
    // Process each WP_Post object here
    // For example, you might display the post title or other details
    echo '<h2>' . get_the_title($post->ID) . '</h2>';
}

9.2) By termmeta (Terms)

Here we need to employ the WP_Term_Query class:

$args = array(
    'taxonomy'   => 'category', // Replace with your taxonomy
    'meta_query' => array(
        array(
            'key'     => 'your_map_field', // Replace with your custom field key
            'value'   => 'Germany', // put the target city or country here
            'compare' => 'LIKE', // Use LIKE to search within the serialized array
        )
    )
);

// Execute the query
$term_query = new WP_Term_Query($args);

// Loop through the results
foreach ($term_query->get_terms() as $term) {
    // Process each WP_Term object
    // Example: echo $term->name;
}

9.3) By usermeta (user profile)

In this case, we need to use the WP_User_Query class:

$args = array(
    'meta_query' => array(
        array(
            'key'     => 'your_map_field', // Replace with your custom field key
            'value'   => 'Germany', // put the target city or country here
            'compare' => 'LIKE', // Use LIKE to search within the serialized array
        )
    )
);

$user_query = new WP_User_Query($args);

// Loop through the results
foreach ($user_query->get_results() as $user) {
    // Process each WP_User object
    // Example: echo $user->user_login;
}

9.4) Inside ACF Blocks

ACF Blocks save their data as JSON in the post_content. This data cannot be queried directly. However, if you enable the "Save in Meta" feature for ACF Blocks, the field values are also saved in post meta, allowing you to query them similarly to other postmeta fields.

10. Related Map field filters and hooks

ACF offers a variety of filters and hooks that enable you to modify field data and behavior, extending WordPress's core hooks functionality. You can utilize the standard add_action and add_filter functions to attach custom code to these hooks.

Below are some of the most commonly used hooks along with the ACF Map field type:

10.1) acf/fields/google_map/api

Using the acf/fields/google_map/api filter, you can define the Google map API key:

add_filter('acf/fields/google_map/api', function ( array $api ): array {
    $api['key'] = 'YOUR_API_KEY'; // Replace 'YOUR_API_KEY' with your actual key
    return $api;
});

10.2) acf/load_field

The acf/load_field filter allows you to modify the field settings before it is displayed on the editor's screen. This filter is particularly useful for dynamically adjusting the field's configuration, such as setting default values, changing labels, or customizing available options.

add_filter('acf/load_field/name=my_map_field', function (array $field): array {
    // Modify the field settings as needed
    $field['instructions'] = 'Please fill out the map field.';

    return $field;
});

10.3) acf/render_field

The acf/render_field action allows you to add custom HTML before or after a field's input on the editor's screen in the WordPress admin. This can be particularly useful for adding custom-styled text, icons, styles, or additional interactive elements to enhance the field's functionality.

add_action('acf/render_field/key=field_123456789abc', function (array $field): void {
    // Your custom HTML
    echo '<div class="my-custom-class">Custom map-related element</div>';
});

Note: By default, the acf/render_field action is triggered after the field input has been printed. If you need to print your HTML before the input, you should set the priority number to 9 or lower in the third argument of the add_action WordPress function.

10.4) acf/validate_value

Use the acf/validate_value filter to validate the values entered into the field. You can ensure that the entered value meets specific criteria:

add_filter('acf/validate_value/name=my_map_field', function($valid, $value, array $field) {
    if (true !== $valid) {
        return $valid; // Skip validation if there is an existing error
    }

    // Custom validation: Ensure the latitude is greater than a specific value
    $specific_lat = 50.0; // Replace with your specific latitude value
    if ($value['lat'] <= $specific_lat) {
        $valid = 'The latitude must be greater than ' . $specific_lat . '.';
    }

    return $valid;
}, 10, 3);

10.5) acf/validate_save_post

The acf/validate_save_post action allows you to perform custom form validations before saving ACF fields. This is particularly useful when you need to validate one field's value based on another field's input or enforce specific rules across multiple fields.

add_action('acf/validate_save_post', function () {
    // Get the map field values from ACF fields
    $map_1 = $_POST['acf']['field_map_id_1'] ?? [];
    $map_2 = $_POST['acf']['field_map_id_2'] ?? [];

    if (empty($map_1) && empty($map_2)) {
        acf_add_validation_error('field_map_id_1', 'At least one map field must be defined.');
        acf_add_validation_error('field_map_id_2', 'At least one map field must be defined.');
    }
});

Tip: To find the field key, navigate to the ACF field group in the WordPress admin. Click on the Screen Options button at the top right corner of the page, then check the Field Keys option. Once enabled, the field keys will be displayed next to each field name in the group.

10.6) acf/save_post

The acf/save_post action is triggered when the current item (page/product/user) is saved. You can use it to perform any additional actions, e.g. updating related data.

add_action('acf/save_post', function ($post_id) {
    // Retrieve the map field value (replace with your actual map field key)
    $map = get_field('project_map', $post_id); // Field key for map

    if (!$map) {
        return;
    }

    // Get the country from the map field
    $country = $map['country']; // Replace with your actual sub-field key for country

    if (!$country) {
        return;
    }

    // Update the country string field with the country value
    update_field('country_string', $country, $post_id); // Replace 'country_string' with your actual field key for the country string
});

Stuck with development or facing an issue?

WPLake offers affordable on-demand website development and design.

No matter the size of your project - contact us now, and we'll get it done for you!

Get assistance now

FAQ mode

/

Learning mode

  1. Can I bind a custom marker popup to show custom info?

    Yes, map markers can have their own popups with custom info. To achieve this, you'll need to customize the marker creation settings. Refer to the Google Maps library or Leaflet documentation for guidance, depending on the map vendor you are using.

  2. Can I make a location query to find items in a specific area?

    Partially. Location information is stored as a serialized array, so you can query items based on broader criteria like city or country, which are string parts inside the array. However, narrowing the search to specific coordinates is not feasible because it's impossible to directly compare numbers inside serialized arrays in the database.

Was this article helpful?

Totally useless

Slightly helpful

Very helpful

Related articles

Content links (88)

About the Author

Baxter Jones

With over 15 years of experience in the web industry, I specialize in design, user experience, and web best practices. I have a keen eye for detail and thrive on following a structured process in my work. I’m passionate about WordPress, considering it the best innovation since sliced bread. When I’m not at my computer, you’ll find me enjoying time in the garden.

2 Comments

Josh
-
06 Jan, 2024
Thanks for this tip. I needed a way to show a map on custom posts derived from the ACF Map field, without coding (using blocks). This worked. Much appreciated.
Reply
Baxter Jones
-
08 Jan, 2024
Admin
We're glad to hear it.
Thank you for the support.
Reply

    Leave a comment

    Reply to 

    Please be considerate when leaving a comment.

    Not shown publicly


    Got it