lundi 12 avril 2010

JSF 2 Composite Google Maps Component

I needed a simple Google Maps component for some application. Although there were some examples, none of them was using the composite approach, which I wanted to use for its benefits.

I thought it would be useful to share my example, so here it is.

It accepts a couple of parameters (but has reasonible defaults for most of them): zoom, address, ...

Maybe I'll improve this later. Any suggestion welcome.

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:composite="http://java.sun.com/jsf/composite">

    <!-- INTERFACE -->
    <composite:interface>
        <composite:attribute name="zoom" type="java.lang.Integer" default="14" />
        <composite:attribute name="width" type="java.lang.Integer" default="400" />
        <composite:attribute name="height" type="java.lang.Integer" default="300" />
        <composite:attribute name="address" type="java.lang.String" required="true" />
        <composite:attribute name="mapType" type="java.lang.String" default="ROADMAP" />
        <composite:attribute name="lat" type="java.lang.Number" default="0" />
        <composite:attribute name="long" type="java.lang.Number" default="0" />
    </composite:interface>

    <!-- IMPLEMENTATION -->
    <composite:implementation>
        <h:outputScript target="head">
            var geocoder;
            var map;

            function loadApi(src) {
                var scriptElement = document.createElement("script");
                scriptElement.src = src;
                scriptElement.type = "text/javascript";
                var headElement = document.getElementsByTagName("head")[0];
                headElement.appendChild(scriptElement);
            }
           
            function loadGMapsApi() {
                if (google) {
                    return;
                }
                loadApi("http://maps.google.com/maps/api/js?sensor=false");
            }

            function initializeMap(elementId) {
                loadGMapsApi();
                geocoder = new google.maps.Geocoder();
                var latlng = new google.maps.LatLng(#{cc.attrs.lat}, #{cc.attrs.long});
                var myOptions = {
                    zoom: #{cc.attrs.zoom},
                    center: latlng,
                    mapTypeId: google.maps.MapTypeId.#{cc.attrs.mapType}
                }
                map = new google.maps.Map(document.getElementById(elementId), myOptions);
                gotoAddress("#{cc.attrs.address}");
                }

                function gotoAddress(address) {
                if (geocoder) {
                    geocoder.geocode( { 'address': address}, function(results, status) {
                        if (status == google.maps.GeocoderStatus.OK) {
                            map.setCenter(results[0].geometry.location);
                            var marker = new google.maps.Marker({
                                map: map,
                                position: results[0].geometry.location
                            });
                        } else {
                            //alert("Geocode was not successful for the following reason: " + status);
                        }
                    });
                }
            }
        </h:outputScript>
        <h:panelGroup layout="block" style="width: #{cc.attrs.width}px; height: #{cc.attrs.height}px;" id="map_canvas" />
        <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"/>
        <h:outputScript>
            initializeMap("#{cc.clientId}:map_canvas");
        </h:outputScript>

    </composite:implementation>
</html>