xref: /webtrees/app/Location.php (revision 0ea23b7829d8bd38dc862b61285e14b0e1858a16)
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                    ]) : $this->record->pl_lati;
89            default:
90                return $this->record->pl_lati;
91        }
92    }
93
94    /**
95     * @param string $format
96     *
97     * @return string|float
98     */
99    public function getLon($format = 'signed')
100    {
101        switch ($format) {
102            case 'signed':
103                return $this->record->pl_long ?
104                    (float) strtr($this->record->pl_long, [
105                        'E' => '',
106                        'W' => '-',
107                        ',' => '.',
108                    ]) : $this->record->pl_long;
109            default:
110                return $this->record->pl_long;
111        }
112    }
113
114    /**
115     * @return array
116     */
117    public function getLatLonJSArray(): array
118    {
119        return [
120            $this->getLat('signed'),
121            $this->getLon('signed'),
122        ];
123    }
124
125    /**
126     * GeoJSON requires the parameters to be in the order longitude, latitude
127     *
128     * @return array
129     */
130    public function getGeoJsonCoords(): array
131    {
132        return [
133            $this->getLon('signed'),
134            $this->getLat('signed'),
135        ];
136    }
137
138    /**
139     * @return string
140     */
141    public function getId(): string
142    {
143        return $this->record->pl_id;
144    }
145
146    /**
147     * @return string
148     */
149    public function getLevel(): string
150    {
151        return $this->record->pl_level;
152    }
153
154    /**
155     * @return bool
156     */
157    public function isValid(): bool
158    {
159        return $this->record->pl_id !== 0;
160    }
161
162    /**
163     * @return string
164     */
165    public function getPlace(): string
166    {
167        return $this->record->pl_place;
168    }
169
170    /**
171     * @return string|null
172     */
173    public function getZoom()
174    {
175        return $this->record->pl_zoom;
176    }
177
178    /**
179     * @return string|null
180     */
181    public function getIcon()
182    {
183        return $this->record->pl_icon;
184    }
185
186    /**
187     * @return stdClass
188     */
189    public function getRecord(): stdClass
190    {
191        return $this->record;
192    }
193
194    /**
195     * @return int
196     * @throws Exception
197     */
198    public function add()
199    {
200        $this->record->pl_id = Database::prepare("SELECT IFNULL(MAX(pl_id)+1, 1) FROM `##placelocation`")
201            ->execute()
202            ->fetchOne();
203
204        Database::prepare(
205            "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)"
206        )->execute(
207            [
208                'id'        => $this->record->pl_id,
209                'parent_id' => $this->record->pl_parent_id,
210                'level'     => $this->record->pl_level,
211                'place'     => $this->record->pl_place,
212                'long'      => $this->record->pl_long ?? null,
213                'lati'      => $this->record->pl_lati ?? null,
214                'zoom'      => $this->record->pl_zoom ?? null,
215                'icon'      => $this->record->pl_icon ?? null,
216            ]
217        );
218
219        return $this->record->pl_id;
220    }
221
222    /**
223     * @param stdClass $new_data
224     *
225     * @throws Exception
226     */
227    public function update(stdClass $new_data)
228    {
229        Database::prepare(
230            "UPDATE `##placelocation` SET pl_lati=:lati, pl_long=:long, pl_zoom=:zoom, pl_icon=:icon WHERE pl_id=:id"
231        )
232            ->execute([
233                'lati' => $new_data->pl_lati ?? $this->record->pl_lati,
234                'long' => $new_data->pl_long ?? $this->record->pl_long,
235                'zoom' => $new_data->pl_zoom ?? $this->record->pl_zoom,
236                'icon' => $new_data->pl_icon ?? $this->record->pl_icon,
237                'id'   => $this->record->pl_id,
238            ]);
239    }
240
241    /**
242     * @param string $gedcomName
243     *
244     * @return null|stdClass
245     * @throws Exception
246     */
247    private function getRecordFromName($gedcomName)
248    {
249
250        return Database::prepare(
251            "
252				SELECT
253				  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,
254					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
255				FROM `##placelocation` AS t1
256				LEFT JOIN `##placelocation` AS t2 ON t1.pl_parent_id = t2.pl_id
257				LEFT JOIN `##placelocation` AS t3 ON t2.pl_parent_id = t3.pl_id
258				LEFT JOIN `##placelocation` AS t4 ON t3.pl_parent_id = t4.pl_id
259				LEFT JOIN `##placelocation` AS t5 ON t4.pl_parent_id = t5.pl_id
260				LEFT JOIN `##placelocation` AS t6 ON t5.pl_parent_id = t6.pl_id
261				LEFT JOIN `##placelocation` AS t7 ON t6.pl_parent_id = t7.pl_id
262				LEFT JOIN `##placelocation` AS t8 ON t7.pl_parent_id = t8.pl_id
263				HAVING fqpn=:gedcomName;
264			   "
265        )
266            ->execute(
267                [
268                    'separator'  => Place::GEDCOM_SEPARATOR,
269                    'gedcomName' => $gedcomName,
270                ]
271            )
272            ->fetchOneRow();
273    }
274}
275