GoogleMapsV3 CakePHP Helper

This is a helper to generate GoogleMap maps (dynamic and static ones) in your CakePHP views.

Note: Google Maps API V2 is marked "deprecated". API V3 is supposed to be faster and more compatible to mobile browsers. Details
Also new: A googlemaps key is not necessary anymore.

Preamble

You can either dynamically collect the geo data, or you can use my behavior to retreive and store them persistently. I prefer storing them in a table using lat/lng fields as it will make displaying the markers faster (no need to fetch at runtime) – it also makes it possible to make distance queries using radius and such.

Usage

I wanted to keep it as simple as possible (otherwise you could use the maps with plain js right away).

You may set up configs/defaults in your config file:

$config['Google'] = array(
    'zoom' => 6,
    'lat' => 51.1,
    'lng' => 11.2,
    'type' => 'H', // Roadmap, Satellite, Hybrid, Terrain
    'size' => array('width'=>'100%', 'height'=>400),
    'staticSize' => '500x450',
);

Please include the Tools Plugin in your bootstrap with CakePlugin::loadAll() or CakePlugin::load('Tools').

Now you need to add the helper to the controller (or the action):

// globally
public $helpers = array(..., 'Tools.GoogleMapV3');
// OR in the action:
public function map() {
    $this->helpers[] = 'Tools.GoogleMapV3';
    // rest of your code       
}

Since there are many possible ways to include the required javascript file I formed an own method for it:

<?php
// include jQuery >= 1.4
echo $this->Html->script('jquery'); // or wherever it is in your js folder

// include the Google js code
echo $this->Html->script($this->GoogleMapV3->apiUrl());

// OR include it manually without cake (or use your own asset stuff)
echo '<script type="text/javascript" src="'.$this->GoogleMapV3->apiUrl().'"></script>';

You may also let the helper include it automatically – just pass the option "autoScript" => true to the map() method in the next step.

Now we can use it.

Interactive Maps

// init map (prints container)
echo $this->GoogleMapV3->map(array('div'=>array('height'=>'400', 'width'=>'100%')));

// add markers
$options = array(
    'lat' => 48.95145,
    'lng' => 11.6981,
    'icon'=> 'url_to_icon', // optional
    'title' => 'Some title', // optional
    'content' => '<b>HTML</b> Content for the Bubble/InfoWindow' // optional
);
// tip: use it inside a for loop for multiple markers
$this->GoogleMapV3->addMarker($options);

// print js
echo $this->GoogleMapV3->script();

You can also add windows and (custom) events. See the code and its tests for details on that.

Buffering scripts instead of outputting them directly

With 2.x you can also write the JS to the buffer and output it combined anywhere you want in your layout.
Just call

$this->GoogleMapV3->finalize(); // replaces script()

instead of echoing the script() then. The script() call must not be used then. Don’t mix those two methods.

Also make sure you got echo $this->Js->writeBuffer(array('inline' => true)); somewhere in your layout then.
Most developers put CSS in the head and JS at the very bottom (before the closing </body> tag). This way, the layout renders very fast and the JavaScript will render last and does not impede the page loading process.
See the next chapter for details.

Directions (with or without additional text)

If you want to print directions from point A to point B, you can do so with:

echo $this->GoogleMapV3->map();

$from = 'Munich'; // needs to be geocoded at runtime
$to =  array('lat' => 50.51, 'lng' => 13.40);
$this->GoogleMapV3->addDirections($from, $to);

$this->GoogleMapV3->finalize();

You can either pass in a string to be automatically geocoded or pass an array with lat/lng coordinates.

Note: Geocoding on demand is possible, but slightly slower and less resourceful. It is recommended to geocode in the backend – using PHP and the Plugin classes listed below – and output the coordinates here directly.

Static Maps

These are just images. Very handy if you don’t need the js overhead.

// markers
$markers = array(
    array('lat'=>48.2, 'lng'=>11.1),
    array('lat'=>48.1, 'lng'=>11.2, 'color' => 'green', ...)
);
// paths
$paths = array(
    array(
        'path' => array('Berlin', 'Stuttgart'),
        'color' => 'green',
    ),
    array(
        'path' => array('44.2,11.1', '43.1,12.2', '44.3,11.3', '43.3,12.3'),
    ),
    array(
        'path' => array(array('lat'=>'48.1','lng'=>'11.1'), array('lat'=>'48.4','lng'=>'11.2')), //'Frankfurt'
        'color' => 'red',
        'weight' => 10
    )
);

// some options and image attributes
$options = array(
    'size' => '500x400',
    'center' => true,
    'markers' => $this->GoogleMapV3->staticMarkers($markers),
    'paths' => $this->GoogleMapV3->staticPaths($paths), 
);
$attr = array(
    'title'=>'Yeah'
);

// now display the map image
echo $this->GoogleMapV3->staticMap($options, $attr);

// you can even add an url to click on
$attr['url'] = $this->GoogleMapV3->mapUrl(array('to'=>'Munich, Germany'));
echo $this->GoogleMapV3->staticMap($options, $attr);

As you can see, we can now mix lat/lng and normal addresses (which get automatically geocoded in the background).

Map Links

If you want to redirect to maps.google.com (for directions etc) you can use this method.

// leave "from" empty for the user
$url = $this->GoogleMapV3->mapUrl(array('to'=>'Munich, Germany'));
echo $this->Html->link('Visit Me', $url, array('target'=>'_blank')

// coming from a posted form or whatever
$url = $this->GoogleMapV3->mapUrl(array('to'=>'Munich, Germany', 'from'=>$from));
echo $this->Html->link('Directions to me', $url, array('target'=>'_blank')

Last but not least

Other updates:

  • multiple paths and markers
  • visible scope
  • custom icons possible

For more examples check out the test case. It contains several more sophisticated examples.

Helper Code

The code can be found in my github rep:
Tools Plugin GoogleMapV3 helper

Update 2012-02

All URLs/links are now HTTPS sensitive. So if you display the map on a secure site (https://...) it will also use the same connection for all the google stuff (images, js, …). This is necessary for HTTPS to be valid.

Update 2012-09: GoogleMapsV3Helper v1.3 – not backwards compatible

The helper is now E_STRICT compliant. The methods url() and link() are now mapUrl() and mapLink().

Update 2013-02

directions() has been added to print directions from point A to B. Also basic geocoding capabilities have been added for this method as well as addMarker().

Update 2013-10

The "open" option for markers allows you to display their infoWindow opened at loading. This was a feature request on github. It is available for single and multi windows mode. Note that for single window mode (default), only one marker can be shown as open at once (the last one declared).

Update 2018

The API key is now necessary/required, as such there is an update for 2.x.

And the CakePHP 3.x repo can be found at github.com/dereuromark/cakephp-geo.

5.00 avg. rating (96% score) - 3 votes

106 Comments

  1. Is it possible that you got an old version of it?
    The line you are talking about is 961 in my file.

  2. Is this currentley working with CakePHP 2.x ??

    I’ve tried it, geting ‘Map cannot be displayed!’

  3. yes, all 2.x
    then you didnt include your JS files correctly.

    user proper debugging tools like firefox+firebug to display those js errors and correct your mistakes. then it should run flawlessly.

  4. Hi Mark, Thanks for this very useful piece of code.
    I’m using your helper with cake 2.2.1, it’s working ok, but I keep getting this error:

    Strict (2048): Declaration of GoogleMapV3Helper::url() should be compatible with Helper::url($url = NULL, $full = false) [APP/View/Helper/GoogleMapV3Helper.php, line 28]

    Any idea what may be causing it?
    Thanks for your help!

  5. hello ! thank for your work πŸ˜‰

    but i don’t understand how i can do for a draggable marker ( and automatic position in a input form )

    thank for help

    sebasien

  6. It would be interesting, that i can place a link extern to the map, that can open a infoWindow of a assigned marker. Is it possible?

  7. Hello Mark,

    Nice work, is the "Draggable" marker option ready in the helper? I can’t seem to find it.

  8. Hi Mark,

    thanks for this great helper πŸ˜‰

    I got i little problem regarding the markers.

    The map works fine, but i dont see any marker i added.

    Any experience with this kind of bug?

  9. @carlos:
    use cutom js to get this information via browser (if allowed) or geocode ip etc in the backend via php

    @marius:
    Without any code I cannot tell πŸ™‚
    But they work fine for most of us anyway.

  10. I included the jquery:

    Html->script('jquery.js'); ?>

    and the Maps Aip-Url:

    Html->script($this->GoogleMapV3->apiUrl()); ?>

    Heres the code from my view:

    echo $this->GoogleMapV3->map(array('div'=>array('height'=>'750', 'width'=>'100%','autoScript' => true)));
    
                    $options =
                    array(
                    'lat' => 54.683441,
                    'lng' => 8.551665,
                    'icon'=> 'url_to_icon',
                    'title' => 'Kita XYZ',
                    'content' => '<b>HTML</b> Content for the Bubble/InfoWindow'
                    )
                    ;
    
                    /*
                    for($i = 0; $iGoogleMapV3->addMarker($options[$i]);
                    }
                    */
                    debug($this->GoogleMapV3->addMarker($options));
    
                    echo $this->GoogleMapV3->script();

    This is the debug result:

    \app\View\Cities\search.ctp (line 93)
    (int) 0

    Thanks for your fast reply πŸ™‚

  11. Sorry, includes look like this:

    Html-&gt;script(‘jquery.js’); ?&gt;

    Html-&gt;script($this-&gt;GoogleMapV3-&gt;apiUrl()); ?&gt;

  12. use firebug/firefox to debug your js
    why are you including the scripts twice? autoScript should already do that.

  13. I forgot to remove the auotinclude. I used it for a test. But also didnt work.

    var matching = {
    };
    var gIcons0 = {
    };
    jQuery(document).ready(function() {
    var initialLocation = new google.maps.LatLng(51, 11);
    var browserSupportFlag = new Boolean();
    var myOptions = {zoom: 5, streetViewControl: false, navigationControl: true, mapTypeControl: true, scaleControl: true, scrollwheel: false, keyboardShortcuts: true, center: initialLocation, mapTypeId: google.maps.MapTypeId.ROADMAP};
    // deprecated
    gMarkers0 = new Array();
    gInfoWindows0 = new Array();
    gWindowContents0 = new Array();
    var map0 = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
    var x0 = new google.maps.Marker({
    position: new google.maps.LatLng(54.683441,8.551665),
    map: map0,
    title: "Kita XYZ",
    icon: "url_to_icon"
    });
    gMarkers0.push(
    x0
    );
    gInfoWindows0.push( new google.maps.InfoWindow({
    position: map0.getCenter(),
    content: "",
    maxWidth: 300,
    pixelOffset: 0
    /*zIndex: 200,*/
    }));
    gWindowContents0.push("<b>HTML Content for the Bubble\/InfoWindow");
    google.maps.event.addListener(gMarkers0[0], 'click', function() {
    gInfoWindows0[0].setContent(gWindowContents0[0]);
    gInfoWindows0[0].open(map0, gMarkers0[0]);
    });
    });

    This is the created js, so he got the marker…
    I really dont understand that.

  14. "careful: with only one marker this can result in too high zoom values!"

    I found this statement in the source code with one marker.
    I use one marker and my zoom level is showing 21 and not over ridden by anything.

  15. You need to leave autoCenter disabled (should be the default) or disable it again by using ‘autoCenter’ => false in your initial options for the map. Also make sure you pass a manual zoom level in at some point to avoid google to auto-zoom here.

  16. I received the cakePHP error message:
    "Notice (8): Undefined index: inline [APP/Plugin/Tools/View/Helper/GoogleMapV3Helper.php, line 378]"

    I added:

    'inline' => true

    to the options array and the error message went away. My this indicate why I am receiving "Map cannot be displayed!" message? No javascript errors in the console. my whole view file looks like:

    <?php
    /**

    • File: /app/View/Clients/view.ctp
      */
      // Client header
      $page = "" . h ( $client ['Client'] ['firstname'] . " " . $client ['Client'] ['lastname'] ) . "";
      $page .= "Created: " .$client ['Client'] ['created']. " / Last updated: " . $client ['Client'] ['lastupdate'] . "";

    // Add the google map
    $googleMapAddressString = $client ['Client']['street'].','.$client ['Client']['city'] . ',' . $client ['Client']['state'] . ',' . $client ['Client']['zip'];
    $googleMapId = "client_address_canvas";
    $googleMapOptions = array(
    'autoScript' => true,
    'inline' => true,
    'id' => $googleMapId,
    'width' => '400px',
    'height' => '100%',
    'type' => 'ROADMAP',
    'localize' => false,
    'zoom' => 7,
    'address' => $googleMapAddressString,
    );

    $page .= "Map address: ".h($googleMapAddressString)."";
    $page .= $this->GoogleMapV3->map($googleMapOptions);
    $page .= $this->GoogleMapV3->addMarker($googleMapId, 1, "167 Main St West,Manmouth, OR, 97361", array ('markerTitle' => "YB"));

    // Display the page
    echo $page;

    ?>

  17. Hello Mark,

    thanks fΓΌr your Helper.

    How can i change the zoomlevel? Over the cakephp\app\Config\core.php
    $config[‘Google’] = array() i can’t change it
    Thanks for your help.

  18. That is explained above. You can directly pass it into the map() method.

    If you plan on using the configs, please dont put it into core.php but the configs file…

  19. Hello mark,

    i’ve a Problem. The map will not be sized to my need.
    $googleMapOptions = array(
    ‘autoScript’ =&gt; true,
    ‘inline’ =&gt; true,
    ‘id’ =&gt; $atla[‘Atla’][‘id’],
    ‘width’ =&gt; ‘200px’,
    ‘height’ =&gt; ‘200px’,
    ‘defaultZoom’ =&gt; 9,
    ‘lat’ =&gt; 48.775556, # Stuttgart
    ‘lng’ =&gt; 9.182778, # Stuttgart
    ‘scrollwheel’ =&gt; true,
    ‘type’ =&gt; ‘ROADMAP’,
    ‘localize’ =&gt; true,
    ‘geolocate’ =&gt; true,
    ‘sensor’ =&gt; true,
    The maps is over the whole screen and 400px height.

    echo $this-&gt;GoogleMapV3-&gt;map($googleMapOptions);
    Where is my fault?

  20. Did you try to use

    'div' => array('width' => ..., 'height' => ..., 'style'=> ..) etc?

    You cannot just pass those directly. Take a look at the helper code for clarification (tip: "$_defaultOptions").

  21. This runs now, but how i can load the rest of the options out of the array.
    The way to add the array in the map doesn’t run.

    Marcus

  22. Yes,

    if i use this

    'content' => '<a href="$atla['Atla']['website']" rel="nofollow">$atla['Atla']['website']</a>'

    i get the

    syntax error, unexpected ‘Atla’ (T_STRING), expecting ‘)’

    error

  23. Because that is invalid PHP. Use an IDE with live syntax correction check.

  24. Hello Mark,

    why get i the list of ids from my foreach from addMarker.
    Under my my i can see 0123456789…..
    But why?

    Marcus

  25. Hello Mark,

    Verry nice plugin!
    i have only one problem: is there something to echo the distance in KM when using : $this->GoogleMapV3->addDirections($from, $to); ?

    It show A and B correctly but i need to echo the distance.

    Thx

  26. I don’t know. Use the normal way of geocoding it in the backend (PHP) and work with the coordinates in your view then. That will be no issue then.

  27. Shouldn’t the shadow URL be urlencoded in the same manner that the icon URL is? i.e. from

                if (!empty($p['shadow'])) {
                    $values[] = 'shadow:'.$p['shadow'];
                }
                if (!empty($p['icon'])) {
                    $values[] = 'icon:'.urlencode($p['icon']);
                }

    to

                if (!empty($p['shadow'])) {
                    $values[] = 'shadow:'.urlencode($p['shadow']);
                }
                if (!empty($p['icon'])) {
                    $values[] = 'icon:'.urlencode($p['icon']);
                }
  28. .. in fact – that’s not the problem I have…

    PHP code:

    $options = array(
    'lat' => $establishment['Establishment']['latitude'],
    'lng' => $establishment['Establishment']['longitude'],
    'icon'=> 'http://images.example.com/maps/pink-dot.png',
    'shadow'=> 'http://images.example.com/maps/msmarker.shadow.png'};

    Resultant CSS:

    var x0 = new google.maps.Marker({
    position: new google.maps.LatLng(54....,-1.....),
    map: map0,
    icon: "http:\/\/images.example.com\/maps\/pink-dot.png",
    shadow: http://images.example.com/maps/msmarker.shadow.png
    });

    Something I’m doing wrong?

  29. There was indeed a small bug regarding url strings here.
    It is fixed in the current master branch.
    Thank you for pointing it out.

  30. Hi Mark, thanks for your great work, it helped me a lot, i have now a problem, when i try to display the map in a modal box it displays: Map cannot be display, i was trying to fix that but i can’t figure out a solution, i don’t know if there is a workaround.

  31. my map does not load with correct size, but when I resize my browser, then it loads in the correct size. here is what I have written, it is enough I guess…

    echo $this->Html->script($this->GoogleMapV3->apiUrl());
    echo $this->GoogleMapV3->map(array(
        'inline' => true,															
        'map' => array(
            'defaultZoom'=>5,
            'defaultLat' => $property['Property']['latitude'],
            'defaultLng' => $property['Property']['longitude']),
            'infoWindow' => array('maxWidth'=>500),
            'autoCenter' => true,
            'zoom' =>5
        )
    ); 
    $options = array(
        'lat' => $property['Property']['latitude'],
        'lng' => $property['Property']['longitude'],							
        'title' => $property['Property']['title'],
        'content' => $property['Property']['title']
    );							
    $this->GoogleMapV3->addMarker($options); 
    echo $this->GoogleMapV3->script();
  32. Hello Mark, thank you very much for this helper πŸ™‚

    I have a problem, when i want to use addDirections i have a warning message in return:
    Warning (512): Method GoogleMapV3Helper::addDirections does not exist [CORE\Cake\View\Helper.php, line 191]

    I checked the php file and I have not found the function addDirections.
    Can you help me please?

  33. Please make sure you check out the latest version on github master branch. It’s definitively there.

  34. Yes i have the latest version on github master branch.
    but i don’t have the addDirections function.

    ca, you give me the adress plz ?

  35. The current version is linked above under the headline "Helper Code".
    What you linked is an outdated/deprecated repository.

  36. I was trying to initialize the map, prior to adding a marker, via an address instead of lat/lon coordinates. looking at the helper class it appears that the initialization of the map must take in the coordinates and does not take in an address. Only thing I could think of, in order to add a marker and have to map center the marker, was to go back and add the GeoCoding Behavior to my Model so that I always have the lat/lon coordinates for each location I want to map. The behavior is also in the Tools Plugin and is documented here: http://www.dereuromark.de/2012/06/12/geocoding-with-cakephp/
    If anyone has come with a solution to this without having to go back to getting the coordinates of each location I would greatly appreciate an example.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.