xref: /webtrees/app/PlaceLocation.php (revision 5333da530bbb05cbe9c1d8c809f8b090fc91365e)
1*5333da53SGreg Roach<?php
2*5333da53SGreg Roach
3*5333da53SGreg Roach/**
4*5333da53SGreg Roach * webtrees: online genealogy
5*5333da53SGreg Roach * Copyright (C) 2020 webtrees development team
6*5333da53SGreg Roach * This program is free software: you can redistribute it and/or modify
7*5333da53SGreg Roach * it under the terms of the GNU General Public License as published by
8*5333da53SGreg Roach * the Free Software Foundation, either version 3 of the License, or
9*5333da53SGreg Roach * (at your option) any later version.
10*5333da53SGreg Roach * This program is distributed in the hope that it will be useful,
11*5333da53SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
12*5333da53SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13*5333da53SGreg Roach * GNU General Public License for more details.
14*5333da53SGreg Roach * You should have received a copy of the GNU General Public License
15*5333da53SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
16*5333da53SGreg Roach */
17*5333da53SGreg Roach
18*5333da53SGreg Roachdeclare(strict_types=1);
19*5333da53SGreg Roach
20*5333da53SGreg Roachnamespace Fisharebest\Webtrees;
21*5333da53SGreg Roach
22*5333da53SGreg Roachuse Fisharebest\Webtrees\Services\GedcomService;
23*5333da53SGreg Roachuse Illuminate\Database\Capsule\Manager as DB;
24*5333da53SGreg Roachuse Illuminate\Support\Collection;
25*5333da53SGreg Roachuse stdClass;
26*5333da53SGreg Roach
27*5333da53SGreg Roach/**
28*5333da53SGreg Roach * Class PlaceLocation
29*5333da53SGreg Roach */
30*5333da53SGreg Roachclass PlaceLocation
31*5333da53SGreg Roach{
32*5333da53SGreg Roach    /** @var string e.g. "Westminster, London, England" */
33*5333da53SGreg Roach    private $location_name;
34*5333da53SGreg Roach
35*5333da53SGreg Roach    /** @var Collection|string[] The parts of a location name, e.g. ["Westminster", "London", "England"] */
36*5333da53SGreg Roach    private $parts;
37*5333da53SGreg Roach
38*5333da53SGreg Roach    /**
39*5333da53SGreg Roach     * Create a place-location.
40*5333da53SGreg Roach     *
41*5333da53SGreg Roach     * @param string $location_name
42*5333da53SGreg Roach     */
43*5333da53SGreg Roach    public function __construct(string $location_name)
44*5333da53SGreg Roach    {
45*5333da53SGreg Roach        // Ignore any empty parts in location names such as "Village, , , Country".
46*5333da53SGreg Roach        $this->parts = (new Collection(preg_split(Gedcom::PLACE_SEPARATOR_REGEX, $location_name)))
47*5333da53SGreg Roach            ->filter();
48*5333da53SGreg Roach
49*5333da53SGreg Roach        // Rebuild the location name in the correct format.
50*5333da53SGreg Roach        $this->location_name = $this->parts->implode(Gedcom::PLACE_SEPARATOR);
51*5333da53SGreg Roach    }
52*5333da53SGreg Roach
53*5333da53SGreg Roach    /**
54*5333da53SGreg Roach     * Get the higher level location.
55*5333da53SGreg Roach     *
56*5333da53SGreg Roach     * @return PlaceLocation
57*5333da53SGreg Roach     */
58*5333da53SGreg Roach    public function parent(): PlaceLocation
59*5333da53SGreg Roach    {
60*5333da53SGreg Roach        return new self($this->parts->slice(1)->implode(Gedcom::PLACE_SEPARATOR));
61*5333da53SGreg Roach    }
62*5333da53SGreg Roach
63*5333da53SGreg Roach    /**
64*5333da53SGreg Roach     * The database row that contains this location.
65*5333da53SGreg Roach     * Note that due to database collation, both "Quebec" and "Québec" will share the same row.
66*5333da53SGreg Roach     *
67*5333da53SGreg Roach     * @return int
68*5333da53SGreg Roach     */
69*5333da53SGreg Roach    public function id(): int
70*5333da53SGreg Roach    {
71*5333da53SGreg Roach        return app('cache.array')->remember('location-' . $this->location_name, function () {
72*5333da53SGreg Roach            // The "top-level" location won't exist in the database.
73*5333da53SGreg Roach            if ($this->parts->isEmpty()) {
74*5333da53SGreg Roach                return 0;
75*5333da53SGreg Roach            }
76*5333da53SGreg Roach
77*5333da53SGreg Roach            $parent_location_id = $this->parent()->id();
78*5333da53SGreg Roach
79*5333da53SGreg Roach            $location_id = (int) DB::table('placelocation')
80*5333da53SGreg Roach                ->where('pl_place', '=', $this->parts->first())
81*5333da53SGreg Roach                ->where('pl_parent_id', '=', $parent_location_id)
82*5333da53SGreg Roach                ->value('pl_id');
83*5333da53SGreg Roach
84*5333da53SGreg Roach            if ($location_id === 0) {
85*5333da53SGreg Roach                $location = $this->parts->first();
86*5333da53SGreg Roach
87*5333da53SGreg Roach                $location_id = 1 + (int) DB::table('placelocation')->max('pl_id');
88*5333da53SGreg Roach
89*5333da53SGreg Roach                DB::table('placelocation')->insert([
90*5333da53SGreg Roach                    'pl_id'        => $location_id,
91*5333da53SGreg Roach                    'pl_place'     => $location,
92*5333da53SGreg Roach                    'pl_parent_id' => $parent_location_id,
93*5333da53SGreg Roach                    'pl_level'     => $this->parts->count() - 1,
94*5333da53SGreg Roach                    'pl_lati'      => '',
95*5333da53SGreg Roach                    'pl_long'      => '',
96*5333da53SGreg Roach                    'pl_icon'      => '',
97*5333da53SGreg Roach                    'pl_zoom'      => 2,
98*5333da53SGreg Roach                ]);
99*5333da53SGreg Roach            }
100*5333da53SGreg Roach
101*5333da53SGreg Roach            return $location_id;
102*5333da53SGreg Roach        });
103*5333da53SGreg Roach    }
104*5333da53SGreg Roach
105*5333da53SGreg Roach    /**
106*5333da53SGreg Roach     * Does this location exist in the database?  Note that calls to PlaceLocation::id() will
107*5333da53SGreg Roach     * create the row, so this function is only meaningful when called before a call to PlaceLocation::id().
108*5333da53SGreg Roach     *
109*5333da53SGreg Roach     * @return bool
110*5333da53SGreg Roach     */
111*5333da53SGreg Roach    public function exists(): bool
112*5333da53SGreg Roach    {
113*5333da53SGreg Roach        $location_id = 0;
114*5333da53SGreg Roach
115*5333da53SGreg Roach        $this->parts->reverse()->each(static function (string $place) use (&$location_id) {
116*5333da53SGreg Roach            if ($location_id !== null) {
117*5333da53SGreg Roach                $location_id = DB::table('placelocation')
118*5333da53SGreg Roach                    ->where('pl_parent_id', '=', $location_id)
119*5333da53SGreg Roach                    ->where('pl_place', '=', $place)
120*5333da53SGreg Roach                    ->value('pl_id');
121*5333da53SGreg Roach            }
122*5333da53SGreg Roach        });
123*5333da53SGreg Roach
124*5333da53SGreg Roach        return $location_id !== null;
125*5333da53SGreg Roach    }
126*5333da53SGreg Roach
127*5333da53SGreg Roach    /**
128*5333da53SGreg Roach     * @return stdClass
129*5333da53SGreg Roach     */
130*5333da53SGreg Roach    private function details(): stdClass
131*5333da53SGreg Roach    {
132*5333da53SGreg Roach        return app('cache.array')->remember('location-details-' . $this->id(), function () {
133*5333da53SGreg Roach            // The "top-level" location won't exist in the database.
134*5333da53SGreg Roach            if ($this->parts->isEmpty()) {
135*5333da53SGreg Roach                return (object) [
136*5333da53SGreg Roach                    'pl_id'        => '0',
137*5333da53SGreg Roach                    'pl_parent_id' => '0',
138*5333da53SGreg Roach                    'pl_level' => null,
139*5333da53SGreg Roach                    'pl_place' => '',
140*5333da53SGreg Roach                    'pl_lati' => null,
141*5333da53SGreg Roach                    'pl_long' => null,
142*5333da53SGreg Roach                    'pl_zoom' => null,
143*5333da53SGreg Roach                    'pl_icon' => null,
144*5333da53SGreg Roach                    'pl_media' => null,
145*5333da53SGreg Roach                ];
146*5333da53SGreg Roach            }
147*5333da53SGreg Roach
148*5333da53SGreg Roach            return DB::table('placelocation')
149*5333da53SGreg Roach                ->where('pl_id', '=', $this->id())
150*5333da53SGreg Roach                ->first();
151*5333da53SGreg Roach        });
152*5333da53SGreg Roach    }
153*5333da53SGreg Roach
154*5333da53SGreg Roach    /**
155*5333da53SGreg Roach     * Latitude of the location.
156*5333da53SGreg Roach     *
157*5333da53SGreg Roach     * @return float
158*5333da53SGreg Roach     */
159*5333da53SGreg Roach    public function latitude(): float
160*5333da53SGreg Roach    {
161*5333da53SGreg Roach        $gedcom_service = new GedcomService();
162*5333da53SGreg Roach
163*5333da53SGreg Roach        return $gedcom_service->readLatitude($this->details()->pl_lati ?? '');
164*5333da53SGreg Roach    }
165*5333da53SGreg Roach
166*5333da53SGreg Roach    /**
167*5333da53SGreg Roach     * Latitude of the longitude.
168*5333da53SGreg Roach     *
169*5333da53SGreg Roach     * @return float
170*5333da53SGreg Roach     */
171*5333da53SGreg Roach    public function longitude(): float
172*5333da53SGreg Roach    {
173*5333da53SGreg Roach        $gedcom_service = new GedcomService();
174*5333da53SGreg Roach
175*5333da53SGreg Roach        return $gedcom_service->readLongitude($this->details()->pl_long ?? '');
176*5333da53SGreg Roach    }
177*5333da53SGreg Roach
178*5333da53SGreg Roach    /**
179*5333da53SGreg Roach     * The icon for the location.
180*5333da53SGreg Roach     *
181*5333da53SGreg Roach     * @return string
182*5333da53SGreg Roach     */
183*5333da53SGreg Roach    public function icon(): string
184*5333da53SGreg Roach    {
185*5333da53SGreg Roach        return (string) $this->details()->pl_icon;
186*5333da53SGreg Roach    }
187*5333da53SGreg Roach
188*5333da53SGreg Roach    /**
189*5333da53SGreg Roach     * Zoom level for the location.
190*5333da53SGreg Roach     *
191*5333da53SGreg Roach     * @return int
192*5333da53SGreg Roach     */
193*5333da53SGreg Roach    public function zoom(): int
194*5333da53SGreg Roach    {
195*5333da53SGreg Roach        return (int) $this->details()->pl_zoom;
196*5333da53SGreg Roach    }
197*5333da53SGreg Roach
198*5333da53SGreg Roach    /**
199*5333da53SGreg Roach     * @return string
200*5333da53SGreg Roach     */
201*5333da53SGreg Roach    public function locationName(): string
202*5333da53SGreg Roach    {
203*5333da53SGreg Roach        return (string) $this->parts->first();
204*5333da53SGreg Roach    }
205*5333da53SGreg Roach}
206