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 source code listing of this sample program is included at the end of this section. The code consists of the following main parts:

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.

FileDescription
zoomscrolldemo2.phpMain source code for this demo.
pointer.gif
zoomInIcon.gif
zoomOutIcon.gif
The images used in the push buttons in this demo.
getchart.phpA 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>