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