1 : <?php
2 : /**
3 : * Provide a method of interfacing with OpenStreetMap servers.
4 : *
5 : * PHP Version 5
6 : *
7 : * @category Services
8 : * @package Services_OpenStreetMap
9 : * @author Ken Guest <kguest@php.net>
10 : * @license BSD http://www.opensource.org/licenses/bsd-license.php
11 : * @version Release: @package_version@
12 : * @link http://pear.php.net/package/Services_OpenStreetMap
13 : * @link http://wiki.openstreetmap.org/wiki/Api06
14 : */
15 :
16 : require_once 'HTTP/Request2.php';
17 : spl_autoload_register(array('Services_OpenStreetMap', 'autoload'));
18 : #spl_autoload_register(array('Services_OpenStreetMap', 'autoload'));
19 :
20 : /**
21 : * Services_OpenStreetMap - interface with OpenStreetMap
22 : *
23 : * @category Services
24 : * @package Services_OpenStreetMap
25 : * @author Ken Guest <kguest@php.net>
26 : * @copyright 2010 Ken Guest
27 : * @license BSD http://www.opensource.org/licenses/bsd-license.php
28 : * @version Release: 0.0.1
29 : * @link http://pear.php.net/package/Services_OpenStreetMap
30 : */
31 : class Services_OpenStreetMap
32 : {
33 : /**
34 : * Default config settings
35 : *
36 : * @var Services_OpenStreetMap_Config
37 : * @see Services_OpenStreetMap::getConfig
38 : * @see Services_OpenStreetMap::setConfig
39 : */
40 : protected $config = null;
41 :
42 : /**
43 : * [Retrieved] XML
44 : * @var string
45 : * @internal
46 : */
47 : protected $xml = null;
48 :
49 : protected $transport = null;
50 :
51 : /**
52 : * autoloader
53 : *
54 : * @param string $class Name of class
55 : *
56 : * @return boolean
57 : */
58 : public static function autoload($class)
59 : {
60 5 : $dir = dirname(dirname(__FILE__));
61 5 : $file = $dir . '/' . str_replace('_', '/', $class) . '.php';
62 5 : if (file_exists($file)) {
63 5 : echo "utoloading $file\n";
64 5 : return include_once $file;
65 : }
66 0 : return false;
67 : }
68 :
69 : /**
70 : * constructor; which optionally sets config details.
71 : *
72 : * @param array $config Defaults to empty array if none provided
73 : *
74 : * @return Services_OpenStreetMap
75 : */
76 : public function __construct($config = array())
77 : {
78 10 : if ($config == array()) {
79 0 : $config = $this->config;
80 0 : }
81 10 : $this->getConfig()->setTransport($this->getTransport());
82 10 : if (!is_null($config)) {
83 10 : $this->getConfig()->setValue($config);
84 10 : }
85 10 : $version = $this->getConfig()->getValue('api_version');
86 10 : $api = "Services_OpenStreetMap_API_V" . str_replace('.', '', $version);
87 10 : $this->api = new $api;
88 10 : $this->api->setTransport($this->getTransport());
89 10 : $this->api->setConfig($this->getConfig());
90 10 : }
91 :
92 : /**
93 : * Convert a 'bbox' ordered set of coordinates to ordering required for get
94 : * method.
95 : *
96 : * <code>
97 : * $osm = new Services_OpenStreetMap();
98 : * $osm->get($osm->bboxToMinMax($minLat, $minLon, $maxLat, $maxLon));
99 : * file_put_contents("area_covered.osm", $osm->getXML());
100 : * </code>
101 : *
102 : * @param mixed $minLat min Latitude
103 : * @param mixed $minLon min Longitude
104 : * @param mixed $maxLat max Latitude
105 : * @param mixed $maxLon max Longitude
106 : *
107 : * @return array
108 : */
109 : public function bboxToMinMax($minLat, $minLon, $maxLat, $maxLon)
110 : {
111 0 : return array($minLon, $minLat, $maxLon, $maxLat);
112 : }
113 :
114 : /**
115 : * Get XML describing area prescribed by the given co-ordinates.
116 : *
117 : * <code>
118 : * $osm = new Services_OpenStreetMap();
119 : * $osm->get(-8.3564758, 52.821022799999994, -7.7330017, 53.0428644);
120 : * file_put_contents("area_covered.osm", $osm->getXML());
121 : * </code>
122 : *
123 : * @param string $minLon min Longitude (leftmost point)
124 : * @param string $minLat min Latitude (bottom point)
125 : * @param string $maxLon max Longitude (rightmost point)
126 : * @param string $maxLat max Latitude (top point)
127 : *
128 : * @return void
129 : */
130 :
131 : public function get($minLon, $minLat, $maxLon, $maxLat)
132 : {
133 0 : $config = $this->getConfig();
134 0 : $url = $config->getValue('server')
135 : . 'api/'
136 0 : . $config->getValue('api_version')
137 0 : . "/map?bbox=$minLat,$minLon,$maxLat,$maxLon";
138 0 : $response = $this->getTransport()->getResponse($url);
139 0 : $this->xml = $response->getBody();
140 0 : }
141 :
142 : /**
143 : * Get co-ordinates of some named place
144 : *
145 : * <code>
146 : * $coords = $osm->getCoordsOfPlace('Limerick, Ireland');
147 : * </code>
148 : *
149 : * @param string $place name
150 : *
151 : * @return array Associated array of lat/lon values.
152 : * @throws Services_OpenStreetMap_Exception If the place can not be found.
153 : */
154 : public function getCoordsOfPlace($place)
155 : {
156 : $url = 'http://nominatim.openstreetmap.org/search?q='
157 0 : . urlencode($place) . '&limit=1&format=xml';
158 0 : $response = $this->getTransport()->getResponse($url);
159 0 : $xml = simplexml_load_string($response->getBody());
160 0 : $obj = $xml->xpath('//place');
161 0 : if (empty($obj)) {
162 0 : throw new Services_OpenStreetMap_Exception(
163 : 'Could not get coords for ' . $place
164 0 : );
165 : }
166 0 : $attrs = $xml->named[0];
167 0 : $attrs = $obj[0]->attributes();
168 0 : $lat = (string) $attrs['lat'];
169 0 : $lon = (string) $attrs['lon'];
170 0 : return compact('lat', 'lon');
171 : }
172 :
173 : /**
174 : * Given the results of a call to func_get_args return an array of unique
175 : * valid IDs specified in those results (either 1 per argument or each
176 : * argument containing an array of IDs).
177 : *
178 : * @param mixed $args results of call to func_get_args
179 : *
180 : * @return array
181 : *
182 : */
183 : public static function getIDs($args)
184 : {
185 1 : $IDs = array();
186 1 : foreach ($args as $arg) {
187 1 : if (is_array($arg)) {
188 0 : $IDs = array_merge($arg, $IDs);
189 1 : } elseif (is_numeric($arg)) {
190 1 : $IDs[] = $arg;
191 1 : }
192 1 : }
193 1 : return array_unique($IDs);
194 : }
195 :
196 : /**
197 : * Load XML from [cache] file.
198 : *
199 : * @param string $file filename
200 : *
201 : * @return void
202 : */
203 : public function loadXml($file)
204 : {
205 0 : $this->xml = file_get_contents($file);
206 0 : }
207 :
208 : /**
209 : * return XML.
210 : *
211 : * @return string
212 : */
213 : public function getXml()
214 : {
215 0 : return $this->xml;
216 : }
217 :
218 :
219 : /**
220 : * search based on given criteria.
221 : *
222 : * returns an array of objects such as Services_OpenStreetMap_Node etc.
223 : *
224 : * <code>
225 : * $osm = new Services_OpenStreetMap();
226 : *
227 : * $osm->loadXML("./osm.osm");
228 : * $results = $osm->search(array("amenity" => "pharmacy"));
229 : * echo "List of Pharmacies\n";
230 : * echo "==================\n\n";
231 : *
232 : * foreach ($results as $result) {
233 : * $name = $result->getTag('name');
234 : * $addrStreet = $result->getTag('addr:street');
235 : * $addrCity = $result->getTag('addr:city');
236 : * $addrCountry = $result->getTag('addr:country');
237 : * $addrHouseName = $result->getTag('addr:housename');
238 : * $addrHouseNumber = $result->getTag('addr:housenumber');
239 : * $openingHours = $result->getTag('opening_hours');
240 : * $phone = $result->getTag('phone');
241 : *
242 : * $line1 = ($addrHouseNumber) ? $addrHouseNumber : $addrHouseName;
243 : * if ($line1 != null) {
244 : * $line1 .= ', ';
245 : * }
246 : * echo "$name\n{$line1}{$addrStreet}\n$phone\n$openingHours\n\n";
247 : * }
248 : * </code>
249 : *
250 : * @param array $criteria what to search for
251 : *
252 : * @return array
253 : */
254 : public function search(array $criteria)
255 : {
256 0 : $results = array();
257 :
258 0 : $xml = simplexml_load_string($this->xml);
259 0 : if ($xml === false) {
260 0 : return array();
261 : }
262 0 : foreach ($criteria as $key => $value) {
263 0 : foreach ($xml->xpath('//way') as $node) {
264 0 : $results = array_merge(
265 0 : $results,
266 0 : $this->_searchNode($node, $key, $value, 'way')
267 0 : );
268 0 : }
269 0 : foreach ($xml->xpath('//node') as $node) {
270 0 : $results = array_merge(
271 0 : $results,
272 0 : $this->_searchNode($node, $key, $value, 'node')
273 0 : );
274 0 : }
275 0 : }
276 0 : return $results;
277 : }
278 :
279 : /**
280 : * Search node for a specific key/value pair, allowing for value to be
281 : * included in a semicolon delimited list.
282 : *
283 : * @param SimpleXMLElement $node Node to search
284 : * @param string $key Key to search for (Eg 'amenity')
285 : * @param string $value Value to search for (Eg 'pharmacy')
286 : * @param string $type Type of object to return.
287 : *
288 : * @return array
289 : */
290 : private function _searchNode(SimpleXMLElement $node, $key, $value, $type)
291 : {
292 0 : $class = 'Services_OpenStreetMap_' . ucfirst(strtolower($type));
293 0 : $results = array();
294 0 : foreach ($node->tag as $tag) {
295 0 : if ($tag['k'] == $key) {
296 0 : if ($tag['v'] == $value) {
297 0 : $obj = new $class();
298 0 : $obj->setTransport($this->getTransport());
299 0 : $obj->setConfig($this->getConfig());
300 0 : $obj->setXml(simplexml_load_string($node->saveXML()));
301 0 : $results[] = $obj;
302 0 : } elseif (strpos($tag['v'], ';')) {
303 0 : $array = explode(';', $tag['v']);
304 0 : if (in_array($value, $array)) {
305 0 : $obj = new $class();
306 0 : $obj->setTransport($this->getTransport());
307 0 : $obj->setConfig($this->getConfig());
308 0 : $obj->setXml(simplexml_load_string($node->saveXML()));
309 0 : $results[] = $obj;
310 0 : }
311 0 : }
312 0 : }
313 0 : }
314 0 : return $results;
315 : }
316 :
317 : /**
318 : * Return the number of seconds that must elapse before a connection is
319 : * considered to have timed-out.
320 : *
321 : * @return int
322 : */
323 : public function getTimeout()
324 : {
325 0 : return $this->getConfig()->getTimeout();
326 : }
327 :
328 : /**
329 : * minVersion - min API version supported by connected server.
330 : *
331 : * <code>
332 : * $config = array('user' => 'fred@example.net', 'password' => 'wilma4eva');
333 : * $osm = new Services_OpenStreetMap($config);
334 : * $min = $osm->getMinVersion();
335 : * </code>
336 : *
337 : * @return float
338 : */
339 : public function getMinVersion()
340 : {
341 0 : return $this->getConfig()->getMinVersion();
342 : }
343 :
344 : /**
345 : * maxVersion - max API version supported by connected server.
346 : *
347 : * <code>
348 : * $config = array('user' => 'fred@example.net', 'password' => 'wilma4eva');
349 : * $osm = new Services_OpenStreetMap($config);
350 : * $max = $osm->getMaxVersion();
351 : * </code>
352 : *
353 : * @return float
354 : */
355 : public function getMaxVersion()
356 : {
357 0 : return $this->getConfig()->getMaxVersion();
358 : }
359 :
360 : /**
361 : * Max size of area that can be downloaded in one request.
362 : *
363 : * <code>
364 : * $osm = new Services_OpenStreetMap();
365 : * $area_allowed = $osm->getMaxArea();
366 : * </code>
367 : *
368 : * @return float
369 : */
370 : public function getMaxArea()
371 : {
372 0 : return $this->getConfig()->getMaxArea();
373 : }
374 :
375 : /**
376 : * Maximum number of tracepoints per page.
377 : *
378 : * <code>
379 : * $osm = new Services_OpenStreetMap();
380 : * $tracepoints = $osm->getTracepointsPerPage();
381 : * </code>
382 : *
383 : * @return float
384 : */
385 : public function getTracepointsPerPage()
386 : {
387 0 : return $this->getConfig()->getTracepointsPerPage();
388 : }
389 :
390 : /**
391 : * Maximum number of nodes per way.
392 : *
393 : * Anymore than that and the way must be split.
394 : *
395 : * <code>
396 : * $osm = new Services_OpenStreetMap();
397 : * $max = $osm->getMaxNodes();
398 : * </code>
399 : *
400 : * @return float
401 : */
402 : public function getMaxNodes()
403 : {
404 0 : return $this->getConfig()->getMaxNodes();
405 : }
406 :
407 : /**
408 : * Number of elements allowed per changeset
409 : *
410 : * <code>
411 : * $osm = new Services_OpenStreetMap();
412 : * $max = $osm->getMaxElements();
413 : * </code>
414 : *
415 : * @return float
416 : */
417 : public function getMaxElements()
418 : {
419 0 : return $this->getConfig()->getMaxElements();
420 : }
421 :
422 : /**
423 : * Set Config object
424 : *
425 : * @param Services_OpenStreetMap_Config $config Config settings.
426 : *
427 : * @return Services_OpenStreetMap_API_V06
428 : */
429 : public function setConfig(Services_OpenStreetMap_Config $config)
430 : {
431 0 : $this->config = $config;
432 0 : return $this;
433 : }
434 :
435 : /**
436 : * Get current Config object
437 : *
438 : * @return Services_OpenStreetMap_Config
439 : */
440 : public function getConfig()
441 : {
442 10 : if (is_null($this->config)) {
443 10 : $config = new Services_OpenStreetMap_Config();
444 10 : $this->config = $config;
445 10 : }
446 10 : return $this->config;
447 : }
448 :
449 : /**
450 : * Get current Transport object.
451 : *
452 : * If one is not defined, create it.
453 : *
454 : * @return Services_OpenStreetMap_Transport
455 : */
456 : public function getTransport()
457 : {
458 10 : if (is_null($this->transport)) {
459 10 : $transport = new Services_OpenStreetMap_Transport();
460 10 : $transport->setConfig($this->getConfig());
461 10 : $this->transport = $transport;
462 :
463 10 : }
464 10 : return $this->transport;
465 : }
466 :
467 : /**
468 : * __call
469 : *
470 : * If possible, call the appropriate method of the API instance.
471 : *
472 : * @param string $name Name of missing method to call.
473 : * @param array $arguments Arguments to be used when calling method.
474 : *
475 : * @return void
476 : * @throws Services_OpenStreetMap_Exception If the method is not supported
477 : * by the API instance.
478 : */
479 : public function __call($name, $arguments)
480 : {
481 10 : if (method_exists($this->api, $name)) {
482 10 : return call_user_func_array(array($this->api, $name), $arguments);
483 : } else {
484 0 : throw new Services_OpenStreetMap_Exception(
485 0 : sprintf(
486 0 : 'Method %s does not exist.',
487 : $name
488 0 : )
489 0 : );
490 : }
491 : }
492 : }
493 : // vim:set et ts=4 sw=4:
494 : ?>
|