xref: /webtrees/app/Location.php (revision 8f53f488f13e53e44dc48778e8f51ec9f91352dd)
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     * @throws Exception
198     */
199    public function add()
200    {
201        $this->record->pl_id = Database::prepare("SELECT IFNULL(MAX(pl_id)+1, 1) FROM `##placelocation`")
202            ->execute()
203            ->fetchOne();
204
205        Database::prepare(
206            "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)"
207        )->execute(
208            [
209                'id'        => $this->record->pl_id,
210                'parent_id' => $this->record->pl_parent_id,
211                'level'     => $this->record->pl_level,
212                'place'     => $this->record->pl_place,
213                'long'      => $this->record->pl_long ?? null,
214                'lati'      => $this->record->pl_lati ?? null,
215                'zoom'      => $this->record->pl_zoom ?? null,
216                'icon'      => $this->record->pl_icon ?? null,
217            ]
218        );
219
220        return $this->record->pl_id;
221    }
222
223    /**
224     * @param stdClass $new_data
225     *
226     * @throws Exception
227     */
228    public function update(stdClass $new_data)
229    {
230        Database::prepare(
231            "UPDATE `##placelocation` SET pl_lati=:lati, pl_long=:long, pl_zoom=:zoom, pl_icon=:icon WHERE pl_id=:id"
232        )
233            ->execute([
234                'lati' => $new_data->pl_lati ?? $this->record->pl_lati,
235                'long' => $new_data->pl_long ?? $this->record->pl_long,
236                'zoom' => $new_data->pl_zoom ?? $this->record->pl_zoom,
237                'icon' => $new_data->pl_icon ?? $this->record->pl_icon,
238                'id'   => $this->record->pl_id,
239            ]);
240    }
241
242    /**
243     * @param string $gedcomName
244     *
245     * @return null|stdClass
246     * @throws Exception
247     */
248    private function getRecordFromName($gedcomName)
249    {
250
251        return Database::prepare(
252            "
253				SELECT
254				  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,
255					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
256				FROM `##placelocation` AS t1
257				LEFT JOIN `##placelocation` AS t2 ON t1.pl_parent_id = t2.pl_id
258				LEFT JOIN `##placelocation` AS t3 ON t2.pl_parent_id = t3.pl_id
259				LEFT JOIN `##placelocation` AS t4 ON t3.pl_parent_id = t4.pl_id
260				LEFT JOIN `##placelocation` AS t5 ON t4.pl_parent_id = t5.pl_id
261				LEFT JOIN `##placelocation` AS t6 ON t5.pl_parent_id = t6.pl_id
262				LEFT JOIN `##placelocation` AS t7 ON t6.pl_parent_id = t7.pl_id
263				LEFT JOIN `##placelocation` AS t8 ON t7.pl_parent_id = t8.pl_id
264				HAVING fqpn=:gedcomName;
265			   "
266        )
267            ->execute(
268                [
269                    'separator'  => Place::GEDCOM_SEPARATOR,
270                    'gedcomName' => $gedcomName,
271                ]
272            )
273            ->fetchOneRow();
274    }
275}
276