ChartDirector Ver 4.1 (PHP Edition Edition)

Zooming and Scrolling Demonstration (2)

This sample program demonstrates a general zoomable and scrollable chart. The followings are the key elements of this sample program:

- The axis ranges of the chart can be controlled by mouse actions, such as drag to scroll, drag to zoom and click to zoom. The view port can also be moved by dragging on the rectangle on the navigation pad.
- Charts are updated using partial chart update (AJAX requests) by default. If the browser does not support AJAX, it will fall back to full page update.
- Data points are with tool tips.

The source code listing of this sample program is included at the end of this section. The code consists of the following main parts:

- createFirstChart: This function is for handing the initial chart request. In this example, it simply initializes the initial state of the JsChartViewer. It then calls drawChart to draw the chart.
- processPartialUpdate: This function is for handling partial chart updates (AJAX request). In this example, as there is no external controls that needs server side support, this function simply calls drawChart to update the chart.
- processFullUpdate: This is the function for handling full page updates (postback). Similar to processPartialUpdate, it simply calls drawChart to update the chart.
- drawChart: This is the main charting code. It draws the chart according to the selected axis range.
- HTML: The HTML part of the code is for creating the web page. This include Javascript code for implementing the push buttons, and the navigation pad with the draggable rectangle.
- initJsChartViewer: This Javascript function performs browser side initialization. It is invoked by the onload event handler of the <BODY> tag. It connects the browser side JsChartViewer with buttons for setting the mouse usage modes, and to the navigation pad for directly setting the view port. It detects if the browser supports partial chart updates and sets up the ViewPortChanged event handler accordingly.

Files in this Demonstration

This sample script requires the following files. When developing your own script using this sample script as a template, please ensure you copy the necessary files to the proper directories (typically the same directory as the script) and reference them using proper paths.

File | Description
--- | ---
zoomscrolldemo2.php | Main source code for this demo.
pointer.gif zoomInIcon.gif zoomOutIcon.gif | The images used in the push buttons in this demo.
getchart.php | A utility script used in the image url for retrieving the chart image from a session variable (see BaseChart.makeSession).
cdjcv.js zoomin.cur zoomout.cur nozoom.cur wait.gif | The script file for JsChartViewer, and the supporting cursor and image files. These files must be in the same directory.

Source Code Listing

[File: phpdemo/zoomscrolldemo2.php]

    <?php
    require_once("../lib/phpchartdir.php");

    #
    # Copyright 2006 Advanced Software Engineering Limited
    #
    # You may use and modify the sample code in this file in your application, provided the Sample Code
    # and its modifications are used only in conjunction with ChartDirector. Usage of this software is
    # subjected to the terms and condition of the ChartDirector license.
    #
    #/
    #/ We need to handle 3 types of request:
    #/ - initial request for the full web page
    #/ - partial update (AJAX chart update) to update the chart without reloading the page
    #/ - full page update for old browsers that does not support partial updates
    #/

    #/ <summary>
    #/ Handles the initial request
    #/ </summary>
    #/ <param name="WebChartViewer">The WebChartViewer object to handle the chart.</param>
    function createFirstChart(&$viewer) {
        # Initialize the Javascript ChartViewer
        $viewer->setScrollDirection(DirectionHorizontalVertical);
        $viewer->setZoomDirection(DirectionHorizontalVertical);
        $viewer->setMouseUsage(MouseUsageScroll);

        # Draw the chart
        drawChart($viewer);
    }

    #/ <summary>
    #/ Handles partial update (AJAX chart update)
    #/ </summary>
    #/ <param name="WebChartViewer">The WebChartViewer object to handle the chart.</param>
    function processPartialUpdate(&$viewer) {
        # In this demo, we just need to redraw the chart
        drawChart($viewer);
    }

    #/ <summary>
    #/ Handles full update
    #/ </summary>
    #/ <param name="WebChartViewer">The WebChartViewer object to handle the chart.</param>
    function processFullUpdate(&$viewer) {
        # In this demo, we just need to redraw the chart
        drawChart($viewer);
    }

    #/ <summary>
    #/ Draw the chart
    #/ </summary>
    #/ <param name="WebChartViewer">The WebChartViewer object to handle the chart.</param>
    function drawChart(&$viewer) {
        #
        # For simplicity, in this demo, we just use hard coded data. In a real application, the data may
        # come from a dynamic source such as a database. (See "Using Data Sources with ChartDirector" in
        # the ChartDirector documentation if you need some sample code on how to read data from database
        # to array variables.)
        #
        $dataX0 = array(10, 15, 6, -12, 14, -8, 13, -3, 16, 12, 10.5, -7, 3, -10, -5, 2, 5);
        $dataY0 = array(130, 150, 80, 110, -110, -105, -130, -15, -170, 125, 125, 60, 25, 150, 150, 15,
            120);
        $dataX1 = array(6, 7, -4, 3.5, 7, 8, -9, -10, -12, 11, 8, -3, -2, 8, 4, -15, 15);
        $dataY1 = array(65, -40, -40, 45, -70, -80, 80, 10, -100, 105, 60, 50, 20, 170, -25, 50, 75);
        $dataX2 = array(-10, -12, 11, 8, 6, 12, -4, 3.5, 7, 8, -9, 3, -13, 16, -7.5, -10, -15);
        $dataY2 = array(65, -80, -40, 45, -70, -80, 80, 90, -100, 105, 60, -75, -150, -40, 120, -50, -30
            );

        # Create an XYChart object 500 x 480 pixels in size, with light blue (c0c0ff) as background
        # color
        $c = new XYChart(500, 480, 0xc0c0ff);

        # Set the plotarea at (50, 40) and of size 400 x 400 pixels. Use light grey (c0c0c0) horizontal
        # and vertical grid lines. Set 4 quadrant coloring, where the colors of the quadrants alternate
        # between lighter and deeper grey (dddddd/eeeeee).
        $plotAreaObj = $c->setPlotArea(50, 40, 400, 400, -1, -1, -1, 0xc0c0c0, 0xc0c0c0);
        $plotAreaObj->set4QBgColor(0xdddddd, 0xeeeeee, 0xdddddd, 0xeeeeee, 0x000000);

        # Enable clipping mode to clip the part of the data that is outside the plot area.
        $c->setClipping();

        # Set 4 quadrant mode, with both x and y axes symetrical around the origin
        $c->setAxisAtOrigin(XYAxisAtOrigin, XAxisSymmetric + YAxisSymmetric);

        # Add a legend box at (450, 40) (top right corner of the chart) with vertical layout and 8 pts
        # Arial Bold font. Set the background color to semi-transparent grey (40dddddd).
        $legendBox = $c->addLegend(450, 40, true, "arialbd.ttf", 8);
        $legendBox->setAlignment(TopRight);
        $legendBox->setBackground(0x40dddddd);

        # Add titles to axes
        $c->xAxis->setTitle("Alpha Index");
        $c->yAxis->setTitle("Beta Index");

        # Set axes line width to 2 pixels
        $c->xAxis->setWidth(2);
        $c->yAxis->setWidth(2);

        # The default ChartDirector settings has a denser y-axis grid spacing and less-dense x-axis grid
        # spacing. In this demo, we want the tick spacing to be symmetrical. We use around 40 pixels
        # between major ticks and 20 pixels between minor ticks.
        $c->xAxis->setTickDensity(40, 20);
        $c->yAxis->setTickDensity(40, 20);

        #
        # In this example, we represent the data by scatter points. If you want to represent the data by
        # somethings else (lines, bars, areas, floating boxes, etc), just modify the code below to use
        # the layer type of your choice.
        #

        # Add scatter layer, using 11 pixels red (ff33333) X shape symbols
        $c->addScatterLayer($dataX0, $dataY0, "Group A", Cross2Shape(), 11, 0xff3333);

        # Add scatter layer, using 11 pixels green (33ff33) circle symbols
        $c->addScatterLayer($dataX1, $dataY1, "Group B", CircleShape, 11, 0x33ff33);

        # Add scatter layer, using 11 pixels blue (3333ff) triangle symbols
        $c->addScatterLayer($dataX2, $dataY2, "Group C", TriangleSymbol, 11, 0x3333ff);

        # if (viewer.getCustomAttr("minY") == null) {
        if (($viewer->getCustomAttr("minY") == null) || ($viewer->getCustomAttr("minY") == "")) {
            # The axis scale has not yet been set up. This means this is the first time the chart is
            # drawn and it is drawn with no zooming. We can use auto-scaling to determine the
            # axis-scales, then remember them for future use.

            # explicitly auto-scale axes so we can get the axis scales
            $c->layout();

            # save the axis scales for future use
            $xAxisObj = $c->xAxis;
            $viewer->setCustomAttr("minX", $xAxisObj->getMinValue());
            $xAxisObj = $c->xAxis;
            $viewer->setCustomAttr("maxX", $xAxisObj->getMaxValue());
            $yAxisObj = $c->yAxis;
            $viewer->setCustomAttr("minY", $yAxisObj->getMinValue());
            $yAxisObj = $c->yAxis;
            $viewer->setCustomAttr("maxY", $yAxisObj->getMaxValue());
        } else {
            # Retrieve the original full axes scale
            $minX = $viewer->getCustomAttr("minX");
            $maxX = $viewer->getCustomAttr("maxX");
            $minY = $viewer->getCustomAttr("minY");
            $maxY = $viewer->getCustomAttr("maxY");

            # Compute the zoomed-in axis scales by multiplying the full axes scale with the view port
            # ratio
            $xScaleMin = $minX + ($maxX - $minX) * $viewer->getViewPortLeft();
            $xScaleMax = $minX + ($maxX - $minX) * ($viewer->getViewPortLeft() +
                $viewer->getViewPortWidth());
            $yScaleMax = $maxY - ($maxY - $minY) * $viewer->getViewPortTop();
            $yScaleMin = $maxY - ($maxY - $minY) * ($viewer->getViewPortTop() +
                $viewer->getViewPortHeight());

            # Set the axis scales
            $c->xAxis->setLinearScale($xScaleMin, $xScaleMax);
            $c->yAxis->setLinearScale($yScaleMin, $yScaleMax);

            # By default, ChartDirector will round the axis scale to the tick position. For zooming, we
            # want to use the exact computed axis scale and so we disable rounding.
            $c->xAxis->setRounding(false, false);
            $c->yAxis->setRounding(false, false);
        }

        # Create the image and save it in a temporary location
        $chartQuery = $c->makeSession($viewer->getId());

        # Include tool tip for the chart
        $imageMap = $c->getHTMLImageMap("", "",
            "title='[{dataSetName}] Alpha = {x|G}, Beta = {value|G}'");

        # Set the chart URL, image map, and chart metrics to the viewer
        $viewer->setImageUrl("getchart.php?".$chartQuery);
        $viewer->setImageMap($imageMap);
        $viewer->setChartMetrics($c->getChartMetrics());
    }

    # Create the WebChartViewer object
    $viewer = new WebChartViewer("chart1");

    if ($viewer->isPartialUpdateRequest()) {
        # Is a partial update request (AJAX chart update)
        processPartialUpdate($viewer);
        # Since it is a partial update, there is no need to output the entire web page. We stream the
        # chart and then terminate the script immediately.
        print($viewer->partialUpdateChart());
        exit();
    } else if ($viewer->isFullUpdateRequest()) {
        # Is a full update request
        processFullUpdate($viewer);
    } else {
        # Is a initial request
        createFirstChart($viewer);
    }
    ?>
    <html>
    <head>
        <title>ChartDirector Zoom and Scroll Demonstration (2)</title>
        <script language="Javascript" src="cdjcv.js"></script>
        <style>
            div.chartPushButtonSelected { padding:5px; background:#ccffcc; cursor:hand; }
            div.chartPushButton { padding:5px; cursor:hand; }
            td.chartPushButton { font-family:Verdana; font-size:9pt; cursor:pointer; border-bottom:#000000 1px solid; }
        </style>
    </head>
    <body leftMargin="0" topMargin="0" onload="initJsChartViewer()">
    <script>
    // Initialize browser side Javascript controls
    function initJsChartViewer()
    {
        // Check if the Javascript ChartViewer library is loaded
        if (!window.JsChartViewer)
            return;

        // Get the Javascript ChartViewer object
        var viewer = JsChartViewer.get('<?php echo $viewer->getId()?>');

        // Connect the mouse usage buttons to the Javascript ChartViewer object
        connectViewerMouseUsage('ViewerMouseUsage1', viewer);

        // Connect the view port window navigation pad to the Javascript ChartViewer object
        connectViewerViewPort('ViewerViewPort1', viewer);

        // Detect if browser is capable of support partial update (AJAX chart update)
        if (JsChartViewer.canSupportPartialUpdate())
            // Browser can support partial update, so connect the view port change event to
            // trigger a partial update
            viewer.attachHandler("ViewPortChanged", viewer.partialUpdate);
        else
            // Browser cannot support partial update - so use full page update
            viewer.attachHandler("ViewPortChanged", function() { document.forms[0].submit(); });
    }
    </script>
    <form method="post">
    <table cellSpacing="0" cellPadding="0" border="0">
        <tr>
            <td align="right" bgColor="#000088" colSpan="2">
                <div style="padding-bottom:2px; padding-right:3px; font-weight:bold; font-size:10pt; font-style:italic; font-family:Arial;">
                    <A style="color:#FFFF00; text-decoration:none" href="http://www.advsofteng.com/">Advanced Software Engineering</a>
                </div>
            </td>
        </tr>
        <tr valign="top">
            <td style="border-left:black 1px solid; border-top:black 1px solid; border-bottom:black 1px solid;" width="130" bgColor="#e0e0e0">
                <!-- The following table is to create 3 cells for 3 buttons. The buttons are used to control
                     the mouse usage mode of the Javascript ChartViewer. -->
                <table id="ViewerMouseUsage1" cellSpacing="0" cellPadding="0" width="100%" border="0">
                    <tr>
                        <td class="chartPushButton">
                            <div class="chartPushButton" id="ViewerMouseUsage1_Scroll" title="Pointer">
                                <img src="pointer.gif" align="absMiddle" width="16" height="16">  Pointer
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="chartPushButton">
                            <div class="chartPushButton" id="ViewerMouseUsage1_ZoomIn" title="Zoom In">
                                <img src="zoomInIcon.gif" align="absMiddle" width="16" height="16">  Zoom In
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="chartPushButton">
                            <div class="chartPushButton" id="ViewerMouseUsage1_ZoomOut" title="Zoom Out">
                                <img src="zoomOutIcon.gif" align="absMiddle" width="16" height="16">  Zoom Out
                            </div>
                        </td>
                    </tr>
                </table>
                <script>
                // Connect the mouse usage buttons to the Javascript ChartViewer
                function connectViewerMouseUsage(controlId, viewer)
                {
                    // A cross browser utility to get the object by id.
                    function getObj(id) { return document.getElementById ? document.getElementById(id) : document.all[id]; }

                    // Set the button styles (colors) based on mouse usage mode
                    function syncButtons()
                    {
                        getObj(controlId + "_Scroll").className = (viewer.getMouseUsage() == JsChartViewer.Scroll) ?
                            "chartPushButtonSelected" : "chartPushButton";
                        getObj(controlId + "_ZoomIn").className = (viewer.getMouseUsage() == JsChartViewer.ZoomIn) ?
                            "chartPushButtonSelected" : "chartPushButton";
                        getObj(controlId + "_ZoomOut").className = (viewer.getMouseUsage() == JsChartViewer.ZoomOut) ?
                            "chartPushButtonSelected" : "chartPushButton";
                    }
                    syncButtons();

                    // Run syncButtons whenever the Javascript ChartViewer is updated
                    viewer.attachHandler("PostUpdate", syncButtons);

                    // Set the Javascript ChartViewer mouse usage mode if the buttons are clicked.
                    getObj(controlId + "_Scroll").onclick = function() { viewer.setMouseUsage(JsChartViewer.Scroll); syncButtons(); }
                    getObj(controlId + "_ZoomIn").onclick = function() { viewer.setMouseUsage(JsChartViewer.ZoomIn); syncButtons(); }
                    getObj(controlId + "_ZoomOut").onclick = function() { viewer.setMouseUsage(JsChartViewer.ZoomOut); syncButtons(); }
                }
                </script>
                <br><br><br><br>
                <!-- The following DIV blocks constitute the view port navigation pad. -->
                <div id="ViewerViewPort1" style="margin: 10px 5px; text-align: center">
                    <div style="border:black 1px solid; padding:0px; margin:0px; width:120px; height:120px; background-color:#c0c0ff;
                        text-align:left">
                        <div id="ViewerViewPort1_ViewPort" style="border:black 1px solid; padding:0px; margin:0px; visibility:hidden;
                            width:60px; height:60px; position:relative; background-color:#c0c0c0">
                            <img style="display:none" height="1" width="1">
                        </div>
                    </div>
                </div>
                <script>
                // Connect the view port navigation pad to the Javascript ChartViewer
                function connectViewerViewPort(controlId, viewer)
                {
                    // A cross browser utility to get the object by id.
                    function getObj(id) { return document.getElementById ? document.getElementById(id) : document.all[id]; }

                    // Get the inner rectangle representing the visible view port
                    var p = getObj(controlId + "_ViewPort");

                    // Set up the mouse down event handler
                    p.onmousedown = ViewerViewPort_startDrag;
                    p.viewer = viewer;

                    // Remember the width and height of the outer container of the navigation pad. The exact definition of
                    // width and height differs depending on browsers (some includes the borders and some exclude the borders).
                    var parent = p.parentElement || p.parentNode;
                    p.parentW = parent.offsetWidth - (document.all ? 2 : 4);
                    p.parentH = parent.offsetHeight - (document.all ? 2 : 4);

                    // Connect the view port to the viewer PostUpdate handler
                    connectViewPortHandler(controlId, viewer);

                    // The navigation pad has been set up, so can display it now.
                    p.style.visibility = "visible";
                }
                // Connect the view port to the viewer PostUpdate handler
                function connectViewPortHandler(controlId, viewer)
                {
                    // Set the navigation pad size and position depending on the Javascript ChartViewer view port state
                    var syncViewPort = function()
                    {
                        // A cross browser utility to get the object by id.
                        function getObj(id) { return document.getElementById ? document.getElementById(id) : document.all[id]; }

                        // Get the inner rectangle representing the visible view port
                        var p = getObj(controlId + "_ViewPort");

                        // Set the size and position based on Javascript ChartViewer view port state
                        p.currentWidth = p.style.width = Math.round(p.parentW * viewer.getViewPortWidth());
                        p.currentHeight = p.style.height = Math.round(p.parentH * viewer.getViewPortHeight());
                        p.currentX = p.style.left = Math.round(p.parentW * viewer.getViewPortLeft());
                        p.currentY = p.style.top = Math.round(p.parentH * viewer.getViewPortTop());
                    }
                    syncViewPort();

                    // Run syncViewPort whenever the Javascript ChartViewer is updated
                    viewer.attachHandler("PostUpdate", syncViewPort);
                }
                // Mouse down event handler
                function ViewerViewPort_startDrag(e)
                {
                    if (document.onmousemove != ViewerViewPort_mouseMove)
                    {
                        // Remember the current onmousemove and onmouseup event handler and replace them
                        // with our own handler
                        document.ViewerViewPort_onmousemovesave = document.onmousemove;
                        document.ViewerViewPort_onmouseupsave = document.onmouseup;
                        document.onmousemove = ViewerViewPort_mouseMove;
                        document.onmouseup = ViewerViewPort_endDrag;
                    }

                    // Remember the mouse down position
                    document.ViewerViewPort_dragObj = this;
                    this.refX = this.currentX - (window.event || e).clientX;
                    this.refY = this.currentY - (window.event || e).clientY;
                }
                // Mouse move event handler
                function ViewerViewPort_mouseMove(e)
                {
                    // Set the position of the navigation pad depending on how far the mouse has been dragged
                    var obj = document.ViewerViewPort_dragObj;
                    obj.currentX = obj.style.left =
                        Math.max(0, Math.min(obj.refX + (window.event || e).clientX, obj.parentW - obj.currentWidth));
                    obj.currentY = obj.style.top =
                        Math.max(0, Math.min(obj.refY + (window.event || e).clientY, obj.parentH - obj.currentHeight));
                    return false;
                }
                // Mouse up event handler
                function ViewerViewPort_endDrag(e)
                {
                    // Restore the previous nmousemove and onmouseup event handler
                    document.onmousemove = document.ViewerViewPort_onmousemovesave;
                    document.onmouseup = document.ViewerViewPort_onmouseupsave;

                    // Set the new view port position based on the mouse position
                    var obj = document.ViewerViewPort_dragObj;
                    var newVpLeft = obj.currentX / (obj.parentW - obj.currentWidth) * (1 - obj.viewer.getViewPortWidth());
                    var newVpTop = obj.currentY / (obj.parentH - obj.currentHeight) * (1 - obj.viewer.getViewPortHeight());

                    // Change the view port only when the new view port position is really different from
                    // existing position (to avoid unnecessary partial or full update)
                    if ((Math.abs(obj.viewer.getViewPortLeft() - newVpLeft) > 0.0000001) ||
                        (Math.abs(obj.viewer.getViewPortTop() - newVpTop) > 0.0000001))
                    {
                        obj.viewer.setViewPortLeft(newVpLeft);
                        obj.viewer.setViewPortTop(newVpTop);
                        obj.viewer.applyHandlers("ViewPortChanged");
                    }
                }
                </script>
            </td>
            <td style="border: black 1px solid; background-color: #c0c0ff">
                <div style="padding:5px">
                    <!-- ****** Here is the chart image ****** -->
                    <?php echo $viewer->renderHTML()?>
                </div>
            </td>
        </tr>
    </table>
    </form>
    </body>
    </html>

© 2006 Advanced Software Engineering Limited. All rights reserved.