| 1 | <?php |
||
| 2 | |||
| 3 | /** @noinspection PhpDocMissingThrowsInspection */ |
||
| 4 | |||
| 5 | require_once 'vendor/autoload.php'; |
||
| 6 | |||
| 7 | use Vctls\IntervalGraph\IntervalGraph; |
||
| 8 | use Vctls\IntervalGraph\Util\Date as D; |
||
| 9 | |||
| 10 | $today = new DateTime('today'); |
||
| 11 | |||
| 12 | $base = D::intv(0, 5); |
||
| 13 | $intv1 = D::intv(0, 4, 7 / 10); |
||
| 14 | $intv2 = D::intv(1, 5, 3 / 10); |
||
| 15 | $intv3 = D::intv(2, 3, 3 / 10); |
||
| 16 | $overlapped1 = D::intvg([$base, $intv1]); |
||
| 17 | $overlapped2 = D::intvg([$base, $intv2]); |
||
| 18 | $overlapped3 = D::intvg([$base, $intv3]); |
||
| 19 | $overlapped = D::intvg([$base, $intv1, $intv2, $intv3]); |
||
| 20 | |||
| 21 | $withNull1 = D::intvg([$base, D::intv(0, 3, 4 / 10)]); |
||
| 22 | $withNull2 = D::intvg([$base, D::intv(1, 2)]); |
||
| 23 | $withNull3 = D::intvg([$base, D::intv(2, 3, 4 / 10)]); |
||
| 24 | $withNull4 = D::intvg([$base, D::intv(4, 5, 5 / 10)]); |
||
| 25 | $withNullIntervals = D::intvg([ |
||
| 26 | D::intv(0, 3, 4 / 10), |
||
| 27 | D::intv(1, 2), |
||
| 28 | D::intv(2, 3, 4 / 10), |
||
| 29 | D::intv(4, 5, 5 / 10), |
||
| 30 | ]); |
||
| 31 | |||
| 32 | $longIntervals = [ |
||
| 33 | [$today, new DateTime('today + 3 days'), 2 / 10], |
||
| 34 | D::intv(1, 4, 2 / 10), |
||
| 35 | D::intv(2, 5, 3 / 10), |
||
| 36 | D::intv(3, 6, 5 / 10), |
||
| 37 | D::intv(4, 7, 4 / 10), |
||
| 38 | D::intv(5, 8, 2 / 10), |
||
| 39 | D::intv(6, 9, 2 / 10), |
||
| 40 | ]; |
||
| 41 | |||
| 42 | $longDateFormat = function (DateTime $bound) { |
||
| 43 | return $bound->format('Y-m-d H:i:s'); |
||
| 44 | }; |
||
| 45 | |||
| 46 | $long = (D::intvg($longIntervals))->setBoundToString($longDateFormat); |
||
| 47 | |||
| 48 | /* |
||
| 49 | * CUSTOM VALUE TYPES |
||
| 50 | */ |
||
| 51 | |||
| 52 | // An aggregate function for arrays representing fractions with the same denominator. |
||
| 53 | $agg = function ($a, $b) { |
||
| 54 | if ($a === null && $b === null) return null; |
||
| 55 | return [$a[0] + $b[0], $b[1]]; |
||
| 56 | }; |
||
| 57 | |||
| 58 | // A toNumeric function… |
||
| 59 | $toNumeric = function ($a) { |
||
| 60 | return $a === null ? null : (int)($a[0] / $a[1] * 100); |
||
| 61 | }; |
||
| 62 | |||
| 63 | // A toString function… |
||
| 64 | $toString = function ($a) { |
||
| 65 | return $a === null ? null : ($a[0] . '/' . $a[1]); |
||
| 66 | }; |
||
| 67 | |||
| 68 | $fractions = [ |
||
| 69 | D::intv(1, 4, [2, 10]), |
||
| 70 | D::intv(2, 5, [3, 10]), |
||
| 71 | D::intv(3, 6, [5, 10]), |
||
| 72 | D::intv(4, 7, [4, 10]), |
||
| 73 | D::intv(5, 8, [2, 10]), |
||
| 74 | D::intv(6, 9, [2, 10]), |
||
| 75 | ]; |
||
| 76 | $fractim = (D::intvg($fractions)) |
||
| 77 | ->setAggregate($agg) |
||
| 78 | ->setValueToNumeric($toNumeric) |
||
| 79 | ->setValueToString($toString); |
||
| 80 | $fract = $fractim->draw(); |
||
| 81 | |||
| 82 | /* /CUSTOM VALUE TYPES */ |
||
| 83 | |||
| 84 | /* |
||
| 85 | * TRUNCATED INTERVALS |
||
| 86 | */ |
||
| 87 | try { |
||
| 88 | $intv1 = (clone $today)->add(new DateInterval('PT60H')); |
||
| 89 | $intv2 = (clone $today)->add(new DateInterval('PT108H')); |
||
| 90 | $intv3 = (clone $intv2)->add(new DateInterval('PT60H')); |
||
| 91 | } catch (Exception $e) { |
||
|
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
Loading history...
|
|||
| 92 | } |
||
| 93 | |||
| 94 | $truncated = ($fractim->setIntervals(IntervalGraph::truncate($fractions, $intv1, $intv2))) |
||
| 95 | ->setBoundToString($longDateFormat); |
||
| 96 | /* /TRUNCATED INTERVALS */ |
||
| 97 | |||
| 98 | $withDates = (D::intvg([ |
||
| 99 | [$intv1, $intv1], |
||
| 100 | D::intv(0, 4, 7 / 10), |
||
| 101 | [$intv2, $intv2], |
||
| 102 | D::intv(1, 5, 3 / 10), |
||
| 103 | D::intv(2, 3, 3 / 10), |
||
| 104 | [$intv3, $intv3], |
||
| 105 | ])) |
||
| 106 | ->setBoundToString($longDateFormat);; |
||
| 107 | |||
| 108 | $intvGraphs = []; |
||
| 109 | foreach (range(0, 20) as $t) { |
||
| 110 | $intervals = []; |
||
| 111 | $j = (int)rand(3, 6); |
||
| 112 | for ($i = 0; $i < $j; $i++) { |
||
| 113 | $intervals[] = [D::rdm(), D::rdm(), rand(1, 9) / 10]; |
||
| 114 | } |
||
| 115 | $intvGraphs[] = (D::intvg($intervals))->checkIntervals(); |
||
| 116 | } |
||
| 117 | |||
| 118 | |||
| 119 | ?> |
||
| 120 | <!doctype html> |
||
| 121 | <html lang="en"> |
||
| 122 | <head> |
||
| 123 | <title>Php IntervalGraph demo</title> |
||
| 124 | <link rel="stylesheet" href="styles.css"> |
||
| 125 | <script type="application/javascript" src="app.js"></script> |
||
| 126 | </head> |
||
| 127 | <body style="font-family: sans-serif;"> |
||
| 128 | <header> |
||
| 129 | <h1>PHP Interval Graph demo</h1> |
||
| 130 | </header> |
||
| 131 | <p> |
||
| 132 | PHP Interval Graph is a small utility to manipulate and |
||
| 133 | display arrays of intervals. |
||
| 134 | </p> |
||
| 135 | <a href="https://github.com/vctls/php-interval-graph"> |
||
| 136 | https://github.com/vctls/php-interval-graph |
||
| 137 | </a> |
||
| 138 | <h2>How it works</h2> |
||
| 139 | <p> |
||
| 140 | Here are three overlapping date intervals. |
||
| 141 | Each one has a linked rate, displayed as a percentage when hovering it. |
||
| 142 | <br>They are all displayed over the same period of time, which has no rate. |
||
| 143 | </p> |
||
| 144 | <div style="margin-bottom: 2px"><?= $overlapped1 ?></div> |
||
| 145 | <div style="margin-bottom: 2px"><?= $overlapped2 ?></div> |
||
| 146 | <div style="margin-bottom: 2px"><?= $overlapped3 ?></div> |
||
| 147 | |||
| 148 | <p> |
||
| 149 | Gathered on the same graph, they are displayed as follows. |
||
| 150 | </p> |
||
| 151 | <?= $overlapped ?> |
||
| 152 | |||
| 153 | <h2>More examples</h2> |
||
| 154 | <p> |
||
| 155 | Overlapping intervals with a couple null intervals.<br> |
||
| 156 | The first null interval overlaps a non null one. |
||
| 157 | This cuts the non null interval, while the weight remains the same.<br> |
||
| 158 | The second null interval is implicit. |
||
| 159 | It is simply the gap between the two last intervals. |
||
| 160 | </p> |
||
| 161 | <div style="margin-bottom: 2px"><?= $withNull1 ?></div> |
||
| 162 | <div style="margin-bottom: 2px"><?= $withNull2 ?></div> |
||
| 163 | <div style="margin-bottom: 2px"><?= $withNull3 ?></div> |
||
| 164 | <div style="margin-bottom: 2px"><?= $withNull4 ?></div> |
||
| 165 | <br> |
||
| 166 | <?= $withNullIntervals ?> |
||
| 167 | |||
| 168 | |||
| 169 | <h2>Custom value types</h2> |
||
| 170 | <p> |
||
| 171 | The following graph takes arrays of two values |
||
| 172 | and displays them as fractions.<br> |
||
| 173 | In order to use custom value types, you need to set the custom functions |
||
| 174 | that will aggregate the values, convert them to numeric values and strings. |
||
| 175 | </p> |
||
| 176 | <?= $fract ?> |
||
| 177 | |||
| 178 | <p> |
||
| 179 | The same graph, truncated between <?= $intv1->format('Y-m-d H:i:s') ?> |
||
| 180 | and <?= $intv2->format('Y-m-d H:i:s') ?>. |
||
| 181 | <?= $truncated ?> |
||
| 182 | </p> |
||
| 183 | |||
| 184 | <p> |
||
| 185 | A graph with three isolated dates, shown as black bars. |
||
| 186 | <br>One of the dates goes beyond all intervals. |
||
| 187 | <?= $withDates ?> |
||
| 188 | </p> |
||
| 189 | |||
| 190 | <?php |
||
| 191 | |||
| 192 | /* ADDITIONAL INFORMATION */ |
||
| 193 | $toString2 = function ($a) { |
||
| 194 | if ($a === null) { |
||
| 195 | return null; |
||
| 196 | } |
||
| 197 | return $a[0] . '/' . $a[1] . ($a[2] ? '*' : ''); |
||
| 198 | }; |
||
| 199 | |||
| 200 | $agg2 = function ($a, $b) { |
||
| 201 | if ($a === null && $b === null) return null; |
||
| 202 | return [ |
||
| 203 | $a[0] + $b[0], |
||
| 204 | $b[1], |
||
| 205 | $a[2] || $b[2] |
||
| 206 | ]; |
||
| 207 | }; |
||
| 208 | |||
| 209 | $addInfo = D::intvg([ |
||
| 210 | D::intv(0, 3), |
||
| 211 | D::intv(0, 2, [1, 3, false]), |
||
| 212 | D::intv(1, 3, [2, 5, true]), |
||
| 213 | ]) |
||
| 214 | ->setValueToString($toString2) |
||
| 215 | ->setValueToNumeric($toNumeric) |
||
| 216 | ->setAggregate($agg2); |
||
| 217 | ?> |
||
| 218 | |||
| 219 | <h2>Passing additional information</h2> |
||
| 220 | <p> |
||
| 221 | You can take advantage of the closures and templates to display additional information on the graph. |
||
| 222 | Here for example, interval values hold a boolean. Depending on the boolean, an asterisk is added to the string, |
||
| 223 | and a class is set on the corresponding bars. |
||
| 224 | <?= $addInfo ?> |
||
| 225 | </p> |
||
| 226 | |||
| 227 | <p> |
||
| 228 | A bunch of random graphs, this time generated through JavaScript: |
||
| 229 | <br> |
||
| 230 | </p> |
||
| 231 | <div id="random"></div> |
||
| 232 | <script> |
||
| 233 | 'use strict'; |
||
| 234 | const graphs = JSON.parse('<?= json_encode($intvGraphs) ?>'); |
||
| 235 | const el = document.getElementById('random'); |
||
| 236 | |||
| 237 | try { |
||
| 238 | graphs.forEach(function (graph) { |
||
| 239 | let html = document.createRange().createContextualFragment(intvg(graph)); |
||
| 240 | el.appendChild(html); |
||
| 241 | }); |
||
| 242 | } catch (e) { |
||
| 243 | el.innerHTML = 'The JavaScript function uses ES6 string literals. Sorry not sorry, IE.'; |
||
| 244 | } |
||
| 245 | </script> |
||
| 246 | </body> |
||
| 247 | </html> |