Saturday, August 11, 2012

ZK Gmaps Extension: Gdirection


Introduction

This post is about a custom ZK component (Gdirection) that let ZK Gmaps works with Google Direction Service.

Warm Up

To understand this post well, you may reed the articles below first:

ZK Quick Start

ZK Component Development Tutorial: Getting Started

The Program

Let's skipp some files, only take a look at Gdirection.java, Gdirection.js  and test zul file here

Gdirection.java

package org.zkoss.gmaps.extended.components;

import org.zkoss.gmaps.Gmaps;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zul.Div;
import org.zkoss.zul.impl.XulElement;

public class Gdirection extends XulElement {
    private String _panelId;
    private Div _panel;
    private String _mapId;
    private Gmaps _map;
    private String _start;
    private String _end;
    private String[] _direction;

    /**
     * Set panel to google direction display,
     * note you should make sure the panel is already attached to page before call this function
     * because the uuid will be changed when attache to page
     * @param panel
     */
    public void setPanel (Div panel) {
        _panel = panel;
        setPanelId(panel.getUuid());
    }
    /**
     * set panel id directly
     * @param panelId
     */
    public void setPanelId (String panelId) {
        _panelId = panelId;
        smartUpdate("panelId", _panelId);
    }
    /**
     * get panel id
     * @return
     */
    public String getPanelId () {
        return _panelId;
    }
    /**
     * Set map to google direction display,
     * note you should make sure the map is already attached to page before call this function
     * because the uuid will be changed when attache to page
     * @param map
     */
    public void setMap (Gmaps map) {
        _map = map;
        setMapId(map.getUuid());
    }
    /**
     * set map id directly
     * @param mapId
     */
    public void setMapId (String mapId) {
        _mapId = mapId;
        smartUpdate("mapId", _mapId);
    }
    /**
     * get map id
     * @return
     */
    public String getMapId () {
        return _mapId;
    }
    /**
     * Sets the start point of direction
     * @param start
     */
    public void setStart (String start) {
        _start = start;
        setDirection (_start, _end);
    }
    /**
     * Sets the end point of direction
     * @param end
     */
    public void setEnd (String end) {
        _end = end;
        setDirection (_start, _end);
    }
    /**
     * set direction to route
     * @param start the start point
     * @param end the end point
     */
    public void setDirection (String start, String end) {
        if (start != null && end != null) {
            _direction = new String[] {start, end};
            smartUpdate("direction", _direction);
        } else {
            _direction = null;
        }
    }
    /**
     * set direction to route
     * @param direction the String array [start, end]
     */
    public void setDirection (String[] direction) {
        if (direction.length != 2)
            throw new WrongValueException ("the direction should exactly contains [start point, end point]");
        _direction = direction;
        smartUpdate("direction", _direction);
    }
    public String[] getDirection () {
        return _direction;
    }
    public String getZclass() {
        return _zclass == null ? "z-gdirection" : _zclass;
    }
    //-- ComponentCtrl --//
    protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer)
    throws java.io.IOException {
        super.renderProperties(renderer);
        if (_mapId != null)
            render(renderer, "mapId", _mapId);
        if (_panelId != null)
            render(renderer, "panelId", _panelId);
        if (_direction != null)
            render(renderer, "direction", _direction);
    }
}


It will render start point, end point and the id of gmap and panel div to client side.

Gdirection.js


gmaps.extended.Gdirection = zk.$extends(zul.Widget, {
    $define: {
        /**
         * sets the mapId of the map for direction display
         * @param v
         */
        mapId: function (v) {
            var map,
                service,
                direction;
            map = this._map = zk.Widget.$('#' + v);
            if (service = this._directionsService) {
                // do route if ready
                if (direction = this._direction)
                    this.setDirection(direction, {force: true});
            } else
                this._init();
        },
        /**
         * sets the panelId of the panel for direction display
         * @param v
         */
        panelId: function (v) {
            var panel,
                service,
                direction;
            panel = this._panel = jq('#' + v)[0];
            if (service = this._directionsService) {
                // do route if ready
                if (direction = this._direction)
                    this.setDirection(direction, {force: true});
            } else
                this._init();
        },
        /**
         * sets the direction to route
         * @param v
         */
        direction: function (v) {
            var display = this._directionsDisplay;

            if (display = this._directionsDisplay) {
                var s = v? $eval(v) : null,
                        service;
                // wrong arguments or not binded
                if (s.length != 2 || !(service = this._directionsService)) return;
                var start = s[0],
                    end = s[1],
                    request = {
                        origin: start,
                        destination: end,
                        travelMode: google.maps.DirectionsTravelMode.DRIVING
                    };

                service.route(request, function(response, status) {
                    if (status == google.maps.DirectionsStatus.OK) {
                        display.setDirections(response);
                    }
                });
            }
        }
    },
    bind_: function () {
        this.$supers(gmaps.extended.Gdirection, 'bind_', arguments);
        this._tryBind();
    },
    
    _tryBind: function () {
        var mapId, panelId;
        // init if google api, mapId and panelId are ready
        if (window.google && window.google.maps
            && (mapId = this._mapId)
            && (panelId = this._panelId))
            this._init();
        else if ((mapId = this._mapId)
                && (panelId = this._panelId)) {
            // retry if the info for init is ready
            var wgt = this;
            setTimeout (function () {wgt._tryBind()}, 100);
        }
    },
    _init: function () {
        var mapId = this._mapId,
            panelId = this._panelId,
            map,
            panel,
            direction,
            directionsDisplay;

        if (!(map = this._map))
            map = this._map = zk.Widget.$('#' + mapId);
        if (!(panel = this._panel))
            panel = this._panel = jq('#' + panelId)[0];
        // prevent multiple init
        if (directionsDisplay = this._directionsDisplay)
            return;

        // while map and panel are ready
        if (map && map._gmaps && panel) {
            this._directionsService = new google.maps.DirectionsService();
            this._directionsDisplay = directionsDisplay = new google.maps.DirectionsRenderer();

            if ((map = this._map)
                && (map = map._gmaps))
                directionsDisplay.setMap(map);
            if (panel = this._panel)
                directionsDisplay.setPanel(panel);

            if (map
                && panel
                && (direction = this._direction))
                this.setDirection(direction, {force: true});
        } else if (mapId
                && panelId) {
            // retry if the info for routing is ready
            var wgt = this,
                timer;
            if (timer = this._initTimer) clearTimeout(timer);
            this._initTimer = setTimeout (function () {wgt._init()}, 100);
        }
    },
    getZclass: function () {
        return 'z-gdirection';
    }
});


This will contunuously trying initiate Google Direction objects if the informations (map/panel id) are ready, and then call route API to show the result if has directions.

google_direction_test.zul

<zk xmlns:w="client">
    <combobox id="cbxOne" value="Chicago">
        <comboitem label="Chicago" />
        <comboitem label="Hammond" />
        <comboitem label="Joliet" />
        <comboitem label="Kankakee" />
        <comboitem label="St Louis" />
        <comboitem label="Peoria" />
        <attribute name="onChange">
            gdirection.setDirection(self.getValue(), cbxTwo.getValue());
        </attribute>
    </combobox>
    to
    <combobox id="cbxTwo" value="St Louis">
        <comboitem label="Chicago" />
        <comboitem label="Hammond" />
        <comboitem label="Joliet" />
        <comboitem label="Kankakee" />
        <comboitem label="St Louis" />
        <comboitem label="Peoria" />
        <attribute name="onChange">
            gdirection.setDirection(cbxOne.getValue(), self.getValue());
        </attribute>
    </combobox>
    <hbox id="hbox">
        <gmaps id="gmaps" width="500px" height="500px" />
        <div id="panel" width="300px" height="500px" style="overflow: auto;" />
    </hbox>
    <gdirection id="gdirection" start="chicago" end="st louis"
        onCreate='self.setMap(gmaps); self.setPanel(panel);' />
</zk>


The Result

Demo flash on line
http://screencast.com/t/2cpHVkMlGC

Reference
https://google-developers.appspot.com/maps/documentation/javascript/examples/directions-simple

Download

Full project
https://github.com/benbai123/ZK_Practice/tree/master/Components/projects/Addon_Practice/GmapsPractice

gmapsextended.jar
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Addon_Practice/GmapsPractice/target/gmapsextended.jar

demo swf
https://github.com/benbai123/ZK_Practice/blob/master/Components/demos/addon/GmapsWithGoogleDirectionService.swf


No comments:

Post a Comment