GeocodeXyz::geocode()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 3
rs 10
1
<?php
2
3
namespace LeKoala\GeoTools\Services;
4
5
use Exception;
6
use LeKoala\GeoTools\Models\Address;
7
use LeKoala\GeoTools\Models\Country;
8
use LeKoala\GeoTools\Models\Coordinates;
9
10
/**
11
 * @link https://geocode.xyz
12
 */
13
class GeocodeXyz implements Geocoder, Geolocator
14
{
15
    const API_URL = 'https://geocode.xyz/';
16
17
    /**
18
     * @link https://geocode.xyz/api
19
     * @param ?string $query
20
     * @param array<int|string,mixed> $params region, citybias
21
     * @return Address
22
     * @throws Exception when there is a problem with the api, otherwise may return an empty address
23
     */
24
    protected function query($query, $params = [])
25
    {
26
        $url = self::API_URL;
27
28
        $defaultParams = [
29
            'locate' => $query,
30
            'json' => 1
31
        ];
32
        $params = array_merge($defaultParams, $params);
33
34
        $url .= '?' . http_build_query($params);
35
36
        $headers = [
37
            "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
38
            "accept-encoding: gzip, deflate, br",
39
            "accept-language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7,nb;q=0.6",
40
            "cache-control: max-age=0",
41
            "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36",
42
        ];
43
        $opts = array('http' =>
44
        array(
45
            'method' => 'POST',
46
            'header' => implode("\r\n", $headers),
47
        ));
48
49
        $context = stream_context_create($opts, $opts);
50
        $result = file_get_contents($url, false, $context);
51
        if (!$result) {
52
            throw new Exception("The api returned no result");
53
        }
54
55
        $data = json_decode($result, true);
56
57
        if (!$data) {
58
            throw new Exception("Failed to decode api results");
59
        }
60
61
        $location = [];
62
        $countryCode = $countryName = null;
63
        $lat = $lon = null;
64
65
        // normalize
66
        if (isset($data['standard'])) {
67
            $location = [
68
                'streetName' => $data['standard']['addresst'] ?? null,
69
                'streetNumber' => $data['standard']['stno'] ?? null,
70
                'postalCode' => $data['standard']['postal'] ?? null,
71
                'locality' => $data['standard']['city'] ?? null,
72
            ];
73
            $countryCode = $data['standard']['prov'] ?? null;
74
            $countryName = $data['standard']['countryname'] ?? null;
75
        } elseif (isset($data['staddress'])) {
76
            $location = [
77
                'streetName' => $data['staddress'] ?? null,
78
                'streetNumber' => $data['stnumber'] ?? null,
79
                'postalCode' => $data['postal'] ?? null,
80
                'locality' => $data['city'] ?? null,
81
            ];
82
            $countryCode = $data['prov'] ?? null;
83
            $countryName = $data['country'] ?? null;
84
        }
85
86
        // Make sure we have a string
87
        if (empty($location['postalCode'])) {
88
            $location['postalCode'] = '';
89
        }
90
91
        if (!empty($data['latt'])) {
92
            $lat = $data['latt'];
93
            $lon = $data['longt'];
94
        }
95
96
        $country = new Country($countryCode, $countryName);
97
        $coordinates = new Coordinates($lat, $lon);
98
99
        return new Address($location, $country, $coordinates);
100
    }
101
102
    /**
103
     * @return array<string>
104
     */
105
    public static function listRegions()
106
    {
107
        return explode(', ', 'AF, AX, AL, DZ, AS, AD, AO, AI, AQ, AG, AR, AM, AW, AU, AT, AZ, BS, BH, BD, BB, BY, BE, BZ, BJ, BM, BT, BO, BQ, BA, BW, BR, IO, VG, BN, BG, BF, BI, KH, CM, CA, CV, KY, CF, TD, CL, CN, CX, CC, CO, KM, CG, CK, CR, HR, CU, CW, CY, CZ, CI, DK, DJ, DM, DO, EC, EG, SV, GQ, ER, EE, ET, FK, FO, FJ, FI, FR, GF, PF, TF, GA, GM, GE, DE, GH, GI, GR, GL, GD, GP, GU, GT, GG, GN, GW, GY, HT, HN, HK, HU, IS, IN, ID, IR, IQ, IE, IM, IL, IT, JM, JP, JE, JO, KZ, KE, KI, KS, KW, KG, LA, LV, LB, LS, LR, LY, LI, LT, LU, MO, MK, MG, MW, MY, MV, ML, MT, MH, MQ, MR, MU, YT, MX, FM, MD, MC, MN, ME, MS, MA, MZ, MM, NA, NR, NP, NL, AN, NC, NZ, NI, NE, NG, NU, NF, KP, MP, NO, OM, PK, PW, PS, PA, PG, PY, PE, PH, PN, PL, PT, PR, QA, RO, RU, RW, RE, GS, SH, KN, LC, PM, VC, BL, SX, MF, WS, SM, ST, SA, SN, RS, SC, SL, SG, SK, SI, SB, SO, ZA, KR, SS, ES, LK, SD, SR, SJ, SZ, SE, CH, SY, TW, TJ, TZ, TH, TL, TG, TK, TO, TT, TN, TR, TM, TC, TV, UM, UG, UA, AE, UK, US, UY, UZ, VU, VA, VE, VN, VI, WF, EH, YE, CD, ZM, ZW');
108
    }
109
110
    /**
111
     * @inheritDoc
112
     */
113
    public function reverseGeocode($lat, $lon, $params = [])
114
    {
115
        return $this->query("$lat,$lon", $params);
116
    }
117
118
    /**
119
     * @inheritDoc
120
     */
121
    public function geocode($address, $params = [])
122
    {
123
        return $this->query($address, $params);
124
    }
125
126
    /**
127
     * @inheritDoc
128
     */
129
    public function geolocate($ip, $params = [])
130
    {
131
        return $this->query($ip, $params);
132
    }
133
}
134