ASE Home Page Products Download Purchase Support About ASE
ChartDirector Support
Forum HomeForum Home   SearchSearch

Message ListMessage List     Post MessagePost Message

  Discrete Heatmap with viewport.
Posted by Treavor Gagne on Aug-14-2021 05:56
I assume it's possible to have a Discrete Heatmap with a time on the x axis with a viewport. If someone has gotten this to work in past could they let me know and provide an example too? I'm working in PHP and javascript. Specifically, at the moment I am unable to get the discreteheatmap.php demo features into the viewportcontroldemo.php which I'm using as a base template of my code.

  Re: Discrete Heatmap with viewport.
Posted by Peter Kwan on Aug-15-2021 10:12
Hi Treavor,

Yes, you can use date/time axis. There is a XYChart.addDiscreteHeatMapLayer2 that allows you to specify the x and y grids of the cells in terms of axis units, which can be date/time.

https://www.advsofteng.com/doc/cdphp.htm#XYChart.addDiscreteHeatMapLayer2.htm

I will try to create an example on Monday.

Regards
Peter Kwan

  Re: Discrete Heatmap with viewport.
Posted by Peter Kwan on Aug-17-2021 01:15
Attachments:
Hi Treavor,

I have just modified the viewportzoomscroll.php to use a discrete heat map instead of a line layer, and it appears to work OK. I have attached a screen shot and my modified code for your reference.

Regards
Peter Kwan
zoomscrollheatmap.png
viewportcontroldemo.php
<?php
require_once("../lib/phpchartdir.php");

#
# Initialize the WebChartViewer when the page is first loaded
#
function initViewer($viewer) {
    # The full x-axis range is from Jan 1, 2020 to Jan 1, 2021
    $startDate = chartTime(2020, 1, 1);
    $endDate = chartTime(2021, 1, 1);
    $viewer->setFullRange("x", $startDate, $endDate);

    # Initialize the view port to show the last 61 days (out of 366 days)
    $viewer->setViewPortWidth(61 / 366.0);
    $viewer->setViewPortLeft(1 - $viewer->getViewPortWidth());

    # Set the maximum zoom to 10 days (out of 366 days)
    $viewer->setZoomInWidthLimit(10.0 / 366.0);
}

#
# Create a random table for demo purpose.
#
function getRandomTable() {
    $r = new RanTable(127, 11, 367);
    $r->setDateCol(0, chartTime(2020, 1, 1), 86400);
	 
	# 10 rows of cells
	for ($i = 1; $i <= 10; ++$i)
		$r->setCol($i, 0, 100);
    return $r;
}

#
# Draw the chart
#
function drawChart($viewer) {
    # Determine the visible x-axis range
    $viewPortStartDate = $viewer->getValueAtViewPort("x", $viewer->getViewPortLeft());
    $viewPortEndDate = $viewer->getValueAtViewPort("x", $viewer->getViewPortRight());

    # We need to get the data within the visible x-axis range. In real code, this can be by using a
    # database query or some other means as specific to the application. In this demo, we just
    # generate a random data table, and then select the data within the table.
    $r = getRandomTable();

    # Select the data for the visible date range viewPortStartDate to viewPortEndDate. It is
    # possible there is no data point at exactly viewPortStartDate or viewPortEndDate. In this case,
    # we also need the data points that are just outside the visible date range to "overdraw" the
    # line a little bit (the "overdrawn" part will be clipped to the plot area) In this demo, we do
    # this by adding a one day margin to the date range when selecting the data.
    $r->selectDate(0, $viewPortStartDate - 86400, $viewPortEndDate + 86400);

    # The selected data from the random data table
    $timeStamps = $r->getCol(0);
	
	# 10 rows of cells
	$yGrid = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 
	
	# In the discrete heat map, the cell data are supplied in a single array
	$data = array();
	for ($i = 1; $i <=10; ++$i) {	
		$data = array_merge($data, $r->getCol($i));
		# The number of cells is one less than the number of grids. 
		array_pop($data);
	}
	
    #
    # Now we have obtained the data, we can plot the chart.
    #

    #================================================================================
    # Configure overall chart appearance.
    #================================================================================

    # Create an XYChart object of size 640 x 400 pixels
    $c = new XYChart(640, 400);

    # Set the plotarea at (55, 55) with width 80 pixels less than chart width, and height 90 pixels
    # less than chart height. Use a vertical gradient from light blue (f0f6ff) to sky blue (a0c0ff)
    # as background. Set border to transparent and grid lines to white (ffffff).
    $c->setPlotArea(55, 25, $c->getWidth() - 80, $c->getHeight() - 90, $c->linearGradientColor(0,
        55, 0, $c->getHeight() - 35, 0xf0f6ff, 0xa0c0ff), -1, Transparent, 0xffffff, 0xffffff);

    # As the data can lie outside the plotarea in a zoomed chart, we need to enable clipping.
    $c->setClipping();

    # Add a title box using dark grey (0x333333) 18pt Arial Bold font
    $c->addTitle("   Zooming and Scrolling with Viewport Control", "Arial Bold", 15, 0x333333);
	
    if ($viewer->isAttachmentRequest()) {
        $b = $c->addLegend(55, 28, false, "Arial Bold", 10);
        $b->setBackground(Transparent, Transparent);
        $b->setLineStyleKey();
    }

    # Set the x and y axis stems to transparent and the label font to 10pt Arial
    $c->xAxis->setColors(Transparent);
    $c->yAxis->setColors(Transparent);
    $c->xAxis->setLabelStyle("Arial", 10);
    $c->yAxis->setLabelStyle("Arial", 10);

    # Add axis title using 10pt Arial Bold font
    $c->yAxis->setTitle("Ionic Temperature (C) ", "Arial Bold", 10);

    #================================================================================
    # Add data to chart
    #================================================================================

	$layer = $c->addDiscreteHeatMapLayer2($timeStamps, $yGrid, $data);
	$layer->setBorderColor(SameAsMainColor);

    #================================================================================
    # Configure axis scale and labelling
    #================================================================================

    # Set the x-axis as a date/time axis with the scale according to the view port x range.
    $viewer->syncDateAxisWithViewPort("x", $c->xAxis);
	
    # For the automatic y-axis labels, set the minimum spacing to 30 pixels.
    $c->yAxis->setTickDensity(30);

    #
    # In this demo, the time range can be from a few years to a few days. We demonstrate how to set
    # up different date/time format based on the time range.
    #

    # If all ticks are yearly aligned, then we use "yyyy" as the label format.
    $c->xAxis->setFormatCondition("align", 360 * 86400);
    $c->xAxis->setLabelFormat("{value|yyyy}");

    # If all ticks are monthly aligned, then we use "mmm yyyy" in bold font as the first label of a
    # year, and "mmm" for other labels.
    $c->xAxis->setFormatCondition("align", 30 * 86400);
    $c->xAxis->setMultiFormat(StartOfYearFilter(), "<*font=bold*>{value|mmm<*br*>yyyy}",
        AllPassFilter(), "{value|mmm}");

    # If all ticks are daily algined, then we use "mmm dd<*br*>yyyy" in bold font as the first label
    # of a year, and "mmm dd" in bold font as the first label of a month, and "dd" for other labels.
    $c->xAxis->setFormatCondition("align", 86400);
    $c->xAxis->setMultiFormat(StartOfYearFilter(),
        "<*block,halign=left*><*font=bold*>{value|mmm dd<*br*>yyyy}", StartOfMonthFilter(),
        "<*font=bold*>{value|mmm dd}");
    $c->xAxis->setMultiFormat2(AllPassFilter(), "{value|dd}");

    # For all other cases (sub-daily ticks), use "hh:nn<*br*>mmm dd" for the first label of a day,
    # and "hh:nn" for other labels.
    $c->xAxis->setFormatCondition("else");
    $c->xAxis->setMultiFormat(StartOfDayFilter(), "<*font=bold*>{value|hh:nn<*br*>mmm dd}",
        AllPassFilter(), "{value|hh:nn}");

    #================================================================================
    # Step 5 - Output the chart
    #================================================================================

    if ($viewer->isAttachmentRequest()) {
        # Output as PDF attachment
        $viewer->streamAttachment($c->makeChart2(PDF), "viewportcontroldemo.pdf");
        exit();
    } else {
        # Output the chart
        $viewer->setChart($c, SVG);
    }
}

function drawFullChart($vp, $viewer) {
    # We need to draw a small thumbnail chart for the full data range. The simplest method is to
    # simply get the full data to draw the chart. If the full data are very large (eg. millions of
    # points), for such a small thumbnail chart, it is often acceptable to just retreive a small
    # sample of the data.
    #
    # In this example, there are only around 5500 points for the 3 data series. This amount is not
    # large to ChartDirector, so we simply pass all the data to ChartDirector.
    $r = getRandomTable();

	# The selected data from the random data table
    $timeStamps = $r->getCol(0);
	
	# 10 rows of cells
	$yGrid = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 
	
	# In the discrete heat map, the cell data are supplied in a single array
	$data = array();
	for ($i = 1; $i <=10; ++$i) {	
		$data = array_merge($data, $r->getCol($i));
		# The number of cells is one less than the number of grids. 
		array_pop($data);
	}
	
    # Create an XYChart object of size 640 x 80 pixels
    $c = new XYChart(640, 80);

    # Set the plotarea with the same horizontal position as that in the main chart for alignment.
    $c->setPlotArea(55, 0, $c->getWidth() - 80, $c->getHeight() - 20, 0xc0d8ff, -1, 0x888888,
        Transparent, 0xffffff);

    # Set the x axis stem to transparent and the label font to 10pt Arial
    $c->xAxis->setColors(Transparent);
    $c->xAxis->setLabelStyle("Arial", 10);

    # Use setLabelAlignment to put the label at the right side of the tick.
    $c->xAxis->setLabelAlignment(1);

    # Set the y axis stem and labels to transparent (that is, hide the labels)
    $c->yAxis->setColors(Transparent, Transparent);

    # Add a discrete heat map layer for the lines with fast line mode enabled
	$layer = $c->addDiscreteHeatMapLayer2($timeStamps, $yGrid, $data);
	$layer->setBorderColor(SameAsMainColor);

    # The x axis scales should reflect the full range of the view port
    $c->xAxis->setDateScale($viewer->getValueAtViewPort("x", 0), $viewer->getValueAtViewPort("x", 1)
        );

    # For the automatic x-axis labels, set the minimum spacing to 75 pixels.
    $c->xAxis->setTickDensity(75);

    # For the auto-scaled y-axis, as we hide the labels, we can disable axis rounding. This can make
    # the axis scale fit the data tighter.
    $c->yAxis->setRounding(false, false);

    # Output the chart
    $vp->setChart($c, SVG);
}

#
# This script handles both the full page request, as well as the subsequent partial updates (AJAX
# chart updates). We need to determine the type of request first before we processing it.
#

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

if ($viewer->isPartialUpdateRequest()) {
    # Is a partial update request. Draw the chart and perform a partial response.
    drawChart($viewer);
    print($viewer->partialUpdateChart());
    exit();
}

#
# If the code reaches here, it is a full page request.
#

# Initialize the WebChartViewer and draw the chart.
initViewer($viewer);
drawChart($viewer);

# Create the WebViewPortControl object
$viewPortCtrl = new WebViewPortControl("fullchart1");
drawFullChart($viewPortCtrl, $viewer);
?>

<!DOCTYPE html>
<html>
<head>
    <title>Zooming and Scrolling with Viewport Control</title>
    <script type="text/javascript" src="cdjcv.js"></script>
    <style type="text/css">
        .chartButton { font:12px Verdana; border-bottom:#000000 1px solid; padding:5px; cursor:pointer;}
        .chartButtonSpacer { font:12px Verdana; border-bottom:#000000 1px solid; padding:5px;}
        .chartButton:hover { box-shadow:inset 0px 0px 0px 2px #444488; }
        .chartButtonPressed { background-color: #CCFFCC; }
    </style>
</head>
<body style="margin:0px;">
<script type="text/javascript">

//
// Execute the following initialization code after the web page is loaded
//
JsChartViewer.addEventListener(window, 'load', function() {
    // Update the chart when the view port has changed (eg. when the user zooms in using the mouse)
    var viewer = JsChartViewer.get('<?php echo $viewer->getId() ?>');
    viewer.attachHandler("ViewPortChanged", viewer.partialUpdate);

    // Initialize the navigation pad
    JsViewPortControl.get('<?php echo $viewPortCtrl->getId() ?>').setViewer(viewer);
});

//
// This method is called when the user clicks on the Pointer, Zoom In or Zoom Out buttons
//
function setMouseMode(mode)
{
    var viewer = JsChartViewer.get('<?php echo $viewer->getId() ?>');
    if (mode == viewer.getMouseUsage())
        mode = JsChartViewer.Default;

    // Set the button color based on the selected mouse mode
    document.getElementById("scrollButton").className = "chartButton" +
        ((mode  == JsChartViewer.Scroll) ? " chartButtonPressed" : "");
    document.getElementById("zoomInButton").className = "chartButton" +
        ((mode  == JsChartViewer.ZoomIn) ? " chartButtonPressed" : "");
    document.getElementById("zoomOutButton").className = "chartButton" +
        ((mode  == JsChartViewer.ZoomOut) ? " chartButtonPressed" : "");

    // Set the mouse mode
    viewer.setMouseUsage(mode);
}

//
// This method is called when the user clicks on the buttons that selects the last NN days
//
function setTimeRange(duration)
{
    var viewer = JsChartViewer.get('<?php echo $viewer->getId() ?>');

    // Set the view port width to represent the required duration (as a ratio to the total x-range)
    viewer.setViewPortWidth(Math.min(1,
        duration / (viewer.getValueAtViewPort("x", 1) - viewer.getValueAtViewPort("x", 0))));

    // Set the view port left so that the view port is moved to show the latest data
    viewer.setViewPortLeft(1 - viewer.getViewPortWidth());

    // Trigger a view port change event
    viewer.raiseViewPortChangedEvent();
}

</script>
<form method="post" id="ZoomScrollTrack" runat="server">
<table cellspacing="0" cellpadding="0" style="border:black 1px solid;">
    <tr>
        <td align="right" colspan="2" style="background:#000088; color:#ffff00; padding:0px 4px 2px 0px;">
            <a style="color:#FFFF00; font:italic bold 10pt Arial; text-decoration:none" href="http://www.advsofteng.com/">
                Advanced Software Engineering
            </a>
        </td>
    </tr>
    <tr valign="top">
        <td style="width:130px; background:#c0c0ff;">
           <div style="width:130px">
            <!-- The following table is to create 3 cells for 3 buttons to control the mouse usage mode. -->
            <table style="width:100%; padding:0px; border:0px; border-spacing:0px;">
                <tr>
                    <td class="chartButton" id="scrollButton" onclick="setMouseMode(JsChartViewer.Scroll)"
                        ontouchstart="this.onclick(event); event.preventDefault();">
                        <img src="scrollew.gif" style="vertical-align:middle" alt="Drag" />&nbsp;&nbsp;Drag to Scroll
                    </td>
                </tr>
                <tr>
                    <td class="chartButton" id="zoomInButton" onclick="setMouseMode(JsChartViewer.ZoomIn)"
                        ontouchstart="this.onclick(event); event.preventDefault();">
                        <img src="zoomInIcon.gif" style="vertical-align:middle" alt="Zoom In" />&nbsp;&nbsp;Zoom In
                    </td>
                </tr>
                <tr>
                    <td class="chartButton" id="zoomOutButton" onclick="setMouseMode(JsChartViewer.ZoomOut)"
                        ontouchstart="this.onclick(event); event.preventDefault();">
                        <img src="zoomOutIcon.gif" style="vertical-align:middle" alt="Zoom Out" />&nbsp;&nbsp;Zoom Out
                    </td>
                </tr>
                <tr>
                    <td class="chartButtonSpacer">
                        <div style="padding:2px">&nbsp;</div>
                    </td>
                </tr>
                <tr>
                    <td class="chartButton" onclick="setTimeRange(30 * 86400);"
                        ontouchstart="this.onclick(event); event.preventDefault();">
                        <img src="goto.gif" style="vertical-align:middle" alt="Last 30 days" />&nbsp;&nbsp;Last 30 days
                    </td>
                </tr>
                <tr>
                    <td class="chartButton" onclick="setTimeRange(90 * 86400);"
                        ontouchstart="this.onclick(event); event.preventDefault();">
                        <img src="goto.gif" style="vertical-align:middle" alt="Last 90 days" />&nbsp;&nbsp;Last 90 days
                    </td>
                </tr>
                <tr>
                    <td class="chartButton" onclick="setTimeRange(366 * 86400);"
                        ontouchstart="this.onclick(event); event.preventDefault();">
                        <img src="goto.gif" style="vertical-align:middle" alt="Last Year" />&nbsp;&nbsp;Last Year
                    </td>
                </tr>
                <tr>
                    <td class="chartButton" onclick="setTimeRange(1E15);"
                        ontouchstart="this.onclick(event); event.preventDefault();">
                        <img src="goto.gif" style="vertical-align:middle" alt="All Time" />&nbsp;&nbsp;All Time
                    </td>
                </tr>
            </table>
            <br />
            <br />
            <br />
            <br />
            <div style="text-align:center;">
                <input type="button" value="Download PDF" style="font:13px Arial; width:124px;"
                    onclick="JsChartViewer.get('<?php echo $viewer->getId() ?>').partialUpdateAsAttachment();" />
            </div>
        </div>
        </td>
        <td style="border-left:black 1px solid; padding:10px 5px 10px 5px;">
            <!-- ****** Here is the chart image ****** -->
            <?php echo $viewer->renderHTML() ?><br />
            <!-- ****** Here is the view port control ****** -->
            <?php echo $viewPortCtrl->renderHTML() ?>
        </td>
    </tr>
</table>
</form>
</body>
</html>

  Re: Discrete Heatmap with viewport.
Posted by Treavor Gagne on Aug-17-2021 02:24
Wow, thank you so much this is great!