phplot/ImageMaps.txt - Documentation for an experimental feature Last updated for PHPlot-5.7.0 on 2012-02-20 The project home page is http://sourceforge.net/projects/phplot/ ----------------------------------------------------------------------------- Overview: This file documents a new, experimental callback which allows scripts using PHPlot to generate image maps with linked areas covering the plotted data. This is planned for release in PHPlot-5.7.0 as a experimental feature. NOTICE: The callback documented in this file is experimental. This means anything about it may change in future releases, in ways that might be incompatible with the current implementation, or the feature might even be removed completely. The new feature is not yet documented in the PHPlot Reference Manual. This text file is the only documentation. Feedback on this feature is welcome. Please use the "Help & Discussion" forum at http://sourceforge.net/projects/phplot/ ----------------------------------------------------------------------------- Overview: A new callback is introduced to make the device coordinates of plotted data available to a calling script. (See the PHPlot Reference Manual for more on callbacks, and device coordinates.) If a script registers a function as a handler for the 'data_points' callback name, the function will be called repeatedly when the plot is being generated. Each call will contain parameters describing the graphics for the corresponding data point. These parameters can be used to generate an image map. This feature is most likely only useful when generating embedded image data with PHPlot. Refer to the function reference for EncodeImage() in the PHPlot Reference Manual for details on embedded image data. (See the Notes at the end of this document for use with non-embedded images.) The data_points callback works with the following plot types: + bars and stackedbars (both horizontal and vertical) + points and linepoints (both standard and error bar plots) + pie + bubbles + ohlc, candlesticks, and candlesticks2 No other plot type is supported. This includes lines, squared, thinbarline, area, and stackedarea. (These plot types do not have well-defined areas which could map to data points.) The rest of this document contains details on the callback, usage with each plot type, and examples. ----------------------------------------------------------------------------- General Usage: To register a callback function to handle 'data_points', use: $plot->SetCallback('data_points', 'my_handler', $my_arg); where my_handler is the name of your function, $my_arg is an optional additional argument to pass to your function ('my_handler'). The data_points callback function (my_handler above) will be called with 5 fixed-purpose arguments followed by variable arguments. The number and purpose of the variable arguments depends on the plot type. If your handler only needs to work with one specific plot type, you can declare these arguments in your function definition. If your handler must handle multiple plot types, define your function without arguments and use func_get_arg() or func_get_args() to access the arguments. You can use the value of the 3rd argument, $shape, to determine the number and usage of the variable arguments, since there is one specific function argument set for each value of $shape. See "Summary of data_points callback parameters..." for details. Here are the fixed arguments to the data_points callback: function my_handler($img, $passthru, $shape, $row, $column, ...) $img : The image resource (standard for all callbacks). Not typically used with a data_points callback. $passthru : Pass-through argument, supplied in the SetCallback() call (standard for all callbacks, referred to above as $my_arg). You can use this however you want, or ignore it. $shape : A word describing the shape of the area being described. Note: this does not always correspond to an HTML image map shape. Each plot type passes a specific $shape value, and the $shape value defines the usage of the variable arguments. $row : The number of the data row being plotted. This typically corresponds to the ordinal of the X axis values, starting with 0 for the first point, 1 for the second point, etc. $column : The column, or data set index, starting with 0 for the first data set. This indicates which Y value for a given X, for example, or which bar within a bar group, or segment within a stacked bar. Always 0 for pie charts and OHLC plots, which only have a single data set. For the variable arguments, refer to the sections below which describe each plot type. ----------------------------------------------------------------------------- Usage with bar and stackedbar plots: With plot types 'bars' and 'stackedbars', an image map can be produced which indicates the area of each bar or stacked bar segment. This works with both vertical and horizontal plots. Arguments to the 'data_points' callback for bar and stackedbar plots: function my_handler($img, $passthru, $shape, $row, $column, $x1, $y1, $x2, $y2) The first 5 arguments are as described above for all plot types. $shape : is always 'rect' $row : This is the bar group index. $column : This is the bar index within a group. For vertical plot type 'bars', index 0 is the left-most in the group. For vertical plot type 'stackedbars', index 0 is the bar segment closest to the X axis. $x1, $y1 : Device coordinates of the upper left corner of a bar/segment. $x2, $y2 : Device coordinates of the lower right corner of a bar/segment. Generating an image map for bar and stackedbar plots: The provided $shape and coordinates are compatible with HTML markup. Here is an example of generating one area in an image map from a bar or stackedbar plot. You must provide the URL, alternate text, and optionally a title (tooltip text). This appends the image map line to a string. sprintf() is used to convert the coordinates to integers for cleaner HTML. $coords = sprintf("%d,%d,%d,%d", $x1, $y1, $x2, $y2); $image_map .= " \"$alt_text\"\n"; The $url, $title_text, and $alt_text would typically depend on the passed $row and $column. Refer to Example 1 in this document for a complete example. ----------------------------------------------------------------------------- Usage with points and linepoints plots: With plot types 'points' and 'linepoints', an image map can be produced which indicates the area around each point. (The lines in a linepoints plot are not part of the image map areas.) This also works with error plots that use these plot types, however the error bars are not part of the image map areas. Arguments to the 'data_points' callback for points and linepoints plots: function my_handler($img, $passthru, $shape, $row, $column, $x, $y) The first 5 arguments are as described above for all plot types. $shape : is always 'dot' $row : Row - the index for the independent variable (usually X) $column : Column - the index for the dependent variable (usually Y) $x, $y : Device coordinates of the center of the point marker. Generating an image map for points and linepoints plots: PHPlot provides only the center coordinates for each point marker. It does not indicate the shape or size of the marker, nor the coordinates of any line segments or error bars. To generate an image map, you should use a fixed radius size and define the image map areas as circles with that radius centered around each data point. You need to choose a radius. A larger radius provides a larger clickable area, but the area of adjacent points may overlap. Here is an example of generating one area in an image map from a points or linepoints plot. You must provide the URL, alternate text, and optionally a title (tooltip text). This appends the image map line to a string. sprintf() is used to convert the coordinates to integers for cleaner HTML. This example uses a 20 pixel radius for the areas around each data point. define('MAP_RADIUS', 20); // Capture area circle radii $coords = sprintf("%d,%d,%d", $x, $y, MAP_RADIUS); $image_map .= " \"$alt_text\"\n"; The $url, $title_text, and $alt_text would typically depend on the passed $row and $column. Other than the above code, a script to generate an image map for a points or linepoints plot would be similar to Example 1 in this document, which produces a bar chart. ----------------------------------------------------------------------------- Usage with pie charts: With plot type 'pie', an image map can be produced which indicates the area of each pie segment. However, PHPlot only supplies the values which identify the outline of the pie segment, and HTML maps do not support areas of this shape. Therefore, the callback handler function needs to generate one or more polygons which approximate the pie segment area. Note: PHPlot does not attempt to draw pie segments that are too small (due to the implementation of the PHP/GD drawing function). If a segment's calculated start angle and end angle are equal (after converting to integers), the segment will not be drawn, and the data_points callback will not be called for that segment. Arguments to the 'data_points' callback for pie charts: function my_handler($img, $passthru, $shape, $row, $column, $x, $y, $pie_width, $pie_height, $start_angle, $end_angle) The first 5 arguments are as described above for all plot types. $shape : is always 'pie' $row : The pie segment index, starting at 0 for the first segment, 1 for the next segment (counter-clockwise), etc. $column : Always 0 (not used). $x, $y : Center of the pie, in device coordinates. $pie_width : Horizontal diameter of the pie, in pixels. $pie_height : Vertical diameter of the pie, in pixels. $start_angle : Starting angle for the segment, in clockwise degrees. $end_angle : Ending angle for the segment, in clockwise degrees. Note: "clockwise degrees" means 360-A where A is an angle measured in the conventional sense, from the X axis counter-clockwise. For example, a pie segment that starts at the top of the pie ("North") and ends at the bottom point ("South") would have start_angle=270 and end_angle=90. The start_angle is always greater than the end angle. Pie segments are drawn in order, counter-clockwise starting at the "East" position. Generating an image map for pie charts: The pie segment is defined by the ellipse center point ($x, $y), the two diameter values ($pie_width, $pie_height), and the start and end angles. Due to the PHP/GD implementation, PHPlot uses clockwise angles (360-A), so the start_angle is greater than the end_angle, and the segment is drawn clockwise from the end_angle to the start_angle. HTML image maps do not support this shape directly. Instead, your callback function can approximate the area using one or more polygons. The method shown in Example 2 in this document approximates the pie segment area using a polygon with one point at the pie center, one point at each end of the arc, and zero or more points along the arc , such that the maximum separation of points along the circumference is no more than 20 degrees. This has been found to produce a good fit for image maps. More details on the method can be found in the example below. Once you have your points (converted to integers, in an array $points with X and Y values alternating), you can generate a line in the image map as follows: $coords = implode(',', $points); $image_map .= " \"$alt_text\"\n"; The $url, $title_text, and $alt_text would typically depend on the passed $row. Refer to Example 2 in this document for a complete example. ----------------------------------------------------------------------------- Usage with bubble plots: With plot type 'bubbles', an image map can be produced which indicates the area of each bubble. Arguments to the 'data_points' callback for bubble plots: function my_handler($img, $passthru, $shape, $row, $column, $x, $y, $diam) The first 5 arguments are as described above for all plot types. $shape : is always 'circle' $row : The index of the data row, starting with 0 for the first X value, 1 for the second X value, etc. $column : The data set index. This is 0 for the first Y value for a given X, 1 for the second Y value for that same X, etc. $x, $y : Center of the bubble, in device coordinates. $diam : Diameter of the bubble, in pixels. Generating an image map for bubble plots: The $shape and parameters are almost compatible with HTML image map markup, except that PHPlot provides a diameter, and the radius is needed. Here is an example of generating one area in an image map from a bubble plot. You must provide the URL, alternate text, and optionally a title (tooltip text). This appends the image map line to a string. sprintf() is used to convert the coordinates to integers for cleaner HTML. $coords = sprintf("%d,%d,%d", $x, $y, $diam / 2); $image_map .= " \"$alt_text\"\n"; The $url, $title_text, and $alt_text would typically depend on the passed $row and $column. Other than the above code, a script to generate an image map for a bubbles plot would be similar to Example 1 in this document, which produces a bar chart. ----------------------------------------------------------------------------- Usage with OHLC plots: With plot types 'ohlc', 'candlesticks', and 'candlesticks2', an image map can be produced which indicates the area of each data point. For candlesticks and candlesticks2 plots, this area is the bounding box containing the candlestick and its wicks. For ohlc plots, this area is the bounding box formed by the vertical line and horizontal ticks. Arguments to the 'data_points' callback for OHLC plots: function my_handler($img, $passthru, $shape, $row, $column, $x1, $y1, $x2, $y2) The first 5 arguments are as described above for all plot types. $shape : is always 'rect' $row : The index of the data row, starting with 0 for the first X value, 1 for the second X value, etc. $column : Always 0 (not used). $x1, $y1 : Device coordinates of the upper left corner $x2, $y2 : Device coordinates of the lower right corner Generating an image map for OHLC plots: The provided $shape and coordinates are compatible with HTML markup. Here is an example of generating one area in an image map from an OHLC plot. You must provide the URL, alternate text, and optionally a title (tooltip text). This appends the image map line to a string. sprintf() is used to convert the coordinates to integers for cleaner HTML. $coords = sprintf("%d,%d,%d,%d", $x1, $y1, $x2, $y2); $image_map .= " \"$alt_text\"\n"; The $url, $title_text, and $alt_text would typically depend on the passed $row. Example 1 in this document contains a complete example for bar charts, and the parameters and usage for OHLC plots is the same. ----------------------------------------------------------------------------- Summary of data_points callback parameters: Points, Linepoints : $img, $passthru, $shape='dot', $row, $column, $x, $y Note: x,y is center. You must supply a radius for . Bars, Stackedbars: $img, $passthru, $shape='rect', $row, $column, $x1, $y1, $x2, $y2 Pie: $img, $passthru, $shape='pie', $row, 0, $x, $y, $width, $height, $start_angle, $end_angle Note: You must fit a polygon to the arc segment for an image map. Bubbles: $img, $passthru, $shape='circle', $row, $column, $x, $y, $diam Note: x,y is center; use diam/2 for radius in . OHLC, Candlesticks, Candlesticks2: $img, $passthru, $shape='rect', $row, 0, $x1, $y1, $x2, $y2 ----------------------------------------------------------------------------- Example 1: Embedded image bar chart with image map This produces an HTML page with an embedded image containing a bar chart, with an image map. "Tool tip" float-over text identifies the bars and groups, and clicking on a bar provides a popup alert window with the same information. line in the image map: function store_map($im, $passthru, $shape, $row, $column, $x1, $y1, $x2, $y2) { global $image_map; $title = "Group $row, Bar $column"; // Title, also tool-tip text $alt = "Region for group $row, bar $column"; // Required alt-text $href = "javascript:alert('($row, $column)')"; // Demo URL $coords = sprintf("%d,%d,%d,%d", $x1, $y1, $x2, $y2); // Convert to integers $image_map .= " \"$alt\"\n"; } $plot = new PHPlot(640, 480); $plot->SetFailureImage(False); // No error images $plot->SetPrintImage(False); // No automatic output $plot->SetTitle("PHPlot Example: Bar Chart with Image Map"); $plot->SetImageBorderType('plain'); $plot->SetDataValues($data); $plot->SetDataType('text-data'); $plot->SetPlotType('bars'); $plot->SetCallback('data_points', 'store_map'); $plot->SetPlotAreaWorld(NULL, 0, NULL, 100); $plot->DrawGraph(); ?> PHPlot Example: Bar Chart with Image Map

PHPlot Example: Bar Chart with Image Map

This is a plot with image map and tooltip text.

Plot Image ----------------------------------------------------------------------------- Example 2: Embedded image pie chart with image map This produces an HTML page with an embedded image containing a pie chart, with an image map. The pie segment shapes are interpolated with polygon points. The image map provides tool-tip text and a clickable area producing an alert box for each segment. This example also shows use of the data values. The callback handler accesses the data array to get the current segment value and uses that in the tool-top text and alert box. This is dependent on the data type of the array, and the code shown here only works with data type text-data-single =0 additional points on the circumference*, spaced no more than 20 degrees apart. (20 is chosen by trial and error for a reasonable fit.) So small segments will be approximated by a single triangle. Larger segments will have more vertices. *Note: These points are actually slightly outside the circumference. This is done by increasing the two radius values by a small amount (2 pixels). This produces a better fit, for the case where we want to make sure all the interior is covered, even if some of the exterior is also included. (Using the actual radii would result in the area omitting a small part of the pie interior. For an image map, this would result in dead spaces.) The segment subdivisions are made to have about equal angles. This results in a closer fit. For example, with a maximum sub-segment arc of 20 degrees, and a segment of 24 degrees, we make two 12 degree sub-segments rather than a 20 degree and a 4 degree. Note: Web image map coordinates have 0,0 in upper left, so Y is reversed. The pass-through argument gets the data array. This is used to include the pie segment value in the URL and/or tooltip. This will only work with data type text-data-single, where array values map 1:1 to segment values. */ function store_map($im, $data, $shape, $segment, $unused, $xc, $yc, $wd, $ht, $start_angle, $end_angle) { global $image_map; // Choose the largest step_angle <= 20 degrees that divides the segment // into equal parts. (20 degrees is chosen as a threshold.) // Note start_angle > end_angle due to reversal (360-A) of arguments. $arc_angle = $start_angle - $end_angle; $n_steps = (int)ceil($arc_angle / 20); $step_angle = $arc_angle / $n_steps; // Radius along horizontal and vertical, plus a tiny adjustment factor. $rx = $wd / 2 + 2; $ry = $ht / 2 + 2; // Push the initial point into the array: the center of the pie. $points = array($xc, $yc); // Loop by step_angle from end_angle to start_angle. // Don't use "$theta += $step_angle" because of cumulative error. // Note $theta and $done_angle are in radians; $step_angle and $end_angle // are in degrees. $done_angle = deg2rad($start_angle); for ($i = 0; ; $i++) { // Advance to next step, but not past the end: $theta = min($done_angle, deg2rad($end_angle + $i * $step_angle)); // Generate a point at the current angle: $points[] = (int)($xc + $rx * cos($theta)); $points[] = (int)($yc + $ry * sin($theta)); // All done after generating a point at done_angle. if ($theta >= $done_angle) break; } // Demonstration data: Title (and tool-tip text), alt text, URL: // Fetch segment value from data arrayL $value = $data[$segment][1]; $title = "Segment $segment = $value"; $alt = "Region for segment $segment"; $href = "javascript:alert('Segment $segment = $value')"; $coords = implode(',', $points); // Generate the image map area: $image_map .= " \"$alt\"\n"; } $plot = new PHPlot(640, 480); // Disable error images, since this script produces HTML: $plot->SetFailureImage(False); // Disable automatic output of the image: $plot->SetPrintImage(False); // Set up the rest of the plot: $plot->SetTitle("PHPlot Example: Pie Chart with Image Map"); $plot->SetImageBorderType('plain'); $plot->SetDataValues($data); $plot->SetDataType('text-data-single'); $plot->SetPlotType('pie'); // Set the data_points callback which will generate the image map. // Include the data array as the pass-through argument, for tooltip text: $plot->SetCallback('data_points', 'store_map', $data); // Produce the graph; this also creates the image map via callback: $plot->DrawGraph(); // Now output the HTML page, with image map and embedded image: ?> PHPlot Example: Pie Chart with Image Map

PHPlot Example: Pie Chart with Image Map

This is a plot with image map and tooltip text.

Plot Image ----------------------------------------------------------------------------- Implementation Notes and Limitations: Since most PHPlot scripts will not produce image maps, the burden of converting data points into a format for HTML image maps was left to the implementation in the callback function. This is why PHPlot passes the diameter values it already calculated (instead of radius values), and why pie chart segments must be interpolated by the callback function. You can generate image maps for error plots (data type data-data-error) with points and linepoints plot types, but the image map areas will not include the error bars. For these plot types, the only the coordinates of the data points are provided. For OHLC plots (candlesticks, candlesticks2, and ohlc plot types) there is no way to get the coordinates of individual features (such as the candlestick body rather than the high point wick). PHPlot only provides the bounding box rectangle for the overall data point graphic. PHPlot does not provide the data values from the data array that go with the points. If you need these values, for example in tooltips, your callback function needs to get them from the data array, using the $row and $column index values. This may be complex for pie charts (for example, if you want tooltip text to show the percentage value of the pie segment). Example 2 above shows access to data values for the simple case of data type 'text-data-single'. ----------------------------------------------------------------------------- Image maps with non-embedded image data: It is also possible to generate an image map when your PHPlot script produces an image in the normal way (not embedded) and returns it to the browser as image data. But your script will need to run twice: once to to generate the PHPlot image, and once to generate the containing HTML page with the image map. (You can use two separate scripts instead, but this is not recommended, since both operations must create identical plots so the image map areas correspond to the plot areas.) In this example, the script normally generates an HTML page. The page contains an image map, generated with PHPlot, and also an image tag which points back to the same script to have it generate the plot image. When requesting the image, the script adds a parameter 'mode=plot' to the URL. This tells the second execution of the script to generate the image data instead of the HTML page. The obvious drawback to this method is that you are processing a complete plot twice each time a plot is needed. The first time, only the image map is needed, and the plot itself is discarded. This is inefficient, especially if your plotting script needs to query a database or perform extensive calculations. Another concern is if your script queries a database, the data could change between the two uses of the script, resulting in an image map and plot which do not correlate. The rest of this section contains annotated code from an example. Start by checking for the mode parameter: # Check the URL parameter to determine the mode: $do_html = empty($_GET['mode']) || $_GET['mode'] != 'plot'; The callback handler is the same as the example above, in that it appends to the global string $image_map one line from the image map. # Callback for 'data_points' : Generate 1 line in the image map. function store_map($im, $unused, $shape, $row, $column, ...) // Pseudo-code { .... } The generate_html() function creates the containing HTML page. Only parts of the code are shown. function generate_html() { global $image_map; Create a self-referencing URL with mode=plot parameter for the tag: # Pick separator depending on if the URL already has parameters: $sep = empty($_SERVER['QUERY_STRING']) ? '?' : '&'; # URL to ourself, with parameter: $url = htmlspecialchars($_SERVER['REQUEST_URI'] . $sep . 'mode=plot'); Now you generate the HTML page: echo "\n\n..."; To create the image map: echo "\n$image_map\n\n"; To include the plot image: echo " Produce the rest of the HTMl page as needed. This ends the generate_html function. In the main body of your script, create a PHPlot object and configure the plot as usual. Set the data_points callback (you need only do this in the case of generating HTML, not the image case). There are two other places where the operation differs for the HTML or image generation, based on the $do_html flag: disabling automatic output with SetPrintImage, and calling the generate_html function at the end. $plot = new PHPlot(800, 600); if ($do_html) { // Do not output the image in this mode: $plot->SetPrintImage(False); // Set the callback for image map generation: $plot->SetCallback('data_points', 'store_map'); } ... // Set up the plot, data values, plot type, etc. // Output the image (in plot mode), or build the image map (in html mode): $plot->DrawGraph(); if ($do_html) generate_html(); That's all. If $do_html is true, no image will be produced (due to SetPrintImage(False)), and generate_html() will be called. If $do_html is false, DrawGraph() will output the plot image, and the map data will be not be produced or output. ----------------------------------------------------------------------------- History: 2012-02-19 First version of data_points callback committed, for next release PHPlot-5.7.0 as an experimental feature. 2011-11-25 Feature request #2442041 was written requesting data for image maps. As the feature request author pointed out, PHPlot was now able to produce image data embedded in an HTML file, so it was now practical to also generate image map data. 2007-07-07 Feature request #1749683 was written requesting data for image maps. The request was closed as rejected, because there was no good way at the time for a script to generate the image file and the map and make them both available at the same time.