Posts Tagged ‘Google Maps’

Aug 13
2009

google maps, ebubbles, and links

we at boathouse like the google maps plugin ebubbles. it’s a great little plugin that allows us to create custom info bubbles for our custom map mashups.

the only problem we were having was that they’re created in such a way that any links created on the bubbles don’t work, because the whole bubble is controlled by a close function. according to the documentation,

In order to minimise strange click-through behaviour, by default the EBubble closes when clicked.

this doesn’t cut it for us.

we needed a solution for this, and a little bit of engineering got us underway.

starting at line 59 in the ebubbles js code, we found this line:

GEvent.addDomListener(this.div1,"mousedown", function() {
if (!that.noCloseOnClick) that.hide();
GEvent.trigger(that,"click");
});

after a little bit of playing (firebug’s console api is easier than writing alerts), we were able to figure out that if we did a function(e), when we clicked the link the event target would end up being a url. so we could change our code to this:

GEvent.addDomListener(this.div1,"mousedown", function(e) {
var firstFour = e.target.toString().substring(0,4);
if(firstFour == "http") return;
if (!that.noCloseOnClick) that.hide();
GEvent.trigger(that,"click");
});

adding the
var firstFour = e.target.toString().substring(0,4);
if(firstFour == "http") return;

just makes sure that the link actually gets through as a link by checking to see if it’s a URL. if it is, we return out of the function without going into the bubble.hide() function.

and the good thing — relative path or no, the target will always return a full path, so the substring of the first 4 characters should always return “http”, unless you’re dealing with some other protocol (but then you can always rewrite the if/then statement to include whatever protocols you want it to).

NOTE: the mousedown functions grabs this before the click function. we don’t need to attach this same check on the click function, but you can always put it in there if it makes you feel safer.

Aug 12
2009

jquery, expression engine and google maps

Based on a comment i made on a few blog posts on the always interesting viget, i was asked to relate how i might populate a google map with information from expression engine using jquery.

Expression Engine is great in its ability to dynamically generate xml the same way it generates any other page - you call for it, and it generates it. When you create a new template the template type dropdown gives you the option of xml. The code for generating that page uses the same syntax as generating any other page:


<?xml version="1.0" encoding="utf-8"?>
    <network>
    {exp:weblog:entries weblog="my_weblog" sort="asc"}
    {if place_lat && place_long }
    <place>
        <home>{is_home}</home>
        <latitude>{place_lat}</latitude>
        <longitude>{place_long}</longitude>
        <place_loc>{place_city}</place_loc>
        <place_name>{place_title}</place_name>
    </place>
    {/if}
    {/exp:weblog:entries}
</network>

Basically, what this is doing is making sure that the latitude and longitude are set (we have entries for the coordinates in the form field with the location of a good address geocoder in the caption, so the client does not need to wonder how to get those), then filling in all the other information as it’s needed using the regular EE syntax.

We could be using the GClientGeocoder option baked into google maps, but it’s a little fussy sometimes and we’re looking for the best solution.

So now that EE is giving us a dynamically generated XML file, we’re moving on to our google maps with jquery. I’m assuming we all know how to call the google maps api and jquery into our page, so i’ll skip over that.

Just as a warning, i’m a big fan of namespacing my scripts. This (obviously) doesn’t need to be done, but it helps me keep my functions distinct and use variables throughout that might interfere with other scripts. I haven’t detailed how i set it up, but Dustin Diaz has a great tutorial if you really want to know.

The first part is the easy part - setting up the maps API.


BH.maps.load = function () {
    if (GBrowserIsCompatible()) {
        map = new GMap2(document.getElementById("map"));
        map.setCenter(new GLatLng(0,0),0);
        var bounds = new GLatLngBounds( );

This is the obvious stuff, i would think. the setting a center just allows the map to work. Don’t worry, we’ll recenter soon (hence the GLatLngBounds)

Now we can go ahead and create different icons for different uses. If you look back, the first item in is . Ours is a simple boolean set through a dropdown in the form field, though it can be used multiple ways to generate as many types of icons as are needed.


var greenIcon = new GIcon(G_DEFAULT_ICON);
greenIcon.image = "/EE_Global/img/icons/green_tack.png";
greenIcon.iconSize = new GSize(17,46);
greenIcon.shadow = "/EE_Global/img/icons/tack_shadow.png";
greenIcon.shadowSize = new GSize(0,0);
greenIcon.iconAnchor = new GPoint(3,42);

var redIcon = new GIcon(G_DEFAULT_ICON);
redIcon.image = "/EE_Global/img/icons/red_tack.png";
redIcon.iconSize = new GSize(27,44);
redIcon.shadow = "/EE_Global/img/icons/tack_shadow.png";
redIcon.shadowSize = new GSize(0,0);
redIcon.iconAnchor = new GPoint(4,38);

Now here’s where the jquery comes in. Getting through this is a breeze. We use a get function, then look through the XML which we happened to define in a variable for reuse of this function in other pages using different XML, but which could be called explicitly here.


$.get(mapXML, {}, function(xml){
    $('place', xml).each(function(){
        if ($(this).find("home").text() == "true") {
            markerOptions = { icon:redIcon };
        } else {
            markerOptions = { icon:greenIcon };
        }

        var newLat = parseFloat($(this).find("latitude").text());
        var newLong = parseFloat($(this).find("longitude").text());
        var new_point = new GLatLng(newLat,newLong);
        var theHTMLa = $(this).find("place_name").text();
        var theHTMLb = $(this).find("place_loc").text();
        var theHTMLall = theHTMLa + "<br>" + theHTMLb;
        var marker = BH.maps.createMarker(new_point,markerOptions,theHTMLall);
        map.addOverlay(marker);
        bounds.extend(new_point);
        map.setCenter(bounds.getCenter());
        map.setZoom(map.getBoundsZoomLevel(bounds));
    });
});

So we’re ripping through the XML, finding the tags we need to populate different variables, then popping those into our createMarker function. going through it a few lines at a time:


$.get(mapXML, {}, function(xml){
    $('place', xml).each(function(){

We get the xml, and each “place” tag gets a function.


if ($(this).find("home").text() == "true") {
    markerOptions = { icon:redIcon };
} else {
    markerOptions = { icon:greenIcon };
}

We find our boolean to determine which marker we’re using. Again, this could be done with something other than a boolean, if you wanted to use 3 or more markers.


var newLat = parseFloat($(this).find("latitude").text());
var newLong = parseFloat($(this).find("longitude").text());
var new_point = new GLatLng(newLat,newLong);

We read our latitude and longitude, then put them into a GLatLng point.


var theHTMLa = $(this).find("place_name").text();
var theHTMLb = $(this).find("place_loc").text();
var theHTMLall = theHTMLa + "<br>" + theHTMLb;

Read a few pieces of info and concatenate them into one string. This could be done in the xml generator, but we chose to do it in the JS.


var marker = BH.maps.createMarker(new_point,markerOptions,theHTMLall);

Then we go to our marker creation function and hand it the GLatLng point, the color of the marker it should be using, and the html to put in the info bubble.


bounds.extend(new_point);
map.setCenter(bounds.getCenter());
map.setZoom(map.getBoundsZoomLevel(bounds));

Changing the bounds by adding the new point, then setting the zoom level and center based on that new addition. Yes, it does go through each point and make the change, but A) it happens so quickly you’re not seeing it redraw each time, and B) we found this way gives us less hassle in different browsers than trying to add them one at a time and then redrawing when all points are added.

The createMarker function is pretty bog standard:


BH.maps.createMarker = function (point,markerOptions,code) {
    var marker = new GMarker(point,markerOptions);
    GEvent.addListener(marker, "click", function() {
        marker.openInfoWindowHtml(code);
    });
    gmarkers.push(marker);
    return marker;
};

We’re not pushing the marker to any separate arrays for different marker icons, because we don’t need it. But you totally could.

So the code, all in one chunk:


BH.maps.load = function () {
    if (GBrowserIsCompatible()) {
        map = new         GMap2(document.getElementById("map"));
        map.setCenter(new GLatLng(0,0),0);
        var bounds = new GLatLngBounds( );

        var greenIcon = new GIcon(G_DEFAULT_ICON);
        greenIcon.image = "/EE_Global/img/icons/green_tack.png";
        greenIcon.iconSize = new GSize(17,46);
        greenIcon.shadow = "/EE_Global/img/icons/tack_shadow.png";
        greenIcon.shadowSize = new GSize(0,0);
        greenIcon.iconAnchor = new GPoint(3,42);

        var redIcon = new GIcon(G_DEFAULT_ICON);
        redIcon.image = "/EE_Global/img/icons/red_tack.png";
        redIcon.iconSize = new GSize(27,44);
        redIcon.shadow = "/EE_Global/img/icons/tack_shadow.png";
        redIcon.shadowSize = new GSize(0,0);
        redIcon.iconAnchor = new GPoint(4,38);

        $.get(mapXML, {}, function(xml){
            $('school', xml).each(function(i){
                if ($(this).find("home").text() == "true") {
                    markerOptions = { icon:redIcon };
                } else {
                    markerOptions = { icon:greenIcon };
                }
                var newLat = parseFloat($(this).find("latitude").text());
                var newLong = parseFloat($(this).find("longitude").text());
                var new_point = new GLatLng(newLat,newLong);
                var theHTMLa = $(this).find("place_name").text();
                var theHTMLb = $(this).find("place_loc").text();
                var theHTMLall = theHTMLa + "<br>" + theHTMLb;
                var marker = BH.maps.createMarker(new_point,markerOptions,theHTMLall);
                map.addOverlay(marker);
                bounds.extend(new_point);
                map.setCenter(bounds.getCenter());
                map.setZoom(map.getBoundsZoomLevel(bounds));});
            });
        }
    };

BH.maps.createMarker = function (point,markerOptions,code) {
    var marker = new GMarker(point,markerOptions);
    GEvent.addListener(marker, "click", function() {
        marker.openInfoWindowHtml(code);
    });
    gmarkers.push(marker);
    return marker;
};

I may edit this as time goes by to respond to any questions (i’m a coder, not a writer), but there it is in a nutshell. As soon as the site we did this work on is up, we can post a link!