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; 19 20use Exception; 21use stdClass; 22 23/** 24 * Class Location 25 * 26 * @package Fisharebest\Webtrees 27 */ 28class Location 29{ 30 31 /** 32 * @var stdClass $record 33 */ 34 protected $record; 35 36 /** 37 * Location constructor. 38 * 39 * @param string $gedcomName 40 * @param array $record 41 * 42 * @throws Exception 43 */ 44 public function __construct($gedcomName, $record = []) 45 { 46 $tmp = $this->getRecordFromName($gedcomName); 47 if ($tmp !== null) { 48 $this->record = $tmp; 49 } elseif (!empty($record)) { 50 $this->record = (object)$record; 51 } else { 52 $this->record = (object)[ 53 'fqpn' => '', 54 'pl_id' => 0, 55 'pl_parent_id' => 0, 56 'pl_level' => null, 57 'pl_place' => '', 58 'pl_long' => null, 59 'pl_lati' => null, 60 'pl_zoom' => null, 61 'pl_icon' => null, 62 ]; 63 } 64 } 65 66 /** 67 * @return bool 68 */ 69 public function knownLatLon(): bool 70 { 71 return ($this->record->pl_lati && $this->record->pl_long); 72 } 73 74 /** 75 * @param string $format 76 * 77 * @return string|float 78 */ 79 public function getLat($format = 'signed') 80 { 81 switch ($format) { 82 case 'signed': 83 return $this->record->pl_lati ? 84 (float)strtr($this->record->pl_lati, [ 85 'N' => '', 86 'S' => '-', 87 ',' => '.', 88 ]) : 89 $this->record->pl_lati; 90 default: 91 return $this->record->pl_lati; 92 } 93 } 94 95 /** 96 * @param string $format 97 * 98 * @return string|float 99 */ 100 public function getLon($format = 'signed') 101 { 102 switch ($format) { 103 case 'signed': 104 return $this->record->pl_long ? 105 (float)strtr($this->record->pl_long, [ 106 'E' => '', 107 'W' => '-', 108 ',' => '.', 109 ]) : 110 $this->record->pl_long; 111 default: 112 return $this->record->pl_long; 113 } 114 } 115 116 /** 117 * @return array 118 */ 119 public function getLatLonJSArray(): array 120 { 121 return [ 122 $this->getLat('signed'), 123 $this->getLon('signed'), 124 ]; 125 } 126 127 /** 128 * GeoJSON requires the parameters to be in the order longitude, latitude 129 * 130 * @return array 131 */ 132 public function getGeoJsonCoords(): array 133 { 134 return [ 135 $this->getLon('signed'), 136 $this->getLat('signed'), 137 ]; 138 } 139 140 /** 141 * @return string 142 */ 143 public function getId(): string 144 { 145 return $this->record->pl_id; 146 } 147 148 /** 149 * @return string 150 */ 151 public function getLevel(): string 152 { 153 return $this->record->pl_level; 154 } 155 156 /** 157 * @return bool 158 */ 159 public function isValid(): bool 160 { 161 return $this->record->pl_id !== 0; 162 } 163 164 /** 165 * @return string 166 */ 167 public function getPlace(): string 168 { 169 return $this->record->pl_place; 170 } 171 172 /** 173 * @return string|null 174 */ 175 public function getZoom() 176 { 177 return $this->record->pl_zoom; 178 } 179 180 /** 181 * @return string|null 182 */ 183 public function getIcon() 184 { 185 return $this->record->pl_icon; 186 } 187 188 /** 189 * @return stdClass 190 */ 191 public function getRecord(): stdClass 192 { 193 return $this->record; 194 } 195 196 /** 197 * @return void 198 * @throws Exception 199 */ 200 public function add() 201 { 202 $this->record->pl_id = Database::prepare("SELECT IFNULL(MAX(pl_id)+1, 1) FROM `##placelocation`") 203 ->execute() 204 ->fetchOne(); 205 206 Database::prepare( 207 "INSERT INTO `##placelocation` (pl_id, pl_parent_id, pl_level, pl_place, pl_long, pl_lati, pl_zoom, pl_icon) VALUES(:id, :parent_id, :level, :place, :long, :lati, :zoom, :icon)" 208 )->execute( 209 [ 210 'id' => $this->record->pl_id, 211 'parent_id' => $this->record->pl_parent_id, 212 'level' => $this->record->pl_level, 213 'place' => $this->record->pl_place, 214 'long' => $this->record->pl_long ?? null, 215 'lati' => $this->record->pl_lati ?? null, 216 'zoom' => $this->record->pl_zoom ?? null, 217 'icon' => $this->record->pl_icon ?? null, 218 ] 219 ); 220 221 return $this->record->pl_id; 222 } 223 224 /** 225 * @param stdClass $new_data 226 * 227 * @throws Exception 228 */ 229 public function update(stdClass $new_data) 230 { 231 Database::prepare( 232 "UPDATE `##placelocation` SET pl_lati=:lati, pl_long=:long, pl_zoom=:zoom, pl_icon=:icon WHERE pl_id=:id" 233 ) 234 ->execute([ 235 'lati' => $new_data->pl_lati ?? $this->record->pl_lati, 236 'long' => $new_data->pl_long ?? $this->record->pl_long, 237 'zoom' => $new_data->pl_zoom ?? $this->record->pl_zoom, 238 'icon' => $new_data->pl_icon ?? $this->record->pl_icon, 239 'id' => $this->record->pl_id, 240 ]); 241 } 242 243 /** 244 * @param string $gedcomName 245 * 246 * @return null|stdClass 247 * @throws Exception 248 */ 249 private function getRecordFromName($gedcomName) 250 { 251 252 return Database::prepare( 253 " 254 SELECT 255 CONCAT_WS(:separator, t1.pl_place, t2.pl_place, t3.pl_place, t4.pl_place, t5.pl_place, t6.pl_place, t7.pl_place, t8.pl_place) AS fqpn, 256 t1.pl_level, t1.pl_place, t1.pl_id, t1.pl_parent_id, t1.pl_lati, t1.pl_long, t1.pl_zoom, t1.pl_icon 257 FROM `##placelocation` AS t1 258 LEFT JOIN `##placelocation` AS t2 ON t1.pl_parent_id = t2.pl_id 259 LEFT JOIN `##placelocation` AS t3 ON t2.pl_parent_id = t3.pl_id 260 LEFT JOIN `##placelocation` AS t4 ON t3.pl_parent_id = t4.pl_id 261 LEFT JOIN `##placelocation` AS t5 ON t4.pl_parent_id = t5.pl_id 262 LEFT JOIN `##placelocation` AS t6 ON t5.pl_parent_id = t6.pl_id 263 LEFT JOIN `##placelocation` AS t7 ON t6.pl_parent_id = t7.pl_id 264 LEFT JOIN `##placelocation` AS t8 ON t7.pl_parent_id = t8.pl_id 265 HAVING fqpn=:gedcomName; 266 " 267 ) 268 ->execute( 269 [ 270 'separator' => Place::GEDCOM_SEPARATOR, 271 'gedcomName' => $gedcomName, 272 ] 273 ) 274 ->fetchOneRow(); 275 } 276} 277