image */ private $image; /** @var boolean $darkmode */ private $darkmode; /** @var boolean $debug */ private $debug; public function __construct(array $displaydates, array $stations, array $optstations = [], $wantedstations = [], $darkmode = false) { $this->abbreviations = []; $this->training = $this->event = false; $this->displaydates = $displaydates; $this->stations = $stations; $this->optstations = $optstations; $this->wantedstations = $wantedstations; $this->improps = []; $this->darkmode = $darkmode; } public function createImage() { $rawData = $this->fetchData(); $relevantBookings = []; foreach($this->stations as $station) { $relevantBookings[$station] = []; } foreach($rawData as $item) { if(in_array($item['station']['ident'], $this->stations)) { $relevantBookings[$item['station']['ident']][] = [ 'starts_at' => new DateTime($item['starts_at']), 'ends_at' => new DateTime($item['ends_at']), 'lastname' => $item['controller']['lastname'], 'firstname' => $item['controller']['firstname'], 'training' => $item['training'], 'event' => $item['event'] ]; } } putenv('GDFONTPATH=' . realpath('.')); $this->improps['font'] = "UbuntuMono-Regular"; // $this->improps['font'] = "EuroScope"; $this->improps['width'] = 100 * count($this->displaydates) + 100; $this->improps['height'] = 25 * count($this->stations) + 600; if(!$this->darkmode) { $this->image = imagecreatetruecolor($this->improps['width'], $this->improps['height']); $this->improps['bg'] = imagecolorallocate($this->image, 255, 255, 255); $this->improps['black'] = imagecolorexact($this->image, 0, 0, 0); $this->improps['open'] = imagecolorexact($this->image, 155, 155, 155); $this->improps['wanted'] = imagecolorexact($this->image, 255, 0, 0); $this->improps['event'] = imagecolorexact($this->image, 0, 255, 0); $this->improps['training'] = imagecolorexact($this->image, 50, 100, 255); $this->improps['eventtraining'] = imagecolorexact($this->image, 0, 255, 255); } else { $this->image = imagecreatetruecolor($this->improps['width'], $this->improps['height']); $this->improps['bg'] = imagecolorallocate($this->image, 0, 0, 0); $this->improps['black'] = imagecolorexact($this->image, 255, 255, 255); $this->improps['open'] = imagecolorexact($this->image, 155, 155, 155); $this->improps['wanted'] = imagecolorexact($this->image, 255, 0, 0); $this->improps['event'] = imagecolorexact($this->image, 0, 255, 0); $this->improps['training'] = imagecolorexact($this->image, 50, 100, 255); $this->improps['eventtraining'] = imagecolorexact($this->image, 0, 255, 255); } imagefill($this->image, 0, 0, $this->improps['bg']); $this->improps['lineheight'] = 20; $sepheight = 25; for($i = 0; $i < count($this->displaydates); $i++) { imagefttext($this->image, 11, 0, ($i) * 100 + 100, 25, $this->improps['black'], $this->improps['font'], $this->displaydates[$i]->format("D d.m.")); } $height = 40; foreach($this->stations as $station) { if($this->debug) echo "\n". $station . "\th$height"; imageline($this->image, 0, $height + 5, $this->improps['width'], $height + 5, $this->improps['open']); if($station == '--') { $height += $sepheight; continue; } $height += $this->improps['lineheight']; $lines = 0; for($i = 0; $i < count($this->displaydates); $i++) { $l = $this->addBookingData($station, $this->displaydates[$i], $relevantBookings[$station], $i * 100 + 100, $height); $lines = $l > $lines ? $l : $lines; if($this->debug) echo "\t" . $this->displaydates[$i]->format("m-d") . ": " . $lines; } if(!in_array($station, $this->optstations) && $lines < 1) $lines = 1; if($this->debug) echo "\tl$lines"; if($lines > 0) { imagefttext($this->image, 11, 0, 10, $height, $this->improps['black'], $this->improps['font'], $station); imageline($this->image, 0, $height + 5, $this->improps['width'], $height + 5, $this->improps['open']); } $height += ($lines - 1) * $this->improps['lineheight']; } for($i = 0; $i < count($this->displaydates); $i++) { imageline($this->image, 100 * $i + 92, 0, 100 * $i + 92, $height + 5, $this->improps['open']); } $height += $this->improps['lineheight'] + 5; imagefttext($this->image, 11, 0, 10, $height, $this->improps['wanted'], $this->improps['font'], 'Wanted'); imagefttext($this->image, 11, 0, 100, $height, $this->improps['training'], $this->improps['font'], 'Training'); imagefttext($this->image, 11, 0, 200, $height, $this->improps['event'], $this->improps['font'], 'Event'); imagefttext($this->image, 11, 0, 300, $height, $this->improps['eventtraining'], $this->improps['font'], 'Event + Training'); $height += $this->improps['lineheight'] * 2; $namelength = 0; asort($this->abbreviations); foreach($this->abbreviations as $name) { $namelength = strlen($name) > $namelength ? strlen($name) : $namelength; } $collength = ($namelength + 10) * 8; $cols = floor( ($this->improps['width'] - 10) / $collength); $col = 0; $row = 0; foreach($this->abbreviations as $abbrv => $name) { imagefttext($this->image, 11, 0, $col * $collength + 10, $height + $row * $this->improps['lineheight'], $this->improps['black'], $this->improps['font'], $abbrv . ": " . $name); $row = ($col + 1) >= $cols ? $row + 1 : $row; $col = ($col + 1) >= $cols ? 0 : $col + 1; } $height += $this->improps['lineheight'] * ($row +2); imagefttext($this->image, 11, 0, 10, $height, $this->improps['open'], $this->improps['font'], "Generated " . (new DateTime("now", new DateTimeZone('UTC')))->format('d.m.Y H:i:s') . "z" ); $this->image = imagecrop($this->image, ['x' => 0, 'y' => 0, 'width' => $this->improps['width'], 'height' => $height+5]); return $this->image; } private function addBookingData($station, $date, $bookings, $x, $height) { $count = 0; foreach($bookings as $booking) { if($booking['starts_at']->format('Y-m-d') == $date->format('Y-m-d')) { $abbrv = substr($booking['firstname'], 0, 3) . substr($booking['lastname'], 0, 3); //TODO $this->abbreviations[$abbrv] = $booking['firstname'] . " " . $booking['lastname']; if($booking['training'] && $booking['event']) $color = $this->improps['eventtraining']; elseif($booking['training']) $color = $this->improps['training']; elseif($booking['event']) $color = $this->improps['event']; else $color = $this->improps['black']; imagefttext($this->image, 11, 0, $x, $height + $count * $this->improps['lineheight'], $color, $this->improps['font'], $abbrv . " " . $booking['starts_at']->format('H') . "-" . $booking['ends_at']->format('H')); $count++; } } if($count == 0 && !in_array($station, $this->optstations)) { if(!$this->dayIsWanted($station, $date)) imagefttext($this->image, 10, 0, $x, $height, $this->improps['open'], $this->improps['font'], "Open"); else imagefttext($this->image, 10, 0, $x, $height, $this->improps['wanted'], $this->improps['font'], "WANTED!"); } return $count; } private function fetchData() { $url = sprintf("https://vatsim-germany.org/api/booking/atc/daterange/%s/%s", $this->displaydates[0]->format("d.m.Y"), $this->displaydates[count($this->displaydates) -1]->format("d.m.Y")); $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url ) ; curl_setopt($curl, CURLOPT_HTTPHEADER, ["X-Requested-With: XMLHttpRequest"]); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // curl_setopt($curl, CURLOPT_VERBOSE, true); // $verbose = fopen('php://temp', 'w+'); // curl_setopt($curl, CURLOPT_STDERR, $verbose); $result = curl_exec($curl); // if ($result === FALSE) { // printf("cUrl error (#%d): %s
\n", curl_errno($curl), // htmlspecialchars(curl_error($curl))); // } // rewind($verbose); // $verboseLog = stream_get_contents($verbose); // echo "Verbose information:\n
", htmlspecialchars($verboseLog), "
\n"; curl_close($curl); return json_decode($result, true); } public static function EveryXDaysFromStartdate($startdate, $x = 1, $numberofdates = 1) { $startdate = new DateTime($startdate, new DateTimeZone('UTC')); $days = []; while(count($days) < $numberofdates) { if(new DateTime('today', new DateTimeZone('UTC')) <= $startdate) { $days[] = clone($startdate); } $startdate->add(new DateInterval(sprintf("P%dD", $x))); } return $days; } public static function EveryXWeeksFromStartdate($startdate, $x = 1, $numberofdates = 1) { return self::EveryXDaysFromStartdate($startdate, $x * 7, $numberofdates); } public static function EveryWeekday($dayoftheweek, $numberofdates = 1) { $startdate = new DateTime($dayoftheweek, new DateTimeZone('UTC')); $days = []; while(count($days) < $numberofdates) { $days[] = clone($startdate); $startdate->add(new DateInterval("P7D")); } return $days; } public static function WholeWeek($startdate = "today") { $days = []; $startdate = new DateTime($startdate, new DateTimeZone('UTC')); for($i = 0; $i < 7; $i++) { $days[] = clone($startdate); $startdate->add(new DateInterval("P1D")); } return $days; } public static function processWantedDays(...$args) { if(count($args) % 2 == 1) throw new Exception("Even number of arguments required."); $r = []; for($i = 0; $i < count($args); $i += 2) { foreach($args[$i] as $station) { if(!isset($r[$station])) $r[$station] = []; foreach($args[$i + 1] as $date) { $r[$station][] = $date; } } } return $r; } private function dayIsWanted($station, $date) { if(!isset($this->wantedstations[$station])) return false; foreach($this->wantedstations[$station] as $target) { if($target->format("Y-m-d") == $date->format("Y-m-d")) return true; } return false; } }