Subversion Repositories Sites.tela-botanica.org

Rev

Blame | Last modification | View Log | RSS feed

<?php
/*=======================================================================
// File:        JPGRAPH_LINE.PHP
// Description: Line plot extension for JpGraph
// Created:     2001-01-08
// Author:      Johan Persson (johanp@aditus.nu)
// Ver:         $Id: jpgraph_line.php,v 1.1 2004/06/15 10:13:19 jpm Exp $
//
// License:     This code is released under QPL
// Copyright (C) 2001,2002 Johan Persson
//========================================================================
*/
 
// constants for the (filled) area
DEFINE("LP_AREA_FILLED", true);
DEFINE("LP_AREA_NOT_FILLED", false);
DEFINE("LP_AREA_BORDER",false);
DEFINE("LP_AREA_NO_BORDER",true);

//===================================================
// CLASS LinePlot
// Description: 
//===================================================
class LinePlot extends Plot{
    var $filled=false;
    var $fill_color='blue';
    var $mark=null;
    var $step_style=false, $center=false;
    var $line_style=1;  // Default to solid
    var $filledAreas = array(); // array of arrays(with min,max,col,filled in them)
    var $barcenter=false;  // When we mix line and bar. Should we center the line in the bar.
    var $fillFromMin = false ;

//---------------
// CONSTRUCTOR
    function LinePlot(&$datay,$datax=false) {
        $this->Plot($datay,$datax);
        $this->mark = new PlotMark();
    }
//---------------
// PUBLIC METHODS       

    // Set style, filled or open
    function SetFilled($aFlag=true) {
        JpGraphError::Raise('LinePlot::SetFilled() is deprecated. Use SetFillColor()');
    }
        
    function SetBarCenter($aFlag=true) {
        $this->barcenter=$aFlag;
    }

    function SetStyle($aStyle) {
        $this->line_style=$aStyle;
    }
        
    function SetStepStyle($aFlag=true) {
        $this->step_style = $aFlag;
    }
        
    function SetColor($aColor) {
        parent::SetColor($aColor);
    }
        
    function SetFillFromYMin($f = true ) {
        $this->fillFromMin = $f ;
    }
    
    function SetFillColor($aColor,$aFilled=true) {
        $this->fill_color=$aColor;
        $this->filled=$aFilled;
    }
        
    function Legend(&$graph) {
        if( $this->legend!="" ) {
            if( $this->filled ) {
                $graph->legend->Add($this->legend,
                                    $this->fill_color,$this->mark,0,
                                    $this->legendcsimtarget,$this->legendcsimalt);
            } else {
                $graph->legend->Add($this->legend,
                                    $this->color,$this->mark,$this->line_style,
                                    $this->legendcsimtarget,$this->legendcsimalt);
            }
        }       
    }

    function AddArea($aMin=0,$aMax=0,$aFilled=LP_AREA_NOT_FILLED,$aColor="gray9",$aBorder=LP_AREA_BORDER) {
        if($aMin > $aMax) {
            // swap
            $tmp = $aMin;
            $aMin = $aMax;
            $aMax = $tmp;
        } 
        $this->filledAreas[] = array($aMin,$aMax,$aColor,$aFilled,$aBorder);
    }
        
    // Gets called before any axis are stroked
    function PreStrokeAdjust(&$graph) {

        // If another plot type have already adjusted the
        // offset we don't touch it.
        // (We check for empty in case the scale is  a log scale 
        // and hence doesn't contain any xlabel_offset)
        if( empty($graph->xaxis->scale->ticks->xlabel_offset) ||
            $graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
            if( $this->center ) {
                ++$this->numpoints;
                $a=0.5; $b=0.5;
            } else {
                $a=0; $b=0;
            }
            $graph->xaxis->scale->ticks->SetXLabelOffset($a);
            $graph->SetTextScaleOff($b);                                                
            //$graph->xaxis->scale->ticks->SupressMinorTickMarks();
        }
    }
        
    function Stroke(&$img,&$xscale,&$yscale) {
        $numpoints=count($this->coords[0]);
        if( isset($this->coords[1]) ) {
            if( count($this->coords[1])!=$numpoints )
                JpGraphError::Raise("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
            else
                $exist_x = true;
        }
        else 
            $exist_x = false;

        if( $this->barcenter ) 
            $textadj = 0.5-$xscale->text_scale_off;
        else
            $textadj = 0;

        // Find the first numeric data point
        $startpoint=0;
        while( $startpoint < $numpoints && !is_numeric($this->coords[0][$startpoint]) )
            ++$startpoint;

        // Bail out if no data points
        if( $startpoint == $numpoints ) 
            return;

        if( $exist_x )
            $xs=$this->coords[1][$startpoint];
        else
            $xs= $textadj+$startpoint;

        $img->SetStartPoint($xscale->Translate($xs),
                            $yscale->Translate($this->coords[0][$startpoint]));

                
        if( $this->filled ) {
            $cord[] = $xscale->Translate($xs);
            $min = $yscale->GetMinVal();
            if( $min > 0 || $this->fillFromMin )
                $cord[] = $yscale->Translate($min);
            else
                $cord[] = $yscale->Translate(0);
        }
        $xt = $xscale->Translate($xs);
        $yt = $yscale->Translate($this->coords[0][$startpoint]);
        $cord[] = $xt;
        $cord[] = $yt;
        $yt_old = $yt;

        $this->value->Stroke($img,$this->coords[0][$startpoint],$xt,$yt);

        $img->SetColor($this->color);
        $img->SetLineWeight($this->weight);
        $img->SetLineStyle($this->line_style);
        for( $pnts=$startpoint+1; $pnts<$numpoints; ++$pnts) {
            
            if( $exist_x ) $x=$this->coords[1][$pnts];
            else $x=$pnts+$textadj;
            $xt = $xscale->Translate($x);
            $yt = $yscale->Translate($this->coords[0][$pnts]);
            
            $y=$this->coords[0][$pnts];
            if( $this->step_style && is_numeric($y) ) {
                $img->StyleLineTo($xt,$yt_old);
                $img->StyleLineTo($xt,$yt);

                $cord[] = $xt;
                $cord[] = $yt_old;
        
                $cord[] = $xt;
                $cord[] = $yt;

            }
            else {
                if( is_numeric($y) || (is_string($y) && $y != "-") ) {
                    $tmp1=$this->coords[0][$pnts];
                    $tmp2=$this->coords[0][$pnts-1];                                    
                    if( is_numeric($tmp1)  && (is_numeric($tmp2) || $tmp2=="-" ) ) { 
                        $img->StyleLineTo($xt,$yt);
                    } 
                    else {
                        $img->SetStartPoint($xt,$yt);
                    }

                    if( is_numeric($tmp1)  && 
                        (is_numeric($tmp2) || $tmp2=="-" || ($this->filled && $tmp2=='') ) ) { 
                        $cord[] = $xt;
                        $cord[] = $yt;
                    } 
                }
            }
            $yt_old = $yt;

            $this->StrokeDataValue($img,$this->coords[0][$pnts],$xt,$yt);

        }       

        if( $this->filled  ) {
            $cord[] = $xt;
            if( $min > 0 || $this->fillFromMin )
                $cord[] = $yscale->Translate($min);
            else
                $cord[] = $yscale->Translate(0);
            $img->SetColor($this->fill_color);  
            $img->FilledPolygon($cord);
            $img->SetColor($this->color);
            $img->Polygon($cord);
        }

        if(!empty($this->filledAreas)) {

            $minY = $yscale->Translate($yscale->GetMinVal());
            $factor = ($this->step_style ? 4 : 2);

            for($i = 0; $i < sizeof($this->filledAreas); ++$i) {
                // go through all filled area elements ordered by insertion
                // fill polygon array
                $areaCoords[] = $cord[$this->filledAreas[$i][0] * $factor];
                $areaCoords[] = $minY;

                $areaCoords =
                    array_merge($areaCoords,
                                array_slice($cord,
                                            $this->filledAreas[$i][0] * $factor,
                                            ($this->filledAreas[$i][1] - $this->filledAreas[$i][0] + ($this->step_style ? 0 : 1))  * $factor));
                $areaCoords[] = $areaCoords[sizeof($areaCoords)-2]; // last x
                $areaCoords[] = $minY; // last y
            
                if($this->filledAreas[$i][3]) {
                    $img->SetColor($this->filledAreas[$i][2]);
                    $img->FilledPolygon($areaCoords);
                    $img->SetColor($this->color);
                }
                // Check if we should draw the frame.
                // If not we still re-draw the line since it might have been
                // partially overwritten by the filled area and it doesn't look
                // very good.
                // TODO: The behaviour is undefined if the line does not have
                // any line at the position of the area.
                if( $this->filledAreas[$i][4] )
                    $img->Polygon($areaCoords);
                else
                    $img->Polygon($cord);

                $areaCoords = array();
            }
        }       

        if( $this->mark->type == -1 || $this->mark->show == false )
            return;

        for( $pnts=0; $pnts<$numpoints; ++$pnts) {

            if( $exist_x ) $x=$this->coords[1][$pnts];
            else $x=$pnts+$textadj;
            $xt = $xscale->Translate($x);
            $yt = $yscale->Translate($this->coords[0][$pnts]);

            if( is_numeric($this->coords[0][$pnts]) ) {
                if( !empty($this->csimtargets[$pnts]) ) {
                    $this->mark->SetCSIMTarget($this->csimtargets[$pnts]);
                    $this->mark->SetCSIMAlt($this->csimalts[$pnts]);
                }
                if( $exist_x )
                    $x=$this->coords[1][$pnts];
                else
                    $x=$pnts;
                $this->mark->SetCSIMAltVal($this->coords[0][$pnts],$x);
                $this->mark->Stroke($img,$xt,$yt);      
                $this->csimareas .= $this->mark->GetCSIMAreas();
                $this->StrokeDataValue($img,$this->coords[0][$pnts],$xt,$yt);
            }
        }


    }
} // Class


//===================================================
// CLASS AccLinePlot
// Description: 
//===================================================
class AccLinePlot extends Plot {
    var $plots=null,$nbrplots=0,$numpoints=0;
//---------------
// CONSTRUCTOR
    function AccLinePlot($plots) {
        $this->plots = $plots;
        $this->nbrplots = count($plots);
        $this->numpoints = $plots[0]->numpoints;                
    }

//---------------
// PUBLIC METHODS       
    function Legend(&$graph) {
        foreach( $this->plots as $p )
            $p->DoLegend($graph);
    }
        
    function Max() {
        list($xmax) = $this->plots[0]->Max();
        $nmax=0;
        for($i=0; $i<count($this->plots); ++$i) {
            $n = count($this->plots[$i]->coords[0]);
            $nmax = max($nmax,$n);
            list($x) = $this->plots[$i]->Max();
            $xmax = Max($xmax,$x);
        }
        for( $i = 0; $i < $nmax; $i++ ) {
            // Get y-value for line $i by adding the
            // individual bars from all the plots added.
            // It would be wrong to just add the
            // individual plots max y-value since that
            // would in most cases give to large y-value.
            $y=$this->plots[0]->coords[0][$i];
            for( $j = 1; $j < $this->nbrplots; $j++ ) {
                $y += $this->plots[ $j ]->coords[0][$i];
            }
            $ymax[$i] = $y;
        }
        $ymax = max($ymax);
        return array($xmax,$ymax);
    }   

    function Min() {
        $nmax=0;
        list($xmin,$ysetmin) = $this->plots[0]->Min();
        for($i=0; $i<count($this->plots); ++$i) {
            $n = count($this->plots[$i]->coords[0]);
            $nmax = max($nmax,$n);
            list($x,$y) = $this->plots[$i]->Min();
            $xmin = Min($xmin,$x);
            $ysetmin = Min($y,$ysetmin);
        }
        for( $i = 0; $i < $nmax; $i++ ) {
            // Get y-value for line $i by adding the
            // individual bars from all the plots added.
            // It would be wrong to just add the
            // individual plots min y-value since that
            // would in most cases give to small y-value.
            $y=$this->plots[0]->coords[0][$i];
            for( $j = 1; $j < $this->nbrplots; $j++ ) {
                $y += $this->plots[ $j ]->coords[0][$i];
            }
            $ymin[$i] = $y;
        }
        $ymin = Min($ysetmin,Min($ymin));
        return array($xmin,$ymin);
    }

    // Gets called before any axis are stroked
    function PreStrokeAdjust(&$graph) {

        // If another plot type have already adjusted the
        // offset we don't touch it.
        // (We check for empty in case the scale is  a log scale 
        // and hence doesn't contain any xlabel_offset)
        
        if( empty($graph->xaxis->scale->ticks->xlabel_offset) ||
            $graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
            if( $this->center ) {
                ++$this->numpoints;
                $a=0.5; $b=0.5;
            } else {
                $a=0; $b=0;
            }
            $graph->xaxis->scale->ticks->SetXLabelOffset($a);
            $graph->SetTextScaleOff($b);                                                
            $graph->xaxis->scale->ticks->SupressMinorTickMarks();
        }
        
    }

    // To avoid duplicate of line drawing code here we just
    // change the y-values for each plot and then restore it
    // after we have made the stroke. We must do this copy since
    // it wouldn't be possible to create an acc line plot
    // with the same graphs, i.e AccLinePlot(array($pl,$pl,$pl));
    // since this method would have a side effect.
    function Stroke(&$img,&$xscale,&$yscale) {
        $img->SetLineWeight($this->weight);
        $this->numpoints = count($this->plots[0]->coords[0]);
        // Allocate array
        $coords[$this->nbrplots][$this->numpoints]=0;
        for($i=0; $i<$this->numpoints; $i++) {
            $coords[0][$i]=$this->plots[0]->coords[0][$i]; 
            $accy=$coords[0][$i];
            for($j=1; $j<$this->nbrplots; ++$j ) {
                $coords[$j][$i] = $this->plots[$j]->coords[0][$i]+$accy; 
                $accy = $coords[$j][$i];
            }
        }
        for($j=$this->nbrplots-1; $j>=0; --$j) {
            $p=$this->plots[$j];
            for( $i=0; $i<$this->numpoints; ++$i) {
                $tmp[$i]=$p->coords[0][$i];
                $p->coords[0][$i]=$coords[$j][$i];
            }
            $p->Stroke($img,$xscale,$yscale);
            for( $i=0; $i<$this->numpoints; ++$i) 
                $p->coords[0][$i]=$tmp[$i];
            $p->coords[0][]=$tmp;
        }
    }
} // Class


/* EOF */
?>