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