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