GoogleGeocode::read()   C
last analyzed

Complexity

Conditions 17
Paths 104

Size

Total Lines 62
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 35
c 1
b 0
f 0
dl 0
loc 62
rs 5.1833
cc 17
nc 104
nop 5

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace geoPHP\Adapter;
3
4
use geoPHP\Geometry\Geometry;
5
use geoPHP\Geometry\Point;
6
use geoPHP\Geometry\MultiPoint;
7
use geoPHP\Geometry\LineString;
8
use geoPHP\Geometry\Polygon;
9
use geoPHP\Geometry\MultiPolygon;
10
11
/*
12
 * (c) Camptocamp <[email protected]>
13
 * (c) Patrick Hayes
14
 *
15
 * This code is open-source and licenced under the Modified BSD License.
16
 * For the full copyright and license information, please view the LICENSE
17
 * file that was distributed with this source code.
18
 */
19
20
/**
21
 * PHP Google Geocoder Adapter
22
 *
23
 *
24
 * @package    geoPHP
25
 * @author     Patrick Hayes <[email protected]>
26
 */
27
class GoogleGeocode implements GeoAdapter
28
{
29
30
    /** @var \stdClass $result */
31
    protected $result;
32
33
    /**
34
     * Makes a geocoding (lat/lon lookup) with an address string or array geometry objects
35
     *
36
     * @param string $address Address to geocode
37
     * @param string $apiKey Your application's Google Maps Geocoding API key
38
     * @param string $returnType Type of Geometry to return. Can either be 'points' or 'bounds' (polygon)
39
     * @param array<string, int|float>|Geometry|null $bounds Limit the search area to within this region.
40
     *        For example by default geocoding "Cairo" will return the location of Cairo Egypt.
41
     *        If you pass a polygon of Illinois, it will return Cairo IL.
42
     * @param boolean $returnMultiple - Return all results in a multipoint or multipolygon
43
     *
44
     * @return Geometry
45
     * @throws \Exception If geocoding fails
46
     */
47
    public function read(
48
        string $address,
49
        $apiKey = null,
50
        string $returnType = 'point',
51
        $bounds = null,
52
        bool $returnMultiple = false
53
    ): Geometry {
54
        //if (is_array($address)) {
55
        //    $address = join(',', $address);
56
        //}
57
58
        if (is_object($bounds)) {
59
            $bounds = $bounds->getBBox();
60
        }
61
        if (is_array($bounds)) {
62
            $boundsString = '&bounds=' . $bounds['miny'] . ',' . $bounds['minx'] . '|' . $bounds['maxy'] . ',' . $bounds['maxx'];
63
        } else {
64
            $boundsString = '';
65
        }
66
67
        $url = "http://maps.googleapis.com/maps/api/geocode/json";
68
        $url .= '?address=' . urlencode($address);
69
        $url .= $boundsString . ($apiKey ? '&key=' . $apiKey : '');
70
        $this->result = json_decode(file_get_contents($url));
71
72
        if ($this->result->status === 'OK') {
73
            if (!$returnMultiple) {
74
                if ($returnType === 'point') {
75
                    return $this->getPoint();
76
                }
77
                if ($returnType === 'bounds' || $returnType === 'polygon') {
78
                    return $this->getPolygon();
79
                }
80
            } else {
81
                if ($returnType === 'point') {
82
                    $points = [];
83
                    foreach ($this->result->results as $delta => $item) {
84
                        $points[] = $this->getPoint($delta);
85
                    }
86
                    return new MultiPoint($points);
87
                }
88
                if ($returnType === 'bounds' || $returnType === 'polygon') {
89
                    $polygons = [];
90
                    foreach ($this->result->results as $delta => $item) {
91
                        $polygons[] = $this->getPolygon($delta);
92
                    }
93
                    return new MultiPolygon($polygons);
94
                }
95
            }
96
        } elseif ($this->result->status === 'ZERO_RESULTS') {
97
            return new Point;
98
        }
99
        
100
        if ($this->result->status) {
101
            throw new \Exception(
102
                'Error in Google Reverse Geocoder: '
103
                . $this->result->status
104
                . (isset($this->result->error_message) ? '. ' . $this->result->error_message : '')
105
            );
106
        }
107
        
108
        throw new \Exception('Unknown error in Google Reverse Geocoder');
109
    }
110
111
    /**
112
     * Makes a Reverse Geocoding (address lookup) with the (center) point of Geometry
113
     * Detailed documentation of response values can be found in:
114
     *
115
     * @see https://developers.google.com/maps/documentation/geocoding/intro#ReverseGeocoding
116
     *
117
     * @param Geometry $geometry
118
     * @param string $apiKey Your application's Google Maps Geocoding API key
119
     * @param string $language The language in which to return results. If not set, geocoder tries to use the native language of the domain.
120
     *
121
     * @return string A formatted address
122
     * @throws \Exception If geocoding fails
123
     */
124
    public function write(Geometry $geometry, $apiKey = null, $language = null): string
125
    {
126
        $centroid = $geometry->getCentroid();
127
        $lat = $centroid->getY();
128
        $lon = $centroid->getX();
129
130
        $url = "http://maps.googleapis.com/maps/api/geocode/json";
131
        /** @noinspection SpellCheckingInspection */
132
        $url .= '?latlng=' . $lat . ',' . $lon;
133
        $url .= ($language ? '&language=' . $language : '');
134
        $url .= ($apiKey ? '&key=' . $apiKey : '');
135
136
        $this->result = json_decode(file_get_contents($url));
137
138
        if ($this->result->status === 'OK') {
139
            return $this->result->results[0]->formatted_address;
140
        } elseif ($this->result->status === 'ZERO_RESULTS') {
141
            return '';
142
        }
143
        
144
        if ($this->result->status) {
145
            throw new \Exception(
146
                'Error in Google Reverse Geocoder: '
147
                . $this->result->status
148
                . (isset($this->result->error_message) ? '. ' . $this->result->error_message : '')
149
            );
150
        }
151
        
152
        throw new \Exception('Unknown error in Google Reverse Geocoder');
153
    }
154
155
    /**
156
     * @param int $delta
157
     * @return Point
158
     */
159
    private function getPoint(int $delta = 0): Point
160
    {
161
        $lat = $this->result->results[$delta]->geometry->location->lat;
162
        $lon = $this->result->results[$delta]->geometry->location->lng;
163
        return new Point($lon, $lat);
164
    }
165
166
    /**
167
     * @param int $delta
168
     * @return Polygon
169
     */
170
    private function getPolygon(int $delta = 0): Polygon
171
    {
172
        $points = [
173
            $this->getTopLeft($delta),
174
            $this->getTopRight($delta),
175
            $this->getBottomRight($delta),
176
            $this->getBottomLeft($delta),
177
            $this->getTopLeft($delta),
178
        ];
179
        $outerRing = new LineString($points);
180
        return new Polygon([$outerRing]);
181
    }
182
183
    /**
184
     * @param int $delta
185
     * @return Point
186
     */
187
    private function getTopLeft(int $delta = 0): Point
188
    {
189
        $lat = $this->result->results[$delta]->geometry->bounds->northeast->lat;
190
        $lon = $this->result->results[$delta]->geometry->bounds->southwest->lng;
191
        return new Point($lon, $lat);
192
    }
193
194
    /**
195
     * @param int $delta
196
     * @return Point
197
     */
198
    private function getTopRight(int $delta = 0): Point
199
    {
200
        $lat = $this->result->results[$delta]->geometry->bounds->northeast->lat;
201
        $lon = $this->result->results[$delta]->geometry->bounds->northeast->lng;
202
        return new Point($lon, $lat);
203
    }
204
205
    /**
206
     * @param int $delta
207
     * @return Point
208
     */
209
    private function getBottomLeft(int $delta = 0): Point
210
    {
211
        $lat = $this->result->results[$delta]->geometry->bounds->southwest->lat;
212
        $lon = $this->result->results[$delta]->geometry->bounds->southwest->lng;
213
        return new Point($lon, $lat);
214
    }
215
216
    /**
217
     * @param int $delta
218
     * @return Point
219
     */
220
    private function getBottomRight(int $delta = 0): Point
221
    {
222
        $lat = $this->result->results[$delta]->geometry->bounds->southwest->lat;
223
        $lon = $this->result->results[$delta]->geometry->bounds->northeast->lng;
224
        return new Point($lon, $lat);
225
    }
226
}
227