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!

Oct 02
2008

NSTAR Green Calculator

Using Eyewonder and Gigya, we have launched a rich media banner campaign focused on a green energy calculator related to the new wind power options from NSTAR.

Feb 25
2008

Powered by WordPress

This website is now powered by the self-hosted version of WordPress.

We have used only a few basic plugins:

  • Local Analytics for Google Analytics (including local, 24-hour browser caching of Google’s tracking JavaScript)
  • WP-OpenID to allow visitors to use their OpenID accounts to logon and post comments
  • WP-Yadis to allow us to set this website URL as our single identifier for our multiple OpenID accounts ( Yadis)
  • ShotCode to create 2D bar codes for mobile phone bookmarking
  • AddThis to allow users to easily bookmark individual entries on this site (and allows us to see reports on this activity)

We did come across a few problems with the installation - most of them revolving our our use of Microsoft IIS as a server. The biggest problem was our inability to make “pretty” permalinks to each post. Additional information about this problem (and work arounds) is found here.

Feb 22
2008

OpenID and phpMyID

We have been exploring the use of OpenID as both a provider (so our employees could potentially use one logon for multiple websites) and to integrate with the sites we produce (e.g., allowing easy logon for visitors to our clients’ sites).

From the OpenID website:

OpenID eliminates the need for multiple usernames across different websites, simplifying your online experience.

We wanted to explore the creation of a single logon for our development resources. A simple solution for this is phpMyID.

From the phpMyID website:

phpMyID is a single user (though, if you were so inclined, you could easily turn it into a multi-user setup) IdP, or “Identity Provider” for the OpenID framework.

You can view our sample logon page here:
http://gym.boathouseinc.com/openid/

Next up: Using Yadis to allow a single person to use multiple open authentication services (including setting your own website or blog name as your OpenID).

Feb 05
2008

Apple Touch Icons

The recent changes to the iPod Touch and iPhone operating system (”Apple Touch”) allow for users to create custom icons on their (aka “Web Clips”) on the Apple Touch Home Screen.

While the documentation in the Apple Developer Connection was helpful, we found that the recommended size of 57×57 pixels does not allow for the best looking icons. In fact, Apple does not even follow this recommendation for their own icon:

http://www.apple.com/apple-touch-icon.png

After some research ( here and here) and experimenting, we discovered the following settings provide the best results:

129 x 129 at 163dpi

The new Boathouse icons are now part of all of our sites:

Black-on-White (apple-touch-icon.png)

White-on-Black (apple-touch-icon-black.png)

Feb 04
2008

Web Surveys: LimeSurvey

In an effort to explore alternatives to SurveyMonkey and other similar web-based survey tools we have been exploring open-source solutions. Our goal was to find a solution powerful enough to be used for the quick creation of low-end to mid-range surveys, while providing a customizable platform. During our research we came across and are now evaluating the LimeSurvey solution.

LimeSurvey Highlights

  • Open-source platform: PHP and mySQL
  • Advanced reporting through integration of JpGraph (open-source) and exports to SPSS.
  • Integration with company directory servers (LDAP)

Our initial implementations are set to private/internal-only. As soon as we create a few public examples, we will post them here.

Jan 01
2008

Introducing The Boathouse Gym

Welcome to our new website! Starting in 2008, the Boathouse digital team will be using this site to publish some of our internal work within the constantly evolving digital landscape.

Our digital strategists, designers, developers, and architects will be using the Gym for three primary purposes:

  • Study - Researching and building a shared understanding of new technologies as they are released (and sometimes before)
  • Cultivate - Defining the scaffolding, framework, templates, etc. on which the new technologies will be produced
  • Break-in - Learning how we can best apply the new technologies for clients by applying them to test examples

Please check back again in throughout the year as we publish our examples and learnings.