jablonka.czprosek.czf

weathermap

Subversion Repositories:
[/] [WeatherMap.functions.php] - Blame information for rev 135

 

Line No. Rev Author Line
185simandl<?php
2// PHP Weathermap 0.97a
3// Copyright Howard Jones, 2005-2010 howie@thingy.com
4// http://www.network-weathermap.com/
5// Released under the GNU Public License
6 
7// Utility functions
8// Check for GD & PNG support This is just in here so that both the editor and CLI can use it without the need for another file
9function module_checks()
10{
11 if (!extension_loaded('gd'))
12 {
13 warn ("\n\nNo image (gd) extension is loaded. This is required by weathermap. [WMWARN20]\n\n");
14 warn ("\nrun check.php to check PHP requirements.\n\n");
15 
16 return (FALSE);
17 }
18 
19 if (!function_exists('imagecreatefrompng'))
20 {
21 warn ("Your GD php module doesn't support PNG format. [WMWARN21]\n");
22 warn ("\nrun check.php to check PHP requirements.\n\n");
23 return (FALSE);
24 }
25 
26 if (!function_exists('imagecreatetruecolor'))
27 {
28 warn ("Your GD php module doesn't support truecolor. [WMWARN22]\n");
29 warn ("\nrun check.php to check PHP requirements.\n\n");
30 return (FALSE);
31 }
32 
33 if (!function_exists('imagecopyresampled'))
34 {
35 warn ("Your GD php module doesn't support thumbnail creation (imagecopyresampled). [WMWARN23]\n");
36 }
37 return (TRUE);
38}
39 
40function debug($string)
41{
42 global $weathermap_debugging;
43 global $weathermap_map;
44 global $weathermap_debug_suppress;
45 
46 if ($weathermap_debugging)
47 {
48 $calling_fn = "";
49 if(function_exists("debug_backtrace"))
50 {
51 $bt = debug_backtrace();
52 $index = 1;
53 # $class = (isset($bt[$index]['class']) ? $bt[$index]['class'] : '');
54 $function = (isset($bt[$index]['function']) ? $bt[$index]['function'] : '');
55 $index = 0;
56 $file = (isset($bt[$index]['file']) ? basename($bt[$index]['file']) : '');
57 $line = (isset($bt[$index]['line']) ? $bt[$index]['line'] : '');
58 
59 $calling_fn = " [$function@$file:$line]";
60 
61 if(is_array($weathermap_debug_suppress) && in_array(strtolower($function),$weathermap_debug_suppress)) return;
62 }
63 
64 // use Cacti's debug log, if we are running from the poller
65 if (function_exists('debug_log_insert') && (!function_exists('show_editor_startpage')))
66 { cacti_log("DEBUG:$calling_fn " . ($weathermap_map==''?'':$weathermap_map.": ") . rtrim($string), true, "WEATHERMAP"); }
67 else
68 {
69 $stderr=fopen('php://stderr', 'w');
70 fwrite($stderr, "DEBUG:$calling_fn " . ($weathermap_map==''?'':$weathermap_map.": ") . $string);
71 fclose ($stderr);
72 
73 // mostly this is overkill, but it's sometimes useful (mainly in the editor)
74 if(1==0)
75 {
76 $log=fopen('debug.log', 'a');
77 fwrite($log, "DEBUG:$calling_fn " . ($weathermap_map==''?'':$weathermap_map.": ") . $string);
78 fclose ($log);
79 }
80 }
81 }
82}
83 
84function warn($string,$notice_only=FALSE)
85{
86 global $weathermap_map;
87 global $weathermap_warncount;
88 
89 $message = "";
90 
91 if(!$notice_only)
92 {
93 $weathermap_warncount++;
94 $message .= "WARNING: ";
95 }
96 
97 $message .= ($weathermap_map==''?'':$weathermap_map.": ") . rtrim($string);
98 
99 // use Cacti's debug log, if we are running from the poller
100 if (function_exists('cacti_log') && (!function_exists('show_editor_startpage')))
101 { cacti_log($message, true, "WEATHERMAP"); }
102 else
103 {
104 $stderr=fopen('php://stderr', 'w');
105 fwrite($stderr, $message."\n");
106 fclose ($stderr);
107 }
108}
109 
110function js_escape($str, $wrap=TRUE)
111{
112 $str=str_replace('\\', '\\\\', $str);
113 $str=str_replace('"', '\\"', $str);
114 
115 if($wrap) $str='"' . $str . '"';
116 
117 return ($str);
118}
119 
120function mysprintf($format,$value,$kilo=1000)
121{
122 $output = "";
123 
124 debug("mysprintf: $format $value\n");
125 if(preg_match("/%(\d*\.?\d*)k/",$format,$matches))
126 {
127 $spec = $matches[1];
128 $places = 2;
129 if($spec !='')
130 {
131 preg_match("/(\d*)\.?(\d*)/",$spec,$matches);
132 if($matches[2] != '') $places=$matches[2];
133 // we don't really need the justification (pre-.) part...
134 }
135 debug("KMGT formatting $value with $spec.\n");
136 $result = nice_scalar($value, $kilo, $places);
137 $output = preg_replace("/%".$spec."k/",$format,$result);
138 }
139 else
140 {
141 debug("Falling through to standard sprintf\n");
142 $output = sprintf($format,$value);
143 }
144 return $output;
145}
146 
147// ParseString is based on code from:
148// http://www.webscriptexpert.com/Php/Space-Separated%20Tag%20Parser/
149 
150function ParseString($input)
151{
152 $output = array(); // Array of Output
153 $cPhraseQuote = null; // Record of the quote that opened the current phrase
154 $sPhrase = null; // Temp storage for the current phrase we are building
155 
156 // Define some constants
157 $sTokens = " \t"; // Space, Tab
158 $sQuotes = "'\""; // Single and Double Quotes
159 
160 // Start the State Machine
161 do
162 {
163 // Get the next token, which may be the first
164 $sToken = isset($sToken)? strtok($sTokens) : strtok($input, $sTokens);
165 
166 // Are there more tokens?
167 if ($sToken === false)
168 {
169 // Ensure that the last phrase is marked as ended
170 $cPhraseQuote = null;
171 }
172 else
173 {
174 // Are we within a phrase or not?
175 if ($cPhraseQuote !== null)
176 {
177 // Will the current token end the phrase?
178 if (substr($sToken, -1, 1) === $cPhraseQuote)
179 {
180 // Trim the last character and add to the current phrase, with a single leading space if necessary
181 if (strlen($sToken) > 1) $sPhrase .= ((strlen($sPhrase) > 0)? ' ' : null) . substr($sToken, 0, -1);
182 $cPhraseQuote = null;
183 }
184 else
185 {
186 // If not, add the token to the phrase, with a single leading space if necessary
187 $sPhrase .= ((strlen($sPhrase) > 0)? ' ' : null) . $sToken;
188 }
189 }
190 else
191 {
192 // Will the current token start a phrase?
193 if (strpos($sQuotes, $sToken[0]) !== false)
194 {
195 // Will the current token end the phrase?
196 if ((strlen($sToken) > 1) && ($sToken[0] === substr($sToken, -1, 1)))
197 {
198 // The current token begins AND ends the phrase, trim the quotes
199 $sPhrase = substr($sToken, 1, -1);
200 }
201 else
202 {
203 // Remove the leading quote
204 $sPhrase = substr($sToken, 1);
205 $cPhraseQuote = $sToken[0];
206 }
207 }
208 else
209 $sPhrase = $sToken;
210 }
211 }
212 
213 // If, at this point, we are not within a phrase, the prepared phrase is complete and can be added to the array
214 if (($cPhraseQuote === null) && ($sPhrase != null))
215 {
216 $output[] = $sPhrase;
217 $sPhrase = null;
218 }
219 }
220 while ($sToken !== false); // Stop when we receive FALSE from strtok()
221 
222 return $output;
223}
224 
225// wrapper around imagecolorallocate to try and re-use palette slots where possible
226function myimagecolorallocate($image, $red, $green, $blue)
227{
228 // it's possible that we're being called early - just return straight away, in that case
229 if(!isset($image)) return(-1);
230 
231 $existing=imagecolorexact($image, $red, $green, $blue);
232 
233 if ($existing > -1)
234 return $existing;
235 
236 return (imagecolorallocate($image, $red, $green, $blue));
237}
238 
239function screenshotify($input)
240{
241 $tmp = $input;
242 
243 $tmp = preg_replace("/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/","127.0.0.1",$tmp);
244 $tmp = preg_replace("/([A-Za-z]{3,})/e","str_repeat('x',strlen('\\1'))",$tmp);
245 
246 return($tmp);
247}
248 
249function render_colour($col)
250{
251 if (($col[0] == -1) && ($col[1] == -1) && ($col[1] == -1)) { return 'none'; }
252 else if (($col[0] == -2) && ($col[1] == -2) && ($col[1] == -2)) { return 'copy'; }
253 else if (($col[0] == -3) && ($col[1] == -3) && ($col[1] == -3)) { return 'contrast'; }
254 else { return sprintf("%d %d %d", $col[0], $col[1], $col[2]); }
255}
256 
257// take the same set of points that imagepolygon does, but don't close the shape
258function imagepolyline($image, $points, $npoints, $color)
259{
260 for ($i=0; $i < ($npoints - 1); $i++)
261 {
262 imageline($image, $points[$i * 2], $points[$i * 2 + 1], $points[$i * 2 + 2], $points[$i * 2 + 3],
263 $color);
264 }
265}
266 
267// draw a filled round-cornered rectangle
268function imagefilledroundedrectangle($image , $x1 , $y1 , $x2 , $y2 , $radius, $color)
269{
270 imagefilledrectangle($image, $x1,$y1+$radius, $x2,$y2-$radius, $color);
271 imagefilledrectangle($image, $x1+$radius,$y1, $x2-$radius,$y2, $color);
272 
273 imagefilledarc($image, $x1+$radius, $y1+$radius, $radius*2, $radius*2, 0, 360, $color, IMG_ARC_PIE);
274 imagefilledarc($image, $x2-$radius, $y1+$radius, $radius*2, $radius*2, 0, 360, $color, IMG_ARC_PIE);
275 
276 imagefilledarc($image, $x1+$radius, $y2-$radius, $radius*2, $radius*2, 0, 360, $color, IMG_ARC_PIE);
277 imagefilledarc($image, $x2-$radius, $y2-$radius, $radius*2, $radius*2, 0, 360, $color, IMG_ARC_PIE);
278 
279 # bool imagefilledarc ( resource $image , int $cx , int $cy , int $width , int $height , int $start , int $end , int $color , int $style )
280}
281 
282// draw a round-cornered rectangle
283function imageroundedrectangle( $image , $x1 , $y1 , $x2 , $y2 , $radius, $color )
284{
285 
286 imageline($image, $x1+$radius, $y1, $x2-$radius, $y1, $color);
287 imageline($image, $x1+$radius, $y2, $x2-$radius, $y2, $color);
288 imageline($image, $x1, $y1+$radius, $x1, $y2-$radius, $color);
289 imageline($image, $x2, $y1+$radius, $x2, $y2-$radius, $color);
290 
291 imagearc($image, $x1+$radius, $y1+$radius, $radius*2, $radius*2, 180, 270, $color);
292 imagearc($image, $x2-$radius, $y1+$radius, $radius*2, $radius*2, 270, 360, $color);
293 imagearc($image, $x1+$radius, $y2-$radius, $radius*2, $radius*2, 90, 180, $color);
294 imagearc($image, $x2-$radius, $y2-$radius, $radius*2, $radius*2, 0, 90, $color);
295}
296 
297function imagecreatefromfile($filename)
298{
299 $bgimage=NULL;
300 $formats = imagetypes();
301 if (is_readable($filename))
302 {
303 list($width, $height, $type, $attr) = getimagesize($filename);
304 switch($type)
305 {
306 case IMAGETYPE_GIF:
307 if(imagetypes() & IMG_GIF)
308 {
309 $bgimage=imagecreatefromgif($filename);
310 }
311 else
312 {
313 warn("Image file $filename is GIF, but GIF is not supported by your GD library. [WMIMG01]\n");
314 }
315 break;
316 
317 case IMAGETYPE_JPEG:
318 if(imagetypes() & IMG_JPEG)
319 {
320 $bgimage=imagecreatefromjpeg($filename);
321 }
322 else
323 {
324 warn("Image file $filename is JPEG, but JPEG is not supported by your GD library. [WMIMG02]\n");
325 }
326 break;
327 
328 case IMAGETYPE_PNG:
329 if(imagetypes() & IMG_PNG)
330 {
331 $bgimage=imagecreatefrompng($filename);
332 }
333 else
334 {
335 warn("Image file $filename is PNG, but PNG is not supported by your GD library. [WMIMG03]\n");
336 }
337 break;
338 
339 default:
340 warn("Image file $filename wasn't recognised (type=$type). Check format is supported by your GD library. [WMIMG04]\n");
341 break;
342 }
343 }
344 else
345 {
346 warn("Image file $filename is unreadable. Check permissions. [WMIMG05]\n");
347 }
348 return $bgimage;
349}
350 
351// taken from here:
352// http://www.php.net/manual/en/function.imagefilter.php#62395
353// ( with some bugfixes and changes)
354//
355// Much nicer colorization than imagefilter does, AND no special requirements.
356// Preserves white, black and transparency.
357//
358function imagecolorize($im, $r, $g, $b)
359{
360 //We will create a monochromatic palette based on
361 //the input color
362 //which will go from black to white
363 //Input color luminosity: this is equivalent to the
364 //position of the input color in the monochromatic
365 //palette
366 $lum_inp = round(255 * ($r + $g + $b) / 765); //765=255*3
367 
368 //We fill the palette entry with the input color at its
369 //corresponding position
370 
371 $pal[$lum_inp]['r'] = $r;
372 $pal[$lum_inp]['g'] = $g;
373 $pal[$lum_inp]['b'] = $b;
374 
375 //Now we complete the palette, first we'll do it to
376 //the black,and then to the white.
377 
378 //FROM input to black
379 //===================
380 //how many colors between black and input
381 $steps_to_black = $lum_inp;
382 
383 //The step size for each component
384 if ($steps_to_black)
385 {
386 $step_size_red = $r / $steps_to_black;
387 $step_size_green = $g / $steps_to_black;
388 $step_size_blue = $b / $steps_to_black;
389 }
390 
391 for ($i = $steps_to_black; $i >= 0; $i--)
392 {
393 $pal[$steps_to_black - $i]['r'] = $r - round($step_size_red * $i);
394 $pal[$steps_to_black - $i]['g'] = $g - round($step_size_green * $i);
395 $pal[$steps_to_black - $i]['b'] = $b - round($step_size_blue * $i);
396 }
397 
398 //From input to white:
399 //===================
400 //how many colors between input and white
401 $steps_to_white = 255 - $lum_inp;
402 
403 if ($steps_to_white)
404 {
405 $step_size_red = (255 - $r) / $steps_to_white;
406 $step_size_green = (255 - $g) / $steps_to_white;
407 $step_size_blue = (255 - $b) / $steps_to_white;
408 }
409 else
410 $step_size_red = $step_size_green = $step_size_blue = 0;
411 
412 //The step size for each component
413 for ($i = ($lum_inp + 1); $i <= 255; $i++)
414 {
415 $pal[$i]['r'] = $r + round($step_size_red * ($i - $lum_inp));
416 $pal[$i]['g'] = $g + round($step_size_green * ($i - $lum_inp));
417 $pal[$i]['b'] = $b + round($step_size_blue * ($i - $lum_inp));
418 }
419 
420 //--- End of palette creation
421 
422 //Now,let's change the original palette into the one we
423 //created
424 for ($c = 0; $c < imagecolorstotal($im); $c++)
425 {
426 $col = imagecolorsforindex($im, $c);
427 $lum_src = round(255 * ($col['red'] + $col['green'] + $col['blue']) / 765);
428 $col_out = $pal[$lum_src];
429 
430 # printf("%d (%d,%d,%d) -> %d -> (%d,%d,%d)\n", $c,
431 # $col['red'], $col['green'], $col['blue'],
432 # $lum_src,
433 # $col_out['r'], $col_out['g'], $col_out['b']
434 # );
435 
436 imagecolorset($im, $c, $col_out['r'], $col_out['g'], $col_out['b']);
437 }
438 
439 return($im);
440}
441 
442// find the point where a line from x1,y1 through x2,y2 crosses another line through x3,y3 and x4,y4
443// (the point might not be between those points, but beyond them)
444// - doesn't handle parallel lines. In our case we will never get them.
445// - make sure we remove colinear points, or this will not be true!
446function line_crossing($x1,$y1,$x2,$y2, $x3,$y3,$x4,$y4)
447{
448 
449 // First, check that the slope isn't infinite.
450 // if it is, tweak it to be merely huge
451 if($x1 != $x2) { $slope1 = ($y2-$y1)/($x2-$x1); }
452 else { $slope1 = 1e10; debug("Slope1 is infinite.\n");}
453 
454 if($x3 != $x4) { $slope2 = ($y4-$y3)/($x4-$x3); }
455 else { $slope2 = 1e10; debug("Slope2 is infinite.\n");}
456 
457 $a1 = $slope1;
458 $a2 = $slope2;
459 $b1 = -1;
460 $b2 = -1;
461 $c1 = ($y1 - $slope1 * $x1 );
462 $c2 = ($y3 - $slope2 * $x3 );
463 
464 $det_inv = 1/($a1*$b2 - $a2*$b1);
465 
466 $xi = (($b1*$c2 - $b2*$c1)*$det_inv);
467 $yi = (($a2*$c1 - $a1*$c2)*$det_inv);
468 
469 return(array($xi,$yi));
470}
471 
472// rotate a list of points around cx,cy by an angle in radians, IN PLACE
473function RotateAboutPoint(&$points, $cx,$cy, $angle=0)
474{
475 $npoints = count($points)/2;
476 
477 for($i=0;$i<$npoints;$i++)
478 {
479 $ox = $points[$i*2] - $cx;
480 $oy = $points[$i*2+1] - $cy;
481 $rx = $ox * cos($angle) - $oy*sin($angle);
482 $ry = $oy * cos($angle) + $ox*sin($angle);
483 
484 $points[$i*2] = $rx + $cx;
485 $points[$i*2+1] = $ry + $cy;
486 }
487}
488 
489// calculate the points for a span of the curve. We pass in the distance so far, and the array index, so that
490// the chunk of array generated by this function can be array_merged with existing points from before.
491// Considering how many array functions there are, PHP has horrible list support
492// Each point is a 3-tuple - x,y,distance - which is used later to figure out where the 25%, 50% marks are on the curve
493function calculate_catmull_rom_span($startn, $startdistance, $numsteps, $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)
494{
495 $Ap_x=-$x0 + 3 * $x1 - 3 * $x2 + $x3;
496 $Bp_x=2 * $x0 - 5 * $x1 + 4 * $x2 - $x3;
497 $Cp_x=-$x0 + $x2;
498 $Dp_x=2 * $x1;
499 
500 $Ap_y=-$y0 + 3 * $y1 - 3 * $y2 + $y3;
501 $Bp_y=2 * $y0 - 5 * $y1 + 4 * $y2 - $y3;
502 $Cp_y=-$y0 + $y2;
503 $Dp_y=2 * $y1;
504 
505 $d=2;
506 $n=$startn;
507 $distance=$startdistance;
508 
509 $lx=$x0;
510 $ly=$y0;
511 
512 $allpoints[]=array
513 (
514 $x0,
515 $y0,
516 $distance
517 );
518 
519 for ($i=0; $i <= $numsteps; $i++)
520 {
521 $t=$i / $numsteps;
522 $t2=$t * $t;
523 $t3=$t2 * $t;
524 $x=(($Ap_x * $t3) + ($Bp_x * $t2) + ($Cp_x * $t) + $Dp_x) / $d;
525 $y=(($Ap_y * $t3) + ($Bp_y * $t2) + ($Cp_y * $t) + $Dp_y) / $d;
526 
527 if ($i > 0)
528 {
529 $step=sqrt((($x - $lx) * ($x - $lx)) + (($y - $ly) * ($y - $ly)));
530 $distance=$distance + $step;
531 $allpoints[$n]=array
532 (
533 $x,
534 $y,
535 $distance
536 );
537 
538 $n++;
539 }
540 
541 $lx=$x;
542 $ly=$y;
543 }
544 
545 return array($allpoints, $distance, $n);
546}
547 
548function find_distance_coords(&$pointarray,$distance)
549{
550 // We find the nearest lower point for each distance,
551 // then linearly interpolate to get a more accurate point
552 // this saves having quite so many points-per-curve
553 
554 $index=find_distance($pointarray, $distance);
555 
556 $ratio=($distance - $pointarray[$index][2]) / ($pointarray[$index + 1][2] - $pointarray[$index][2]);
557 $x = $pointarray[$index][0] + $ratio * ($pointarray[$index + 1][0] - $pointarray[$index][0]);
558 $y = $pointarray[$index][1] + $ratio * ($pointarray[$index + 1][1] - $pointarray[$index][1]);
559 
560 return(array($x,$y,$index));
561}
562 
563function find_distance_coords_angle(&$pointarray,$distance)
564{
565 // This is the point we need
566 list($x,$y,$index) = find_distance_coords($pointarray,$distance);
567 
568 // now to find one either side of it, to get a line to find the angle of
569 $left = $index;
570 $right = $left+1;
571 $max = count($pointarray)-1;
572 // if we're right up against the last point, then step backwards one
573 if($right>=$max)
574 {
575 $left--;
576 $right--;
577 }
578 # if($left<=0) { $left = 0; }
579 
580 $x1 = $pointarray[$left][0];
581 $y1 = $pointarray[$left][1];
582 
583 $x2 = $pointarray[$right][0];
584 $y2 = $pointarray[$right][1];
585 
586 $dx = $x2 - $x1;
587 $dy = $y2 - $y1;
588 
589 $angle = rad2deg(atan2(-$dy,$dx));
590 
591 return(array($x,$y,$index,$angle));
592}
593 
594// return the index of the point either at (unlikely) or just before the target distance
595// we will linearly interpolate afterwards to get a true point - pointarray is an array of 3-tuples produced by the function above
596function find_distance(&$pointarray, $distance)
597{
598 $left=0;
599 $right=count($pointarray) - 1;
600 
601 if ($left == $right)
602 return ($left);
603 
604 // if the distance is zero, there's no need to search (and it doesn't work anyway)
605 if($distance==0) return($left);
606 
607 // if it's a point past the end of the line, then just return the end of the line
608 // Weathermap should *never* ask for this, anyway
609 if ($pointarray[$right][2] < $distance) { return ($right); }
610 
611 // if somehow we have a 0-length curve, then don't try and search, just give up
612 // in a somewhat predictable manner
613 if ($pointarray[$left][2] == $pointarray[$right][2]) { return ($left); }
614 
615 while ($left <= $right)
616 {
617 $mid=floor(($left + $right) / 2);
618 
619 if (($pointarray[$mid][2] < $distance) && ($pointarray[$mid + 1][2] >= $distance)) { return $mid; }
620 
621 if ($distance <= $pointarray[$mid][2]) { $right=$mid - 1; }
622 else { $left=$mid + 1; }
623 }
624 
625 print "FELL THROUGH\n";
626 die ("Howie's crappy binary search is wrong after all.\n");
627}
628 
629// Give a list of key points, calculate a curve through them
630// return value is an array of triples (x,y,distance)
631function calc_curve(&$in_xarray, &$in_yarray,$pointsperspan = 32)
632{
633 // search through the point list, for consecutive duplicate points
634 // (most common case will be a straight link with both NODEs at the same place, I think)
635 // strip those out, because they'll break the binary search/centre-point stuff
636 
637 $last_x=NULL;
638 $last_y=NULL;
639 
640 for ($i=0; $i < count($in_xarray); $i++)
641 {
642 if (($in_xarray[$i] == $last_x) && ($in_yarray[$i] == $last_y)) { debug
643 ("Dumping useless duplicate point on curve\n"); }
644 else
645 {
646 $xarray[]=$in_xarray[$i];
647 $yarray[]=$in_yarray[$i];
648 }
649 
650 $last_x=$in_xarray[$i];
651 $last_y=$in_yarray[$i];
652 }
653 
654 // only proceed if we still have at least two points!
655 if(count($xarray) <= 1)
656 {
657 warn ("Arrow not drawn, as it's 1-dimensional.\n");
658 return (array(NULL, NULL, NULL, NULL));
659 }
660 
661 // duplicate the first and last points, so that all points are drawn
662 // (C-R normally would draw from x[1] to x[n-1]
663 array_unshift($xarray, $xarray[0]);
664 array_unshift($yarray, $yarray[0]);
665 
666 $x=array_pop($xarray);
667 $y=array_pop($yarray);
668 array_push($xarray, $x);
669 array_push($xarray, $x);
670 array_push($yarray, $y);
671 array_push($yarray, $y);
672 
673 $npoints=count($xarray);
674 
675 $curvepoints=array
676 (
677 );
678 
679 // add in the very first point manually (the calc function skips this one to avoid duplicates, which mess up the distance stuff)
680 $curvepoints[]=array
681 (
682 $xarray[0],
683 $yarray[0],
684 
685 );
686 
687 $np=0;
688 $distance=0;
689 
690 for ($i=0; $i < ($npoints - 3); $i++)
691 {
692 list($newpoints,
693 $distance,
694 $np)=calculate_catmull_rom_span($np, $distance, $pointsperspan, $xarray[$i],
695 $yarray[$i], $xarray[$i + 1], $yarray[$i + 1], $xarray[$i + 2],
696 $yarray[$i + 2], $xarray[$i + 3], $yarray[$i + 3]);
697 $curvepoints=$curvepoints + $newpoints;
698 }
699 
700 return ($curvepoints);
701}
702 
703// Give a list of key points, calculate a "curve" through them
704// return value is an array of triples (x,y,distance)
705// this is here to mirror the real 'curve' version when we're using angled VIAs
706// it means that all the stuff that expects an array of points with distances won't be upset.
707function calc_straight(&$in_xarray, &$in_yarray,$pointsperspan = 12)
708{
709 // search through the point list, for consecutive duplicate points
710 // (most common case will be a straight link with both NODEs at the same place, I think)
711 // strip those out, because they'll break the binary search/centre-point stuff
712 $last_x=NULL;
713 $last_y=NULL;
714 
715 for ($i=0; $i < count($in_xarray); $i++)
716 {
717 if (($in_xarray[$i] == $last_x) && ($in_yarray[$i] == $last_y)) { debug
718 ("Dumping useless duplicate point on curve\n"); }
719 else
720 {
721 $xarray[]=$in_xarray[$i];
722 $yarray[]=$in_yarray[$i];
723 }
724 
725 $last_x=$in_xarray[$i];
726 $last_y=$in_yarray[$i];
727 }
728 
729 // only proceed if we still have at least two points!
730 if(count($xarray) <= 1)
731 {
732 warn ("Arrow not drawn, as it's 1-dimensional.\n");
733 return (array(NULL, NULL, NULL, NULL));
734 }
735 
736 $npoints=count($xarray);
737 
738 $curvepoints=array();
739 
740 $np=0;
741 $distance=0;
742 
743 for ($i=0; $i < ($npoints -1); $i++)
744 {
745 // still subdivide the straight line, becuase other stuff makes assumptions about
746 // how often there is a point - at least find_distance_coords_angle breaks
747 $newdistance = sqrt( pow($xarray[$i+1] - $xarray[$i],2) + pow($yarray[$i+1] - $yarray[$i],2) );
748 $dx = ($xarray[$i+1] - $xarray[$i])/$pointsperspan;
749 $dy = ($yarray[$i+1] - $yarray[$i])/$pointsperspan;
750 $dd = $newdistance/$pointsperspan;
751 
752 for($j=0; $j< $pointsperspan; $j++)
753 {
754 $x = $xarray[$i]+$j*$dx;
755 $y = $yarray[$i]+$j*$dy;
756 $d = $distance + $j*$dd;
757 
758 $curvepoints[] = array($x,$y,$d);
759 $np++;
760 }
761 $distance += $newdistance;
762 }
763 $curvepoints[] = array($xarray[$npoints-1],$yarray[$npoints-1],$distance);
764 
765# print_r($curvepoints);
766 
767 return ($curvepoints);
768}
769 
770function calc_arrowsize($width,&$map,$linkname)
771{
772 $arrowlengthfactor=4;
773 $arrowwidthfactor=2;
774 
775 // this is so I can use it in some test code - sorry!
776 if($map !== NULL)
777 {
778 if ($map->links[$linkname]->arrowstyle == 'compact')
779 {
780 $arrowlengthfactor=1;
781 $arrowwidthfactor=1;
782 }
783 
784 if (preg_match('/(\d+) (\d+)/', $map->links[$linkname]->arrowstyle, $matches))
785 {
786 $arrowlengthfactor=$matches[1];
787 $arrowwidthfactor=$matches[2];
788 }
789 }
790 
791 $arrowsize = $width * $arrowlengthfactor;
792 $arrowwidth = $width * $arrowwidthfactor;
793 
794 return( array($arrowsize,$arrowwidth) );
795}
796 
797function draw_straight($image, &$curvepoints, $widths, $outlinecolour, $fillcolours, $linkname, &$map,
798 $q2_percent=50, $unidirectional=FALSE)
799{
800 $totaldistance = $curvepoints[count($curvepoints)-1][DISTANCE];
801 
802 if($unidirectional)
803 {
804 $halfway = $totaldistance;
805 $dirs = array(OUT);
806 $q2_percent = 100;
807 $halfway = $totaldistance * ($q2_percent/100);
808 list($halfway_x,$halfway_y,$halfwayindex) = find_distance_coords($curvepoints,$halfway);
809 
810 $spine[OUT] = $curvepoints;
811 }
812 else
813 {
814 // we'll split the spine in half here.
815 # $q2_percent = 50;
816 $halfway = $totaldistance * ($q2_percent/100);
817 
818 $dirs = array(OUT,IN);
819 # $dirs = array(IN);
820 
821 list($halfway_x,$halfway_y,$halfwayindex) = find_distance_coords($curvepoints,$halfway);
822 # print "Midpoint is: $totaldistance $halfway $halfwayindex $halfway_x,$halfway_y\n";
823 
824 $spine[OUT] = array();
825 $spine[IN] = array();
826 $npoints = count($curvepoints)-1;
827 
828 for($i=0; $i<=$halfwayindex; $i++)
829 {
830 $spine[OUT] []= $curvepoints[$i];
831 }
832 // finally, add the actual midpoint
833 $spine[OUT] []= array($halfway_x,$halfway_y, $halfway);
834 
835 // and then from the end to the middle for the other arrow
836 for($i=$npoints; $i>$halfwayindex; $i--)
837 {
838 // copy the original spine, but reversing the distance calculation
839 $spine[IN] []= array($curvepoints[$i][X], $curvepoints[$i][Y], $totaldistance - $curvepoints[$i][DISTANCE]);
840 }
841 // finally, add the actual midpoint
842 $spine[IN] []= array($halfway_x,$halfway_y, $totaldistance - $halfway);
843 }
844 
845 # wm_draw_marker_box($image,$map->selected, $halfway_x, $halfway_y );
846 
847 // now we have two seperate spines, with distances, so that the arrowhead is the end of each.
848 // (or one, if it's unidir)
849 
850 // so we can loop along the spine for each one as a seperate entity
851 
852 // we calculate the arrow size up here, so that we can decide on the
853 // minimum length for a link. The arrowheads are the limiting factor.
854 list( $arrowsize[IN], $arrowwidth[IN] ) = calc_arrowsize( $widths[IN], $map, $linkname );
855 list( $arrowsize[OUT], $arrowwidth[OUT] ) = calc_arrowsize( $widths[OUT], $map, $linkname );
856 
857 // the 1.2 here is empirical. It ought to be 1 in theory.
858 // in practice, a link this short is useless anyway, especially with bwlabels.
859 $minimumlength = 1.2*($arrowsize[IN]+$arrowsize[OUT]);
860 
861 foreach ($dirs as $dir)
862 {
863 # draw_spine($image, $spine[$dir],$map->selected);
864 #draw_spine_chain($image, $spine[$dir],$map->selected,3);
865 #print "=================\n$linkname/$dir\n";
866 #dump_spine($spine[$dir]);
867 $n = count($spine[$dir]) - 1;
868 $l = $spine[$dir][$n][DISTANCE];
869 
870 #print "L=$l N=$n\n";
871 
872 // loop increment, start point, width, labelpos, fillcolour, outlinecolour, commentpos
873 $arrowsettings = array(+1, 0, $widths[$dir], 0, $fillcolours[$dir], $outlinecolour, 5);
874 
875 # print "Line is $n points to a distance of $l\n";
876 if($l < $minimumlength)
877 {
878 warn("Skipping too-short line.\n");
879 }
880 else
881 {
882 $arrow_d = $l - $arrowsize[$dir];
883 # print "LENGTHS $l $arrow_d ".$arrowsize[$dir]."\n";
884 list($pre_mid_x,$pre_mid_y,$pre_midindex) = find_distance_coords($spine[$dir], $arrow_d);
885 # print "POS $pre_mid_x,$pre_mid_y $pre_midindex\n";
886 $out = array_slice($spine[$dir], 0, $pre_midindex);
887 $out []= array($pre_mid_x, $pre_mid_y, $arrow_d);
888 
889 # wm_draw_marker_diamond($image, $map->selected, $pre_mid_x, $pre_mid_y, 5);
890 # imagearc($image,$pre_mid_x, $pre_mid_y ,15,15,0,360,$map->selected);
891 
892 # imagearc($image,$spine[$dir][$pre_midindex+1][X],$spine[$dir][$pre_midindex+1][Y],20,20,0,360,$map->selected);
893 # imagearc($image,$spine[$dir][$pre_midindex][X],$spine[$dir][$pre_midindex][Y],20,20,0,360,$map->selected);
894 #imagearc($image,$pre_mid_x,$pre_mid_y,20,20,0,360,$map->selected);
895 #imagearc($image,$spine[$dir][$pre_midindex][X],$spine[$dir][$pre_midindex][Y],12,12,0,360,$map->selected);
896 
897 $spine[$dir] = $out;
898 
899 $adx=($halfway_x - $pre_mid_x);
900 $ady=($halfway_y - $pre_mid_y);
901 $ll=sqrt(($adx * $adx) + ($ady * $ady));
902 
903 $anx = $ady / $ll;
904 $any = -$adx / $ll;
905 
906 $ax1 = $pre_mid_x + $widths[$dir] * $anx;
907 $ay1 = $pre_mid_y + $widths[$dir] * $any;
908 
909 $ax2 = $pre_mid_x + $arrowwidth[$dir] * $anx;
910 $ay2 = $pre_mid_y + $arrowwidth[$dir] * $any;
911 
912 $ax3 = $halfway_x;
913 $ay3 = $halfway_y;
914 
915 $ax5 = $pre_mid_x - $widths[$dir] * $anx;
916 $ay5 = $pre_mid_y - $widths[$dir] * $any;
917 
918 $ax4 = $pre_mid_x - $arrowwidth[$dir] * $anx;
919 $ay4 = $pre_mid_y - $arrowwidth[$dir] * $any;
920 
921 # draw_spine($image,$spine[$dir],$map->selected);
922 
923 $simple = simplify_spine($spine[$dir]);
924 $newn = count($simple);
925 
926 # draw_spine($image,$simple,$map->selected);
927 
928 # print "Simplified to $newn points\n";
929 # if($draw_skeleton) draw_spine_chain($im,$simple,$blue, 12);
930 # draw_spine_chain($image,$simple,$map->selected, 12);
931 # draw_spine_chain($image,$spine[$dir],$map->selected, 10);
932 
933 # draw_spine_chain($image,$simple,$map->selected, 12);
934 # draw_spine($image,$simple,$map->selected);
935 
936 // now do the actual drawing....
937 
938 $numpoints=0;
939 $numrpoints=0;
940 
941 $finalpoints = array();
942 $reversepoints = array();
943 
944 $finalpoints[] = $simple[0][X];
945 $finalpoints[] = $simple[0][Y];
946 $numpoints++;
947 
948 $reversepoints[] = $simple[0][X];
949 $reversepoints[] = $simple[0][Y];
950 $numrpoints++;
951 
952 // before the main loop, add in the jump out to the corners
953 // if this is the first step, then we need to go from the middle to the outside edge first
954 // ( the loop may not run, but these corners are required)
955 $i = 0;
956 $v1 = new Vector($simple[$i+1][X] - $simple[$i][X], $simple[$i+1][Y] - $simple[$i][Y]);
957 $n1 = $v1->get_normal();
958 
959 $finalpoints[] = $simple[$i][X] + $n1->dx*$widths[$dir];
960 $finalpoints[] = $simple[$i][Y] + $n1->dy*$widths[$dir];
961 $numpoints++;
962 
963 $reversepoints[] = $simple[$i][X] - $n1->dx*$widths[$dir];
964 $reversepoints[] = $simple[$i][Y] - $n1->dy*$widths[$dir];
965 $numrpoints++;
966 
967 $max_start = count($simple)-2;
968 # print "max_start is $max_start\n";
969 for ($i=0; $i <$max_start; $i++)
970 {
971 $v1 = new Vector($simple[$i+1][X] - $simple[$i][X], $simple[$i+1][Y] - $simple[$i][Y]);
972 $v2 = new Vector($simple[$i+2][X] - $simple[$i+1][X], $simple[$i+2][Y] - $simple[$i+1][Y]);
973 $n1 = $v1->get_normal();
974 $n2 = $v2->get_normal();
975 
976 $capping = FALSE;
977 // figure out the angle between the lines - for very sharp turns, we should do something special
978 // (actually, their normals, but the angle is the same and we need the normals later)
979 $angle = rad2deg(atan2($n2->dy,$n2->dx) - atan2($n1->dy,$n1->dx));
980 if($angle > 180) $angle -= 360;
981 if($angle < -180) $angle += 360;
982 
983 if(abs($angle)>169)
984 {
985 $capping = TRUE;
986 # print "Would cap. ($angle)\n";
987 }
988 
989 // $capping = FALSE; // override that for now
990 // now figure out the geometry for where the next corners are
991 
992 list($xi1,$yi1) = line_crossing( $simple[$i][X] + $n1->dx * $widths[$dir], $simple[$i][Y] + $n1->dy * $widths[$dir],
993 $simple[$i+1][X] + $n1->dx * $widths[$dir], $simple[$i+1][Y] + $n1->dy * $widths[$dir],
994 $simple[$i+1][X] + $n2->dx * $widths[$dir], $simple[$i+1][Y] + $n2->dy * $widths[$dir],
995 $simple[$i+2][X] + $n2->dx * $widths[$dir], $simple[$i+2][Y] + $n2->dy * $widths[$dir]
996 );
997 
998 list($xi2,$yi2) = line_crossing( $simple[$i][X] - $n1->dx * $widths[$dir], $simple[$i][Y] - $n1->dy * $widths[$dir],
999 $simple[$i+1][X] - $n1->dx * $widths[$dir], $simple[$i+1][Y] - $n1->dy * $widths[$dir],
1000 $simple[$i+1][X] - $n2->dx * $widths[$dir], $simple[$i+1][Y] - $n2->dy * $widths[$dir],
1001 $simple[$i+2][X] - $n2->dx * $widths[$dir], $simple[$i+2][Y] - $n2->dy * $widths[$dir]
1002 );
1003 
1004 if(!$capping)
1005 {
1006 $finalpoints[] = $xi1;
1007 $finalpoints[] = $yi1;
1008 $numpoints++;
1009 
1010 $reversepoints[] = $xi2;
1011 $reversepoints[] = $yi2;
1012 $numrpoints++;
1013 }
1014 else
1015 {
1016 // in here, we need to decide which is the 'outside' of the corner,
1017 // because that's what we flatten. The inside of the corner is left alone.
1018 // - depending on the relative angle between the two segments, it could
1019 // be either one of these points.
1020 
1021 list($xi3,$yi3) = line_crossing( $simple[$i][X] + $n1->dx*$widths[$dir], $simple[$i][Y] + $n1->dy*$widths[$dir],
1022 $simple[$i+1][X] + $n1->dx*$widths[$dir], $simple[$i+1][Y] + $n1->dy*$widths[$dir],
1023 $simple[$i+1][X] - $n2->dx*$widths[$dir], $simple[$i+1][Y] - $n2->dy*$widths[$dir],
1024 $simple[$i+2][X] - $n2->dx*$widths[$dir], $simple[$i+2][Y] - $n2->dy*$widths[$dir]
1025 );
1026 
1027 list($xi4,$yi4) = line_crossing( $simple[$i][X] - $n1->dx*$widths[$dir], $simple[$i][Y] - $n1->dy*$widths[$dir],
1028 $simple[$i+1][X] - $n1->dx*$widths[$dir], $simple[$i+1][Y] - $n1->dy*$widths[$dir],
1029 $simple[$i+1][X] + $n2->dx*$widths[$dir], $simple[$i+1][Y] + $n2->dy*$widths[$dir],
1030 $simple[$i+2][X] + $n2->dx*$widths[$dir], $simple[$i+2][Y] + $n2->dy*$widths[$dir]
1031 );
1032 if($angle < 0)
1033 {
1034 $finalpoints[] = $xi3;
1035 $finalpoints[] = $yi3;
1036 $numpoints++;
1037 
1038 $finalpoints[] = $xi4;
1039 $finalpoints[] = $yi4;
1040 $numpoints++;
1041 
1042 $reversepoints[] = $xi2;
1043 $reversepoints[] = $yi2;
1044 $numrpoints++;
1045 }
1046 else
1047 {
1048 $reversepoints[] = $xi4;
1049 $reversepoints[] = $yi4;
1050 $numrpoints++;
1051 
1052 $reversepoints[] = $xi3;
1053 $reversepoints[] = $yi3;
1054 $numrpoints++;
1055 
1056 $finalpoints[] = $xi1;
1057 $finalpoints[] = $yi1;
1058 $numpoints++;
1059 }
1060 
1061 }
1062 }
1063 
1064 // at this end, we add the arrowhead
1065 
1066 $finalpoints[] = $ax1;
1067 $finalpoints[] = $ay1;
1068 $finalpoints[] = $ax2;
1069 $finalpoints[] = $ay2;
1070 $finalpoints[] = $ax3;
1071 $finalpoints[] = $ay3;
1072 $finalpoints[] = $ax4;
1073 $finalpoints[] = $ay4;
1074 $finalpoints[] = $ax5;
1075 $finalpoints[] = $ay5;
1076 
1077 $numpoints += 5;
1078 
1079 // combine the forwards and backwards paths, to make a complete loop
1080 for($i=($numrpoints-1)*2; $i>=0; $i-=2)
1081 {
1082 $x = $reversepoints[$i];
1083 $y = $reversepoints[$i+1];
1084 
1085 $finalpoints[] = $x;
1086 $finalpoints[] = $y;
1087 $numpoints++;
1088 }
1089 // $finalpoints[] contains a complete outline of the line at this stage
1090 
1091 if (!is_null($fillcolours[$dir]))
1092 {
1093 wimagefilledpolygon($image, $finalpoints, count($finalpoints) / 2, $arrowsettings[4]);
1094 }
1095 else
1096 {
1097 debug("Not drawing $linkname ($dir) fill because there is no fill colour\n");
1098 }
1099 
1100 $areaname = "LINK:L" . $map->links[$linkname]->id . ":$dir";
1101 $map->imap->addArea("Polygon", $areaname, '', $finalpoints);
1102 debug ("Adding Poly imagemap for $areaname\n");
1103 
1104 if (!is_null($outlinecolour))
1105 {
1106 wimagepolygon($image, $finalpoints, count($finalpoints) / 2, $arrowsettings[5]);
1107 }
1108 else
1109 {
1110 debug("Not drawing $linkname ($dir) outline because there is no outline colour\n");
1111 }
1112 }
1113 }
1114}
1115 
1116// top-level function that takes a two lists to define some points, and draws a weathermap link
1117// - this takes care of all the extras, like arrowheads, and where to put the bandwidth labels
1118// curvepoints is an array of the points the curve passes through
1119// width is the link width (the actual width is twice this)
1120// outlinecolour is a GD colour reference
1121// fillcolours is an array of two more colour references, one for the out, and one for the in spans
1122function draw_curve($image, &$curvepoints, $widths, $outlinecolour, $fillcolours, $linkname, &$map,
1123 $q2_percent=50, $unidirectional=FALSE)
1124{
1125 // now we have a 'spine' - all the central points for this curve.
1126 // time to flesh it out to the right width, and figure out where to draw arrows and bandwidth boxes...
1127 
1128 // get the full length of the curve from the last point
1129 $totaldistance = $curvepoints[count($curvepoints)-1][2];
1130 // find where the in and out arrows will join (normally halfway point)
1131 $halfway = $totaldistance * ($q2_percent/100);
1132 
1133 $dirs = array(OUT,IN);
1134 
1135 // for a unidirectional map, we just ignore the second half (direction = -1)
1136 if($unidirectional)
1137 {
1138 $halfway = $totaldistance;
1139 $dirs = array(OUT);
1140 }
1141 
1142 // loop increment, start point, width, labelpos, fillcolour, outlinecolour, commentpos
1143 $arrowsettings[OUT] = array(+1, 0, $widths[OUT], 0, $fillcolours[OUT], $outlinecolour, 5);
1144 $arrowsettings[IN] = array(-1, count($curvepoints) - 1, $widths[IN], 0, $fillcolours[IN], $outlinecolour, 95);
1145 
1146 // we calculate the arrow size up here, so that we can decide on the
1147 // minimum length for a link. The arrowheads are the limiting factor.
1148 list($arrowsize[IN],$arrowwidth[IN]) = calc_arrowsize($widths[IN], $map, $linkname);
1149 list($arrowsize[OUT],$arrowwidth[OUT]) = calc_arrowsize($widths[OUT], $map, $linkname);
1150 
1151 // the 1.2 here is empirical. It ought to be 1 in theory.
1152 // in practice, a link this short is useless anyway, especially with bwlabels.
1153 $minimumlength = 1.2*($arrowsize[IN]+$arrowsize[OUT]);
1154 
1155 # warn("$linkname: Total: $totaldistance $arrowsize $arrowwidth $minimumlength\n");
1156 if($totaldistance <= $minimumlength)
1157 {
1158 warn("Skipping drawing very short link ($linkname). Impossible to draw! Try changing WIDTH or ARROWSTYLE? [WMWARN01]\n");
1159 return;
1160 }
1161 
1162 
1163 list($halfway_x,$halfway_y,$halfwayindex) = find_distance_coords($curvepoints,$halfway);
1164 
1165 // loop over direction here
1166 // direction is 1.0 for the first half (forwards through the pointlist), and -1.0 for the second half (backwards from the end)
1167 // - used as a multiplier on anything that looks forwards or backwards through the list
1168 
1169 foreach ($dirs as $dir)
1170 {
1171 $direction = $arrowsettings[$dir][0];
1172 // $width = $widths[$dir];
1173 // this is the last index before the arrowhead starts
1174 list($pre_mid_x,$pre_mid_y,$pre_midindex) = find_distance_coords($curvepoints,$halfway - $direction * $arrowsize[$dir]);
1175 
1176 $there_points=array();
1177 $back_points=array();
1178 $arrowpoints=array();
1179 
1180 # if ($direction < 0) { $start=count($curvepoints) - 1; }
1181 # else { $start=0; }
1182 $start = $arrowsettings[$dir][1];
1183 
1184 for ($i=$start; $i != $pre_midindex; $i+=$direction)
1185 {
1186 // for each point on the spine, produce two points normal to it's direction,
1187 // each is $width away from the spine, but we build up the two lists in the opposite order,
1188 // so that when they are joined together, we get one continuous line
1189 
1190 $dx=$curvepoints[$i + $direction][0] - $curvepoints[$i][0];
1191 $dy=$curvepoints[$i + $direction][1] - $curvepoints[$i][1];
1192 $l=sqrt(($dx * $dx) + ($dy * $dy));
1193 $nx=$dy / $l;
1194 $ny=-$dx / $l;
1195 
1196 $there_points[]=$curvepoints[$i][0] + $direction * $widths[$dir] * $nx;
1197 $there_points[]=$curvepoints[$i][1] + $direction * $widths[$dir] * $ny;
1198 
1199 $back_points[]=$curvepoints[$i][0] - $direction * $widths[$dir] * $nx;
1200 $back_points[]=$curvepoints[$i][1] - $direction * $widths[$dir] * $ny;
1201 }
1202 
1203 // all the normal line is done, now lets add an arrowhead on
1204 
1205 $adx=($halfway_x - $pre_mid_x);
1206 $ady=($halfway_y - $pre_mid_y);
1207 $l=sqrt(($adx * $adx) + ($ady * $ady));
1208 
1209 $anx=$ady / $l;
1210 $any=-$adx / $l;
1211 
1212 $there_points[]=$pre_mid_x + $direction * $widths[$dir] * $anx;
1213 $there_points[]=$pre_mid_y + $direction * $widths[$dir] * $any;
1214 
1215 $there_points[]=$pre_mid_x + $direction * $arrowwidth[$dir] * $anx;
1216 $there_points[]=$pre_mid_y + $direction * $arrowwidth[$dir] * $any;
1217 
1218 $there_points[]=$halfway_x;
1219 $there_points[]=$halfway_y;
1220 
1221 $there_points[]=$pre_mid_x - $direction * $arrowwidth[$dir] * $anx;
1222 $there_points[]=$pre_mid_y - $direction * $arrowwidth[$dir] * $any;
1223 
1224 $there_points[]=$pre_mid_x - $direction * $widths[$dir] * $anx;
1225 $there_points[]=$pre_mid_y - $direction * $widths[$dir] * $any;
1226 
1227 // all points done, now combine the lists, and produce the final result.
1228 $metapts = "";
1229 $y=array_pop($back_points);
1230 $x=array_pop($back_points);
1231 do
1232 {
1233 $metapts .= " $x $y";
1234 $there_points[]=$x;
1235 $there_points[]=$y;
1236 $y=array_pop($back_points);
1237 $x=array_pop($back_points);
1238 } while (!is_null($y));
1239 
1240 $arrayindex=1;
1241 
1242 if ($direction < 0) $arrayindex=0;
1243 
1244 if (!is_null($fillcolours[$arrayindex]))
1245 {
1246 wimagefilledpolygon($image, $there_points, count($there_points) / 2, $arrowsettings[$dir][4]);
1247 }
1248 else
1249 {
1250 debug("Not drawing $linkname ($dir) fill because there is no fill colour\n");
1251 }
1252 
1253 # $areaname = "LINK:" . $linkname. ":$dir";
1254 $areaname = "LINK:L" . $map->links[$linkname]->id . ":$dir";
1255 $map->imap->addArea("Polygon", $areaname, '', $there_points);
1256 debug ("Adding Poly imagemap for $areaname\n");
1257 
1258 if (!is_null($outlinecolour))
1259 {
1260 wimagepolygon($image, $there_points, count($there_points) / 2, $arrowsettings[$dir][5]);
1261 }
1262 else
1263 {
1264 debug("Not drawing $linkname ($dir) outline because there is no outline colour\n");
1265 }
1266 }
1267}
1268 
1269// Take a spine, and strip out all the points that are co-linear with the points either side of them
1270function simplify_spine(&$input, $epsilon=1e-10)
1271{
1272 $output = array();
1273 
1274 $output []= $input[0];
1275 $n=1;
1276 $c = count($input)-2;
1277 $skip=0;
1278 
1279 for($n=1; $n<=$c; $n++)
1280 {
1281 $x = $input[$n][X];
1282 $y = $input[$n][Y];
1283 
1284 // figure out the area of the triangle formed by this point, and the one before and after
1285 $a = abs($input[$n-1][X] * ( $input[$n][Y] - $input[$n+1][Y] )
1286 + $input[$n][X] * ( $input[$n+1][Y] - $input[$n-1][Y] )
1287 + $input[$n+1][X] * ( $input[$n-1][Y] - $input[$n][Y] ) );
1288 
1289 # print "$n $x,$y $a";
1290 
1291 if ( $a > $epsilon)
1292 // if(1==1)
1293 {
1294 $output []= $input[$n];
1295 # print " KEEP";
1296 }
1297 else
1298 {
1299 // ignore n
1300 $skip++;
1301 # print " SKIP";
1302 
1303 }
1304 # print "\n";
1305 }
1306 
1307 debug("Skipped $skip points of $c\n");
1308 
1309# print "------------------------\n";
1310 
1311 $output []= $input[$c+1];
1312 return $output;
1313}
1314 
1315function unformat_number($instring, $kilo = 1000)
1316{
1317 $matches=0;
1318 $number=0;
1319 
1320 if (preg_match("/([0-9\.]+)(M|G|K|T|m|u)/", $instring, $matches))
1321 {
1322 $number=floatval($matches[1]);
1323 
1324 if ($matches[2] == 'K') { $number=$number * $kilo; }
1325 if ($matches[2] == 'M') { $number=$number * $kilo * $kilo; }
1326 if ($matches[2] == 'G') { $number=$number * $kilo * $kilo * $kilo; }
1327 if ($matches[2] == 'T') { $number=$number * $kilo * $kilo * $kilo * $kilo; }
1328 // new, for absolute datastyle. Think seconds.
1329 if ($matches[2] == 'm') { $number=$number / $kilo; }
1330 if ($matches[2] == 'u') { $number=$number / ($kilo * $kilo); }
1331 }
1332 else { $number=floatval($instring); }
1333 
1334 return ($number);
1335}
1336 
1337// given a compass-point, and a width & height, return a tuple of the x,y offsets
1338function calc_offset($offsetstring, $width, $height)
1339{
1340 if(preg_match("/^([-+]?\d+):([-+]?\d+)$/",$offsetstring,$matches))
1341 {
1342 debug("Numeric Offset found\n");
1343 return(array($matches[1],$matches[2]));
1344 }
1345 elseif(preg_match("/(NE|SE|NW|SW|N|S|E|W|C)(\d+)?$/i",$offsetstring,$matches))
1346 {
1347 $multiply = 1;
1348 if( isset($matches[2] ) )
1349 {
1350 $multiply = intval($matches[2])/100;
1351 debug("Percentage compass offset: multiply by $multiply");
1352 }
1353 
1354 $height = $height * $multiply;
1355 $width = $width * $multiply;
1356 
1357 switch (strtoupper($matches[1]))
1358 {
1359 case 'N':
1360 return (array(0, -$height / 2));
1361 
1362 break;
1363 
1364 case 'S':
1365 return (array(0, $height / 2));
1366 
1367 break;
1368 
1369 case 'E':
1370 return (array(+$width / 2, 0));
1371 
1372 break;
1373 
1374 case 'W':
1375 return (array(-$width / 2, 0));
1376 
1377 break;
1378 
1379 case 'NW':
1380 return (array(-$width / 2, -$height / 2));
1381 
1382 break;
1383 
1384 case 'NE':
1385 return (array($width / 2, -$height / 2));
1386 
1387 break;
1388 
1389 case 'SW':
1390 return (array(-$width / 2, $height / 2));
1391 
1392 break;
1393 
1394 case 'SE':
1395 return (array($width / 2, $height / 2));
1396 
1397 break;
1398 
1399 case 'C':
1400 default:
1401 return (array(0, 0));
1402 
1403 break;
1404 }
1405 }
1406 elseif( preg_match("/(-?\d+)r(\d+)$/i",$offsetstring,$matches) )
1407 {
1408 $angle = intval($matches[1]);
1409 $distance = intval($matches[2]);
1410 
1411 $x = $distance * sin(deg2rad($angle));
1412 $y = - $distance * cos(deg2rad($angle));
1413 
1414 return (array($x,$y));
1415 
1416 }
1417 else
1418 {
1419 warn("Got a position offset that didn't make sense ($offsetstring).");
1420 return (array(0, 0));
1421 }
1422 
1423 
1424}
1425 
1426// These next two are based on perl's Number::Format module
1427// by William R. Ward, chopped down to just what I needed
1428 
1429function format_number($number, $precision = 2, $trailing_zeroes = 0)
1430{
1431 $sign=1;
1432 
1433 if ($number < 0)
1434 {
1435 $number=abs($number);
1436 $sign=-1;
1437 }
1438 
1439 $number=round($number, $precision);
1440 $integer=intval($number);
1441 
1442 if (strlen($integer) < strlen($number)) { $decimal=substr($number, strlen($integer) + 1); }
1443 
1444 if (!isset($decimal)) { $decimal=''; }
1445 
1446 $integer=$sign * $integer;
1447 
1448 if ($decimal == '') { return ($integer); }
1449 else { return ($integer . "." . $decimal); }
1450}
1451 
1452function nice_bandwidth($number, $kilo = 1000,$decimals=1,$below_one=TRUE)
1453{
1454 $suffix='';
1455 
1456 if ($number == 0)
1457 return '0';
1458 
1459 $mega=$kilo * $kilo;
1460 $giga=$mega * $kilo;
1461 $tera=$giga * $kilo;
1462 
1463 $milli = 1/$kilo;
1464 $micro = 1/$mega;
1465 $nano = 1/$giga;
1466 
1467 if ($number >= $tera)
1468 {
1469 $number/=$tera;
1470 $suffix="T";
1471 }
1472 elseif ($number >= $giga)
1473 {
1474 $number/=$giga;
1475 $suffix="G";
1476 }
1477 elseif ($number >= $mega)
1478 {
1479 $number/=$mega;
1480 $suffix="M";
1481 }
1482 elseif ($number >= $kilo)
1483 {
1484 $number/=$kilo;
1485 $suffix="K";
1486 }
1487 elseif ($number >= 1)
1488 {
1489 $number = $number;
1490 $suffix="";
1491 }
1492 elseif (($below_one==TRUE) && ($number >= $milli))
1493 {
1494 $number/=$milli;
1495 $suffix="m";
1496 }
1497 elseif (($below_one==TRUE) && ($number >= $micro))
1498 {
1499 $number/=$micro;
1500 $suffix="u";
1501 }
1502 elseif (($below_one==TRUE) && ($number >= $nano))
1503 {
1504 $number/=$nano;
1505 $suffix="n";
1506 }
1507 
1508 $result=format_number($number, $decimals) . $suffix;
1509 return ($result);
1510}
1511 
1512function nice_scalar($number, $kilo = 1000, $decimals=1)
1513{
1514 $suffix = '';
1515 $prefix = '';
1516 
1517 if ($number == 0)
1518 return '0';
1519 
1520 if($number < 0)
1521 {
1522 $number = -$number;
1523 $prefix = '-';
1524 }
1525 
1526 $mega=$kilo * $kilo;
1527 $giga=$mega * $kilo;
1528 $tera=$giga * $kilo;
1529 
1530 if ($number > $tera)
1531 {
1532 $number/=$tera;
1533 $suffix="T";
1534 }
1535 elseif ($number > $giga)
1536 {
1537 $number/=$giga;
1538 $suffix="G";
1539 }
1540 elseif ($number > $mega)
1541 {
1542 $number/=$mega;
1543 $suffix="M";
1544 }
1545 elseif ($number > $kilo)
1546 {
1547 $number/=$kilo;
1548 $suffix="K";
1549 }
1550 elseif ($number > 1)
1551 {
1552 $number = $number;
1553 $suffix="";
1554 }
1555 elseif ($number < (1 / ($kilo)))
1556 {
1557 $number=$number * $mega;
1558 $suffix="u";
1559 }
1560 elseif ($number < 1)
1561 {
1562 $number=$number * $kilo;
1563 $suffix="m";
1564 }
1565 
1566 $result = $prefix . format_number($number, $decimals) . $suffix;
1567 return ($result);
1568}
1569 
1570 
1571// ***********************************************
1572 
1573// we use enough points in various places to make it worth a small class to save some variable-pairs.
1574class Point
1575{
1576 var $x, $y;
1577 
1578 function Point($x=0,$y=0)
1579 {
1580 $this->x = $x;
1581 $this->y = $y;
1582 }
1583}
1584 
1585// similarly for 2D vectors
1586class Vector
1587{
1588 var $dx, $dy;
1589 
1590 function Vector($dx=0,$dy=0)
1591 {
1592 $this->dx = $dx;
1593 $this->dy = $dy;
1594 }
1595 
1596 function get_normal()
1597 {
1598 $len = $this->length();
1599 
1600 $nx1 = $this->dy / $len;
1601 $ny1 = -$this->dx / $len;
1602 
1603 return( new Vector($nx1, $ny1));
1604 }
1605 
1606 function normalise()
1607 {
1608 $len = $this->length();
1609 $this->dx = $this->dx/$len;
1610 $this->dy = $this->dy/$len;
1611 }
1612 
1613 function length()
1614 {
1615 return( sqrt(($this->dx)*($this->dx) + ($this->dy)*($this->dy)) );
1616 }
1617}
1618 
1619class Colour
1620{
1621 var $r,$g,$b, $alpha;
1622 
1623 
1624 // take in an existing value and create a Colour object for it
1625 function Colour()
1626 {
1627 if(func_num_args() == 3) # a set of 3 colours
1628 {
1629 $this->r = func_get_arg(0); # r
1630 $this->g = func_get_arg(1); # g
1631 $this->b = func_get_arg(2); # b
1632 #print "3 args";
1633 #print $this->as_string()."--";
1634 }
1635 
1636 if( (func_num_args() == 1) && gettype(func_get_arg(0))=='array' ) # an array of 3 colours
1637 {
1638 #print "1 args";
1639 $ary = func_get_arg(0);
1640 $this->r = $ary[0];
1641 $this->g = $ary[1];
1642 $this->b = $ary[2];
1643 }
1644 }
1645 
1646 // Is this a transparent/none colour?
1647 function is_real()
1648 {
1649 if($this->r >= 0 && $this->g >=0 && $this->b >= 0)
1650 {
1651 return true;
1652 }
1653 else
1654 {
1655 return false;
1656 }
1657 }
1658 
1659 // Is this a transparent/none colour?
1660 function is_none()
1661 {
1662 if($this->r == -1 && $this->g == -1 && $this->b == -1)
1663 {
1664 return true;
1665 }
1666 else
1667 {
1668 return false;
1669 }
1670 }
1671 
1672 // Is this a contrast colour?
1673 function is_contrast()
1674 {
1675 if($this->r == -3 && $this->g == -3 && $this->b == -3)
1676 {
1677 return true;
1678 }
1679 else
1680 {
1681 return false;
1682 }
1683 }
1684 
1685 // Is this a copy colour?
1686 function is_copy()
1687 {
1688 if($this->r == -2 && $this->g == -2 && $this->b == -2)
1689 {
1690 return true;
1691 }
1692 else
1693 {
1694 return false;
1695 }
1696 }
1697 
1698 // allocate a colour in the appropriate image context
1699 // - things like scale colours are used in multiple images now (the scale, several nodes, the main map...)
1700 function gdallocate($image_ref)
1701 {
1702 if($this->is_none())
1703 {
1704 return NULL;
1705 }
1706 else
1707 {
1708 return(myimagecolorallocate($image_ref, $this->r, $this->g, $this->b));
1709 }
1710 }
1711 
1712 // based on an idea from: http://www.bennadel.com/index.cfm?dax=blog:902.view
1713 function contrast_ary()
1714 {
1715 if( (($this->r + $this->g + $this->b) > 500)
1716 || ($this->g > 140)
1717 )
1718 {
1719 return( array(0,0,0) );
1720 }
1721 else
1722 {
1723 return( array(255,255,255) );
1724 }
1725 }
1726 
1727 function contrast()
1728 {
1729 return( new Colour($this->contrast_ary() ) );
1730 }
1731 
1732 // make a printable version, for debugging
1733 // - optionally take a format string, so we can use it for other things (like WriteConfig, or hex in stylesheets)
1734 function as_string($format = "RGB(%d,%d,%d)")
1735 {
1736 return (sprintf($format, $this->r, $this->g, $this->b));
1737 }
1738 
1739 function as_config()
1740 {
1741 return $this->as_string("%d %d %d");
1742 }
1743 
1744 function as_html()
1745 {
1746 if($this->is_real())
1747 {
1748 return $this->as_string("#%02x%02x%02x");
1749 }
1750 else
1751 {
1752 return "";
1753 }
1754 }
1755}
1756 
1757// A series of wrapper functions around all the GD function calls
1758// - I added these in so I could make a 'metafile' easily of all the
1759// drawing commands for a map. I have a basic Perl-Cairo script that makes
1760// anti-aliased maps from these, using Cairo instead of GD.
1761 
1762function metadump($string, $truncate=FALSE)
1763{
1764 // comment this line to get a metafile for this map
1765 return;
1766 
1767 if($truncate)
1768 {
1769 $fd = fopen("metadump.txt","w+");
1770 }
1771 else
1772 {
1773 $fd = fopen("metadump.txt","a");
1774 }
1775 fputs($fd,$string."\n");
1776 fclose($fd);
1777}
1778 
1779function metacolour(&$col)
1780{
1781 return ($col['red1']." ".$col['green1']." ".$col['blue1']);
1782}
1783 
1784function wimagecreate($width,$height)
1785{
1786 metadump("NEWIMAGE $width $height");
1787 return(imagecreate($width,$height));
1788}
1789 
1790function wimagefilledrectangle( $image ,$x1, $y1, $x2, $y2, $color )
1791{
1792 if ($color===NULL) return;
1793 
1794 $col = imagecolorsforindex($image, $color);
1795 $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha'];
1796 $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127;
1797 
1798 metadump("FRECT $x1 $y1 $x2 $y2 $r $g $b $a");
1799 return(imagefilledrectangle( $image ,$x1, $y1, $x2, $y2, $color ));
1800}
1801 
1802function wimagerectangle( $image ,$x1, $y1, $x2, $y2, $color )
1803{
1804 if ($color===NULL) return;
1805 
1806 $col = imagecolorsforindex($image, $color);
1807 $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha'];
1808 $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127;
1809 
1810 metadump("RECT $x1 $y1 $x2 $y2 $r $g $b $a");
1811 return(imagerectangle( $image ,$x1, $y1, $x2, $y2, $color ));
1812}
1813 
1814function wimagepolygon($image, $points, $num_points, $color)
1815{
1816 if ($color===NULL) return;
1817 
1818 $col = imagecolorsforindex($image, $color);
1819 $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha'];
1820 $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127;
1821 
1822 $pts = "";
1823 for ($i=0; $i < $num_points; $i++)
1824 {
1825 $pts .= $points[$i * 2]." ";
1826 $pts .= $points[$i * 2+1]." ";
1827 }
1828 
1829 metadump("POLY $num_points ".$pts." $r $g $b $a");
1830 
1831 return(imagepolygon($image, $points, $num_points, $color));
1832}
1833 
1834function wimagefilledpolygon($image, $points, $num_points, $color)
1835{
1836 if ($color===NULL) return;
1837 
1838 $col = imagecolorsforindex($image, $color);
1839 $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha'];
1840 $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127;
1841 
1842 $pts = "";
1843 for ($i=0; $i < $num_points; $i++)
1844 {
1845 $pts .= $points[$i * 2]." ";
1846 $pts .= $points[$i * 2+1]." ";
1847 }
1848 
1849 metadump("FPOLY $num_points ".$pts." $r $g $b $a");
1850 
1851 return(imagefilledpolygon($image, $points, $num_points, $color));
1852}
1853 
1854function wimagecreatetruecolor($width, $height)
1855{
1856 
1857 
1858 metadump("BLANKIMAGE $width $height");
1859 
1860 return imagecreatetruecolor($width,$height);
1861 
1862}
1863 
1864function wimagettftext($image, $size, $angle, $x, $y, $color, $file, $string)
1865{
1866 if ($color===NULL) return;
1867 
1868 $col = imagecolorsforindex($image, $color);
1869 $r = $col['red']; $g = $col['green']; $b = $col['blue']; $a = $col['alpha'];
1870 $r = $r/255; $g=$g/255; $b=$b/255; $a=(127-$a)/127;
1871 
1872 metadump("TEXT $x $y $angle $size $file $r $g $b $a $string");
1873 
1874 return(imagettftext($image, $size, $angle, $x, $y, $color, $file, $string));
1875}
1876 
1877function wm_draw_marker_diamond($im, $col, $x, $y, $size=10)
1878{
1879 $points = array();
1880 
1881 $points []= $x-$size;
1882 $points []= $y;
1883 
1884 $points []= $x;
1885 $points []= $y-$size;
1886 
1887 $points []= $x+$size;
1888 $points []= $y;
1889 
1890 $points []= $x;
1891 $points []= $y+$size;
1892 
1893 $num_points = 4;
1894 
1895 imagepolygon($im, $points, $num_points, $col);
1896}
1897 
1898function wm_draw_marker_box($im, $col, $x, $y, $size=10)
1899{
1900 $points = array();
1901 
1902 $points []= $x-$size;
1903 $points []= $y-$size;
1904 
1905 $points []= $x+$size;
1906 $points []= $y-$size;
1907 
1908 $points []= $x+$size;
1909 $points []= $y+$size;
1910 
1911 $points []= $x-$size;
1912 $points []= $y+$size;
1913 
1914 $num_points = 4;
1915 
1916 imagepolygon($im, $points, $num_points, $col);
1917}
1918 
1919function wm_draw_marker_circle($im, $col, $x, $y, $size=10)
1920{
1921 imagearc($im,$x, $y ,$size,$size,0,360,$col);
1922}
1923 
1924function draw_spine_chain($im,$spine,$col, $size=10)
1925{
1926 $newn = count($spine);
1927 
1928 for ($i=0; $i < $newn; $i++)
1929 {
1930 imagearc($im,$spine[$i][X],$spine[$i][Y],$size,$size,0,360,$col);
1931 }
1932}
1933 
1934function dump_spine($spine)
1935{
1936 print "===============\n";
1937 for($i=0; $i<count($spine); $i++)
1938 {
1939 printf (" %3d: %d,%d (%d)\n", $i, $spine[$i][X], $spine[$i][Y], $spine[$i][DISTANCE] );
1940 }
1941 print "===============\n";
1942}
1943 
1944function draw_spine($im, $spine,$col)
1945{
1946 $max_i = count($spine)-1;
1947 
1948 for ($i=0; $i <$max_i; $i++)
1949 {
1950 imageline($im,
1951 $spine[$i][X],$spine[$i][Y],
1952 $spine[$i+1][X],$spine[$i+1][Y],
1953 $col
1954 );
1955 }
1956}
1957 
1958// vim:ts=4:sw=4:
1959?>

Powered by WebSVN 2.2.1