1<?php 2/** 3 * webtrees: online genealogy 4 * Copyright (C) 2018 webtrees development team 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16declare(strict_types=1); 17 18namespace Fisharebest\Webtrees\Statistics\Google; 19 20use Fisharebest\Webtrees\I18N; 21use Fisharebest\Webtrees\Statistics\AbstractGoogle; 22use Fisharebest\Webtrees\Statistics\Helper\Century; 23use Fisharebest\Webtrees\Tree; 24use Illuminate\Database\Capsule\Manager as DB; 25use Illuminate\Database\Query\JoinClause; 26 27/** 28 * 29 */ 30class ChartAge extends AbstractGoogle 31{ 32 /** 33 * @var Tree 34 */ 35 private $tree; 36 37 /** 38 * @var Century 39 */ 40 private $centuryHelper; 41 42 /** 43 * Constructor. 44 * 45 * @param Tree $tree 46 */ 47 public function __construct(Tree $tree) 48 { 49 $this->tree = $tree; 50 $this->centuryHelper = new Century(); 51 } 52 53 /** 54 * Returns the related database records. 55 * 56 * @return \stdClass[] 57 */ 58 private function queryRecords(): array 59 { 60 $prefix = DB::connection()->getTablePrefix(); 61 62 return DB::table('individuals') 63 ->join('dates AS birth', function (JoinClause $join): void { 64 $join 65 ->on('birth.d_file', '=', 'i_file') 66 ->on('birth.d_gid', '=', 'i_id'); 67 }) 68 ->join('dates AS death', function (JoinClause $join): void { 69 $join 70 ->on('death.d_file', '=', 'i_file') 71 ->on('death.d_gid', '=', 'i_id'); 72 }) 73 ->where('i_file', '=', $this->tree->id()) 74 ->where('birth.d_fact', '=', 'BIRT') 75 ->where('death.d_fact', '=', 'DEAT') 76 ->whereIn('birth.d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 77 ->whereIn('death.d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 78 ->whereColumn('death.d_julianday1', '>=', 'birth.d_julianday2') 79 ->where('birth.d_julianday2', '<>', 0) 80 ->select([ 81 DB::raw('ROUND(AVG(' . $prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1) / 365.25,1) AS age'), 82 DB::raw('ROUND((' . $prefix . 'death.d_year - 50) / 100) AS century'), 83 'i_sex AS sex' 84 ]) 85 ->groupBy(['century', 'sex']) 86 ->orderBy('century') 87 ->orderBy('sex') 88 ->get() 89 ->all(); 90 } 91 92 /** 93 * General query on ages. 94 * 95 * @param string $size 96 * 97 * @return string 98 */ 99 public function chartAge(string $size = '230x250'): string 100 { 101 $sizes = explode('x', $size); 102 $rows = $this->queryRecords(); 103 104 if (empty($rows)) { 105 return ''; 106 } 107 108 $chxl = '0:|'; 109 $countsm = ''; 110 $countsf = ''; 111 $countsa = ''; 112 $out = []; 113 114 foreach ($rows as $values) { 115 $out[(int) $values->century][$values->sex] = $values->age; 116 } 117 118 foreach ($out as $century => $values) { 119 if ($sizes[0] < 980) { 120 $sizes[0] += 50; 121 } 122 $chxl .= $this->centuryHelper->centuryName($century) . '|'; 123 124 $female_age = $values['F'] ?? 0; 125 $male_age = $values['M'] ?? 0; 126 $average_age = $female_age + $male_age; 127 128 if ($female_age > 0 && $male_age > 0) { 129 $average_age /= 2.0; 130 } 131 132 $countsf .= $female_age . ','; 133 $countsm .= $male_age . ','; 134 $countsa .= $average_age . ','; 135 } 136 137 $countsm = substr($countsm, 0, -1); 138 $countsf = substr($countsf, 0, -1); 139 $countsa = substr($countsa, 0, -1); 140 $chd = 't2:' . $countsm . '|' . $countsf . '|' . $countsa; 141 $decades = ''; 142 143 for ($i = 0; $i <= 100; $i += 10) { 144 $decades .= '|' . I18N::number($i); 145 } 146 147 $chxl .= '1:||' . I18N::translate('century') . '|2:' . $decades . '|3:||' . I18N::translate('Age') . '|'; 148 $title = I18N::translate('Average age related to death century'); 149 150 if (\count($rows) > 6 || mb_strlen($title) < 30) { 151 $chtt = $title; 152 } else { 153 $offset = 0; 154 $counter = []; 155 156 while ($offset = strpos($title, ' ', $offset + 1)) { 157 $counter[] = $offset; 158 } 159 160 $half = intdiv(\count($counter), 2); 161 $chtt = substr_replace($title, '|', $counter[$half], 1); 162 } 163 164 $chart_url = 'https://chart.googleapis.com/chart?cht=bvg&chs=' . $sizes[0] . 'x' . $sizes[1] 165 . '&chm=D,FF0000,2,0,3,1|N*f1*,000000,0,-1,11,1|N*f1*,000000,1,-1,11,1&chf=bg,s,ffffff00|c,s,ffffff00&chtt=' 166 . rawurlencode($chtt) . '&chd=' . $chd . '&chco=0000FF,FFA0CB,FF0000&chbh=20,3&chxt=x,x,y,y&chxl=' 167 . rawurlencode($chxl) . '&chdl=' 168 . rawurlencode(I18N::translate('Males') . '|' . I18N::translate('Females') . '|' . I18N::translate('Average age at death')); 169 170 return view( 171 'statistics/other/chart-google', 172 [ 173 'chart_title' => I18N::translate('Average age related to death century'), 174 'chart_url' => $chart_url, 175 'sizes' => $sizes, 176 ] 177 ); 178 } 179} 180