1<?php
2/*
3 * (c) Camptocamp <info@camptocamp.com>
4 * (c) Patrick Hayes
5 *
6 * This code is open-source and licenced under the Modified BSD License.
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11/**
12 * PHP Google Geocoder Adapter
13 *
14 *
15 * @package    geoPHP
16 * @author     Patrick Hayes <patrick.d.hayes@gmail.com>
17 */
18class GoogleGeocode extends GeoAdapter
19{
20
21  /**
22   * Read an address string or array geometry objects
23   *
24   * @param string - Address to geocode
25   * @param string - Type of Geometry to return. Can either be 'points' or 'bounds' (polygon)
26   * @param Geometry|bounds-array - Limit the search area to within this region. For example
27   *                                by default geocoding "Cairo" will return the location of Cairo Egypt.
28   *                                If you pass a polygon of illinois, it will return Cairo IL.
29   * @param return_multiple - Return all results in a multipoint or multipolygon
30   * @return Geometry|GeometryCollection
31   */
32  public function read($address, $return_type = 'point', $bounds = FALSE, $return_multiple = FALSE) {
33    if (is_array($address)) $address = join(',', $address);
34
35    if (gettype($bounds) == 'object') {
36      $bounds = $bounds->getBBox();
37    }
38    if (gettype($bounds) == 'array') {
39      $bounds_string = '&bounds='.$bounds['miny'].','.$bounds['minx'].'|'.$bounds['maxy'].','.$bounds['maxx'];
40    }
41    else {
42      $bounds_string = '';
43    }
44
45    $url = "http://maps.googleapis.com/maps/api/geocode/json";
46    $url .= '?address='. urlencode($address);
47    $url .= $bounds_string;
48    $url .= '&sensor=false';
49    $this->result = json_decode(@file_get_contents($url));
50
51    if ($this->result->status == 'OK') {
52      if ($return_multiple == FALSE) {
53        if ($return_type == 'point') {
54          return $this->getPoint();
55        }
56        if ($return_type == 'bounds' || $return_type == 'polygon') {
57          return $this->getPolygon();
58        }
59      }
60      if ($return_multiple == TRUE) {
61        if ($return_type == 'point') {
62          $points = array();
63          foreach ($this->result->results as $delta => $item) {
64            $points[] = $this->getPoint($delta);
65          }
66          return new MultiPoint($points);
67        }
68        if ($return_type == 'bounds' || $return_type == 'polygon') {
69          $polygons = array();
70          foreach ($this->result->results as $delta => $item) {
71            $polygons[] = $this->getPolygon($delta);
72          }
73          return new MultiPolygon($polygons);
74        }
75      }
76    }
77    else {
78      if ($this->result->status) throw new Exception('Error in Google Geocoder: '.$this->result->status);
79      else throw new Exception('Unknown error in Google Geocoder');
80      return FALSE;
81    }
82  }
83
84  /**
85   * Serialize geometries into a WKT string.
86   *
87   * @param Geometry $geometry
88   * @param string $return_type Should be either 'string' or 'array'
89   *
90   * @return string Does a reverse geocode of the geometry
91   */
92  public function write(Geometry $geometry, $return_type = 'string') {
93    $centroid = $geometry->getCentroid();
94    $lat = $centroid->getY();
95    $lon = $centroid->getX();
96
97    $url = "http://maps.googleapis.com/maps/api/geocode/json";
98    $url .= '?latlng='.$lat.','.$lon;
99    $url .= '&sensor=false';
100    $this->result = json_decode(@file_get_contents($url));
101
102    if ($this->result->status == 'OK') {
103      if ($return_type == 'string') {
104        return $this->result->results[0]->formatted_address;
105      }
106      if ($return_type == 'array') {
107        return $this->result->results[0]->address_components;
108      }
109    }
110    elseif ($this->result->status == 'ZERO_RESULTS') {
111      if ($return_type == 'string') {
112        return '';
113      }
114      if ($return_type == 'array') {
115        return $this->result->results;
116      }
117    }
118    else {
119      if ($this->result->status) throw new Exception('Error in Google Reverse Geocoder: '.$this->result->status);
120      else throw new Exception('Unknown error in Google Reverse Geocoder');
121      return FALSE;
122    }
123  }
124
125  private function getPoint($delta = 0) {
126    $lat = $this->result->results[$delta]->geometry->location->lat;
127    $lon = $this->result->results[$delta]->geometry->location->lng;
128    return new Point($lon, $lat);
129  }
130
131  private function getPolygon($delta = 0) {
132    $points = array (
133      $this->getTopLeft($delta),
134      $this->getTopRight($delta),
135      $this->getBottomRight($delta),
136      $this->getBottomLeft($delta),
137      $this->getTopLeft($delta),
138    );
139    $outer_ring = new LineString($points);
140    return new Polygon(array($outer_ring));
141  }
142
143  private function getTopLeft($delta = 0) {
144    $lat = $this->result->results[$delta]->geometry->bounds->northeast->lat;
145    $lon = $this->result->results[$delta]->geometry->bounds->southwest->lng;
146    return new Point($lon, $lat);
147  }
148
149  private function getTopRight($delta = 0) {
150    $lat = $this->result->results[$delta]->geometry->bounds->northeast->lat;
151    $lon = $this->result->results[$delta]->geometry->bounds->northeast->lng;
152    return new Point($lon, $lat);
153  }
154
155  private function getBottomLeft($delta = 0) {
156    $lat = $this->result->results[$delta]->geometry->bounds->southwest->lat;
157    $lon = $this->result->results[$delta]->geometry->bounds->southwest->lng;
158     return new Point($lon, $lat);
159  }
160
161  private function getBottomRight($delta = 0) {
162    $lat = $this->result->results[$delta]->geometry->bounds->southwest->lat;
163    $lon = $this->result->results[$delta]->geometry->bounds->northeast->lng;
164    return new Point($lon, $lat);
165  }
166}
167