xref: /webtrees/app/Module/ModuleMapGeoLocationTrait.php (revision d85de00fd5bcdcfbee029fa7c1da0240428565da)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2021 webtrees development team
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Module;
21
22use Fig\Http\Message\StatusCodeInterface;
23use Fisharebest\Webtrees\Html;
24use Fisharebest\Webtrees\I18N;
25use Fisharebest\Webtrees\Registry;
26use GuzzleHttp\Client;
27use GuzzleHttp\Psr7\Request;
28use JsonException;
29use Psr\Http\Message\RequestInterface;
30use Psr\Http\Message\ResponseInterface;
31
32use function json_decode;
33
34use const JSON_THROW_ON_ERROR;
35
36/**
37 * Trait ModuleMapGeoLocationTrait - default implementation of ModuleMapGeoLocationInterface
38 */
39trait ModuleMapGeoLocationTrait
40{
41    /**
42     * A sentence describing what this module does.
43     *
44     * @return string
45     */
46    public function description(): string
47    {
48        return I18N::translate('Use an external service to find locations.');
49    }
50
51    /**
52     * @param string $place
53     *
54     * @return array<string>
55     */
56    public function searchPlaceNames(string $place): array
57    {
58        if (strlen($place) <= 2) {
59            return [];
60        }
61
62        $key   = $this->name() . $place;
63        $cache = Registry::cache()->file();
64        $ttl   = 86400;
65
66        return $cache->remember($key, function () use ($place) {
67            $request = $this->searchLocationsRequest($place);
68
69            $client = new Client([
70                'timeout' => 3,
71            ]);
72
73            $response = $client->send($request);
74
75            if ($response->getStatusCode() === StatusCodeInterface::STATUS_OK) {
76                return $this->extractLocationsFromResponse($response);
77            }
78
79            return [];
80        }, $ttl);
81    }
82
83    /**
84     * @param string $place
85     *
86     * @return RequestInterface
87     */
88    protected function searchLocationsRequest(string $place): RequestInterface
89    {
90        $uri = Html::url('https://nominatim.openstreetmap.org/search', [
91            'accept-language' => I18N::languageTag(),
92            'format'          => 'jsonv2',
93            'limit'           => 50,
94            'q'               => $place,
95        ]);
96
97        return new Request('GET', $uri);
98    }
99
100    /**
101     * @param ResponseInterface $response
102     *
103     * @return array<string>
104     */
105    protected function extractLocationsFromResponse(ResponseInterface $response): array
106    {
107        $body = $response->getBody()->getContents();
108
109        try {
110            return json_decode($body, false, 512, JSON_THROW_ON_ERROR);
111        } catch (JsonException $ex) {
112            return [];
113        }
114    }
115}
116