BACK TO DIRECTORY

Advanced Custom Fields: Multiple Coordinates

by Jonas Hjalmarsson

2.3
(4 ratings)

A multi-point Google Maps field for Advanced Custom Fields. Click the map to drop a point and drag it to move it. “Edit points” opens the list of points, where a point can be dragged to a new position or removed. Every point is numbered so the stored order is visible — it is the order the value is stored in and the order the line or area is drawn in — and the last change can always be undone.

The plugin reads your existing Google Maps API key from ACF’s global setting (google_api_key), so no extra configuration is needed if you already use ACF Map fields. If you don’t have one set, you can also provide a key via the acfmc_gmaps_key filter.

Originally inspired by the single-point ACF: Coordinates field by Stupid Studio; this plugin extends the idea to multiple points per field.

Licensed under the GNU General Public License v3. See license.txt for details.

Usage

When you create a new custom field with ACF, set the field type to Coordinates map (under Content). The coordinates chooser will then show up when you edit a post with your custom fields.

Adding points. Click anywhere on the map to drop a point. “Add point” drops one in the middle of the map, which is also the way to add points from the keyboard. Drag a point to move it.

Searching for a place. Type a place in the search field and press Enter: the map pans there and a point is dropped on the spot, carrying the name you searched for. Search for “Kalmar slott” and the point is called Kalmar slott, not the street address Google resolves it to. The name is stored with the point and shown in its popup. Points you click straight onto the map have no name — only searched points get one.

Seeing what a point is. Click a marker and a popup shows its number, its latitude and longitude, and its name when it has one. Nothing is looked up when you click; the popup only shows what is already stored.

Editing the points. “Edit points (N)” opens the list of points, and that is where a point is changed:

  • Drag a row by its handle to move the point to another position. The order is not decoration — it is the order the value is stored in, the order get_field() returns and the order the line or area is drawn in, so the map is renumbered and redrawn as soon as you drop the row.
  • The same handle works from the keyboard: tab to it and press the up or down arrow key.
  • Press Remove on a row to delete that point.

Undo. The undo icon takes back the last change — an accidental point, a removed point, a reordered list, or a marker dragged to the wrong place. It goes back up to 30 steps, until the page is reloaded.

Drawing a shape. The first menu says what is drawn through the points: nothing, a Line or an Area. Pick either one and a second menu says how it is drawn:

  • Sharp — straight segments from point to point, in stored order.
  • Smoothed — a Catmull-Rom curve through every point. Google Maps has no curves of its own, so it is drawn as a dense polyline; an area closes the curve back to the first point.
  • Bounding box — the rectangle the points span, ignoring the path between them.

A line is the outline only; an area is filled. So a bounding box drawn as a line is an empty rectangle around the points, and the same box drawn as an area is a filled one. Choosing a shape also reveals a colour picker and a Markers switch, and the map redraws as soon as you change any of them.

The Markers switch. It controls the front end: turn it off and your theme should draw only the line or the area, without the markers — which is the point of it, since a clean route or outline is often what you want. In the editor the markers stay visible, dimmed, because points you cannot see are points you cannot drag, click or reorder. The switch only appears once a shape is chosen; with no shape the markers are all there is to draw.

The rest. The <> icon reveals and selects the raw stored value so it can be pasted into another Coordinates map field, and the i icon lists what the field can do.

Saving works the way it always has: the value is stored as JSON in postmeta, so field groups and values created with earlier versions keep working unchanged.

This plugin does not render anything on the front end. It stores points; drawing them is the theme’s job. ACF’s own [acf field="..."] shortcode returns an empty string for this field type, so the examples below are the starting point — copy one into your theme and adjust it.

Reading the value. get_field() gives you the points in the order they are stored, plus the zoom level and, when a shape was chosen, the shape, how it is drawn, its colour and whether the markers should be drawn with it:

<?php
$values = get_field('*****FIELD_NAME*****');
print_r($values);
/* gives you something like:
    Array
    (
        [coords] => Array
            (
                [0] => Array
                    (
                        [lat] => 57.156363766336
                        [lng] => 16.364327427978
                    )
                [1] => Array
                    (
                        [lat] => 57.159612809986
                        [lng] => 16.370315551758
                        [label] => Kalmar slott
                    )
            )
        [zoom] => 13
        [shape] => line
        [style] => smoothed
        [color] => #c84812
        [markers] =>
    )
*/
?>

label, `shape`, `style`, `color` and `markers` are all optional. A point that was clicked onto the map has no `label`. A value with no `shape` — which is every value saved before version 1.4.0 — draws no line and no area at all, only the points. `style` is `sharp`, `smoothed` or `bbox` and defaults to `sharp`; `color` defaults to `#999999`; `markers` is only written when it is *off*, so a missing `markers` means the markers should be drawn.

Rendering a map. A complete example that draws the markers, unless they are switched off, and whichever shape was chosen, in the chosen colour. It needs your own Google Maps API key:

<?php
$value  = get_field('*****FIELD_NAME*****');
$coords = ( is_array($value) && ! empty($value['coords']) ) ? $value['coords'] : array();

if ( $coords ) :
    $data = array(
        'coords'  => $coords,
        'zoom'    => isset($value['zoom'])  ? (int) $value['zoom'] : 11,
        'shape'   => isset($value['shape']) ? $value['shape']      : 'none',
        'style'   => isset($value['style']) ? $value['style']      : 'sharp',
        'color'   => isset($value['color']) ? $value['color']      : '#999999',
        // markers are drawn unless the value explicitly says otherwise
        'markers' => ! isset($value['markers']) || $value['markers'],
    );
    ?>
    <div id="my-map" style="height:400px"></div>
    <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
    <script>
    (function (data) {
        var map = new google.maps.Map(document.getElementById('my-map'), {
            zoom: data.zoom,
            center: { lat: data.coords[0].lat, lng: data.coords[0].lng }
        });

        var path = data.coords.map(function (c) {
            return new google.maps.LatLng(c.lat, c.lng);
        });

        if (data.markers) {
            data.coords.forEach(function (c, i) {
                new google.maps.Marker({
                    map: map,
                    position: path[i],
                    // label is only there on points added through the search field
                    title: c.label || ('Point ' + (i + 1))
                });
            });
        }

        // 'shape' is what is drawn: nothing, a line, or a filled area.
        // 'style' is how: straight segments, a smoothed curve, or the
        // bounding box of the points. A value with no shape — every value
        // saved before 1.4.0 — draws the points only.
        var area = data.shape === 'area';

        if (data.shape !== 'none' && path.length > 1) {
            if (data.style === 'bbox') {
                var bounds = new google.maps.LatLngBounds();
                path.forEach(function (p) { bounds.extend(p); });
                new google.maps.Rectangle({ map: map, bounds: bounds,
                    strokeColor: data.color, strokeOpacity: 0.9, strokeWeight: 2,
                    fillColor: data.color, fillOpacity: area ? 0.2 : 0 });
            } else if (area) {
                new google.maps.Polygon({ map: map,
                    paths: data.style === 'smoothed' ? smooth(path, true) : path,
                    strokeColor: data.color, strokeOpacity: 0.9, strokeWeight: 2,
                    fillColor: data.color, fillOpacity: 0.35 });
            } else {
                new google.maps.Polyline({ map: map,
                    path: data.style === 'smoothed' ? smooth(path, false) : path,
                    strokeColor: data.color, strokeOpacity: 0.9, strokeWeight: 3 });
            }
        }

        // Google Maps draws straight segments only, so "Smoothed" is a
        // Catmull-Rom curve through the points, emitted as a dense
        // polyline. A closed curve wraps around, so an area has no seam.
        function smooth(points, closed) {
            if (points.length < 3) { return points; }
            var p = closed
                ? [points[points.length - 1]].concat(points, [points[0], points[1]])
                : [points[0]].concat(points, [points[points.length - 1]]);
            var out = [];
            for (var i = 1; i + 2 < p.length; i++) {
                for (var s = 0; s < 16; s++) {
                    var t = s / 16, t2 = t * t, t3 = t2 * t;
                    out.push(new google.maps.LatLng(
                        0.5 * (2 * p[i].lat() + (-p[i-1].lat() + p[i+1].lat()) * t +
                            (2 * p[i-1].lat() - 5 * p[i].lat() + 4 * p[i+1].lat() - p[i+2].lat()) * t2 +
                            (-p[i-1].lat() + 3 * p[i].lat() - 3 * p[i+1].lat() + p[i+2].lat()) * t3),
                        0.5 * (2 * p[i].lng() + (-p[i-1].lng() + p[i+1].lng()) * t +
                            (2 * p[i-1].lng() - 5 * p[i].lng() + 4 * p[i+1].lng() - p[i+2].lng()) * t2 +
                            (-p[i-1].lng() + 3 * p[i].lng() - 3 * p[i+1].lng() + p[i+2].lng()) * t3)
                    ));
                }
            }
            if (!closed) { out.push(points[points.length - 1]); }
            return out;
        }
    })(<?php echo wp_json_encode($data); ?>);
    </script>
<?php endif; ?>

Rendering without a map. If you only want the coordinates as text, no API key and no JavaScript are needed:

<?php
$value = get_field('*****FIELD_NAME*****');

if ( ! empty($value['coords']) ) {
    echo '<ul class="my-coordinates">';
    foreach ( $value['coords'] as $i => $point ) {
        $label = isset($point['label']) ? $point['label'] : '';
        printf(
            '<li>%s`%s, %s`</li>',
            $label ? '<strong>' . esc_html($label) . '</strong> ' : esc_html( ( $i + 1 ) . '. ' ),
            esc_html($point['lat']),
            esc_html($point['lng'])
        );
    }
    echo '</ul>';
}
?>

Both examples work unchanged on values saved by version 1.0: a missing label prints only the coordinates, and a missing shape draws the markers on their own.

Screenshots

The Coordinates map field in the post editor. Click the map to drop a numbered point and drag a point to move it. The toolbar picks what is drawn through the points — here an Area, Smoothed — its colour, and whether the front end draws the markers with it.

The Coordinates map field in the post editor. Click the map to drop a numbered point and drag a point to move it. The toolbar picks what is drawn through the points — here an Area, Smoothed — its colour, and whether the front end draws the markers with it.

"Edit points" opens the list: every point in stored order with its latitude and longitude, the name of any point added through the search field, a drag handle for moving it up or down, and a Remove button per row.

"Edit points" opens the list: every point in stored order with its latitude and longitude, the name of any point added through the search field, a drag handle for moving it up or down, and a Remove button per row.

Adding the field: pick "Coordinates map" under Content in the ACF field type browser.

Adding the field: pick "Coordinates map" under Content in the ACF field type browser.

Plugin Details

Active Installs
10
Total Downloads
3,952
Version
1.4.0
Requires WP
5.0
Requires PHP
7.0
Tested Up To
7.1
Added
2013-10-24
Last Updated
2026-08-15 8:07am GMT

Ratings

5
1
4
0
3
0
2
1
1
2