[266] | 1 | <?php
|
---|
| 2 | /*=======================================================================
|
---|
| 3 | // File: JPGRAPH_WINDROSE.PHP
|
---|
| 4 | // Description: Windrose extension for JpGraph
|
---|
| 5 | // Created: 2003-09-17
|
---|
| 6 | // Ver: $Id: jpgraph_windrose.php 1928 2010-01-11 19:56:51Z ljp $
|
---|
| 7 | //
|
---|
| 8 | // Copyright (c) Asial Corporation. All rights reserved.
|
---|
| 9 | //========================================================================
|
---|
| 10 | */
|
---|
| 11 |
|
---|
| 12 | require_once('jpgraph_glayout_vh.inc.php');
|
---|
| 13 |
|
---|
| 14 | //------------------------------------------------------------------------
|
---|
| 15 | // Determine how many compass directions to show
|
---|
| 16 | //------------------------------------------------------------------------
|
---|
| 17 | define('WINDROSE_TYPE4',1);
|
---|
| 18 | define('WINDROSE_TYPE8',2);
|
---|
| 19 | define('WINDROSE_TYPE16',3);
|
---|
| 20 | define('WINDROSE_TYPEFREE',4);
|
---|
| 21 |
|
---|
| 22 | //------------------------------------------------------------------------
|
---|
| 23 | // How should the labels for the circular grids be aligned
|
---|
| 24 | //------------------------------------------------------------------------
|
---|
| 25 | define('LBLALIGN_CENTER',1);
|
---|
| 26 | define('LBLALIGN_TOP',2);
|
---|
| 27 |
|
---|
| 28 | //------------------------------------------------------------------------
|
---|
| 29 | // How should the labels around the plot be align
|
---|
| 30 | //------------------------------------------------------------------------
|
---|
| 31 | define('LBLPOSITION_CENTER',1);
|
---|
| 32 | define('LBLPOSITION_EDGE',2);
|
---|
| 33 |
|
---|
| 34 | //------------------------------------------------------------------------
|
---|
| 35 | // Interpretation of ordinal values in the data
|
---|
| 36 | //------------------------------------------------------------------------
|
---|
| 37 | define('KEYENCODING_CLOCKWISE',1);
|
---|
| 38 | define('KEYENCODING_ANTICLOCKWISE',2);
|
---|
| 39 |
|
---|
| 40 | // Internal debug flag
|
---|
| 41 | define('__DEBUG',false);
|
---|
| 42 |
|
---|
| 43 |
|
---|
| 44 | //===================================================
|
---|
| 45 | // CLASS WindrosePlotScale
|
---|
| 46 | //===================================================
|
---|
| 47 | class WindrosePlotScale {
|
---|
| 48 | private $iMax,$iDelta=5;
|
---|
| 49 | private $iNumCirc=3;
|
---|
| 50 | public $iMaxNum=0;
|
---|
| 51 | private $iLblFmt='%.0f%%';
|
---|
| 52 | public $iFontFamily=FF_VERDANA,$iFontStyle=FS_NORMAL,$iFontSize=10;
|
---|
| 53 | public $iZFontFamily=FF_ARIAL,$iZFontStyle=FS_NORMAL,$iZFontSize=10;
|
---|
| 54 | public $iFontColor='black',$iZFontColor='black';
|
---|
| 55 | private $iFontFrameColor=false, $iFontBkgColor=false;
|
---|
| 56 | private $iLblZeroTxt=null;
|
---|
| 57 | private $iLblAlign=LBLALIGN_CENTER;
|
---|
| 58 | public $iAngle='auto';
|
---|
| 59 | private $iManualScale = false;
|
---|
| 60 | private $iHideLabels = false;
|
---|
| 61 |
|
---|
| 62 | function __construct($aData) {
|
---|
| 63 | $max=0;
|
---|
| 64 | $totlegsum = 0;
|
---|
| 65 | $maxnum=0;
|
---|
| 66 | $this->iZeroSum=0;
|
---|
| 67 | foreach( $aData as $idx => $legdata ) {
|
---|
| 68 | $legsum = array_sum($legdata);
|
---|
| 69 | $maxnum = max($maxnum,count($legdata)-1);
|
---|
| 70 | $max = max($legsum-$legdata[0],$max);
|
---|
| 71 | $totlegsum += $legsum;
|
---|
| 72 | $this->iZeroSum += $legdata[0] ;
|
---|
| 73 | }
|
---|
| 74 | if( round($totlegsum) > 100 ) {
|
---|
| 75 | JpGraphError::RaiseL(22001,$legsum);
|
---|
| 76 | //("Total percentage for all windrose legs in a windrose plot can not exceed 100% !\n(Current max is: ".$legsum.')');
|
---|
| 77 | }
|
---|
| 78 | $this->iMax = $max ;
|
---|
| 79 | $this->iMaxNum = $maxnum;
|
---|
| 80 | $this->iNumCirc = $this->GetNumCirc();
|
---|
| 81 | $this->iMaxVal = $this->iNumCirc * $this->iDelta ;
|
---|
| 82 | }
|
---|
| 83 |
|
---|
| 84 | // Return number of grid circles
|
---|
| 85 | function GetNumCirc() {
|
---|
| 86 | // Never return less than 1 circles
|
---|
| 87 | $num = ceil($this->iMax / $this->iDelta);
|
---|
| 88 | return max(1,$num) ;
|
---|
| 89 | }
|
---|
| 90 |
|
---|
| 91 | function SetMaxValue($aMax) {
|
---|
| 92 | $this->iMax = $aMax;
|
---|
| 93 | $this->iNumCirc = $this->GetNumCirc();
|
---|
| 94 | $this->iMaxVal = $this->iNumCirc * $this->iDelta ;
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 | // Set step size for circular grid
|
---|
| 98 | function Set($aMax,$aDelta=null) {
|
---|
| 99 | if( $aDelta==null ) {
|
---|
| 100 | $this->SetMaxValue($aMax);
|
---|
| 101 | return;
|
---|
| 102 | }
|
---|
| 103 | $this->iDelta = $aDelta;
|
---|
| 104 | $this->iNumCirc = ceil($aMax/$aDelta); //$this->GetNumCirc();
|
---|
| 105 | $this->iMaxVal = $this->iNumCirc * $this->iDelta ;
|
---|
| 106 | $this->iMax=$aMax;
|
---|
| 107 | // Remember that user has specified interval so don't
|
---|
| 108 | // do autoscaling
|
---|
| 109 | $this->iManualScale = true;
|
---|
| 110 | }
|
---|
| 111 |
|
---|
| 112 | function AutoScale($aRadius,$aMinDist=30) {
|
---|
| 113 |
|
---|
| 114 | if( $this->iManualScale ) return;
|
---|
| 115 |
|
---|
| 116 | // Make sure distance (in pixels) between two circles
|
---|
| 117 | // is never less than $aMinDist pixels
|
---|
| 118 | $tst = ceil($aRadius / $this->iNumCirc) ;
|
---|
| 119 |
|
---|
| 120 | while( $tst <= $aMinDist && $this->iDelta < 100 ) {
|
---|
| 121 | $this->iDelta += 5;
|
---|
| 122 | $tst = ceil($aRadius / $this->GetNumCirc()) ;
|
---|
| 123 | }
|
---|
| 124 |
|
---|
| 125 | if( $this->iDelta >= 100 ) {
|
---|
| 126 | JpGraphError::RaiseL(22002);//('Graph is too small to have a scale. Please make the graph larger.');
|
---|
| 127 | }
|
---|
| 128 |
|
---|
| 129 | // If the distance is to large try with multiples of 2 instead
|
---|
| 130 | if( $tst > $aMinDist * 3 ) {
|
---|
| 131 | $this->iDelta = 2;
|
---|
| 132 | $tst = ceil($aRadius / $this->iNumCirc) ;
|
---|
| 133 |
|
---|
| 134 | while( $tst <= $aMinDist && $this->iDelta < 100 ) {
|
---|
| 135 | $this->iDelta += 2;
|
---|
| 136 | $tst = ceil($aRadius / $this->GetNumCirc()) ;
|
---|
| 137 | }
|
---|
| 138 |
|
---|
| 139 | if( $this->iDelta >= 100 ) {
|
---|
| 140 | JpGraphError::RaiseL(22002); //('Graph is too small to have a scale. Please make the graph larger.');
|
---|
| 141 | }
|
---|
| 142 | }
|
---|
| 143 |
|
---|
| 144 | $this->iNumCirc = $this->GetNumCirc();
|
---|
| 145 | $this->iMaxVal = $this->iNumCirc * $this->iDelta ;
|
---|
| 146 | }
|
---|
| 147 |
|
---|
| 148 | // Return max of all leg values
|
---|
| 149 | function GetMax() {
|
---|
| 150 | return $this->iMax;
|
---|
| 151 | }
|
---|
| 152 |
|
---|
| 153 | function Hide($aFlg=true) {
|
---|
| 154 | $this->iHideLabels = $aFlg;
|
---|
| 155 | }
|
---|
| 156 |
|
---|
| 157 | function SetAngle($aAngle) {
|
---|
| 158 | $this->iAngle = $aAngle ;
|
---|
| 159 | }
|
---|
| 160 |
|
---|
| 161 | // Translate a Leg value to radius distance
|
---|
| 162 | function RelTranslate($aVal,$r,$ri) {
|
---|
| 163 | $tv = round($aVal/$this->iMaxVal*($r-$ri));
|
---|
| 164 | return $tv ;
|
---|
| 165 | }
|
---|
| 166 |
|
---|
| 167 | function SetLabelAlign($aAlign) {
|
---|
| 168 | $this->iLblAlign = $aAlign ;
|
---|
| 169 | }
|
---|
| 170 |
|
---|
| 171 | function SetLabelFormat($aFmt) {
|
---|
| 172 | $this->iLblFmt = $aFmt ;
|
---|
| 173 | }
|
---|
| 174 |
|
---|
| 175 | function SetLabelFillColor($aBkgColor,$aBorderColor=false) {
|
---|
| 176 |
|
---|
| 177 | $this->iFontBkgColor = $aBkgColor;
|
---|
| 178 | if( $aBorderColor === false ) {
|
---|
| 179 | $this->iFontFrameColor = $aBkgColor;
|
---|
| 180 | }
|
---|
| 181 | else {
|
---|
| 182 | $this->iFontFrameColor = $aBorderColor;
|
---|
| 183 | }
|
---|
| 184 | }
|
---|
| 185 |
|
---|
| 186 | function SetFontColor($aColor) {
|
---|
| 187 | $this->iFontColor = $aColor ;
|
---|
| 188 | $this->iZFontColor = $aColor ;
|
---|
| 189 | }
|
---|
| 190 |
|
---|
| 191 | function SetFont($aFontFamily,$aFontStyle=FS_NORMAL,$aFontSize=10) {
|
---|
| 192 | $this->iFontFamily = $aFontFamily ;
|
---|
| 193 | $this->iFontStyle = $aFontStyle ;
|
---|
| 194 | $this->iFontSize = $aFontSize ;
|
---|
| 195 | $this->SetZFont($aFontFamily,$aFontStyle,$aFontSize);
|
---|
| 196 | }
|
---|
| 197 |
|
---|
| 198 | function SetZFont($aFontFamily,$aFontStyle=FS_NORMAL,$aFontSize=10) {
|
---|
| 199 | $this->iZFontFamily = $aFontFamily ;
|
---|
| 200 | $this->iZFontStyle = $aFontStyle ;
|
---|
| 201 | $this->iZFontSize = $aFontSize ;
|
---|
| 202 | }
|
---|
| 203 |
|
---|
| 204 | function SetZeroLabel($aTxt) {
|
---|
| 205 | $this->iLblZeroTxt = $aTxt ;
|
---|
| 206 | }
|
---|
| 207 |
|
---|
| 208 | function SetZFontColor($aColor) {
|
---|
| 209 | $this->iZFontColor = $aColor ;
|
---|
| 210 | }
|
---|
| 211 |
|
---|
| 212 | function StrokeLabels($aImg,$xc,$yc,$ri,$rr) {
|
---|
| 213 |
|
---|
| 214 | if( $this->iHideLabels ) return;
|
---|
| 215 |
|
---|
| 216 | // Setup some convinient vairables
|
---|
| 217 | $a = $this->iAngle * M_PI/180.0;
|
---|
| 218 | $n = $this->iNumCirc;
|
---|
| 219 | $d = $this->iDelta;
|
---|
| 220 |
|
---|
| 221 | // Setup the font and font color
|
---|
| 222 | $val = new Text();
|
---|
| 223 | $val->SetFont($this->iFontFamily,$this->iFontStyle,$this->iFontSize);
|
---|
| 224 | $val->SetColor($this->iFontColor);
|
---|
| 225 |
|
---|
| 226 | if( $this->iFontBkgColor !== false ) {
|
---|
| 227 | $val->SetBox($this->iFontBkgColor,$this->iFontFrameColor);
|
---|
| 228 | }
|
---|
| 229 |
|
---|
| 230 | // Position the labels relative to the radiant circles
|
---|
| 231 | if( $this->iLblAlign == LBLALIGN_TOP ) {
|
---|
| 232 | if( $a > 0 && $a <= M_PI/2 ) {
|
---|
| 233 | $val->SetAlign('left','bottom');
|
---|
| 234 | }
|
---|
| 235 | elseif( $a > M_PI/2 && $a <= M_PI ) {
|
---|
| 236 | $val->SetAlign('right','bottom');
|
---|
| 237 | }
|
---|
| 238 | }
|
---|
| 239 | elseif( $this->iLblAlign == LBLALIGN_CENTER ) {
|
---|
| 240 | $val->SetAlign('center','center');
|
---|
| 241 | }
|
---|
| 242 |
|
---|
| 243 | // Stroke the labels close to each circle
|
---|
| 244 | $v = $d ;
|
---|
| 245 | $si = sin($a);
|
---|
| 246 | $co = cos($a);
|
---|
| 247 | for( $i=0; $i < $n; ++$i, $v += $d ) {
|
---|
| 248 | $r = $ri + ($i+1) * $rr;
|
---|
| 249 | $x = $xc + $co * $r;
|
---|
| 250 | $y = $yc - $si * $r;
|
---|
| 251 | $val->Set(sprintf($this->iLblFmt,$v));
|
---|
| 252 | $val->Stroke($aImg,$x,$y);
|
---|
| 253 | }
|
---|
| 254 |
|
---|
| 255 | // Print the text in the zero circle
|
---|
| 256 | if( $this->iLblZeroTxt === null ) {
|
---|
| 257 | $this->iLblZeroTxt = sprintf($this->iLblFmt,$this->iZeroSum);
|
---|
| 258 | }
|
---|
| 259 | else {
|
---|
| 260 | $this->iLblZeroTxt = sprintf($this->iLblZeroTxt,$this->iZeroSum);
|
---|
| 261 | }
|
---|
| 262 |
|
---|
| 263 | $val->Set($this->iLblZeroTxt);
|
---|
| 264 | $val->SetAlign('center','center');
|
---|
| 265 | $val->SetParagraphAlign('center');
|
---|
| 266 | $val->SetColor($this->iZFontColor);
|
---|
| 267 | $val->SetFont($this->iZFontFamily,$this->iZFontStyle,$this->iZFontSize);
|
---|
| 268 | $val->Stroke($aImg,$xc,$yc);
|
---|
| 269 | }
|
---|
| 270 | }
|
---|
| 271 |
|
---|
| 272 | //===================================================
|
---|
| 273 | // CLASS LegendStyle
|
---|
| 274 | //===================================================
|
---|
| 275 | class LegendStyle {
|
---|
| 276 | public $iLength = 40, $iMargin = 20 , $iBottomMargin=5;
|
---|
| 277 | public $iCircleWeight=2, $iCircleRadius = 18, $iCircleColor='black';
|
---|
| 278 | public $iTxtFontFamily=FF_VERDANA,$iTxtFontStyle=FS_NORMAL,$iTxtFontSize=8;
|
---|
| 279 | public $iLblFontFamily=FF_VERDANA,$iLblFontStyle=FS_NORMAL,$iLblFontSize=8;
|
---|
| 280 | public $iCircleFontFamily=FF_VERDANA,$iCircleFontStyle=FS_NORMAL,$iCircleFontSize=8;
|
---|
| 281 | public $iLblFontColor='black',$iTxtFontColor='black',$iCircleFontColor='black';
|
---|
| 282 | public $iShow=true;
|
---|
| 283 | public $iFormatString='%.1f';
|
---|
| 284 | public $iTxtMargin=6, $iTxt='';
|
---|
| 285 | public $iZCircleTxt='Calm';
|
---|
| 286 |
|
---|
| 287 | function SetFont($aFontFamily,$aFontStyle=FS_NORMAL,$aFontSize=10) {
|
---|
| 288 | $this->iLblFontFamily = $aFontFamily ;
|
---|
| 289 | $this->iLblFontStyle = $aFontStyle ;
|
---|
| 290 | $this->iLblFontSize = $aFontSize ;
|
---|
| 291 | $this->iTxtFontFamily = $aFontFamily ;
|
---|
| 292 | $this->iTxtFontStyle = $aFontStyle ;
|
---|
| 293 | $this->iTxtFontSize = $aFontSize ;
|
---|
| 294 | $this->iCircleFontFamily = $aFontFamily ;
|
---|
| 295 | $this->iCircleFontStyle = $aFontStyle ;
|
---|
| 296 | $this->iCircleFontSize = $aFontSize ;
|
---|
| 297 | }
|
---|
| 298 |
|
---|
| 299 | function SetLFont($aFontFamily,$aFontStyle=FS_NORMAL,$aFontSize=10) {
|
---|
| 300 | $this->iLblFontFamily = $aFontFamily ;
|
---|
| 301 | $this->iLblFontStyle = $aFontStyle ;
|
---|
| 302 | $this->iLblFontSize = $aFontSize ;
|
---|
| 303 | }
|
---|
| 304 |
|
---|
| 305 | function SetTFont($aFontFamily,$aFontStyle=FS_NORMAL,$aFontSize=10) {
|
---|
| 306 | $this->iTxtFontFamily = $aFontFamily ;
|
---|
| 307 | $this->iTxtFontStyle = $aFontStyle ;
|
---|
| 308 | $this->iTxtFontSize = $aFontSize ;
|
---|
| 309 | }
|
---|
| 310 |
|
---|
| 311 | function SetCFont($aFontFamily,$aFontStyle=FS_NORMAL,$aFontSize=10) {
|
---|
| 312 | $this->iCircleFontFamily = $aFontFamily ;
|
---|
| 313 | $this->iCircleFontStyle = $aFontStyle ;
|
---|
| 314 | $this->iCircleFontSize = $aFontSize ;
|
---|
| 315 | }
|
---|
| 316 |
|
---|
| 317 |
|
---|
| 318 | function SetFontColor($aColor) {
|
---|
| 319 | $this->iTxtFontColor = $aColor ;
|
---|
| 320 | $this->iLblFontColor = $aColor ;
|
---|
| 321 | $this->iCircleFontColor = $aColor ;
|
---|
| 322 | }
|
---|
| 323 |
|
---|
| 324 | function SetTFontColor($aColor) {
|
---|
| 325 | $this->iTxtFontColor = $aColor ;
|
---|
| 326 | }
|
---|
| 327 |
|
---|
| 328 | function SetLFontColor($aColor) {
|
---|
| 329 | $this->iLblFontColor = $aColor ;
|
---|
| 330 | }
|
---|
| 331 |
|
---|
| 332 | function SetCFontColor($aColor) {
|
---|
| 333 | $this->iCircleFontColor = $aColor ;
|
---|
| 334 | }
|
---|
| 335 |
|
---|
| 336 | function SetCircleWeight($aWeight) {
|
---|
| 337 | $this->iCircleWeight = $aWeight;
|
---|
| 338 | }
|
---|
| 339 |
|
---|
| 340 | function SetCircleRadius($aRadius) {
|
---|
| 341 | $this->iCircleRadius = $aRadius;
|
---|
| 342 | }
|
---|
| 343 |
|
---|
| 344 | function SetCircleColor($aColor) {
|
---|
| 345 | $this->iCircleColor = $aColor ;
|
---|
| 346 | }
|
---|
| 347 |
|
---|
| 348 | function SetCircleText($aTxt) {
|
---|
| 349 | $this->iZCircleTxt = $aTxt;
|
---|
| 350 | }
|
---|
| 351 |
|
---|
| 352 | function SetMargin($aMarg,$aBottomMargin=5) {
|
---|
| 353 | $this->iMargin=$aMarg;
|
---|
| 354 | $this->iBottomMargin=$aBottomMargin;
|
---|
| 355 | }
|
---|
| 356 |
|
---|
| 357 | function SetLength($aLength) {
|
---|
| 358 | $this->iLength = $aLength ;
|
---|
| 359 | }
|
---|
| 360 |
|
---|
| 361 | function Show($aFlg=true) {
|
---|
| 362 | $this->iShow = $aFlg;
|
---|
| 363 | }
|
---|
| 364 |
|
---|
| 365 | function Hide($aFlg=true) {
|
---|
| 366 | $this->iShow = ! $aFlg;
|
---|
| 367 | }
|
---|
| 368 |
|
---|
| 369 | function SetFormat($aFmt) {
|
---|
| 370 | $this->iFormatString=$aFmt;
|
---|
| 371 | }
|
---|
| 372 |
|
---|
| 373 | function SetText($aTxt) {
|
---|
| 374 | $this->iTxt = $aTxt ;
|
---|
| 375 | }
|
---|
| 376 |
|
---|
| 377 | }
|
---|
| 378 |
|
---|
| 379 | define('RANGE_OVERLAPPING',0);
|
---|
| 380 | define('RANGE_DISCRETE',1);
|
---|
| 381 |
|
---|
| 382 | //===================================================
|
---|
| 383 | // CLASS WindrosePlot
|
---|
| 384 | //===================================================
|
---|
| 385 | class WindrosePlot {
|
---|
| 386 | private $iAntiAlias=true;
|
---|
| 387 | private $iData=array();
|
---|
| 388 | public $iX=0.5,$iY=0.5;
|
---|
| 389 | public $iSize=0.55;
|
---|
| 390 | private $iGridColor1='gray',$iGridColor2='darkgreen';
|
---|
| 391 | private $iRadialColorArray=array();
|
---|
| 392 | private $iRadialWeightArray=array();
|
---|
| 393 | private $iRadialStyleArray=array();
|
---|
| 394 | private $iRanges = array(1,2,3,5,6,10,13.5,99.0);
|
---|
| 395 | private $iRangeStyle = RANGE_OVERLAPPING ;
|
---|
| 396 | public $iCenterSize=60;
|
---|
| 397 | private $iType = WINDROSE_TYPE16;
|
---|
| 398 | public $iFontFamily=FF_VERDANA,$iFontStyle=FS_NORMAL,$iFontSize=10;
|
---|
| 399 | public $iFontColor='darkgray';
|
---|
| 400 | private $iRadialGridStyle='longdashed';
|
---|
| 401 | private $iAllDirectionLabels = array('E','ENE','NE','NNE','N','NNW','NW','WNW','W','WSW','SW','SSW','S','SSE','SE','ESE');
|
---|
| 402 | private $iStandardDirections = array();
|
---|
| 403 | private $iCircGridWeight=3, $iRadialGridWeight=1;
|
---|
| 404 | private $iLabelMargin=12;
|
---|
| 405 | private $iLegweights = array(2,4,6,8,10,12,14,16,18,20);
|
---|
| 406 | private $iLegColors = array('orange','black','blue','red','green','purple','navy','yellow','brown');
|
---|
| 407 | private $iLabelFormatString='', $iLabels=array();
|
---|
| 408 | private $iLabelPositioning = LBLPOSITION_EDGE;
|
---|
| 409 | private $iColor='white';
|
---|
| 410 | private $iShowBox=false, $iBoxColor='black',$iBoxWeight=1,$iBoxStyle='solid';
|
---|
| 411 | private $iOrdinalEncoding=KEYENCODING_ANTICLOCKWISE;
|
---|
| 412 | public $legend=null;
|
---|
| 413 |
|
---|
| 414 | function __construct($aData) {
|
---|
| 415 | $this->iData = $aData;
|
---|
| 416 | $this->legend = new LegendStyle();
|
---|
| 417 |
|
---|
| 418 | // Setup the scale
|
---|
| 419 | $this->scale = new WindrosePlotScale($this->iData);
|
---|
| 420 |
|
---|
| 421 | // default label for free type i agle and a degree sign
|
---|
| 422 | $this->iLabelFormatString = '%.1f'.SymChar::Get('degree');
|
---|
| 423 |
|
---|
| 424 | $delta = 2*M_PI/16;
|
---|
| 425 | for( $i=0, $a=0; $i < 16; ++$i, $a += $delta ) {
|
---|
| 426 | $this->iStandardDirections[$this->iAllDirectionLabels[$i]] = $a;
|
---|
| 427 | }
|
---|
| 428 | }
|
---|
| 429 |
|
---|
| 430 | // Dummy method to make window plots have the same signature as the
|
---|
| 431 | // layout classes since windrose plots are "leaf" classes in the hierarchy
|
---|
| 432 | function LayoutSize() {
|
---|
| 433 | return 1;
|
---|
| 434 | }
|
---|
| 435 |
|
---|
| 436 | function SetSize($aSize) {
|
---|
| 437 | $this->iSize = $aSize;
|
---|
| 438 | }
|
---|
| 439 |
|
---|
| 440 | function SetDataKeyEncoding($aEncoding) {
|
---|
| 441 | $this->iOrdinalEncoding = $aEncoding;
|
---|
| 442 | }
|
---|
| 443 |
|
---|
| 444 | function SetColor($aColor) {
|
---|
| 445 | $this->iColor = $aColor;
|
---|
| 446 | }
|
---|
| 447 |
|
---|
| 448 | function SetRadialColors($aColors) {
|
---|
| 449 | $this->iRadialColorArray = $aColors;
|
---|
| 450 | }
|
---|
| 451 |
|
---|
| 452 | function SetRadialWeights($aWeights) {
|
---|
| 453 | $this->iRadialWeightArray = $aWeights;
|
---|
| 454 | }
|
---|
| 455 |
|
---|
| 456 | function SetRadialStyles($aStyles) {
|
---|
| 457 | $this->iRadialStyleArray = $aStyles;
|
---|
| 458 | }
|
---|
| 459 |
|
---|
| 460 | function SetBox($aColor='black',$aWeight=1, $aStyle='solid', $aShow=true) {
|
---|
| 461 | $this->iShowBox = $aShow ;
|
---|
| 462 | $this->iBoxColor = $aColor ;
|
---|
| 463 | $this->iBoxWeight = $aWeight ;
|
---|
| 464 | $this->iBoxStyle = $aStyle;
|
---|
| 465 | }
|
---|
| 466 |
|
---|
| 467 | function SetLabels($aLabels) {
|
---|
| 468 | $this->iLabels = $aLabels ;
|
---|
| 469 | }
|
---|
| 470 |
|
---|
| 471 | function SetLabelMargin($aMarg) {
|
---|
| 472 | $this->iLabelMargin = $aMarg ;
|
---|
| 473 | }
|
---|
| 474 |
|
---|
| 475 | function SetLabelFormat($aLblFormat) {
|
---|
| 476 | $this->iLabelFormatString = $aLblFormat ;
|
---|
| 477 | }
|
---|
| 478 |
|
---|
| 479 | function SetCompassLabels($aLabels) {
|
---|
| 480 | if( count($aLabels) != 16 ) {
|
---|
| 481 | JpgraphError::RaiseL(22004); //('Label specification for windrose directions must have 16 values (one for each compass direction).');
|
---|
| 482 | }
|
---|
| 483 | $this->iAllDirectionLabels = $aLabels ;
|
---|
| 484 |
|
---|
| 485 | $delta = 2*M_PI/16;
|
---|
| 486 | for( $i=0, $a=0; $i < 16; ++$i, $a += $delta ) {
|
---|
| 487 | $this->iStandardDirections[$this->iAllDirectionLabels[$i]] = $a;
|
---|
| 488 | }
|
---|
| 489 |
|
---|
| 490 | }
|
---|
| 491 |
|
---|
| 492 | function SetCenterSize($aSize) {
|
---|
| 493 | $this->iCenterSize = $aSize;
|
---|
| 494 | }
|
---|
| 495 | // Alias for SetCenterSize
|
---|
| 496 | function SetZCircleSize($aSize) {
|
---|
| 497 | $this->iCenterSize = $aSize;
|
---|
| 498 | }
|
---|
| 499 |
|
---|
| 500 | function SetFont($aFFam,$aFStyle=FS_NORMAL,$aFSize=10) {
|
---|
| 501 | $this->iFontFamily = $aFFam ;
|
---|
| 502 | $this->iFontStyle = $aFStyle ;
|
---|
| 503 | $this->iFontSize = $aFSize ;
|
---|
| 504 | }
|
---|
| 505 |
|
---|
| 506 | function SetFontColor($aColor) {
|
---|
| 507 | $this->iFontColor=$aColor;
|
---|
| 508 | }
|
---|
| 509 |
|
---|
| 510 | function SetGridColor($aColor1,$aColor2) {
|
---|
| 511 | $this->iGridColor1 = $aColor1;
|
---|
| 512 | $this->iGridColor2 = $aColor2;
|
---|
| 513 | }
|
---|
| 514 |
|
---|
| 515 | function SetGridWeight($aGrid1=1,$aGrid2=2) {
|
---|
| 516 | $this->iCircGridWeight = $aGrid1 ;
|
---|
| 517 | $this->iRadialGridWeight = $aGrid2 ;
|
---|
| 518 | }
|
---|
| 519 |
|
---|
| 520 | function SetRadialGridStyle($aStyle) {
|
---|
| 521 | $aStyle = strtolower($aStyle);
|
---|
| 522 | if( !in_array($aStyle,array('solid','dotted','dashed','longdashed')) ) {
|
---|
| 523 | JpGraphError::RaiseL(22005); //("Line style for radial lines must be on of ('solid','dotted','dashed','longdashed') ");
|
---|
| 524 | }
|
---|
| 525 | $this->iRadialGridStyle=$aStyle;
|
---|
| 526 | }
|
---|
| 527 |
|
---|
| 528 | function SetRanges($aRanges) {
|
---|
| 529 | $this->iRanges = $aRanges;
|
---|
| 530 | }
|
---|
| 531 |
|
---|
| 532 | function SetRangeStyle($aStyle) {
|
---|
| 533 | $this->iRangeStyle = $aStyle;
|
---|
| 534 | }
|
---|
| 535 |
|
---|
| 536 | function SetRangeColors($aLegColors) {
|
---|
| 537 | $this->iLegColors = $aLegColors;
|
---|
| 538 | }
|
---|
| 539 |
|
---|
| 540 | function SetRangeWeights($aWeights) {
|
---|
| 541 | $n=count($aWeights);
|
---|
| 542 | for($i=0; $i< $n; ++$i ) {
|
---|
| 543 | $aWeights[$i] = floor($aWeights[$i]/2);
|
---|
| 544 | }
|
---|
| 545 | $this->iLegweights = $aWeights;
|
---|
| 546 |
|
---|
| 547 | }
|
---|
| 548 |
|
---|
| 549 | function SetType($aType) {
|
---|
| 550 | if( $aType < WINDROSE_TYPE4 || $aType > WINDROSE_TYPEFREE ) {
|
---|
| 551 | JpGraphError::RaiseL(22006); //('Illegal windrose type specified.');
|
---|
| 552 | }
|
---|
| 553 | $this->iType = $aType;
|
---|
| 554 | }
|
---|
| 555 |
|
---|
| 556 | // Alias for SetPos()
|
---|
| 557 | function SetCenterPos($aX,$aY) {
|
---|
| 558 | $this->iX = $aX;
|
---|
| 559 | $this->iY = $aY;
|
---|
| 560 | }
|
---|
| 561 |
|
---|
| 562 | function SetPos($aX,$aY) {
|
---|
| 563 | $this->iX = $aX;
|
---|
| 564 | $this->iY = $aY;
|
---|
| 565 | }
|
---|
| 566 |
|
---|
| 567 | function SetAntiAlias($aFlag) {
|
---|
| 568 | $this->iAntiAlias = $aFlag ;
|
---|
| 569 | if( ! $aFlag )
|
---|
| 570 | $this->iCircGridWeight = 1;
|
---|
| 571 | }
|
---|
| 572 |
|
---|
| 573 | function _ThickCircle($aImg,$aXC,$aYC,$aRad,$aWeight=2,$aColor) {
|
---|
| 574 |
|
---|
| 575 | $aImg->SetColor($aColor);
|
---|
| 576 | $aRad *= 2 ;
|
---|
| 577 | $aImg->Ellipse($aXC,$aYC,$aRad,$aRad);
|
---|
| 578 | if( $aWeight > 1 ) {
|
---|
| 579 | $aImg->Ellipse($aXC,$aYC,$aRad+1,$aRad+1);
|
---|
| 580 | $aImg->Ellipse($aXC,$aYC,$aRad+2,$aRad+2);
|
---|
| 581 | if( $aWeight > 2 ) {
|
---|
| 582 | $aImg->Ellipse($aXC,$aYC,$aRad+3,$aRad+3);
|
---|
| 583 | $aImg->Ellipse($aXC,$aYC,$aRad+3,$aRad+4);
|
---|
| 584 | $aImg->Ellipse($aXC,$aYC,$aRad+4,$aRad+3);
|
---|
| 585 | }
|
---|
| 586 | }
|
---|
| 587 | }
|
---|
| 588 |
|
---|
| 589 | function _StrokeWindLeg($aImg,$xc,$yc,$a,$ri,$r,$weight,$color) {
|
---|
| 590 |
|
---|
| 591 | // If less than 1 px long then we assume this has been caused by rounding problems
|
---|
| 592 | // and should not be stroked
|
---|
| 593 | if( $r < 1 ) return;
|
---|
| 594 |
|
---|
| 595 | $xt = $xc + cos($a)*$ri;
|
---|
| 596 | $yt = $yc - sin($a)*$ri;
|
---|
| 597 | $xxt = $xc + cos($a)*($ri+$r);
|
---|
| 598 | $yyt = $yc - sin($a)*($ri+$r);
|
---|
| 599 |
|
---|
| 600 | $x1 = $xt - $weight*sin($a);
|
---|
| 601 | $y1 = $yt - $weight*cos($a);
|
---|
| 602 | $x2 = $xxt - $weight*sin($a);
|
---|
| 603 | $y2 = $yyt - $weight*cos($a);
|
---|
| 604 |
|
---|
| 605 | $x3 = $xxt + $weight*sin($a);
|
---|
| 606 | $y3 = $yyt + $weight*cos($a);
|
---|
| 607 | $x4 = $xt + $weight*sin($a);
|
---|
| 608 | $y4 = $yt + $weight*cos($a);
|
---|
| 609 |
|
---|
| 610 | $pts = array($x1,$y1,$x2,$y2,$x3,$y3,$x4,$y4);
|
---|
| 611 | $aImg->SetColor($color);
|
---|
| 612 | $aImg->FilledPolygon($pts);
|
---|
| 613 |
|
---|
| 614 | }
|
---|
| 615 |
|
---|
| 616 | function _StrokeLegend($aImg,$x,$y,$scaling=1,$aReturnWidth=false) {
|
---|
| 617 |
|
---|
| 618 | if( ! $this->legend->iShow ) return 0;
|
---|
| 619 |
|
---|
| 620 | $nlc = count($this->iLegColors);
|
---|
| 621 | $nlw = count($this->iLegweights);
|
---|
| 622 |
|
---|
| 623 | // Setup font for ranges
|
---|
| 624 | $value = new Text();
|
---|
| 625 | $value->SetAlign('center','bottom');
|
---|
| 626 | $value->SetFont($this->legend->iLblFontFamily,
|
---|
| 627 | $this->legend->iLblFontStyle,
|
---|
| 628 | $this->legend->iLblFontSize*$scaling);
|
---|
| 629 | $value->SetColor($this->legend->iLblFontColor);
|
---|
| 630 |
|
---|
| 631 | // Remember x-center
|
---|
| 632 | $xcenter = $x ;
|
---|
| 633 |
|
---|
| 634 | // Construct format string
|
---|
| 635 | $fmt = $this->legend->iFormatString.'-'.$this->legend->iFormatString;
|
---|
| 636 |
|
---|
| 637 | // Make sure that the length of each range is enough to cover the
|
---|
| 638 | // size of the labels
|
---|
| 639 | $tst = sprintf($fmt,$this->iRanges[0],$this->iRanges[1]);
|
---|
| 640 | $value->Set($tst);
|
---|
| 641 | $w = $value->GetWidth($aImg);
|
---|
| 642 | $l = round(max($this->legend->iLength * $scaling,$w*1.5));
|
---|
| 643 |
|
---|
| 644 | $r = $this->legend->iCircleRadius * $scaling ;
|
---|
| 645 | $len = 2*$r + $this->scale->iMaxNum * $l;
|
---|
| 646 |
|
---|
| 647 | // We are called just to find out the width
|
---|
| 648 | if( $aReturnWidth ) return $len;
|
---|
| 649 |
|
---|
| 650 | $x -= round($len/2);
|
---|
| 651 | $x += $r;
|
---|
| 652 |
|
---|
| 653 | // 4 pixels extra vertical margin since the circle sometimes is +/- 1 pixel of the
|
---|
| 654 | // theorethical radius due to imperfection in the GD library
|
---|
| 655 | //$y -= round(max($r,$scaling*$this->iLegweights[($this->scale->iMaxNum-1) % $nlw])+4*$scaling);
|
---|
| 656 | $y -= ($this->legend->iCircleRadius + 2)*$scaling+$this->legend->iBottomMargin*$scaling;
|
---|
| 657 |
|
---|
| 658 | // Adjust for bottom text
|
---|
| 659 | if( $this->legend->iTxt != '' ) {
|
---|
| 660 | // Setup font for text
|
---|
| 661 | $value->Set($this->legend->iTxt);
|
---|
| 662 | $y -= /*$this->legend->iTxtMargin + */ $value->GetHeight($aImg);
|
---|
| 663 | }
|
---|
| 664 |
|
---|
| 665 | // Stroke 0-circle
|
---|
| 666 | $this->_ThickCircle($aImg,$x,$y,$r,$this->legend->iCircleWeight,
|
---|
| 667 | $this->legend->iCircleColor);
|
---|
| 668 |
|
---|
| 669 | // Remember the center of the circe
|
---|
| 670 | $xc=$x; $yc=$y;
|
---|
| 671 |
|
---|
| 672 | $value->SetAlign('center','bottom');
|
---|
| 673 | $x += $r+1;
|
---|
| 674 |
|
---|
| 675 | // Stroke all used ranges
|
---|
| 676 | $txty = $y -
|
---|
| 677 | round($this->iLegweights[($this->scale->iMaxNum-1)%$nlw]*$scaling) - 4*$scaling;
|
---|
| 678 | if( $this->scale->iMaxNum >= count($this->iRanges) ) {
|
---|
| 679 | JpGraphError::RaiseL(22007); //('To few values for the range legend.');
|
---|
| 680 | }
|
---|
| 681 | $i=0;$idx=0;
|
---|
| 682 | while( $i < $this->scale->iMaxNum ) {
|
---|
| 683 | $y1 = $y - round($this->iLegweights[$i % $nlw]*$scaling);
|
---|
| 684 | $y2 = $y + round($this->iLegweights[$i % $nlw]*$scaling);
|
---|
| 685 | $x2 = $x + $l ;
|
---|
| 686 | $aImg->SetColor($this->iLegColors[$i % $nlc]);
|
---|
| 687 | $aImg->FilledRectangle($x,$y1,$x2,$y2);
|
---|
| 688 | if( $this->iRangeStyle == RANGE_OVERLAPPING ) {
|
---|
| 689 | $lbl = sprintf($fmt,$this->iRanges[$idx],$this->iRanges[$idx+1]);
|
---|
| 690 | }
|
---|
| 691 | else {
|
---|
| 692 | $lbl = sprintf($fmt,$this->iRanges[$idx],$this->iRanges[$idx+1]);
|
---|
| 693 | ++$idx;
|
---|
| 694 | }
|
---|
| 695 | $value->Set($lbl);
|
---|
| 696 | $value->Stroke($aImg,$x+$l/2,$txty);
|
---|
| 697 | $x = $x2;
|
---|
| 698 | ++$i;++$idx;
|
---|
| 699 | }
|
---|
| 700 |
|
---|
| 701 | // Setup circle font
|
---|
| 702 | $value->SetFont($this->legend->iCircleFontFamily,
|
---|
| 703 | $this->legend->iCircleFontStyle,
|
---|
| 704 | $this->legend->iCircleFontSize*$scaling);
|
---|
| 705 | $value->SetColor($this->legend->iCircleFontColor);
|
---|
| 706 |
|
---|
| 707 | // Stroke 0-circle text
|
---|
| 708 | $value->Set($this->legend->iZCircleTxt);
|
---|
| 709 | $value->SetAlign('center','center');
|
---|
| 710 | $value->ParagraphAlign('center');
|
---|
| 711 | $value->Stroke($aImg,$xc,$yc);
|
---|
| 712 |
|
---|
| 713 | // Setup circle font
|
---|
| 714 | $value->SetFont($this->legend->iTxtFontFamily,
|
---|
| 715 | $this->legend->iTxtFontStyle,
|
---|
| 716 | $this->legend->iTxtFontSize*$scaling);
|
---|
| 717 | $value->SetColor($this->legend->iTxtFontColor);
|
---|
| 718 |
|
---|
| 719 | // Draw the text under the legend
|
---|
| 720 | $value->Set($this->legend->iTxt);
|
---|
| 721 | $value->SetAlign('center','top');
|
---|
| 722 | $value->SetParagraphAlign('center');
|
---|
| 723 | $value->Stroke($aImg,$xcenter,$y2+$this->legend->iTxtMargin*$scaling);
|
---|
| 724 | }
|
---|
| 725 |
|
---|
| 726 | function SetAutoScaleAngle($aIsRegRose=true) {
|
---|
| 727 |
|
---|
| 728 | // If the user already has manually set an angle don't
|
---|
| 729 | // trye to find a position
|
---|
| 730 | if( is_numeric($this->scale->iAngle) )
|
---|
| 731 | return;
|
---|
| 732 |
|
---|
| 733 | if( $aIsRegRose ) {
|
---|
| 734 |
|
---|
| 735 | // Create a complete data for all directions
|
---|
| 736 | // and translate string directions to ordinal values.
|
---|
| 737 | // This will much simplify the logic below
|
---|
| 738 | for( $i=0; $i < 16; ++$i ) {
|
---|
| 739 | $dtxt = $this->iAllDirectionLabels[$i];
|
---|
| 740 | if( !empty($this->iData[$dtxt]) ) {
|
---|
| 741 | $data[$i] = $this->iData[$dtxt];
|
---|
| 742 | }
|
---|
| 743 | elseif( !empty($this->iData[strtolower($dtxt)]) ) {
|
---|
| 744 | $data[$i] = $this->iData[strtolower($dtxt)];
|
---|
| 745 | }
|
---|
| 746 | elseif( !empty($this->iData[$i]) ) {
|
---|
| 747 | $data[$i] = $this->iData[$i];
|
---|
| 748 | }
|
---|
| 749 | else {
|
---|
| 750 | $data[$i] = array();
|
---|
| 751 | }
|
---|
| 752 | }
|
---|
| 753 |
|
---|
| 754 | // Find the leg which has the lowest weighted sum of number of data around it
|
---|
| 755 | $c0 = array_sum($data[0]);
|
---|
| 756 | $c1 = array_sum($data[1]);
|
---|
| 757 | $found = 1;
|
---|
| 758 | $min = $c0+$c1*100; // Initialize to a high value
|
---|
| 759 | for( $i=1; $i < 15; ++$i ) {
|
---|
| 760 | $c2 = array_sum($data[$i+1]);
|
---|
| 761 |
|
---|
| 762 | // Weight the leg we will use more to give preference
|
---|
| 763 | // to a short middle leg even if the 3 way sum is similair
|
---|
| 764 | $w = $c0 + 3*$c1 + $c2 ;
|
---|
| 765 | if( $w < $min ) {
|
---|
| 766 | $min = $w;
|
---|
| 767 | $found = $i;
|
---|
| 768 | }
|
---|
| 769 | $c0 = $c1;
|
---|
| 770 | $c1 = $c2;
|
---|
| 771 | }
|
---|
| 772 | $this->scale->iAngle = $found*22.5;
|
---|
| 773 | }
|
---|
| 774 | else {
|
---|
| 775 | $n = count($this->iData);
|
---|
| 776 | foreach( $this->iData as $dir => $leg ) {
|
---|
| 777 | if( !is_numeric($dir) ) {
|
---|
| 778 | $pos = array_search(strtoupper($dir),$this->iAllDirectionLabels);
|
---|
| 779 | if( $pos !== false ) {
|
---|
| 780 | $dir = $pos*22.5;
|
---|
| 781 | }
|
---|
| 782 | }
|
---|
| 783 | $data[round($dir)] = $leg;
|
---|
| 784 | }
|
---|
| 785 |
|
---|
| 786 | // Get all the angles for the data and sort it
|
---|
| 787 | $keys = array_keys($data);
|
---|
| 788 | sort($keys, SORT_NUMERIC);
|
---|
| 789 |
|
---|
| 790 | $n = count($data);
|
---|
| 791 | $found = false;
|
---|
| 792 | $max = 0 ;
|
---|
| 793 | for( $i=0; $i < 15; ++$i ) {
|
---|
| 794 | $try_a = round(22.5*$i);
|
---|
| 795 |
|
---|
| 796 | if( $try_a > $keys[$n-1] ) break;
|
---|
| 797 |
|
---|
| 798 | if( in_array($try_a,$keys) ) continue;
|
---|
| 799 |
|
---|
| 800 | // Find the angle just lower than this
|
---|
| 801 | $j=0;
|
---|
| 802 | while( $j < $n && $keys[$j] <= $try_a ) ++$j;
|
---|
| 803 | if( $j == 0 ) {
|
---|
| 804 | $kj = 0; $keys[$n-1];
|
---|
| 805 | $d1 = 0; abs($kj-$try_a);
|
---|
| 806 | }
|
---|
| 807 | else {
|
---|
| 808 | --$j;
|
---|
| 809 | $kj = $keys[$j];
|
---|
| 810 | $d1 = abs($kj-$try_a);
|
---|
| 811 | }
|
---|
| 812 |
|
---|
| 813 | // Find the angle just larger than this
|
---|
| 814 | $l=$n-1;
|
---|
| 815 | while( $l >= 0 && $keys[$l] >= $try_a ) --$l;
|
---|
| 816 | if( $l == $n-1) {
|
---|
| 817 | $kl = $keys[0];
|
---|
| 818 | $d2 = abs($kl-$try_a);
|
---|
| 819 | }
|
---|
| 820 | else {
|
---|
| 821 | ++$l;
|
---|
| 822 | $kl = $keys[$l];
|
---|
| 823 | $d2 = abs($kl-$try_a);
|
---|
| 824 | }
|
---|
| 825 |
|
---|
| 826 | // Weight the distance so that legs with large spread
|
---|
| 827 | // gets a better weight
|
---|
| 828 | $w = $d1 + $d2;
|
---|
| 829 | if( $i == 0 ) {
|
---|
| 830 | $w = round(1.4 * $w);
|
---|
| 831 | }
|
---|
| 832 | $diff = abs($d1 - $d2);
|
---|
| 833 | $w *= (360-$diff);
|
---|
| 834 | if( $w > $max ) {
|
---|
| 835 | $found = $i;
|
---|
| 836 | $max = $w;
|
---|
| 837 | }
|
---|
| 838 | }
|
---|
| 839 |
|
---|
| 840 | $a = $found*22.5;
|
---|
| 841 |
|
---|
| 842 | // Some heuristics to have some preferred positions
|
---|
| 843 | if( $keys[$n-1] < 25 ) $a = 45;
|
---|
| 844 | elseif( $keys[0] > 60 ) $a = 45;
|
---|
| 845 | elseif( $keys[0] > 25 && $keys[$n-1] < 340 ) $a = 0;
|
---|
| 846 | elseif( $keys[$n-1] < 75 ) $a = 90;
|
---|
| 847 | elseif( $keys[$n-1] < 120 ) $a = 135;
|
---|
| 848 | elseif( $keys[$n-1] < 160 ) $a = 180;
|
---|
| 849 |
|
---|
| 850 | $this->scale->iAngle = $a ;
|
---|
| 851 | }
|
---|
| 852 | }
|
---|
| 853 |
|
---|
| 854 | function NormAngle($a) {
|
---|
| 855 | while( $a > 360 ) {
|
---|
| 856 | $a -= 360;
|
---|
| 857 | }
|
---|
| 858 | return $a;
|
---|
| 859 | }
|
---|
| 860 |
|
---|
| 861 | function SetLabelPosition($aPos) {
|
---|
| 862 | $this->iLabelPositioning = $aPos ;
|
---|
| 863 | }
|
---|
| 864 |
|
---|
| 865 | function _StrokeFreeRose($dblImg,$value,$scaling,$xc,$yc,$r,$ri) {
|
---|
| 866 |
|
---|
| 867 | // Plot radial grid lines and remember the end position
|
---|
| 868 | // and the angle for later use when plotting the labels
|
---|
| 869 | if( $this->iType != WINDROSE_TYPEFREE ) {
|
---|
| 870 | JpGraphError::RaiseL(22008); //('Internal error: Trying to plot free Windrose even though type is not a free windorose');
|
---|
| 871 | }
|
---|
| 872 |
|
---|
| 873 | // Check if we should auto-position the angle for the
|
---|
| 874 | // labels. Basically we try to find a firection with smallest
|
---|
| 875 | // (or none) data.
|
---|
| 876 | $this->SetAutoScaleAngle(false);
|
---|
| 877 |
|
---|
| 878 | $nlc = count($this->iLegColors);
|
---|
| 879 | $nlw = count($this->iLegweights);
|
---|
| 880 |
|
---|
| 881 | // Stroke grid lines for directions and remember the
|
---|
| 882 | // position for the labels
|
---|
| 883 | $txtpos=array();
|
---|
| 884 | $num = count($this->iData);
|
---|
| 885 |
|
---|
| 886 | $keys = array_keys($this->iData);
|
---|
| 887 |
|
---|
| 888 | foreach( $this->iData as $dir => $legdata ) {
|
---|
| 889 | if( in_array($dir,$this->iAllDirectionLabels,true) === true) {
|
---|
| 890 | $a = $this->iStandardDirections[strtoupper($dir)];
|
---|
| 891 | if( in_array($a*180/M_PI,$keys) ) {
|
---|
| 892 | JpGraphError::RaiseL(22009,round($a*180/M_PI));
|
---|
| 893 | //('You have specified the same direction twice, once with an angle and once with a compass direction ('.$a*180/M_PI.' degrees.)');
|
---|
| 894 | }
|
---|
| 895 | }
|
---|
| 896 | elseif( is_numeric($dir) ) {
|
---|
| 897 | $this->NormAngle($dir);
|
---|
| 898 |
|
---|
| 899 | if( $this->iOrdinalEncoding == KEYENCODING_CLOCKWISE ) {
|
---|
| 900 | $dir = 360-$dir;
|
---|
| 901 | }
|
---|
| 902 |
|
---|
| 903 | $a = $dir * M_PI/180;
|
---|
| 904 | }
|
---|
| 905 | else {
|
---|
| 906 | JpGraphError::RaiseL(22010);//('Direction must either be a numeric value or one of the 16 compass directions');
|
---|
| 907 | }
|
---|
| 908 |
|
---|
| 909 | $xxc = round($xc + cos($a)*$ri);
|
---|
| 910 | $yyc = round($yc - sin($a)*$ri);
|
---|
| 911 | $x = round($xc + cos($a)*$r);
|
---|
| 912 | $y = round($yc - sin($a)*$r);
|
---|
| 913 | if( empty($this->iRadialColorArray[$dir]) ) {
|
---|
| 914 | $dblImg->SetColor($this->iGridColor2);
|
---|
| 915 | }
|
---|
| 916 | else {
|
---|
| 917 | $dblImg->SetColor($this->iRadialColorArray[$dir]);
|
---|
| 918 | }
|
---|
| 919 | if( empty($this->iRadialWeightArray[$dir]) ) {
|
---|
| 920 | $dblImg->SetLineWeight($this->iRadialGridWeight);
|
---|
| 921 | }
|
---|
| 922 | else {
|
---|
| 923 | $dblImg->SetLineWeight($this->iRadialWeightArray[$dir]);
|
---|
| 924 | }
|
---|
| 925 | if( empty($this->iRadialStyleArray[$dir]) ) {
|
---|
| 926 | $dblImg->SetLineStyle($this->iRadialGridStyle);
|
---|
| 927 | }
|
---|
| 928 | else {
|
---|
| 929 | $dblImg->SetLineStyle($this->iRadialStyleArray[$dir]);
|
---|
| 930 | }
|
---|
| 931 | $dblImg->StyleLine($xxc,$yyc,$x,$y);
|
---|
| 932 | $txtpos[] = array($x,$y,$a);
|
---|
| 933 | }
|
---|
| 934 | $dblImg->SetLineWeight(1);
|
---|
| 935 |
|
---|
| 936 | // Setup labels
|
---|
| 937 | $lr = $scaling * $this->iLabelMargin;
|
---|
| 938 |
|
---|
| 939 | if( $this->iLabelPositioning == LBLPOSITION_EDGE ) {
|
---|
| 940 | $value->SetAlign('left','top');
|
---|
| 941 | }
|
---|
| 942 | else {
|
---|
| 943 | $value->SetAlign('center','center');
|
---|
| 944 | $value->SetMargin(0);
|
---|
| 945 | }
|
---|
| 946 |
|
---|
| 947 | for($i=0; $i < $num; ++$i ) {
|
---|
| 948 |
|
---|
| 949 | list($x,$y,$a) = $txtpos[$i];
|
---|
| 950 |
|
---|
| 951 | // Determine the label
|
---|
| 952 |
|
---|
| 953 | $da = $a*180/M_PI;
|
---|
| 954 | if( $this->iOrdinalEncoding == KEYENCODING_CLOCKWISE ) {
|
---|
| 955 | $da = 360 - $da;
|
---|
| 956 | }
|
---|
| 957 |
|
---|
| 958 | //$da = 360-$da;
|
---|
| 959 |
|
---|
| 960 | if( !empty($this->iLabels[$keys[$i]]) ) {
|
---|
| 961 | $lbl = $this->iLabels[$keys[$i]];
|
---|
| 962 | }
|
---|
| 963 | else {
|
---|
| 964 | $lbl = sprintf($this->iLabelFormatString,$da);
|
---|
| 965 | }
|
---|
| 966 |
|
---|
| 967 | if( $this->iLabelPositioning == LBLPOSITION_CENTER ) {
|
---|
| 968 | $dx = $dy = 0;
|
---|
| 969 | }
|
---|
| 970 | else {
|
---|
| 971 | // LBLPOSIITON_EDGE
|
---|
| 972 | if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0;
|
---|
| 973 | if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI;
|
---|
| 974 | if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1;
|
---|
| 975 | if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI);
|
---|
| 976 |
|
---|
| 977 | if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI;
|
---|
| 978 | if( $a<=M_PI/4 ) $dy=(0.5+$a*2/M_PI);
|
---|
| 979 | if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1;
|
---|
| 980 | if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI);
|
---|
| 981 | if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0;
|
---|
| 982 | }
|
---|
| 983 |
|
---|
| 984 | $value->Set($lbl);
|
---|
| 985 | $th = $value->GetHeight($dblImg);
|
---|
| 986 | $tw = $value->GetWidth($dblImg);
|
---|
| 987 | $xt=round($lr*cos($a)+$x) - $dx*$tw;
|
---|
| 988 | $yt=round($y-$lr*sin($a)) - $dy*$th;
|
---|
| 989 |
|
---|
| 990 | $value->Stroke($dblImg,$xt,$yt);
|
---|
| 991 | }
|
---|
| 992 |
|
---|
| 993 | if( __DEBUG ) {
|
---|
| 994 | $dblImg->SetColor('red');
|
---|
| 995 | $dblImg->Circle($xc,$yc,$lr+$r);
|
---|
| 996 | }
|
---|
| 997 |
|
---|
| 998 | // Stroke all the legs
|
---|
| 999 | reset($this->iData);
|
---|
| 1000 | $i=0;
|
---|
| 1001 | foreach($this->iData as $dir => $legdata) {
|
---|
| 1002 | $legdata = array_slice($legdata,1);
|
---|
| 1003 | $nn = count($legdata);
|
---|
| 1004 |
|
---|
| 1005 | $a = $txtpos[$i][2];
|
---|
| 1006 | $rri = $ri/$scaling;
|
---|
| 1007 | for( $j=0; $j < $nn; ++$j ) {
|
---|
| 1008 | // We want the non scaled original radius
|
---|
| 1009 | $legr = $this->scale->RelTranslate($legdata[$j],$r/$scaling,$ri/$scaling) ;
|
---|
| 1010 | $this->_StrokeWindLeg($dblImg, $xc, $yc, $a,
|
---|
| 1011 | $rri *$scaling,
|
---|
| 1012 | $legr *$scaling,
|
---|
| 1013 | $this->iLegweights[$j % $nlw] * $scaling,
|
---|
| 1014 | $this->iLegColors[$j % $nlc]);
|
---|
| 1015 | $rri += $legr;
|
---|
| 1016 | }
|
---|
| 1017 | ++$i;
|
---|
| 1018 | }
|
---|
| 1019 | }
|
---|
| 1020 |
|
---|
| 1021 | // Translate potential string specified compass labels to their
|
---|
| 1022 | // corresponding index.
|
---|
| 1023 | function FixupIndexes($aDataArray,$num) {
|
---|
| 1024 | $ret = array();
|
---|
| 1025 | $keys = array_keys($aDataArray);
|
---|
| 1026 | foreach($aDataArray as $idx => $data) {
|
---|
| 1027 | if( is_string($idx) ) {
|
---|
| 1028 | $idx = strtoupper($idx);
|
---|
| 1029 | $res = array_search($idx,$this->iAllDirectionLabels);
|
---|
| 1030 | if( $res === false ) {
|
---|
| 1031 | JpGraphError::RaiseL(22011,$idx); //('Windrose index must be numeric or direction label. You have specified index='.$idx);
|
---|
| 1032 | }
|
---|
| 1033 | $idx = $res;
|
---|
| 1034 | if( $idx % (16 / $num) !== 0 ) {
|
---|
| 1035 | JpGraphError::RaiseL(22012); //('Windrose radial axis specification contains a direction which is not enabled.');
|
---|
| 1036 | }
|
---|
| 1037 | $idx /= (16/$num) ;
|
---|
| 1038 |
|
---|
| 1039 | if( in_array($idx,$keys,1) ) {
|
---|
| 1040 | JpgraphError::RaiseL(22013,$idx); //('You have specified the look&feel for the same compass direction twice, once with text and once with index (Index='.$idx.')');
|
---|
| 1041 | }
|
---|
| 1042 | }
|
---|
| 1043 | if( $idx < 0 || $idx > 15 ) {
|
---|
| 1044 | JpgraphError::RaiseL(22014); //('Index for copmass direction must be between 0 and 15.');
|
---|
| 1045 | }
|
---|
| 1046 | $ret[$idx] = $data;
|
---|
| 1047 | }
|
---|
| 1048 | return $ret;
|
---|
| 1049 | }
|
---|
| 1050 |
|
---|
| 1051 | function _StrokeRegularRose($dblImg,$value,$scaling,$xc,$yc,$r,$ri) {
|
---|
| 1052 | // _StrokeRegularRose($dblImg,$xc,$yc,$r,$ri)
|
---|
| 1053 | // Plot radial grid lines and remember the end position
|
---|
| 1054 | // and the angle for later use when plotting the labels
|
---|
| 1055 | switch( $this->iType ) {
|
---|
| 1056 | case WINDROSE_TYPE4:
|
---|
| 1057 | $num = 4; break;
|
---|
| 1058 | case WINDROSE_TYPE8:
|
---|
| 1059 | $num = 8; break;
|
---|
| 1060 | case WINDROSE_TYPE16:
|
---|
| 1061 | $num = 16; break;
|
---|
| 1062 | default:
|
---|
| 1063 | JpGraphError::RaiseL(22015);//('You have specified an undefined Windrose plot type.');
|
---|
| 1064 | }
|
---|
| 1065 |
|
---|
| 1066 | // Check if we should auto-position the angle for the
|
---|
| 1067 | // labels. Basically we try to find a firection with smallest
|
---|
| 1068 | // (or none) data.
|
---|
| 1069 | $this->SetAutoScaleAngle(true);
|
---|
| 1070 |
|
---|
| 1071 | $nlc = count($this->iLegColors);
|
---|
| 1072 | $nlw = count($this->iLegweights);
|
---|
| 1073 |
|
---|
| 1074 | $this->iRadialColorArray = $this->FixupIndexes($this->iRadialColorArray,$num);
|
---|
| 1075 | $this->iRadialWeightArray = $this->FixupIndexes($this->iRadialWeightArray,$num);
|
---|
| 1076 | $this->iRadialStyleArray = $this->FixupIndexes($this->iRadialStyleArray,$num);
|
---|
| 1077 |
|
---|
| 1078 | $txtpos=array();
|
---|
| 1079 | $a = 2*M_PI/$num;
|
---|
| 1080 | $dblImg->SetColor($this->iGridColor2);
|
---|
| 1081 | $dblImg->SetLineStyle($this->iRadialGridStyle);
|
---|
| 1082 | $dblImg->SetLineWeight($this->iRadialGridWeight);
|
---|
| 1083 |
|
---|
| 1084 | // Translate any name specified directions to the index
|
---|
| 1085 | // so we can easily use it in the loop below
|
---|
| 1086 | for($i=0; $i < $num; ++$i ) {
|
---|
| 1087 | $xxc = round($xc + cos($a*$i)*$ri);
|
---|
| 1088 | $yyc = round($yc - sin($a*$i)*$ri);
|
---|
| 1089 | $x = round($xc + cos($a*$i)*$r);
|
---|
| 1090 | $y = round($yc - sin($a*$i)*$r);
|
---|
| 1091 | if( empty($this->iRadialColorArray[$i]) ) {
|
---|
| 1092 | $dblImg->SetColor($this->iGridColor2);
|
---|
| 1093 | }
|
---|
| 1094 | else {
|
---|
| 1095 | $dblImg->SetColor($this->iRadialColorArray[$i]);
|
---|
| 1096 | }
|
---|
| 1097 | if( empty($this->iRadialWeightArray[$i]) ) {
|
---|
| 1098 | $dblImg->SetLineWeight($this->iRadialGridWeight);
|
---|
| 1099 | }
|
---|
| 1100 | else {
|
---|
| 1101 | $dblImg->SetLineWeight($this->iRadialWeightArray[$i]);
|
---|
| 1102 | }
|
---|
| 1103 | if( empty($this->iRadialStyleArray[$i]) ) {
|
---|
| 1104 | $dblImg->SetLineStyle($this->iRadialGridStyle);
|
---|
| 1105 | }
|
---|
| 1106 | else {
|
---|
| 1107 | $dblImg->SetLineStyle($this->iRadialStyleArray[$i]);
|
---|
| 1108 | }
|
---|
| 1109 |
|
---|
| 1110 | $dblImg->StyleLine($xxc,$yyc,$x,$y);
|
---|
| 1111 | $txtpos[] = array($x,$y,$a*$i);
|
---|
| 1112 | }
|
---|
| 1113 | $dblImg->SetLineWeight(1);
|
---|
| 1114 |
|
---|
| 1115 | $lr = $scaling * $this->iLabelMargin;
|
---|
| 1116 | if( $this->iLabelPositioning == LBLPOSITION_CENTER ) {
|
---|
| 1117 | $value->SetAlign('center','center');
|
---|
| 1118 | }
|
---|
| 1119 | else {
|
---|
| 1120 | $value->SetAlign('left','top');
|
---|
| 1121 | $value->SetMargin(0);
|
---|
| 1122 | $lr /= 2 ;
|
---|
| 1123 | }
|
---|
| 1124 |
|
---|
| 1125 | for($i=0; $i < $num; ++$i ) {
|
---|
| 1126 | list($x,$y,$a) = $txtpos[$i];
|
---|
| 1127 |
|
---|
| 1128 | // Set the position of the label
|
---|
| 1129 | if( $this->iLabelPositioning == LBLPOSITION_CENTER ) {
|
---|
| 1130 | $dx = $dy = 0;
|
---|
| 1131 | }
|
---|
| 1132 | else {
|
---|
| 1133 | // LBLPOSIITON_EDGE
|
---|
| 1134 | if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0;
|
---|
| 1135 | if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI;
|
---|
| 1136 | if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1;
|
---|
| 1137 | if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI);
|
---|
| 1138 |
|
---|
| 1139 | if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI;
|
---|
| 1140 | if( $a<=M_PI/4 ) $dy=(0.5+$a*2/M_PI);
|
---|
| 1141 | if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1;
|
---|
| 1142 | if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI);
|
---|
| 1143 | if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0;
|
---|
| 1144 | }
|
---|
| 1145 |
|
---|
| 1146 | $value->Set($this->iAllDirectionLabels[$i*(16/$num)]);
|
---|
| 1147 | $th = $value->GetHeight($dblImg);
|
---|
| 1148 | $tw = $value->GetWidth($dblImg);
|
---|
| 1149 | $xt=round($lr*cos($a)+$x) - $dx*$tw;
|
---|
| 1150 | $yt=round($y-$lr*sin($a)) - $dy*$th;
|
---|
| 1151 |
|
---|
| 1152 | $value->Stroke($dblImg,$xt,$yt);
|
---|
| 1153 | }
|
---|
| 1154 |
|
---|
| 1155 | if( __DEBUG ) {
|
---|
| 1156 | $dblImg->SetColor("red");
|
---|
| 1157 | $dblImg->Circle($xc,$yc,$lr+$r);
|
---|
| 1158 | }
|
---|
| 1159 |
|
---|
| 1160 | // Stroke all the legs
|
---|
| 1161 | reset($this->iData);
|
---|
| 1162 | $keys = array_keys($this->iData);
|
---|
| 1163 | foreach($this->iData as $idx => $legdata) {
|
---|
| 1164 | $legdata = array_slice($legdata,1);
|
---|
| 1165 | $nn = count($legdata);
|
---|
| 1166 | if( is_string($idx) ) {
|
---|
| 1167 | $idx = strtoupper($idx);
|
---|
| 1168 | $idx = array_search($idx,$this->iAllDirectionLabels);
|
---|
| 1169 | if( $idx === false ) {
|
---|
| 1170 | JpGraphError::RaiseL(22016);//('Windrose leg index must be numeric or direction label.');
|
---|
| 1171 | }
|
---|
| 1172 | if( $idx % (16 / $num) !== 0 ) {
|
---|
| 1173 | JpGraphError::RaiseL(22017);//('Windrose data contains a direction which is not enabled. Please adjust what labels are displayed.');
|
---|
| 1174 | }
|
---|
| 1175 | $idx /= (16/$num) ;
|
---|
| 1176 |
|
---|
| 1177 | if( in_array($idx,$keys,1) ) {
|
---|
| 1178 | JpgraphError::RaiseL(22018,$idx);//('You have specified data for the same compass direction twice, once with text and once with index (Index='.$idx.')');
|
---|
| 1179 |
|
---|
| 1180 | }
|
---|
| 1181 | }
|
---|
| 1182 | if( $idx < 0 || $idx > 15 ) {
|
---|
| 1183 | JpgraphError::RaiseL(22019);//('Index for direction must be between 0 and 15. You can\'t specify angles for a Regular Windplot, only index and compass directions.');
|
---|
| 1184 | }
|
---|
| 1185 | $a = $idx * (360 / $num) ;
|
---|
| 1186 | $a *= M_PI/180.0;
|
---|
| 1187 | $rri = $ri/$scaling;
|
---|
| 1188 | for( $j=0; $j < $nn; ++$j ) {
|
---|
| 1189 | // We want the non scaled original radius
|
---|
| 1190 | $legr = $this->scale->RelTranslate($legdata[$j], $r/$scaling,$ri/$scaling) ;
|
---|
| 1191 | $this->_StrokeWindLeg($dblImg, $xc, $yc, $a,
|
---|
| 1192 | $rri *$scaling,
|
---|
| 1193 | $legr *$scaling,
|
---|
| 1194 | $this->iLegweights[$j % $nlw] * $scaling,
|
---|
| 1195 | $this->iLegColors[$j % $nlc]);
|
---|
| 1196 | $rri += $legr;
|
---|
| 1197 | }
|
---|
| 1198 | }
|
---|
| 1199 | }
|
---|
| 1200 |
|
---|
| 1201 |
|
---|
| 1202 | function getWidth($aImg) {
|
---|
| 1203 |
|
---|
| 1204 | $scaling = 1;//$this->iAntiAlias ? 2 : 1 ;
|
---|
| 1205 | if( $this->iSize > 0 && $this->iSize < 1 ) {
|
---|
| 1206 | $this->iSize *= min($aImg->width,$aImg->height);
|
---|
| 1207 | }
|
---|
| 1208 |
|
---|
| 1209 |
|
---|
| 1210 | $value = new Text();
|
---|
| 1211 | $value->SetFont($this->iFontFamily,$this->iFontStyle,$this->iFontSize*$scaling);
|
---|
| 1212 | $value->SetColor($this->iFontColor);
|
---|
| 1213 | // Setup extra size around the graph needed so that the labels
|
---|
| 1214 | // doesn't get cut. For this we need to find the largest label.
|
---|
| 1215 | // The code below gives a possible a little to large margin. The
|
---|
| 1216 | // really, really proper way would be to account for what angle
|
---|
| 1217 | // the label are at
|
---|
| 1218 | $n = count($this->iLabels);
|
---|
| 1219 | if( $n > 0 ) {
|
---|
| 1220 | $maxh=0;$maxw=0;
|
---|
| 1221 | foreach($this->iLabels as $key => $lbl) {
|
---|
| 1222 | $value->Set($lbl);
|
---|
| 1223 | $maxw = max($maxw,$value->GetWidth($aImg));
|
---|
| 1224 | }
|
---|
| 1225 | }
|
---|
| 1226 | else {
|
---|
| 1227 | $value->Set('888.888'); // Dummy value to get width/height
|
---|
| 1228 | $maxw = $value->GetWidth($aImg);
|
---|
| 1229 | }
|
---|
| 1230 | // Add an extra margin of 50% the font size
|
---|
| 1231 | $maxw += round($this->iFontSize*$scaling * 0.4) ;
|
---|
| 1232 |
|
---|
| 1233 | $valxmarg = 1.5*$maxw+2*$this->iLabelMargin*$scaling;
|
---|
| 1234 | $w = round($this->iSize*$scaling + $valxmarg);
|
---|
| 1235 |
|
---|
| 1236 | // Make sure that the width of the legend fits
|
---|
| 1237 | $legendwidth = $this->_StrokeLegend($aImg,0,0,$scaling,true)+10*$scaling;
|
---|
| 1238 | $w = max($w,$legendwidth);
|
---|
| 1239 |
|
---|
| 1240 | return $w;
|
---|
| 1241 | }
|
---|
| 1242 |
|
---|
| 1243 | function getHeight($aImg) {
|
---|
| 1244 |
|
---|
| 1245 | $scaling = 1;//$this->iAntiAlias ? 2 : 1 ;
|
---|
| 1246 | if( $this->iSize > 0 && $this->iSize < 1 ) {
|
---|
| 1247 | $this->iSize *= min($aImg->width,$aImg->height);
|
---|
| 1248 | }
|
---|
| 1249 |
|
---|
| 1250 | $value = new Text();
|
---|
| 1251 | $value->SetFont($this->iFontFamily,$this->iFontStyle,$this->iFontSize*$scaling);
|
---|
| 1252 | $value->SetColor($this->iFontColor);
|
---|
| 1253 | // Setup extra size around the graph needed so that the labels
|
---|
| 1254 | // doesn't get cut. For this we need to find the largest label.
|
---|
| 1255 | // The code below gives a possible a little to large margin. The
|
---|
| 1256 | // really, really proper way would be to account for what angle
|
---|
| 1257 | // the label are at
|
---|
| 1258 | $n = count($this->iLabels);
|
---|
| 1259 | if( $n > 0 ) {
|
---|
| 1260 | $maxh=0;$maxw=0;
|
---|
| 1261 | foreach($this->iLabels as $key => $lbl) {
|
---|
| 1262 | $value->Set($lbl);
|
---|
| 1263 | $maxh = max($maxh,$value->GetHeight($aImg));
|
---|
| 1264 | }
|
---|
| 1265 | }
|
---|
| 1266 | else {
|
---|
| 1267 | $value->Set('180.8'); // Dummy value to get width/height
|
---|
| 1268 | $maxh = $value->GetHeight($aImg);
|
---|
| 1269 | }
|
---|
| 1270 | // Add an extra margin of 50% the font size
|
---|
| 1271 | //$maxh += round($this->iFontSize*$scaling * 0.5) ;
|
---|
| 1272 | $valymarg = 2*$maxh+2*$this->iLabelMargin*$scaling;
|
---|
| 1273 |
|
---|
| 1274 | $legendheight = round($this->legend->iShow ? 1 : 0);
|
---|
| 1275 | $legendheight *= max($this->legend->iCircleRadius*2,$this->legend->iTxtFontSize*2)+
|
---|
| 1276 | $this->legend->iMargin + $this->legend->iBottomMargin + 2;
|
---|
| 1277 | $legendheight *= $scaling;
|
---|
| 1278 | $h = round($this->iSize*$scaling + $valymarg) + $legendheight ;
|
---|
| 1279 |
|
---|
| 1280 | return $h;
|
---|
| 1281 | }
|
---|
| 1282 |
|
---|
| 1283 | function Stroke($aGraph) {
|
---|
| 1284 |
|
---|
| 1285 | $aImg = $aGraph->img;
|
---|
| 1286 |
|
---|
| 1287 | if( $this->iX > 0 && $this->iX < 1 ) {
|
---|
| 1288 | $this->iX = round( $aImg->width * $this->iX ) ;
|
---|
| 1289 | }
|
---|
| 1290 |
|
---|
| 1291 | if( $this->iY > 0 && $this->iY < 1 ) {
|
---|
| 1292 | $this->iY = round( $aImg->height * $this->iY ) ;
|
---|
| 1293 | }
|
---|
| 1294 |
|
---|
| 1295 | if( $this->iSize > 0 && $this->iSize < 1 ) {
|
---|
| 1296 | $this->iSize *= min($aImg->width,$aImg->height);
|
---|
| 1297 | }
|
---|
| 1298 |
|
---|
| 1299 | if( $this->iCenterSize > 0 && $this->iCenterSize < 1 ) {
|
---|
| 1300 | $this->iCenterSize *= $this->iSize;
|
---|
| 1301 | }
|
---|
| 1302 |
|
---|
| 1303 | $this->scale->AutoScale(($this->iSize - $this->iCenterSize)/2, round(2.5*$this->scale->iFontSize));
|
---|
| 1304 |
|
---|
| 1305 | $scaling = $this->iAntiAlias ? 2 : 1 ;
|
---|
| 1306 |
|
---|
| 1307 | $value = new Text();
|
---|
| 1308 | $value->SetFont($this->iFontFamily,$this->iFontStyle,$this->iFontSize*$scaling);
|
---|
| 1309 | $value->SetColor($this->iFontColor);
|
---|
| 1310 |
|
---|
| 1311 | $legendheight = round($this->legend->iShow ? 1 : 0);
|
---|
| 1312 | $legendheight *= max($this->legend->iCircleRadius*2,$this->legend->iTxtFontSize*2)+
|
---|
| 1313 | $this->legend->iMargin + $this->legend->iBottomMargin + 2;
|
---|
| 1314 | $legendheight *= $scaling;
|
---|
| 1315 |
|
---|
| 1316 | $w = $scaling*$this->getWidth($aImg);
|
---|
| 1317 | $h = $scaling*$this->getHeight($aImg);
|
---|
| 1318 |
|
---|
| 1319 | // Copy back the double buffered image to the proper canvas
|
---|
| 1320 | $ww = $w / $scaling ;
|
---|
| 1321 | $hh = $h / $scaling ;
|
---|
| 1322 |
|
---|
| 1323 | // Create the double buffer
|
---|
| 1324 | if( $this->iAntiAlias ) {
|
---|
| 1325 | $dblImg = new RotImage($w,$h);
|
---|
| 1326 | // Set the background color
|
---|
| 1327 | $dblImg->SetColor($this->iColor);
|
---|
| 1328 | $dblImg->FilledRectangle(0,0,$w,$h);
|
---|
| 1329 | }
|
---|
| 1330 | else {
|
---|
| 1331 | $dblImg = $aImg ;
|
---|
| 1332 | // Make sure the ix and it coordinates correpond to the new top left center
|
---|
| 1333 | $dblImg->SetTranslation($this->iX-$w/2, $this->iY-$h/2);
|
---|
| 1334 | }
|
---|
| 1335 |
|
---|
| 1336 | if( __DEBUG ) {
|
---|
| 1337 | $dblImg->SetColor('red');
|
---|
| 1338 | $dblImg->Rectangle(0,0,$w-1,$h-1);
|
---|
| 1339 | }
|
---|
| 1340 |
|
---|
| 1341 | $dblImg->SetColor('black');
|
---|
| 1342 |
|
---|
| 1343 | if( $this->iShowBox ) {
|
---|
| 1344 | $dblImg->SetColor($this->iBoxColor);
|
---|
| 1345 | $old = $dblImg->SetLineWeight($this->iBoxWeight);
|
---|
| 1346 | $dblImg->SetLineStyle($this->iBoxStyle);
|
---|
| 1347 | $dblImg->Rectangle(0,0,$w-1,$h-1);
|
---|
| 1348 | $dblImg->SetLineWeight($old);
|
---|
| 1349 | }
|
---|
| 1350 |
|
---|
| 1351 | $xc = round($w/2);
|
---|
| 1352 | $yc = round(($h-$legendheight)/2);
|
---|
| 1353 |
|
---|
| 1354 | if( __DEBUG ) {
|
---|
| 1355 | $dblImg->SetColor('red');
|
---|
| 1356 | $old = $dblImg->SetLineWeight(2);
|
---|
| 1357 | $dblImg->Line($xc-5,$yc-5,$xc+5,$yc+5);
|
---|
| 1358 | $dblImg->Line($xc+5,$yc-5,$xc-5,$yc+5);
|
---|
| 1359 | $dblImg->SetLineWeight($old);
|
---|
| 1360 | }
|
---|
| 1361 |
|
---|
| 1362 | $this->iSize *= $scaling;
|
---|
| 1363 |
|
---|
| 1364 | // Inner circle size
|
---|
| 1365 | $ri = $this->iCenterSize/2 ;
|
---|
| 1366 |
|
---|
| 1367 | // Full circle radius
|
---|
| 1368 | $r = round( $this->iSize/2 );
|
---|
| 1369 |
|
---|
| 1370 | // Get number of grid circles
|
---|
| 1371 | $n = $this->scale->GetNumCirc();
|
---|
| 1372 |
|
---|
| 1373 | // Plot circle grids
|
---|
| 1374 | $ri *= $scaling ;
|
---|
| 1375 | $rr = round(($r-$ri)/$n);
|
---|
| 1376 | for( $i = 1; $i <= $n; ++$i ) {
|
---|
| 1377 | $this->_ThickCircle($dblImg,$xc,$yc,$rr*$i+$ri,
|
---|
| 1378 | $this->iCircGridWeight,$this->iGridColor1);
|
---|
| 1379 | }
|
---|
| 1380 |
|
---|
| 1381 | $num = 0 ;
|
---|
| 1382 |
|
---|
| 1383 | if( $this->iType == WINDROSE_TYPEFREE ) {
|
---|
| 1384 | $this->_StrokeFreeRose($dblImg,$value,$scaling,$xc,$yc,$r,$ri);
|
---|
| 1385 | }
|
---|
| 1386 | else {
|
---|
| 1387 | // Check if we need to re-code the interpretation of the ordinal
|
---|
| 1388 | // number in the data. Internally ordinal value 0 is East and then
|
---|
| 1389 | // counted anti-clockwise. The user might choose an encoding
|
---|
| 1390 | // that have 0 being the first axis to the right of the "N" axis and then
|
---|
| 1391 | // counted clock-wise
|
---|
| 1392 | if( $this->iOrdinalEncoding == KEYENCODING_CLOCKWISE ) {
|
---|
| 1393 | if( $this->iType == WINDROSE_TYPE16 ) {
|
---|
| 1394 | $const1 = 19; $const2 = 16;
|
---|
| 1395 | }
|
---|
| 1396 | elseif( $this->iType == WINDROSE_TYPE8 ) {
|
---|
| 1397 | $const1 = 9; $const2 = 8;
|
---|
| 1398 | }
|
---|
| 1399 | else {
|
---|
| 1400 | $const1 = 4; $const2 = 4;
|
---|
| 1401 | }
|
---|
| 1402 | $tmp = array();
|
---|
| 1403 | $n=count($this->iData);
|
---|
| 1404 | foreach( $this->iData as $key => $val ) {
|
---|
| 1405 | if( is_numeric($key) ) {
|
---|
| 1406 | $key = ($const1 - $key) % $const2 ;
|
---|
| 1407 | }
|
---|
| 1408 | $tmp[$key] = $val;
|
---|
| 1409 | }
|
---|
| 1410 | $this->iData = $tmp;
|
---|
| 1411 | }
|
---|
| 1412 | $this->_StrokeRegularRose($dblImg,$value,$scaling,$xc,$yc,$r,$ri);
|
---|
| 1413 | }
|
---|
| 1414 |
|
---|
| 1415 | // Stroke the labels
|
---|
| 1416 | $this->scale->iFontSize *= $scaling;
|
---|
| 1417 | $this->scale->iZFontSize *= $scaling;
|
---|
| 1418 | $this->scale->StrokeLabels($dblImg,$xc,$yc,$ri,$rr);
|
---|
| 1419 |
|
---|
| 1420 | // Stroke the inner circle again since the legs
|
---|
| 1421 | // might have written over it
|
---|
| 1422 | $this->_ThickCircle($dblImg,$xc,$yc,$ri,$this->iCircGridWeight,$this->iGridColor1);
|
---|
| 1423 |
|
---|
| 1424 | if( $ww > $aImg->width ) {
|
---|
| 1425 | JpgraphError::RaiseL(22020);
|
---|
| 1426 | //('Windrose plot is too large to fit the specified Graph size. Please use WindrosePlot::SetSize() to make the plot smaller or increase the size of the Graph in the initial WindroseGraph() call.');
|
---|
| 1427 | }
|
---|
| 1428 |
|
---|
| 1429 | $x = $xc;
|
---|
| 1430 | $y = $h;
|
---|
| 1431 | $this->_StrokeLegend($dblImg,$x,$y,$scaling);
|
---|
| 1432 |
|
---|
| 1433 | if( $this->iAntiAlias ) {
|
---|
| 1434 | $aImg->Copy($dblImg->img, $this->iX-$ww/2, $this->iY-$hh/2, 0, 0, $ww,$hh, $w,$h);
|
---|
| 1435 | }
|
---|
| 1436 |
|
---|
| 1437 | // We need to restore the translation matrix
|
---|
| 1438 | $aImg->SetTranslation(0,0);
|
---|
| 1439 |
|
---|
| 1440 | }
|
---|
| 1441 |
|
---|
| 1442 | }
|
---|
| 1443 |
|
---|
| 1444 | //============================================================
|
---|
| 1445 | // CLASS WindroseGraph
|
---|
| 1446 | //============================================================
|
---|
| 1447 | class WindroseGraph extends Graph {
|
---|
| 1448 | private $posx, $posy;
|
---|
| 1449 | public $plots=array();
|
---|
| 1450 |
|
---|
| 1451 | function __construct($width=300,$height=200,$cachedName="",$timeout=0,$inline=1) {
|
---|
| 1452 | parent::__construct($width,$height,$cachedName,$timeout,$inline);
|
---|
| 1453 | $this->posx=$width/2;
|
---|
| 1454 | $this->posy=$height/2;
|
---|
| 1455 | $this->SetColor('white');
|
---|
| 1456 | $this->title->SetFont(FF_VERDANA,FS_NORMAL,12);
|
---|
| 1457 | $this->title->SetMargin(8);
|
---|
| 1458 | $this->subtitle->SetFont(FF_VERDANA,FS_NORMAL,10);
|
---|
| 1459 | $this->subtitle->SetMargin(0);
|
---|
| 1460 | $this->subsubtitle->SetFont(FF_VERDANA,FS_NORMAL,8);
|
---|
| 1461 | $this->subsubtitle->SetMargin(0);
|
---|
| 1462 | }
|
---|
| 1463 |
|
---|
| 1464 | function StrokeTexts() {
|
---|
| 1465 | if( $this->texts != null ) {
|
---|
| 1466 | $n = count($this->texts);
|
---|
| 1467 | for($i=0; $i < $n; ++$i ) {
|
---|
| 1468 | $this->texts[$i]->Stroke($this->img);
|
---|
| 1469 | }
|
---|
| 1470 | }
|
---|
| 1471 | }
|
---|
| 1472 |
|
---|
| 1473 | function StrokeIcons() {
|
---|
| 1474 | if( $this->iIcons != null ) {
|
---|
| 1475 | $n = count($this->iIcons);
|
---|
| 1476 | for( $i=0; $i < $n; ++$i ) {
|
---|
| 1477 | // Since Windrose graphs doesn't have any linear scale the position of
|
---|
| 1478 | // each icon has to be given as absolute coordinates
|
---|
| 1479 | $this->iIcons[$i]->_Stroke($this->img);
|
---|
| 1480 | }
|
---|
| 1481 | }
|
---|
| 1482 | }
|
---|
| 1483 |
|
---|
| 1484 | //---------------
|
---|
| 1485 | // PUBLIC METHODS
|
---|
| 1486 | function Add($aObj) {
|
---|
| 1487 | if( is_array($aObj) && count($aObj) > 0 ) {
|
---|
| 1488 | $cl = $aObj[0];
|
---|
| 1489 | }
|
---|
| 1490 | else {
|
---|
| 1491 | $cl = $aObj;
|
---|
| 1492 | }
|
---|
| 1493 | if( $cl instanceof Text ) {
|
---|
| 1494 | $this->AddText($aObj);
|
---|
| 1495 | }
|
---|
| 1496 | elseif( $cl instanceof IconPlot ) {
|
---|
| 1497 | $this->AddIcon($aObj);
|
---|
| 1498 | }
|
---|
| 1499 | elseif( ($cl instanceof WindrosePlot) || ($cl instanceof LayoutRect) || ($cl instanceof LayoutHor)) {
|
---|
| 1500 | $this->plots[] = $aObj;
|
---|
| 1501 | }
|
---|
| 1502 | else {
|
---|
| 1503 | JpgraphError::RaiseL(22021);
|
---|
| 1504 | }
|
---|
| 1505 | }
|
---|
| 1506 |
|
---|
| 1507 | function AddText($aTxt,$aToY2=false) {
|
---|
| 1508 | parent::AddText($aTxt);
|
---|
| 1509 | }
|
---|
| 1510 |
|
---|
| 1511 | function SetColor($c) {
|
---|
| 1512 | $this->SetMarginColor($c);
|
---|
| 1513 | }
|
---|
| 1514 |
|
---|
| 1515 | // Method description
|
---|
| 1516 | function Stroke($aStrokeFileName="") {
|
---|
| 1517 |
|
---|
| 1518 | // If the filename is the predefined value = '_csim_special_'
|
---|
| 1519 | // we assume that the call to stroke only needs to do enough
|
---|
| 1520 | // to correctly generate the CSIM maps.
|
---|
| 1521 | // We use this variable to skip things we don't strictly need
|
---|
| 1522 | // to do to generate the image map to improve performance
|
---|
| 1523 | // as best we can. Therefore you will see a lot of tests !$_csim in the
|
---|
| 1524 | // code below.
|
---|
| 1525 | $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
|
---|
| 1526 |
|
---|
| 1527 | // We need to know if we have stroked the plot in the
|
---|
| 1528 | // GetCSIMareas. Otherwise the CSIM hasn't been generated
|
---|
| 1529 | // and in the case of GetCSIM called before stroke to generate
|
---|
| 1530 | // CSIM without storing an image to disk GetCSIM must call Stroke.
|
---|
| 1531 | $this->iHasStroked = true;
|
---|
| 1532 |
|
---|
| 1533 | if( $this->background_image != "" || $this->background_cflag != "" ) {
|
---|
| 1534 | $this->StrokeFrameBackground();
|
---|
| 1535 | }
|
---|
| 1536 | else {
|
---|
| 1537 | $this->StrokeFrame();
|
---|
| 1538 | }
|
---|
| 1539 |
|
---|
| 1540 | // n holds number of plots
|
---|
| 1541 | $n = count($this->plots);
|
---|
| 1542 | for($i=0; $i < $n ; ++$i) {
|
---|
| 1543 | $this->plots[$i]->Stroke($this);
|
---|
| 1544 | }
|
---|
| 1545 |
|
---|
| 1546 | $this->footer->Stroke($this->img);
|
---|
| 1547 | $this->StrokeIcons();
|
---|
| 1548 | $this->StrokeTexts();
|
---|
| 1549 | $this->StrokeTitles();
|
---|
| 1550 |
|
---|
| 1551 | // If the filename is given as the special "__handle"
|
---|
| 1552 | // then the image handler is returned and the image is NOT
|
---|
| 1553 | // streamed back
|
---|
| 1554 | if( $aStrokeFileName == _IMG_HANDLER ) {
|
---|
| 1555 | return $this->img->img;
|
---|
| 1556 | }
|
---|
| 1557 | else {
|
---|
| 1558 | // Finally stream the generated picture
|
---|
| 1559 | $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
|
---|
| 1560 | $aStrokeFileName);
|
---|
| 1561 | }
|
---|
| 1562 | }
|
---|
| 1563 |
|
---|
| 1564 | } // Class
|
---|
| 1565 |
|
---|
| 1566 | ?>
|
---|