. */ /** * For more information, please contact */ namespace Goat1000\SVGGraph; abstract class HorizontalGridGraph extends GridGraph { public function __construct($w, $h, array $settings, array $fixed_settings = []) { // convert _x and _y labels to _v and _h if(!empty($settings['label_x']) && empty($settings['label_v'])) $settings['label_v'] = $settings['label_x']; if(!empty($settings['label_y']) && empty($settings['label_h'])) $settings['label_h'] = $settings['label_y']; // unset these so GridGraph doesn't use them unset($settings['label_x'], $settings['label_y']); parent::__construct($w, $h, $settings, $fixed_settings); } /** * Swap the axis returned */ protected function getDisplayAxis($axis, $axis_no, $orientation, $type) { return parent::getDisplayAxis($axis, $axis_no, $orientation, $type === 'x' ? 'y' : 'x'); } /** * Returns the factory for an X-axis */ protected function getXAxisFactory() { return new AxisFactory(false, $this->settings, false, false, false); } /** * Returns the factory for a Y-axis */ protected function getYAxisFactory() { $y_bar = $this->getOption('label_centre'); return new AxisFactory($this->getOption('datetime_keys'), $this->settings, true, $y_bar, true); } /** * Creates and returns an X-axis */ protected function createXAxis($factory, $length, $ends, $i, $min_space, $grid_division) { $max_h = $ends['v_max'][$i]; $min_h = $ends['v_min'][$i]; if(!is_numeric($max_h) || !is_numeric($min_h)) throw new \Exception('Non-numeric min/max'); $min_unit = $this->getOption(['minimum_units_y', $i]); $units_after = (string)$this->getOption(['units_y', $i]); $units_before = (string)$this->getOption(['units_before_y', $i]); $decimal_digits = $this->getOption(['decimal_digits_y', $i], 'decimal_digits'); $text_callback = $this->getOption(['axis_text_callback_y', $i], 'axis_text_callback'); $log = $this->getOption(['log_axis_y', $i]); $log_base = $this->getOption(['log_axis_y_base', $i]); $ticks = $this->getOption(['axis_ticks_y', $i]); $values = $levels = null; if($min_h == $max_h) { if($min_unit > 0) { $inc = $min_unit; } else { $fallback = $this->getOption('axis_fallback_max'); $inc = $fallback > 0 ? $fallback : 1; } $max_h += $inc; } $x_axis = $factory->get($length, $min_h, $max_h, $min_unit, $min_space, $grid_division, $units_before, $units_after, $decimal_digits, $text_callback, $values, $log, $log_base, $levels, $ticks); $x_axis->setTightness($this->getOption(['axis_tightness_y', $i])); return $x_axis; } /** * Creates and returns a Y-axis */ protected function createYAxis($factory, $length, $ends, $i, $min_space, $grid_division) { $max_v = $ends['k_max'][$i]; $min_v = $ends['k_min'][$i]; if(!is_numeric($max_v) || !is_numeric($min_v)) throw new \Exception('Non-numeric min/max'); $min_unit = 1; $text_callback = $this->getOption(['axis_text_callback_x', $i], 'axis_text_callback'); $decimal_digits = $this->getOption(['decimal_digits_x', $i], 'decimal_digits'); $units_after = (string)$this->getOption(['units_x', $i]); $units_before = (string)$this->getOption(['units_before_x', $i]); $values = $this->multi_graph ? $this->multi_graph : $this->values; $levels = $this->getOption(['axis_levels_h', $i]); $log = $this->getOption(['log_axis_x', $i]); $log_base = $this->getOption(['log_axis_x_base', $i]); $ticks = $this->getOption('axis_ticks_x'); return $factory->get($length, $min_v, $max_v, $min_unit, $min_space, $grid_division, $units_before, $units_after, $decimal_digits, $text_callback, $values, $log, $log_base, $levels, $ticks); } /** * Calculates the position of grid lines */ protected function calcGrid() { parent::calcGrid(); $this->grid_limit = 0.01 + $this->g_height; if($this->getOption('label_centre')) { $y_axis = $this->y_axes[$this->main_y_axis]; $this->grid_limit -= $y_axis->unit() / 2; } } /** * Returns the subdivisions for a Y-axis */ protected function getSubDivsY($axis) { $a = $this->y_axes[$axis]; $offset = $a->reversed() ? $this->height - $this->pad_bottom : $this->pad_top; return $a->getGridSubdivisions($this->getOption('minimum_subdivision'), 1, $offset, $this->getOption(['subdivision_v', $axis])); } /** * Returns the subdivisions for an X-axis */ protected function getSubDivsX($axis) { $a = $this->x_axes[$axis]; $offset = $a->reversed() ? $this->width - $this->pad_right : $this->pad_left; return $a->getGridSubdivisions($this->getOption('minimum_subdivision'), $this->getOption(['minimum_units_y', $axis]), $offset, $this->getOption(['subdivision_h', $axis])); } /** * Returns fixed min and max option for an axis */ protected function getFixedAxisOptions($axis, $index) { $a = $axis == 'y' ? 'h' : 'v'; $min = $this->getOption(['axis_min_' . $a, $index]); $max = $this->getOption(['axis_max_' . $a, $index]); return [$min, $max]; } /** * Returns the crosshairs code */ protected function getCrossHairs() { if(!$this->getOption('crosshairs')) return ''; $ch = new CrossHairs($this, $this->pad_left, $this->pad_top, $this->g_width, $this->g_height, $this->x_axes[$this->main_x_axis], $this->y_axes[$this->main_y_axis], $this->values->associativeKeys(), true, $this->encoding); if($ch->enabled()) return $ch->getCrossHairs(); return ''; } /** * Returns the grid position for a bar or point, or NULL if not on grid * $item = data item * $index = integer position in array */ protected function gridPosition($item, $index) { $axis = $this->y_axes[$this->main_y_axis]; $offset = $axis->position($index, $item); $zero = -0.01; // catch values close to 0 if($offset >= $zero && floor($offset) <= $this->grid_limit) return $axis->reversed() ? $this->height - $this->pad_bottom - $offset : $this->pad_top + $offset; return null; } /** * Returns the SVG fragment for grid background stripes */ protected function getGridStripes() { if(!$this->getOption('grid_back_stripe')) return ''; // use array of colours if available, otherwise stripe a single colour $colours = $this->getOption('grid_back_stripe_colour'); if(!is_array($colours)) $colours = [null, $colours]; $opacity = $this->getOption('grid_back_stripe_opacity'); $bars = ''; $c = 0; $num_colours = count($colours); $rect = [ 'y' => new Number($this->pad_top), 'height' => new Number($this->g_height), ]; if($opacity != 1) $rect['fill-opacity'] = $opacity; $points = $this->getGridPointsX($this->main_x_axis); $first = array_shift($points); $last_pos = $first->position; foreach($points as $grid_point) { $cc = $colours[$c % $num_colours]; if($cc !== null) { $rect['x'] = $last_pos; $rect['width'] = $grid_point->position - $last_pos; $rect['fill'] = new Colour($this, $cc); $bars .= $this->element('rect', $rect); } $last_pos = $grid_point->position; ++$c; } return $this->element('g', [], null, $bars); } /** * Converts guideline options to more useful member variables */ protected function calcGuidelines() { $this->calcAverages(); $guidelines = $this->getOption('guideline'); if(empty($guidelines) && $guidelines !== 0) return; $this->guidelines = new Guidelines($this, true, $this->values->associativeKeys(), $this->getOption('datetime_keys')); } }