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\Database; 21use Fisharebest\Webtrees\I18N; 22use Fisharebest\Webtrees\Module\ModuleThemeInterface; 23use Fisharebest\Webtrees\Statistics\Helper\Country; 24use Fisharebest\Webtrees\Statistics\AbstractGoogle; 25use Fisharebest\Webtrees\Statistics\Repository\PlaceRepository; 26use Fisharebest\Webtrees\Statistics\Repository\IndividualRepository; 27use Fisharebest\Webtrees\Tree; 28 29/** 30 * Create a chart showing where events occurred. 31 */ 32class ChartDistribution extends AbstractGoogle 33{ 34 /** 35 * @var Tree 36 */ 37 private $tree; 38 39 /** 40 * @var Country 41 */ 42 private $countryHelper; 43 44 /** 45 * @var IndividualRepository 46 */ 47 private $individualRepository; 48 49 /** 50 * @var PlaceRepository 51 */ 52 private $placeRepository; 53 54 /** 55 * Constructor. 56 * 57 * @param Tree $tree 58 */ 59 public function __construct(Tree $tree) 60 { 61 $this->tree = $tree; 62 $this->countryHelper = new Country(); 63 $this->individualRepository = new IndividualRepository($tree); 64 $this->placeRepository = new PlaceRepository($tree); 65 } 66 67 /** 68 * Create a chart showing where events occurred. 69 * 70 * @param int $tot_pl The total number of places 71 * @param string $chart_shows 72 * @param string $chart_type 73 * @param string $surname 74 * 75 * @return string 76 */ 77 public function chartDistribution( 78 int $tot_pl, 79 string $chart_shows = 'world', 80 string $chart_type = '', 81 string $surname = '' 82 ): string { 83 $chart_color1 = app()->make(ModuleThemeInterface::class)->parameter('distribution-chart-no-values'); 84 $chart_color2 = app()->make(ModuleThemeInterface::class)->parameter('distribution-chart-high-values'); 85 $chart_color3 = app()->make(ModuleThemeInterface::class)->parameter('distribution-chart-low-values'); 86 $map_x = app()->make(ModuleThemeInterface::class)->parameter('distribution-chart-x'); 87 $map_y = app()->make(ModuleThemeInterface::class)->parameter('distribution-chart-y'); 88 89 if ($tot_pl === 0) { 90 return ''; 91 } 92 93 $countries = $this->countryHelper->getAllCountries(); 94 95 // Get the country names for each language 96 $country_to_iso3166 = []; 97 foreach (I18N::activeLocales() as $locale) { 98 I18N::init($locale->languageTag()); 99 100 foreach ($this->countryHelper->iso3166() as $three => $two) { 101 $country_to_iso3166[$three] = $two; 102 $country_to_iso3166[$countries[$three]] = $two; 103 } 104 } 105 106 I18N::init(WT_LOCALE); 107 108 switch ($chart_type) { 109 case 'surname_distribution_chart': 110 if ($surname === '') { 111 $surname = $this->individualRepository->getCommonSurname(); 112 } 113 $chart_title = I18N::translate('Surname distribution chart') . ': ' . $surname; 114 // Count how many people are events in each country 115 $surn_countries = []; 116 117 $rows = Database::prepare( 118 '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 )->execute([ 120 'tree_id' => $this->tree->id(), 121 'collate' => I18N::collation(), 122 'surname' => $surname, 123 ])->fetchAll(); 124 125 foreach ($rows as $row) { 126 if (preg_match_all('/^2 PLAC (?:.*, *)*(.*)/m', $row->i_gedcom, $matches)) { 127 // webtrees uses 3 letter country codes and localised country names, 128 // but google uses 2 letter codes. 129 foreach ($matches[1] as $country) { 130 if (\array_key_exists($country, $country_to_iso3166)) { 131 if (\array_key_exists($country_to_iso3166[$country], $surn_countries)) { 132 $surn_countries[$country_to_iso3166[$country]]++; 133 } else { 134 $surn_countries[$country_to_iso3166[$country]] = 1; 135 } 136 } 137 } 138 } 139 } 140 141 break; 142 143 case 'birth_distribution_chart': 144 $chart_title = I18N::translate('Birth by country'); 145 // Count how many people were born in each country 146 $surn_countries = []; 147 $b_countries = $this->placeRepository->statsPlaces('INDI', 'BIRT', 0, true); 148 foreach ($b_countries as $place => $count) { 149 $country = $place; 150 if (\array_key_exists($country, $country_to_iso3166)) { 151 if (!isset($surn_countries[$country_to_iso3166[$country]])) { 152 $surn_countries[$country_to_iso3166[$country]] = $count; 153 } else { 154 $surn_countries[$country_to_iso3166[$country]] += $count; 155 } 156 } 157 } 158 break; 159 160 case 'death_distribution_chart': 161 $chart_title = I18N::translate('Death by country'); 162 // Count how many people were death in each country 163 $surn_countries = []; 164 $d_countries = $this->placeRepository->statsPlaces('INDI', 'DEAT', 0, true); 165 foreach ($d_countries as $place => $count) { 166 $country = $place; 167 if (\array_key_exists($country, $country_to_iso3166)) { 168 if (!isset($surn_countries[$country_to_iso3166[$country]])) { 169 $surn_countries[$country_to_iso3166[$country]] = $count; 170 } else { 171 $surn_countries[$country_to_iso3166[$country]] += $count; 172 } 173 } 174 } 175 break; 176 177 case 'marriage_distribution_chart': 178 $chart_title = I18N::translate('Marriage by country'); 179 // Count how many families got marriage in each country 180 $surn_countries = []; 181 $m_countries = $this->placeRepository->statsPlaces('FAM'); 182 // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes. 183 foreach ($m_countries as $place) { 184 $country = $place->country; 185 if (\array_key_exists($country, $country_to_iso3166)) { 186 if (!isset($surn_countries[$country_to_iso3166[$country]])) { 187 $surn_countries[$country_to_iso3166[$country]] = $place->tot; 188 } else { 189 $surn_countries[$country_to_iso3166[$country]] += $place->tot; 190 } 191 } 192 } 193 break; 194 195 case 'indi_distribution_chart': 196 default: 197 $chart_title = I18N::translate('Individual distribution chart'); 198 // Count how many people have events in each country 199 $surn_countries = []; 200 $a_countries = $this->placeRepository->statsPlaces('INDI'); 201 // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes. 202 foreach ($a_countries as $place) { 203 $country = $place->country; 204 if (\array_key_exists($country, $country_to_iso3166)) { 205 if (!isset($surn_countries[$country_to_iso3166[$country]])) { 206 $surn_countries[$country_to_iso3166[$country]] = $place->tot; 207 } else { 208 $surn_countries[$country_to_iso3166[$country]] += $place->tot; 209 } 210 } 211 } 212 break; 213 } 214 215 $chart_url = 'https://chart.googleapis.com/chart?cht=t&chtm=' . $chart_shows; 216 $chart_url .= '&chco=' . $chart_color1 . ',' . $chart_color3 . ',' . $chart_color2; // country colours 217 $chart_url .= '&chf=bg,s,ECF5FF'; // sea colour 218 $chart_url .= '&chs=' . $map_x . 'x' . $map_y; 219 $chart_url .= '&chld=' . implode('', array_keys($surn_countries)) . '&chd=s:'; 220 221 foreach ($surn_countries as $count) { 222 $chart_url .= substr(self::GOOGLE_CHART_ENCODING, (int) ($count / max($surn_countries) * 61), 1); 223 } 224 225 return view( 226 'statistics/other/chart-distribution', 227 [ 228 'chart_title' => $chart_title, 229 'chart_url' => $chart_url, 230 'chart_color1' => $chart_color1, 231 'chart_color2' => $chart_color2, 232 'chart_color3' => $chart_color3, 233 ] 234 ); 235 } 236} 237