1*8add1155SRico Sonntag<?php 2*8add1155SRico Sonntag/** 3*8add1155SRico Sonntag * webtrees: online genealogy 4*8add1155SRico Sonntag * Copyright (C) 2018 webtrees development team 5*8add1155SRico Sonntag * This program is free software: you can redistribute it and/or modify 6*8add1155SRico Sonntag * it under the terms of the GNU General Public License as published by 7*8add1155SRico Sonntag * the Free Software Foundation, either version 3 of the License, or 8*8add1155SRico Sonntag * (at your option) any later version. 9*8add1155SRico Sonntag * This program is distributed in the hope that it will be useful, 10*8add1155SRico Sonntag * but WITHOUT ANY WARRANTY; without even the implied warranty of 11*8add1155SRico Sonntag * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12*8add1155SRico Sonntag * GNU General Public License for more details. 13*8add1155SRico Sonntag * You should have received a copy of the GNU General Public License 14*8add1155SRico Sonntag * along with this program. If not, see <http://www.gnu.org/licenses/>. 15*8add1155SRico Sonntag */ 16*8add1155SRico Sonntagdeclare(strict_types=1); 17*8add1155SRico Sonntag 18*8add1155SRico Sonntagnamespace Fisharebest\Webtrees\Statistics\Google; 19*8add1155SRico Sonntag 20*8add1155SRico Sonntaguse Fisharebest\Webtrees\Database; 21*8add1155SRico Sonntaguse Fisharebest\Webtrees\I18N; 22*8add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Helper\Country; 23*8add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\AbstractGoogle; 24*8add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Repository\PlaceRepository; 25*8add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Repository\IndividualRepository; 26*8add1155SRico Sonntaguse Fisharebest\Webtrees\Theme; 27*8add1155SRico Sonntaguse Fisharebest\Webtrees\Tree; 28*8add1155SRico Sonntag 29*8add1155SRico Sonntag/** 30*8add1155SRico Sonntag * Create a chart showing where events occurred. 31*8add1155SRico Sonntag */ 32*8add1155SRico Sonntagclass ChartDistribution extends AbstractGoogle 33*8add1155SRico Sonntag{ 34*8add1155SRico Sonntag /** 35*8add1155SRico Sonntag * @var Tree 36*8add1155SRico Sonntag */ 37*8add1155SRico Sonntag private $tree; 38*8add1155SRico Sonntag 39*8add1155SRico Sonntag /** 40*8add1155SRico Sonntag * @var Country 41*8add1155SRico Sonntag */ 42*8add1155SRico Sonntag private $countryHelper; 43*8add1155SRico Sonntag 44*8add1155SRico Sonntag /** 45*8add1155SRico Sonntag * @var IndividualRepository 46*8add1155SRico Sonntag */ 47*8add1155SRico Sonntag private $individualRepository; 48*8add1155SRico Sonntag 49*8add1155SRico Sonntag /** 50*8add1155SRico Sonntag * @var PlaceRepository 51*8add1155SRico Sonntag */ 52*8add1155SRico Sonntag private $placeRepository; 53*8add1155SRico Sonntag 54*8add1155SRico Sonntag /** 55*8add1155SRico Sonntag * Constructor. 56*8add1155SRico Sonntag * 57*8add1155SRico Sonntag * @param Tree $tree 58*8add1155SRico Sonntag */ 59*8add1155SRico Sonntag public function __construct(Tree $tree) 60*8add1155SRico Sonntag { 61*8add1155SRico Sonntag $this->tree = $tree; 62*8add1155SRico Sonntag $this->countryHelper = new Country(); 63*8add1155SRico Sonntag $this->individualRepository = new IndividualRepository($tree); 64*8add1155SRico Sonntag $this->placeRepository = new PlaceRepository($tree); 65*8add1155SRico Sonntag } 66*8add1155SRico Sonntag 67*8add1155SRico Sonntag /** 68*8add1155SRico Sonntag * Create a chart showing where events occurred. 69*8add1155SRico Sonntag * 70*8add1155SRico Sonntag * @param int $tot_pl The total number of places 71*8add1155SRico Sonntag * @param string $chart_shows 72*8add1155SRico Sonntag * @param string $chart_type 73*8add1155SRico Sonntag * @param string $surname 74*8add1155SRico Sonntag * 75*8add1155SRico Sonntag * @return string 76*8add1155SRico Sonntag */ 77*8add1155SRico Sonntag public function chartDistribution( 78*8add1155SRico Sonntag int $tot_pl, 79*8add1155SRico Sonntag string $chart_shows = 'world', 80*8add1155SRico Sonntag string $chart_type = '', 81*8add1155SRico Sonntag string $surname = '' 82*8add1155SRico Sonntag ): string { 83*8add1155SRico Sonntag $chart_color1 = Theme::theme()->parameter('distribution-chart-no-values'); 84*8add1155SRico Sonntag $chart_color2 = Theme::theme()->parameter('distribution-chart-high-values'); 85*8add1155SRico Sonntag $chart_color3 = Theme::theme()->parameter('distribution-chart-low-values'); 86*8add1155SRico Sonntag $map_x = Theme::theme()->parameter('distribution-chart-x'); 87*8add1155SRico Sonntag $map_y = Theme::theme()->parameter('distribution-chart-y'); 88*8add1155SRico Sonntag 89*8add1155SRico Sonntag if ($tot_pl === 0) { 90*8add1155SRico Sonntag return ''; 91*8add1155SRico Sonntag } 92*8add1155SRico Sonntag 93*8add1155SRico Sonntag $countries = $this->countryHelper->getAllCountries(); 94*8add1155SRico Sonntag 95*8add1155SRico Sonntag // Get the country names for each language 96*8add1155SRico Sonntag $country_to_iso3166 = []; 97*8add1155SRico Sonntag foreach (I18N::activeLocales() as $locale) { 98*8add1155SRico Sonntag I18N::init($locale->languageTag()); 99*8add1155SRico Sonntag 100*8add1155SRico Sonntag foreach ($this->countryHelper->iso3166() as $three => $two) { 101*8add1155SRico Sonntag $country_to_iso3166[$three] = $two; 102*8add1155SRico Sonntag $country_to_iso3166[$countries[$three]] = $two; 103*8add1155SRico Sonntag } 104*8add1155SRico Sonntag } 105*8add1155SRico Sonntag 106*8add1155SRico Sonntag I18N::init(WT_LOCALE); 107*8add1155SRico Sonntag 108*8add1155SRico Sonntag switch ($chart_type) { 109*8add1155SRico Sonntag case 'surname_distribution_chart': 110*8add1155SRico Sonntag if ($surname === '') { 111*8add1155SRico Sonntag $surname = $this->individualRepository->getCommonSurname(); 112*8add1155SRico Sonntag } 113*8add1155SRico Sonntag $chart_title = I18N::translate('Surname distribution chart') . ': ' . $surname; 114*8add1155SRico Sonntag // Count how many people are events in each country 115*8add1155SRico Sonntag $surn_countries = []; 116*8add1155SRico Sonntag 117*8add1155SRico Sonntag $rows = Database::prepare( 118*8add1155SRico Sonntag 'SELECT i_gedcom' . ' FROM `##individuals`' . ' JOIN `##name` ON n_id = i_id AND n_file = i_file' . ' WHERE n_file = :tree_id' . ' AND n_surn COLLATE :collate = :surname' 119*8add1155SRico Sonntag )->execute([ 120*8add1155SRico Sonntag 'tree_id' => $this->tree->id(), 121*8add1155SRico Sonntag 'collate' => I18N::collation(), 122*8add1155SRico Sonntag 'surname' => $surname, 123*8add1155SRico Sonntag ])->fetchAll(); 124*8add1155SRico Sonntag 125*8add1155SRico Sonntag foreach ($rows as $row) { 126*8add1155SRico Sonntag if (preg_match_all('/^2 PLAC (?:.*, *)*(.*)/m', $row->i_gedcom, $matches)) { 127*8add1155SRico Sonntag // webtrees uses 3 letter country codes and localised country names, 128*8add1155SRico Sonntag // but google uses 2 letter codes. 129*8add1155SRico Sonntag foreach ($matches[1] as $country) { 130*8add1155SRico Sonntag if (\array_key_exists($country, $country_to_iso3166)) { 131*8add1155SRico Sonntag if (\array_key_exists($country_to_iso3166[$country], $surn_countries)) { 132*8add1155SRico Sonntag $surn_countries[$country_to_iso3166[$country]]++; 133*8add1155SRico Sonntag } else { 134*8add1155SRico Sonntag $surn_countries[$country_to_iso3166[$country]] = 1; 135*8add1155SRico Sonntag } 136*8add1155SRico Sonntag } 137*8add1155SRico Sonntag } 138*8add1155SRico Sonntag } 139*8add1155SRico Sonntag } 140*8add1155SRico Sonntag 141*8add1155SRico Sonntag break; 142*8add1155SRico Sonntag 143*8add1155SRico Sonntag case 'birth_distribution_chart': 144*8add1155SRico Sonntag $chart_title = I18N::translate('Birth by country'); 145*8add1155SRico Sonntag // Count how many people were born in each country 146*8add1155SRico Sonntag $surn_countries = []; 147*8add1155SRico Sonntag $b_countries = $this->placeRepository->statsPlaces('INDI', 'BIRT', 0, true); 148*8add1155SRico Sonntag foreach ($b_countries as $place => $count) { 149*8add1155SRico Sonntag $country = $place; 150*8add1155SRico Sonntag if (\array_key_exists($country, $country_to_iso3166)) { 151*8add1155SRico Sonntag if (!isset($surn_countries[$country_to_iso3166[$country]])) { 152*8add1155SRico Sonntag $surn_countries[$country_to_iso3166[$country]] = $count; 153*8add1155SRico Sonntag } else { 154*8add1155SRico Sonntag $surn_countries[$country_to_iso3166[$country]] += $count; 155*8add1155SRico Sonntag } 156*8add1155SRico Sonntag } 157*8add1155SRico Sonntag } 158*8add1155SRico Sonntag break; 159*8add1155SRico Sonntag 160*8add1155SRico Sonntag case 'death_distribution_chart': 161*8add1155SRico Sonntag $chart_title = I18N::translate('Death by country'); 162*8add1155SRico Sonntag // Count how many people were death in each country 163*8add1155SRico Sonntag $surn_countries = []; 164*8add1155SRico Sonntag $d_countries = $this->placeRepository->statsPlaces('INDI', 'DEAT', 0, true); 165*8add1155SRico Sonntag foreach ($d_countries as $place => $count) { 166*8add1155SRico Sonntag $country = $place; 167*8add1155SRico Sonntag if (\array_key_exists($country, $country_to_iso3166)) { 168*8add1155SRico Sonntag if (!isset($surn_countries[$country_to_iso3166[$country]])) { 169*8add1155SRico Sonntag $surn_countries[$country_to_iso3166[$country]] = $count; 170*8add1155SRico Sonntag } else { 171*8add1155SRico Sonntag $surn_countries[$country_to_iso3166[$country]] += $count; 172*8add1155SRico Sonntag } 173*8add1155SRico Sonntag } 174*8add1155SRico Sonntag } 175*8add1155SRico Sonntag break; 176*8add1155SRico Sonntag 177*8add1155SRico Sonntag case 'marriage_distribution_chart': 178*8add1155SRico Sonntag $chart_title = I18N::translate('Marriage by country'); 179*8add1155SRico Sonntag // Count how many families got marriage in each country 180*8add1155SRico Sonntag $surn_countries = []; 181*8add1155SRico Sonntag $m_countries = $this->placeRepository->statsPlaces('FAM'); 182*8add1155SRico Sonntag // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes. 183*8add1155SRico Sonntag foreach ($m_countries as $place) { 184*8add1155SRico Sonntag $country = $place->country; 185*8add1155SRico Sonntag if (\array_key_exists($country, $country_to_iso3166)) { 186*8add1155SRico Sonntag if (!isset($surn_countries[$country_to_iso3166[$country]])) { 187*8add1155SRico Sonntag $surn_countries[$country_to_iso3166[$country]] = $place->tot; 188*8add1155SRico Sonntag } else { 189*8add1155SRico Sonntag $surn_countries[$country_to_iso3166[$country]] += $place->tot; 190*8add1155SRico Sonntag } 191*8add1155SRico Sonntag } 192*8add1155SRico Sonntag } 193*8add1155SRico Sonntag break; 194*8add1155SRico Sonntag 195*8add1155SRico Sonntag case 'indi_distribution_chart': 196*8add1155SRico Sonntag default: 197*8add1155SRico Sonntag $chart_title = I18N::translate('Individual distribution chart'); 198*8add1155SRico Sonntag // Count how many people have events in each country 199*8add1155SRico Sonntag $surn_countries = []; 200*8add1155SRico Sonntag $a_countries = $this->placeRepository->statsPlaces('INDI'); 201*8add1155SRico Sonntag // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes. 202*8add1155SRico Sonntag foreach ($a_countries as $place) { 203*8add1155SRico Sonntag $country = $place->country; 204*8add1155SRico Sonntag if (\array_key_exists($country, $country_to_iso3166)) { 205*8add1155SRico Sonntag if (!isset($surn_countries[$country_to_iso3166[$country]])) { 206*8add1155SRico Sonntag $surn_countries[$country_to_iso3166[$country]] = $place->tot; 207*8add1155SRico Sonntag } else { 208*8add1155SRico Sonntag $surn_countries[$country_to_iso3166[$country]] += $place->tot; 209*8add1155SRico Sonntag } 210*8add1155SRico Sonntag } 211*8add1155SRico Sonntag } 212*8add1155SRico Sonntag break; 213*8add1155SRico Sonntag } 214*8add1155SRico Sonntag 215*8add1155SRico Sonntag $chart_url = 'https://chart.googleapis.com/chart?cht=t&chtm=' . $chart_shows; 216*8add1155SRico Sonntag $chart_url .= '&chco=' . $chart_color1 . ',' . $chart_color3 . ',' . $chart_color2; // country colours 217*8add1155SRico Sonntag $chart_url .= '&chf=bg,s,ECF5FF'; // sea colour 218*8add1155SRico Sonntag $chart_url .= '&chs=' . $map_x . 'x' . $map_y; 219*8add1155SRico Sonntag $chart_url .= '&chld=' . implode('', array_keys($surn_countries)) . '&chd=s:'; 220*8add1155SRico Sonntag 221*8add1155SRico Sonntag foreach ($surn_countries as $count) { 222*8add1155SRico Sonntag $chart_url .= substr(self::GOOGLE_CHART_ENCODING, (int) ($count / max($surn_countries) * 61), 1); 223*8add1155SRico Sonntag } 224*8add1155SRico Sonntag 225*8add1155SRico Sonntag return view( 226*8add1155SRico Sonntag 'statistics/other/chart-distribution', 227*8add1155SRico Sonntag [ 228*8add1155SRico Sonntag 'chart_title' => $chart_title, 229*8add1155SRico Sonntag 'chart_url' => $chart_url, 230*8add1155SRico Sonntag 'chart_color1' => $chart_color1, 231*8add1155SRico Sonntag 'chart_color2' => $chart_color2, 232*8add1155SRico Sonntag 'chart_color3' => $chart_color3, 233*8add1155SRico Sonntag ] 234*8add1155SRico Sonntag ); 235*8add1155SRico Sonntag } 236*8add1155SRico Sonntag} 237