├── .gitattributes ├── .gitignore ├── src ├── Exception.php ├── AbstractIsochrones.php ├── Service │ ├── Tile.php │ ├── Nearest.php │ ├── Route.php │ ├── Trip.php │ ├── Matcher.php │ └── Table.php ├── autoload.php ├── AbstractResponse.php ├── Response │ ├── Service.php │ └── Isochrones.php ├── AbstractService.php ├── Transport.php └── Isochrones │ └── OpenRouteService.php ├── phpunit.xml.dist ├── .github └── workflows │ └── test.yml ├── examples ├── trip.php ├── matcher.php ├── nearest.php ├── tile.php ├── route.php ├── table.php └── isochrones.php ├── tests ├── Service │ ├── TripTest.php │ ├── TableTest.php │ ├── RouteTest.php │ ├── MatcherTest.php │ ├── NearestTest.php │ └── TileTest.php ├── ResponseServiceTest.php ├── TransportTest.php └── ResponseIsochronesTest.php ├── composer.json ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .phpunit.result.cache 2 | phpunit.xml 3 | composer.lock 4 | composer.phar 5 | /.idea 6 | /vendor 7 | -------------------------------------------------------------------------------- /src/Exception.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ./tests/ 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | jobs: 4 | build-test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v4 8 | 9 | - name: Install dependencies 10 | uses: php-actions/composer@v6 11 | with: 12 | php_version: '7.2' 13 | 14 | - name: PHPUnit Tests 15 | uses: php-actions/phpunit@v3 16 | with: 17 | php_version: '7.2' 18 | version: 8 19 | 20 | - run: phpunit 21 | -------------------------------------------------------------------------------- /examples/trip.php: -------------------------------------------------------------------------------- 1 | fetch('13.388860,52.517037;13.397634,52.529407'); 8 | if ($response->isOK()) 9 | { 10 | echo '
';
11 | 		print_r($response->toArray());
12 | 	} else {
13 | 		echo 'Trip not found.';
14 | 	}
15 | } catch (\Riverside\Osrm\Exception $e) {
16 | 	echo $e->getMessage();
17 | } catch (Exception $e) {
18 | 	echo $e->getMessage();
19 | }
20 | 


--------------------------------------------------------------------------------
/examples/matcher.php:
--------------------------------------------------------------------------------
 1 | fetch('13.388860,52.517037;13.397634,52.529407');
 8 | 	if ($response->isOK())
 9 |     {
10 | 		echo '
';
11 | 		print_r($response->toArray());
12 | 	} else {
13 | 		echo 'Match not found.';
14 | 	}
15 | } catch (\Riverside\Osrm\Exception $e) {
16 | 	echo $e->getMessage();
17 | } catch (Exception $e) {
18 | 	echo $e->getMessage();
19 | }
20 | 


--------------------------------------------------------------------------------
/src/AbstractIsochrones.php:
--------------------------------------------------------------------------------
 1 | setNumber(5)
 9 |         ->fetch('13.388860,52.517037');
10 | 	if ($response->isOK())
11 |     {
12 | 		echo '
';
13 | 		print_r($response->toArray());
14 | 	} else {
15 | 		echo 'Nearest not found.';
16 | 	}
17 | } catch (\Riverside\Osrm\Exception $e) {
18 | 	echo $e->getMessage();
19 | } catch (Exception $e) {
20 | 	echo $e->getMessage();
21 | }
22 | 


--------------------------------------------------------------------------------
/src/Service/Tile.php:
--------------------------------------------------------------------------------
 1 | setProfile('car')
 9 |         ->fetch('tile(1310,3166,13)');
10 | 
11 | 	if ($response->isOK())
12 |     {
13 | 		echo '
';
14 | 		echo strlen($response->getResponse());
15 | 	} else {
16 | 		echo 'Tile not found.';
17 | 	}
18 | 
19 | } catch (\Riverside\Osrm\Exception $e) {
20 | 	echo $e->getMessage();
21 | } catch (Exception $e) {
22 | 	echo $e->getMessage();
23 | }
24 | 


--------------------------------------------------------------------------------
/src/autoload.php:
--------------------------------------------------------------------------------
 1 | setSteps('true')
 9 |         ->fetch('13.388860,52.517037;13.397634,52.529407');
10 | 	if ($response->isOK())
11 |     {
12 | 		echo '
';
13 | 		print_r($response->toArray());
14 | 	} else {
15 | 		echo 'Route not found.';
16 | 	}
17 | } catch (\Riverside\Osrm\Exception $e) {
18 | 	echo $e->getMessage();
19 | } catch (Exception $e) {
20 | 	echo $e->getMessage();
21 | }
22 | 


--------------------------------------------------------------------------------
/examples/table.php:
--------------------------------------------------------------------------------
 1 | setAnnotations('distance')
 9 |         ->fetch('13.388860,52.517037;13.397634,52.529407;13.428555,52.523219');
10 | 	if ($response->isOK())
11 |     {
12 | 		echo '
';
13 | 		print_r($response->toArray());
14 | 	} else {
15 | 		echo 'Table not found.';
16 | 	}
17 | } catch (\Riverside\Osrm\Exception $e) {
18 | 	echo $e->getMessage();
19 | } catch (Exception $e) {
20 | 	echo $e->getMessage();
21 | }
22 | 


--------------------------------------------------------------------------------
/tests/Service/TripTest.php:
--------------------------------------------------------------------------------
 1 | fetch('13.388860,52.517037;13.397634,52.529407');
16 | 
17 |         $this->assertInstanceOf(ServiceResponse::class, $response);
18 |         $this->assertTrue($response->isOK());
19 |     }
20 | }


--------------------------------------------------------------------------------
/tests/Service/TableTest.php:
--------------------------------------------------------------------------------
 1 | fetch('13.388860,52.517037;13.397634,52.529407;13.428555,52.523219');
16 | 
17 |         $this->assertInstanceOf(ServiceResponse::class, $response);
18 |         $this->assertTrue($response->isOK());
19 |     }
20 | }


--------------------------------------------------------------------------------
/tests/Service/RouteTest.php:
--------------------------------------------------------------------------------
 1 | setSteps('true')
17 |             ->fetch('13.388860,52.517037;13.397634,52.529407');
18 | 
19 |         $this->assertInstanceOf(ServiceResponse::class, $response);
20 |         $this->assertTrue($response->isOK());
21 |     }
22 | }


--------------------------------------------------------------------------------
/tests/Service/MatcherTest.php:
--------------------------------------------------------------------------------
 1 | fetch('13.388860,52.517037;13.397634,52.529407');
20 | 
21 |         $this->assertInstanceOf(ServiceResponse::class, $response);
22 |         $this->assertTrue($response->isOK());
23 |     }
24 | }


--------------------------------------------------------------------------------
/tests/Service/NearestTest.php:
--------------------------------------------------------------------------------
 1 | setNumber(5)->fetch('13.388860,52.517037');
20 | 
21 |         $this->assertInstanceOf(ServiceResponse::class, $response);
22 |         $this->assertTrue($response->isOK());
23 |     }
24 | }


--------------------------------------------------------------------------------
/src/Service/Nearest.php:
--------------------------------------------------------------------------------
 1 | = 1))
28 |         {
29 |             throw new Exception('Invalid value for $number');
30 |         }
31 | 
32 |         return $this->setOption('number', $value);
33 |     }
34 | }


--------------------------------------------------------------------------------
/examples/isochrones.php:
--------------------------------------------------------------------------------
 1 | setApiKey('{api_key}');
 7 | 
 8 | try {
 9 |     $response = $inst
10 |         ->setLocations([[8.681495,49.41461],[8.686507,49.41943]])
11 |         ->setRange([300,200])
12 |         ->fetch();
13 |     if ($response->isOK())
14 |     {
15 |         echo '
';
16 |         print_r($response->getFeatures());
17 |         print_r($response->getBbox());
18 |         print_r($response->getMetadata());
19 |         var_dump($response->getType());
20 |         print_r($response->toArray());
21 |     } else {
22 |         echo 'Isochrone not found.';
23 |     }
24 | } catch (\Riverside\Osrm\Exception $e) {
25 |     echo $e->getMessage();
26 | } catch (Exception $e) {
27 |     echo $e->getMessage();
28 | }
29 | 


--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "riverside/php-osrm",
 3 |   "description": "PHP client for Project-OSRM.",
 4 |   "type": "library",
 5 |   "version": "2.0.0",
 6 |   "keywords": ["php", "osm", "osrm", "openstreetmap", "distance"],
 7 |   "homepage": "https://github.com/riverside/php-osrm",
 8 |   "license": "MIT",
 9 |   "authors": [
10 |     {
11 |       "name": "Dimitar Ivanov",
12 |       "email": "biggie@abv.bg",
13 |       "homepage": "https://twitter.com/DimitarIvanov",
14 |       "role": "Developer"
15 |     }
16 |   ],
17 |   "require": {
18 |     "ext-json": "*",
19 |     "ext-curl": "*",
20 |     "php": ">=7.0"
21 |   },
22 |   "require-dev": {
23 |     "phpunit/phpunit": "8"
24 |   },
25 |   "autoload": {
26 |     "psr-4": {
27 |       "Riverside\\Osrm\\": "src/"
28 |     }
29 |   },
30 |   "autoload-dev": {
31 |     "psr-4": {
32 |       "Riverside\\Osrm\\Tests\\": "tests/"
33 |     }
34 |   },
35 |   "scripts": {
36 |     "test": "vendor/bin/phpunit"
37 |   }
38 | }
39 | 


--------------------------------------------------------------------------------
/tests/Service/TileTest.php:
--------------------------------------------------------------------------------
 1 | assertClassHasAttribute($attribute, Tile::class);
21 |         }
22 |     }
23 | 
24 |     public function testTile()
25 |     {
26 |         $tile = new Tile();
27 |         $response = $tile
28 |             ->setProfile('car')
29 |             ->fetch('tile(1310,3166,13)');
30 | 
31 |         $this->assertInstanceOf(ServiceResponse::class, $response);
32 |         $this->assertTrue($response->isOK());
33 |         $this->assertSame(1273121, strlen($response->getResponse()));
34 |     }
35 | }


--------------------------------------------------------------------------------
/src/AbstractResponse.php:
--------------------------------------------------------------------------------
 1 | toArray());
27 |     }
28 | 
29 |     /**
30 |      * Gets the response data as an array
31 |      *
32 |      * @return array
33 |      */
34 |     public function toArray(): array
35 |     {
36 |         return is_array($this->data) ? $this->data : array($this->data);
37 |     }
38 | 
39 |     /**
40 |      * @return mixed
41 |      */
42 |     public function getResponse()
43 |     {
44 |         return $this->data;
45 |     }
46 | 
47 |     /**
48 |      * @return bool
49 |      */
50 |     abstract public function isOK(): bool;
51 | }
52 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2020-2024 Dimitar Ivanov
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/src/Response/Service.php:
--------------------------------------------------------------------------------
 1 | data = $service != 'tile' ? json_decode($data, true) : $data;
30 |         $this->service = $service;
31 |     }
32 | 
33 |     public function getError()
34 |     {
35 |         return !$this->isOK() ? $this->data['code'] : null;
36 |     }
37 | 
38 |     /**
39 |      * Checks if response data is correct
40 |      *
41 |      * @return bool
42 |      */
43 |     public function isOK(): bool
44 |     {
45 |         return $this->service != 'tile'
46 |             ? (isset($this->data['code']) && $this->data['code'] == 'Ok')
47 |             : true;
48 |     }
49 | }
50 | 


--------------------------------------------------------------------------------
/src/Response/Isochrones.php:
--------------------------------------------------------------------------------
 1 | data = json_decode($data, true);
30 |         $this->provider = $provider;
31 |     }
32 | 
33 |     /**
34 |      * Checks if response data is correct
35 |      *
36 |      * @return bool
37 |      */
38 |     public function isOK(): bool
39 |     {
40 |         return isset($this->data['features']) && !empty($this->data['features']);
41 |     }
42 | 
43 |     /**
44 |      * Get metadata
45 |      *
46 |      * @return array
47 |      */
48 |     public function getMetadata(): array
49 |     {
50 |         return $this->data['metadata'];
51 |     }
52 | 
53 |     /**
54 |      * Get bbox
55 |      *
56 |      * @return array
57 |      */
58 |     public function getBbox(): array
59 |     {
60 |         return $this->data['bbox'];
61 |     }
62 | 
63 |     /**
64 |      * Get features
65 |      *
66 |      * @return array
67 |      */
68 |     public function getFeatures(): array
69 |     {
70 |         return $this->data['features'];
71 |     }
72 | 
73 |     /**
74 |      * Get type
75 |      *
76 |      * @return string
77 |      */
78 |     public function getType(): string
79 |     {
80 |         return $this->data['type'];
81 |     }
82 | }
83 | 


--------------------------------------------------------------------------------
/tests/ResponseServiceTest.php:
--------------------------------------------------------------------------------
 1 | assertClassHasAttribute($attribute, ServiceResponse::class);
25 |         }
26 |     }
27 | 
28 |     public function testCode()
29 |     {
30 |         $response = new ServiceResponse($this->data, 'route');
31 | 
32 |         $this->assertTrue($response->isOK(), 'Response code should be OK');
33 |     }
34 | 
35 |     public function testError()
36 |     {
37 |         $response = new ServiceResponse($this->data, 'route');
38 | 
39 |         $this->assertEmpty($response->getError(), 'Response should not have a error');
40 |     }
41 | 
42 |     public function testArray()
43 |     {
44 |         $response = new ServiceResponse($this->data, 'route');
45 | 
46 |         $this->assertIsArray($response->toArray(), 'Response data should be an array');
47 |     }
48 | 
49 |     public function testJson()
50 |     {
51 |         $response = new ServiceResponse($this->data, 'route');
52 | 
53 |         $this->assertJson($response->toJson(), 'Response data should be json');
54 |     }
55 | }
56 | 


--------------------------------------------------------------------------------
/src/Service/Route.php:
--------------------------------------------------------------------------------
  1 | setOption('alternatives', $value);
 33 |     }
 34 | 
 35 |     /**
 36 |      * @param string $value
 37 |      * @return AbstractService
 38 |      * @throws Exception
 39 |      */
 40 |     public function setSteps(string $value): AbstractService
 41 |     {
 42 |         return $this->_setSteps($value);
 43 |     }
 44 | 
 45 |     /**
 46 |      * @param string $value
 47 |      * @return AbstractService
 48 |      * @throws Exception
 49 |      */
 50 |     public function setAnnotations(string $value): AbstractService
 51 |     {
 52 |         return $this->_setAnnotations($value);
 53 |     }
 54 | 
 55 |     /**
 56 |      * @param string $value
 57 |      * @return AbstractService
 58 |      * @throws Exception
 59 |      */
 60 |     public function setGeometries(string $value): AbstractService
 61 |     {
 62 |         return $this->_setGeometries($value);
 63 |     }
 64 | 
 65 |     /**
 66 |      * @param string $value
 67 |      * @return AbstractService
 68 |      * @throws Exception
 69 |      */
 70 |     public function setOverview(string $value): AbstractService
 71 |     {
 72 |         return $this->_setOverview($value);
 73 |     }
 74 | 
 75 |     /**
 76 |      * @param string $value
 77 |      * @return AbstractService
 78 |      * @throws Exception
 79 |      */
 80 |     public function setContinueStraight(string $value): AbstractService
 81 |     {
 82 |         if (!in_array($value, array('default', 'true', 'false')))
 83 |         {
 84 |             throw new Exception('Invalid value for $continue_straight.');
 85 |         }
 86 | 
 87 |         return $this->setOption('continue_straight', $value);
 88 |     }
 89 | 
 90 |     /**
 91 |      * @param string $value
 92 |      * @return AbstractService
 93 |      * @throws Exception
 94 |      */
 95 |     public function setWaypoints(string $value): AbstractService
 96 |     {
 97 |         return $this->_setWaypoints($value);
 98 |     }
 99 | 
100 | }


--------------------------------------------------------------------------------
/src/Service/Trip.php:
--------------------------------------------------------------------------------
  1 | _setOverview($value);
 28 |     }
 29 | 
 30 |     /**
 31 |      * @param string $value
 32 |      * @return AbstractService
 33 |      * @throws Exception
 34 |      */
 35 |     public function setSteps(string $value): AbstractService
 36 |     {
 37 |         return $this->_setSteps($value);
 38 |     }
 39 | 
 40 |     /**
 41 |      * @param string $value
 42 |      * @return AbstractService
 43 |      * @throws Exception
 44 |      */
 45 |     public function setGeometries(string $value): AbstractService
 46 |     {
 47 |         return $this->_setGeometries($value);
 48 |     }
 49 | 
 50 |     /**
 51 |      * @param string $value
 52 |      * @return AbstractService
 53 |      * @throws Exception
 54 |      */
 55 |     public function setAnnotations(string $value): AbstractService
 56 |     {
 57 |         return $this->_setAnnotations($value);
 58 |     }
 59 | 
 60 |     /**
 61 |      * @param string $value
 62 |      * @return AbstractService
 63 |      * @throws Exception
 64 |      */
 65 |     public function setSource(string $value): AbstractService
 66 |     {
 67 |         if (!in_array($value, array('any', 'first')))
 68 |         {
 69 |             throw new Exception('Invalid value for $source.');
 70 |         }
 71 | 
 72 |         return $this->setOption('source', $value);
 73 |     }
 74 | 
 75 |     /**
 76 |      * @param string $value
 77 |      * @return AbstractService
 78 |      * @throws Exception
 79 |      */
 80 |     public function setDestination(string $value): AbstractService
 81 |     {
 82 |         if (!in_array($value, array('any', 'last')))
 83 |         {
 84 |             throw new Exception('Invalid value for $destination.');
 85 |         }
 86 | 
 87 |         return $this->setOption('destination', $value);
 88 |     }
 89 | 
 90 |     /**
 91 |      * @param string $value
 92 |      * @return AbstractService
 93 |      * @throws Exception
 94 |      */
 95 |     public function setRoundtrip(string $value): AbstractService
 96 |     {
 97 |         if (!in_array($value, array('true', 'false')))
 98 |         {
 99 |             throw new Exception('Invalid value for $roundtrip.');
100 |         }
101 | 
102 |         return $this->setOption('roundtrip', $value);
103 |     }
104 | }


--------------------------------------------------------------------------------
/src/Service/Matcher.php:
--------------------------------------------------------------------------------
  1 | _setSteps($value);
 28 |     }
 29 | 
 30 |     /**
 31 |      * @param string $value
 32 |      * @return AbstractService
 33 |      * @throws Exception
 34 |      */
 35 |     public function setGeometries(string $value): AbstractService
 36 |     {
 37 |         return $this->_setGeometries($value);
 38 |     }
 39 | 
 40 |     /**
 41 |      * @param string $value
 42 |      * @return AbstractService
 43 |      * @throws Exception
 44 |      */
 45 |     public function setAnnotations(string $value): AbstractService
 46 |     {
 47 |         return $this->_setAnnotations($value);
 48 |     }
 49 | 
 50 |     /**
 51 |      * @param string $value
 52 |      * @return AbstractService
 53 |      * @throws Exception
 54 |      */
 55 |     public function setOverview(string $value): AbstractService
 56 |     {
 57 |         return $this->_setOverview($value);
 58 |     }
 59 | 
 60 |     /**
 61 |      * @param string $value
 62 |      * @return AbstractService
 63 |      * @throws Exception
 64 |      */
 65 |     public function setTimestamps(string $value): AbstractService
 66 |     {
 67 |         if (!preg_match('/^\d{10}(?:;\d{10})*$/', $value))
 68 |         {
 69 |             throw new Exception('Invalid value for $timestamps.');
 70 |         }
 71 | 
 72 |         return $this->setOption('timestamps', $value);
 73 |     }
 74 | 
 75 |     /**
 76 |      * @param string $value
 77 |      * @return AbstractService
 78 |      * @throws Exception
 79 |      */
 80 |     public function setGaps(string $value): AbstractService
 81 |     {
 82 |         if (!in_array($value, array('split', 'ignore')))
 83 |         {
 84 |             throw new Exception('Invalid value for $gaps.');
 85 |         }
 86 | 
 87 |         return $this->setOption('gaps', $value);
 88 |     }
 89 | 
 90 |     /**
 91 |      * @param string $value
 92 |      * @return AbstractService
 93 |      * @throws Exception
 94 |      */
 95 |     public function setTidy(string $value): AbstractService
 96 |     {
 97 |         if (!in_array($value, array('true', 'false')))
 98 |         {
 99 |             throw new Exception('Invalid value for $tidy.');
100 |         }
101 | 
102 |         return $this->setOption('tidy', $value);
103 |     }
104 | 
105 |     /**
106 |      * @param string $value
107 |      * @return AbstractService
108 |      * @throws Exception
109 |      */
110 |     public function setWaypoints(string $value): AbstractService
111 |     {
112 |         return $this->_setWaypoints($value);
113 |     }
114 | }


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | # php-osrm
 2 | PHP client for Project-OSRM.
 3 | 
 4 | | Build | Stable | License |
 5 | | ----- | ------ | ------- |
 6 | | [![CI][x1]][y1] | [![Latest Stable Version][x2]][y2] | [![License][x3]][y3] |
 7 | 
 8 | ## Installation
 9 | - If Composer is already installed
10 | ```
11 | composer require riverside/php-osrm
12 | ```
13 | - If Composer is not installed on your system yet, you may go ahead and install it using this command line:
14 | ```
15 | $ curl -sS https://getcomposer.org/installer | php
16 | ```
17 | Next, add the following require entry to the `composer.json` file in the root of your project.
18 | ```json
19 | {
20 |     "require" : {
21 |         "riverside/php-osrm" : "^2.0"
22 |     }
23 | }
24 | ```
25 | Finally, use Composer to install **php-osrm** and its dependencies:
26 | ```
27 | $ php composer.phar install
28 | ```
29 | 
30 | ## Loading
31 | ```php
32 | require __DIR__ . '/vendor/autoload.php';
33 | ```
34 | 
35 | ## API
36 | - [Nearest][1] - Snaps a coordinate to the street network and returns the nearest `n` matches.
37 | - [Route][2] - Finds the fastest route between coordinates in the supplied order.
38 | - [Table][3] - Computes the duration of the fastest route between all pairs of supplied coordinates. Returns the durations or distances or both between the coordinate pairs.
39 | - [Match][4] - Map matching matches/snaps given GPS points to the road network in the most plausible way.
40 | - [Trip][5] - Solves the Traveling Salesman Problem using a greedy heuristic (farthest-insertion algorithm) for 10 or more waypoints and uses brute force for less than 10 waypoints.
41 | - [Tile][6] - Generates [Mapbox Vector Tiles][8] that can be viewed with a vector-tile capable slippy-map viewer.
42 | - [Isochrones][7] - Generates travel time isochrones in GeoJSON format.
43 | 
44 | ## Links
45 | - [Project-OSRM (official website)][9]
46 | - [Project-OSRM (GitHub)][10]
47 | - [OSRM (OpenStreetMap.org)][11]
48 | - [ORS (OpenRouteService.org)][12]
49 | 
50 | [1]: https://github.com/riverside/php-osrm/tree/master/examples/nearest.php
51 | [2]: https://github.com/riverside/php-osrm/tree/master/examples/route.php
52 | [3]: https://github.com/riverside/php-osrm/tree/master/examples/table.php
53 | [4]: https://github.com/riverside/php-osrm/tree/master/examples/matcher.php
54 | [5]: https://github.com/riverside/php-osrm/tree/master/examples/trip.php
55 | [6]: https://github.com/riverside/php-osrm/tree/master/examples/tile.php
56 | [7]: https://github.com/riverside/php-osrm/tree/master/examples/isochrones.php
57 | [8]: https://docs.mapbox.com/api/maps/vector-tiles/
58 | [9]: https://project-osrm.org/
59 | [10]: https://github.com/Project-OSRM
60 | [11]: https://wiki.openstreetmap.org/wiki/Open_Source_Routing_Machine
61 | [12]: https://openrouteservice.org/
62 | [x1]: https://github.com/riverside/php-osrm/actions/workflows/test.yml/badge.svg
63 | [y1]: https://github.com/riverside/php-osrm/actions/workflows/test.yml
64 | [x2]: https://poser.pugx.org/riverside/php-osrm/v/stable
65 | [y2]: https://packagist.org/packages/riverside/php-osrm
66 | [x3]: https://poser.pugx.org/riverside/php-osrm/license
67 | [y3]: https://packagist.org/packages/riverside/php-osrm


--------------------------------------------------------------------------------
/src/Service/Table.php:
--------------------------------------------------------------------------------
  1 | setOption('sources', $value);
 33 |     }
 34 | 
 35 |     /**
 36 |      * @param string $value
 37 |      * @return AbstractService
 38 |      * @throws Exception
 39 |      */
 40 |     public function setDestinations(string $value): AbstractService
 41 |     {
 42 |         if (!($value == 'all' || preg_match('/^\d+(?:;\d+)*$/', $value)))
 43 |         {
 44 |             throw new Exception('Invalid value for $destinations.');
 45 |         }
 46 | 
 47 |         return $this->setOption('destinations', $value);
 48 |     }
 49 | 
 50 |     /**
 51 |      * @param string $value
 52 |      * @return AbstractService
 53 |      * @throws Exception
 54 |      */
 55 |     public function setAnnotations(string $value): AbstractService
 56 |     {
 57 |         if (!in_array($value, array('duration', 'distance', 'duration,distance')))
 58 |         {
 59 |             throw new Exception('Invalid value for $annotations.');
 60 |         }
 61 | 
 62 |         return $this->setOption('annotations', $value);
 63 |     }
 64 | 
 65 |     /**
 66 |      * @param float $value
 67 |      * @return AbstractService
 68 |      * @throws Exception
 69 |      */
 70 |     public function setFallbackSpeed(float $value): AbstractService
 71 |     {
 72 |         if (!is_float($value))
 73 |         {
 74 |             throw new Exception('Invalid value for $fallback_speed.');
 75 |         }
 76 | 
 77 |         return $this->setOption('fallback_speed', $value);
 78 |     }
 79 | 
 80 |     /**
 81 |      * @param string $value
 82 |      * @return AbstractService
 83 |      * @throws Exception
 84 |      */
 85 |     public function setFallbackCoordinate(string $value): AbstractService
 86 |     {
 87 |         if (!in_array($value, array('input', 'snapped')))
 88 |         {
 89 |             throw new Exception('Invalid value for $fallback_coordinate.');
 90 |         }
 91 | 
 92 |         return $this->setOption('fallback_coordinate', $value);
 93 |     }
 94 | 
 95 |     /**
 96 |      * @param float $value
 97 |      * @return AbstractService
 98 |      * @throws Exception
 99 |      */
100 |     public function setScaleFactor(float $value): AbstractService
101 |     {
102 |         if (!is_float($value))
103 |         {
104 |             throw new Exception('Invalid value for $scale_factor.');
105 |         }
106 | 
107 |         return $this->setOption('scale_factor', $value);
108 |     }
109 | }


--------------------------------------------------------------------------------
/tests/TransportTest.php:
--------------------------------------------------------------------------------
 1 | assertClassHasAttribute($attribute, Transport::class);
31 |         }
32 |     }
33 | 
34 |     public function testSetters()
35 |     {
36 |         $transport = new Transport();
37 | 
38 |         $this->assertInstanceOf(Transport::class, $transport->setConnectTimeout(30));
39 |         $this->assertInstanceOf(Transport::class, $transport->setData(''));
40 |         $this->assertInstanceOf(Transport::class, $transport->setMethod('GET'));
41 |         $this->assertInstanceOf(Transport::class, $transport->setSslVerifyPeer(true));
42 |         $this->assertInstanceOf(Transport::class, $transport->setTimeout(30));
43 |         $this->assertInstanceOf(Transport::class, $transport->setUserAgent('Chrome'));
44 |         $this->assertInstanceOf(Transport::class, $transport->addHeader('Accept: application/json'));
45 |     }
46 | 
47 |     public function testService()
48 |     {
49 |         $transport = new Transport();
50 | 
51 |         $this->assertInstanceOf(Transport::class, $transport->request('http://router.project-osrm.org/route/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?overview=false'));
52 |         $this->assertIsInt($transport->getHttpCode());
53 |         $this->assertSame(200, $transport->getHttpCode());
54 |         $this->assertNotEmpty($transport->getResponse());
55 |     }
56 | 
57 |     public function testException()
58 |     {
59 |         $transport = new Transport();
60 | 
61 |         $transport
62 |             ->setMethod('POST')
63 |             ->setData('{"locations":[[8.681495,49.41461],[8.686507,49.41943]],	"range":[300,200]}')
64 |             ->addHeader('Content-Type: application/json; charset=utf-8')
65 |             ->addHeader('Accept: application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8');
66 | 
67 |         $this->expectException(Exception::class);
68 | 
69 |         $transport->request('https://api.openrouteservice.org/v2/isochrones/driving-car');
70 |     }
71 | 
72 |     public function testIsochrones()
73 |     {
74 |         $transport = new Transport();
75 | 
76 |         $transport
77 |             ->setMethod('POST')
78 |             ->setData('{"locations":[[8.681495,49.41461],[8.686507,49.41943]],	"range":[300,200]}')
79 |             ->addHeader('Content-Type: application/json; charset=utf-8')
80 |             ->addHeader('Accept: application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8');
81 | 
82 |         try {
83 |             $transport->request('https://api.openrouteservice.org/v2/isochrones/driving-car');
84 |         } catch (Exception $e) {
85 |             $this->assertIsInt($transport->getHttpCode());
86 |             $this->assertSame(401, $transport->getHttpCode());
87 |             $this->assertNotEmpty($transport->getResponse());
88 |         }
89 |     }
90 | }
91 | 


--------------------------------------------------------------------------------
/tests/ResponseIsochronesTest.php:
--------------------------------------------------------------------------------
 1 | assertClassHasAttribute($attribute, IsochronesResponse::class);
25 |         }
26 |     }
27 | 
28 |     public function testCode()
29 |     {
30 |         $response = new IsochronesResponse($this->data, 'openrouteservice');
31 | 
32 |         $this->assertTrue($response->isOK(), 'Response code should be OK');
33 |     }
34 | 
35 |     public function testArray()
36 |     {
37 |         $response = new IsochronesResponse($this->data, 'openrouteservice');
38 | 
39 |         $this->assertIsArray($response->toArray(), 'Response data should be an array');
40 |     }
41 | 
42 |     public function testJson()
43 |     {
44 |         $response = new IsochronesResponse($this->data, 'openrouteservice');
45 | 
46 |         $this->assertJson($response->toJson(), 'Response data should be json');
47 |     }
48 | 
49 |     public function testType()
50 |     {
51 |         $response = new IsochronesResponse($this->data, 'openrouteservice');
52 | 
53 |         $this->assertSame('FeatureCollection', $response->getType());
54 |     }
55 | 
56 |     public function testMetadata()
57 |     {
58 |         $response = new IsochronesResponse($this->data, 'openrouteservice');
59 | 
60 |         $this->assertIsArray($response->getMetadata());
61 |     }
62 | 
63 |     public function testBbox()
64 |     {
65 |         $response = new IsochronesResponse($this->data, 'openrouteservice');
66 | 
67 |         $this->assertIsArray($response->getBbox());
68 |     }
69 | 
70 |     public function testFeatures()
71 |     {
72 |         $response = new IsochronesResponse($this->data, 'openrouteservice');
73 | 
74 |         $this->assertIsArray($response->getFeatures());
75 |     }
76 | }


--------------------------------------------------------------------------------
/src/AbstractService.php:
--------------------------------------------------------------------------------
  1 | coordinates = $coordinates;
 52 | 
 53 |         $transport = new Transport();
 54 |         $transport->request($this->getUri());
 55 | 
 56 |         return new ServiceResponse($transport->getResponse(), $this->service);
 57 |     }
 58 | 
 59 |     /**
 60 |      * @return string
 61 |      */
 62 |     public function getUri(): string
 63 |     {
 64 |         $uri = "http://router.project-osrm.org/{$this->service}/{$this->version}/{$this->profile}/{$this->coordinates}.{$this->format}";
 65 |         if ($this->options)
 66 |         {
 67 |             $uri .= "?" . http_build_query($this->options, "", '&');
 68 |         }
 69 | 
 70 |         return $uri;
 71 |     }
 72 | 
 73 |     /**
 74 |      * @param string $key
 75 |      * @param mixed $value
 76 |      * @return AbstractService
 77 |      */
 78 |     public function setOption(string $key, $value): AbstractService
 79 |     {
 80 |         $this->options[$key] = $value;
 81 | 
 82 |         return $this;
 83 |     }
 84 | 
 85 |     /**
 86 |      * @param string $value
 87 |      * @return AbstractService
 88 |      */
 89 |     public function setProfile(string $value): AbstractService
 90 |     {
 91 |         $this->profile = $value;
 92 | 
 93 |         return $this;
 94 |     }
 95 | 
 96 |     /**
 97 |      * @param string $value
 98 |      * @return AbstractService
 99 |      */
100 |     public function setVersion(string $value): AbstractService
101 |     {
102 |         $this->version = $value;
103 | 
104 |         return $this;
105 |     }
106 | 
107 |     /**
108 |      * @param string $value
109 |      * @return AbstractService
110 |      */
111 |     public function setBearings(string $value): AbstractService
112 |     {
113 |         return $this->setOption('bearings', $value);
114 |     }
115 | 
116 |     /**
117 |      * @param string $value
118 |      * @return AbstractService
119 |      */
120 |     public function setRadiuses(string $value): AbstractService
121 |     {
122 |         return $this->setOption('radiuses', $value);
123 |     }
124 | 
125 |     /**
126 |      * @param string $value
127 |      * @return AbstractService
128 |      * @throws Exception
129 |      */
130 |     public function setGenerateHints(string $value): AbstractService
131 |     {
132 |         if (!in_array($value, array('true', 'false')))
133 |         {
134 |             throw new Exception('Invalid value for $generate_hints.');
135 |         }
136 | 
137 |         return $this->setOption('generate_hints', $value);
138 |     }
139 | 
140 |     /**
141 |      * @param string $value
142 |      * @return AbstractService
143 |      */
144 |     public function setHints(string $value): AbstractService
145 |     {
146 |         return $this->setOption('hints', $value);
147 |     }
148 | 
149 |     /**
150 |      * @param string $value
151 |      * @return AbstractService
152 |      * @throws Exception
153 |      */
154 |     public function setApproaches(string $value): AbstractService
155 |     {
156 |         if (!preg_match('/^(?:curb|unrestricted)(?:;(?:curb|unrestricted))*$/', $value))
157 |         {
158 |             throw new Exception('Invalid value for $approaches.');
159 |         }
160 | 
161 |         return $this->setOption('approaches', $value);
162 |     }
163 | 
164 |     /**
165 |      * @param string $value
166 |      * @return AbstractService
167 |      */
168 |     public function setExclude(string $value): AbstractService
169 |     {
170 |         return $this->setOption('exclude', $value);
171 |     }
172 | 
173 |     /**
174 |      * @param string $value
175 |      * @return AbstractService
176 |      * @throws Exception
177 |      */
178 |     protected function _setGeometries(string $value): AbstractService
179 |     {
180 |         if (!in_array($value, array('polyline', 'polyline6', 'geojson')))
181 |         {
182 |             throw new Exception('Invalid value for $geometries.');
183 |         }
184 | 
185 |         return $this->setOption('geometries', $value);
186 |     }
187 | 
188 |     /**
189 |      * @param string $value
190 |      * @return AbstractService
191 |      * @throws Exception
192 |      */
193 |     protected function _setSteps(string $value): AbstractService
194 |     {
195 |         if (!in_array($value, array('true', 'false')))
196 |         {
197 |             throw new Exception('Invalid value for $steps.');
198 |         }
199 | 
200 |         return $this->setOption('steps', $value);
201 |     }
202 | 
203 |     /**
204 |      * @param string $value
205 |      * @return AbstractService
206 |      * @throws Exception
207 |      */
208 |     protected function _setAnnotations(string $value): AbstractService
209 |     {
210 |         if (!in_array($value, array('true', 'false', 'nodes', 'distance', 'duration', 'datasources', 'weight', 'speed')))
211 |         {
212 |             throw new Exception('Invalid value for $annotations.');
213 |         }
214 | 
215 |         return $this->setOption('annotations', $value);
216 |     }
217 | 
218 |     /**
219 |      * @param string $value
220 |      * @return AbstractService
221 |      * @throws Exception
222 |      */
223 |     protected function _setOverview(string $value): AbstractService
224 |     {
225 |         if (!in_array($value, array('simplified', 'full', 'false')))
226 |         {
227 |             throw new Exception('Invalid value for $overview.');
228 |         }
229 | 
230 |         return $this->setOption('overview', $value);
231 |     }
232 | 
233 |     /**
234 |      * @param string $value
235 |      * @return AbstractService
236 |      * @throws Exception
237 |      */
238 |     protected function _setWaypoints(string $value): AbstractService
239 |     {
240 |         if (!preg_match('/^\d+(?:(?:;\d+)?)+$/', $value))
241 |         {
242 |             throw new Exception('Invalid value for $waypoints.');
243 |         }
244 | 
245 |         return $this->setOption('waypoints', $value);
246 |     }
247 | }
248 | 


--------------------------------------------------------------------------------
/src/Transport.php:
--------------------------------------------------------------------------------
  1 | userAgent);
 90 |         curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectTimeout);
 91 |         curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
 92 |         curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 93 |         curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
 94 |         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->sslVerifyPeer);
 95 |         curl_setopt($ch, CURLOPT_HEADER, false);
 96 |         curl_setopt($ch, CURLOPT_AUTOREFERER, false);
 97 | 
 98 |         $post_fields = $this->getData();
 99 | 
100 |         switch ($this->getMethod())
101 |         {
102 |             case 'POST':
103 |                 curl_setopt($ch, CURLOPT_POST, true);
104 |                 if ($post_fields && is_array($post_fields))
105 |                 {
106 |                     curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: multipart/form-data"));
107 |                 }
108 |                 curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
109 |                 break;
110 |             case 'DELETE':
111 |                 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
112 |                 if (!empty($post_fields))
113 |                 {
114 |                     $url = "{$url}?{$post_fields}";
115 |                 }
116 |                 break;
117 |             case 'HEAD':
118 |                 curl_setopt($ch, CURLOPT_NOBODY, true);
119 |                 break;
120 |         }
121 | 
122 |         $headers = $this->getHeaders();
123 |         if (!empty($headers))
124 |         {
125 |             curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
126 |         }
127 | 
128 |         curl_setopt($ch, CURLOPT_URL, $url);
129 |         $this->response = curl_exec($ch);
130 |         $this->httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
131 | 
132 |         if ($this->httpCode != 200)
133 |         {
134 |             throw new Exception("HTTP status code: {$this->httpCode}. Response: {$this->response}");
135 |         }
136 | 
137 |         if (curl_errno($ch) == 28)
138 |         {
139 |             throw new Exception('Timeout');
140 |         }
141 |         curl_close($ch);
142 | 
143 |         return $this;
144 |     }
145 | 
146 |     /**
147 |      * Get POST data
148 |      *
149 |      * @return array|string
150 |      */
151 |     public function getData()
152 |     {
153 |         return $this->data;
154 |     }
155 | 
156 |     /**
157 |      * Get HTTP status code
158 |      *
159 |      * @return int
160 |      */
161 |     public function getHttpCode(): int
162 |     {
163 |         return $this->httpCode;
164 |     }
165 | 
166 |     /**
167 |      * Get HTTP verb
168 |      *
169 |      * @return string
170 |      */
171 |     public function getMethod(): string
172 |     {
173 |         return $this->method;
174 |     }
175 | 
176 |     /**
177 |      * Get response
178 |      *
179 |      * @return mixed
180 |      */
181 |     public function getResponse()
182 |     {
183 |         return $this->response;
184 |     }
185 | 
186 |     /**
187 |      * Set connection timeout
188 |      *
189 |      * @param int $value
190 |      * @return Transport
191 |      */
192 |     public function setConnectTimeout(int $value): Transport
193 |     {
194 |         $this->connectTimeout = (int) $value;
195 | 
196 |         return $this;
197 |     }
198 | 
199 |     /**
200 |      * Set POST data
201 |      *
202 |      * @param array|string $data
203 |      * @param bool $encode_string
204 |      * @return Transport
205 |      */
206 |     public function setData($data, bool $encode_string=true): Transport
207 |     {
208 |         if (is_array($data) && $encode_string)
209 |         {
210 |             if (version_compare(phpversion(), '5.1.2', '>='))
211 |             {
212 |                 $data = http_build_query($data, NULL, '&');
213 |             } else {
214 |                 $data = http_build_query($data);
215 |             }
216 |         }
217 |         $this->data = $data;
218 | 
219 |         return $this;
220 |     }
221 | 
222 |     /**
223 |      * Set HTTP verb
224 |      *
225 |      * @param string $method
226 |      * @return Transport
227 |      */
228 |     public function setMethod(string $method): Transport
229 |     {
230 |         $this->method = strtoupper($method);
231 | 
232 |         return $this;
233 |     }
234 | 
235 |     /**
236 |      * Set timeout
237 |      *
238 |      * @param int $value
239 |      * @return Transport
240 |      */
241 |     public function setTimeout(int $value): Transport
242 |     {
243 |         $this->timeout = (int) $value;
244 | 
245 |         return $this;
246 |     }
247 | 
248 |     /**
249 |      * Set user agent
250 |      *
251 |      * @param string $value
252 |      * @return Transport
253 |      */
254 |     public function setUserAgent(string $value): Transport
255 |     {
256 |         $this->userAgent = $value;
257 | 
258 |         return $this;
259 |     }
260 | 
261 |     /**
262 |      * Set SSL verify peer
263 |      *
264 |      * @param bool $value
265 |      * @return Transport
266 |      */
267 |     public function setSslVerifyPeer(bool $value): Transport
268 |     {
269 |         $this->sslVerifyPeer = (bool) $value;
270 | 
271 |         return $this;
272 |     }
273 | 
274 |     /**
275 |      * Set request headers
276 |      *
277 |      * @param array $headers
278 |      * @return Transport
279 |      */
280 |     public function setHeaders(array $headers): Transport
281 |     {
282 |         $this->headers = [];
283 | 
284 |         foreach (self::flattenHeaders($headers) as $header)
285 |         {
286 |             $this->addHeader($header);
287 |         }
288 | 
289 |         return $this;
290 |     }
291 | 
292 |     /**
293 |      * Add request header
294 |      *
295 |      * @param string $header
296 |      * @return Transport
297 |      */
298 |     public function addHeader(string $header): Transport
299 |     {
300 |         if (0 === stripos(substr($header, -8), 'HTTP/1.') && 3 == count($parts = explode(' ', $header)))
301 |         {
302 |             list($method, ) = $parts;
303 | 
304 |             $this->setMethod($method);
305 |         } else {
306 |             $this->headers[] = $header;
307 |         }
308 |         return $this;
309 |     }
310 | 
311 |     /**
312 |      * Get request headers
313 |      *
314 |      * @return array
315 |      */
316 |     public function getHeaders(): array
317 |     {
318 |         return $this->headers;
319 |     }
320 | 
321 |     /**
322 |      * Flatten headers
323 |      *
324 |      * @param array $headers
325 |      * @return array
326 |      */
327 |     protected static function flattenHeaders(array $headers): array
328 |     {
329 |         $flattened = [];
330 |         foreach ($headers as $key => $header)
331 |         {
332 |             if (is_int($key))
333 |             {
334 |                 $flattened[] = $header;
335 |             } else {
336 |                 $flattened[] = $key.': '.$header;
337 |             }
338 |         }
339 | 
340 |         return $flattened;
341 |     }
342 | }
343 | 


--------------------------------------------------------------------------------
/src/Isochrones/OpenRouteService.php:
--------------------------------------------------------------------------------
  1 | setMethod('POST')
 91 |             ->setData(json_encode($this->body))
 92 |             ->addHeader("Authorization: {$this->apiKey}")
 93 |             ->addHeader('Content-Type: application/json; charset=utf-8')
 94 |             ->addHeader('Accept: application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8')
 95 |             ->request($this->getUri());
 96 | 
 97 |         return new IsochronesResponse($transport->getResponse(), $this->provider);
 98 |     }
 99 | 
100 |     /**
101 |      * Gets an API endpoint
102 |      *
103 |      * @return string
104 |      */
105 |     public function getUri(): string
106 |     {
107 |         return str_replace('{profile}', $this->profile, $this->uri);
108 |     }
109 | 
110 |     /**
111 |      * Sets an apiKey
112 |      *
113 |      * @param string $apiKey
114 |      * @return OpenRouteService
115 |      */
116 |     public function setApiKey(string $apiKey): OpenRouteService
117 |     {
118 |         $this->apiKey = $apiKey;
119 | 
120 |         return $this;
121 |     }
122 | 
123 |     /**
124 |      * Sets a profile
125 |      *
126 |      * @param string $profile
127 |      * @return OpenRouteService
128 |      * @throws Exception
129 |      */
130 |     public function setProfile(string $profile): OpenRouteService
131 |     {
132 |         if (!in_array($profile, $this->profiles))
133 |         {
134 |             throw new Exception("Invalid {$profile} profile");
135 |         }
136 | 
137 |         $this->profile = $profile;
138 | 
139 |         return $this;
140 |     }
141 | 
142 |     /**
143 |      * Set locations
144 |      *
145 |      * @param array $locations
146 |      * @return OpenRouteService
147 |      */
148 |     public function setLocations(array $locations): OpenRouteService
149 |     {
150 |         $this->body['locations'] = $locations;
151 | 
152 |         return $this;
153 |     }
154 | 
155 |     /**
156 |      * Set range
157 |      *
158 |      * @param array $range
159 |      * @return OpenRouteService
160 |      */
161 |     public function setRange(array $range): OpenRouteService
162 |     {
163 |         $this->body['range'] = $range;
164 | 
165 |         return $this;
166 |     }
167 | 
168 |     /**
169 |      * Set attributes
170 |      *
171 |      * @param array $attributes
172 |      * @return OpenRouteService
173 |      * @throws Exception
174 |      */
175 |     public function setAttributes(array $attributes): OpenRouteService
176 |     {
177 |         $diff = array_diff($attributes, self::ATTRIBUTES);
178 |         if ($diff)
179 |         {
180 |             throw new Exception("'" . join("', '", $diff) . "' are not valid attributes.");
181 |         }
182 |         $this->body['attributes'] = $attributes;
183 | 
184 |         return $this;
185 |     }
186 | 
187 |     /**
188 |      * Set id
189 |      *
190 |      * @param string $id
191 |      * @return OpenRouteService
192 |      */
193 |     public function setId(string $id): OpenRouteService
194 |     {
195 |         $this->body['id'] = $id;
196 | 
197 |         return $this;
198 |     }
199 | 
200 |     /**
201 |      * Set intersections
202 |      *
203 |      * @param bool $intersections
204 |      * @return OpenRouteService
205 |      */
206 |     public function setIntersections(bool $intersections): OpenRouteService
207 |     {
208 |         $this->body['intersections'] = $intersections;
209 | 
210 |         return $this;
211 |     }
212 | 
213 |     /**
214 |      * Set interval
215 |      *
216 |      * @param float $interval
217 |      * @return OpenRouteService
218 |      */
219 |     public function setInterval(float $interval): OpenRouteService
220 |     {
221 |         $this->body['interval'] = $interval;
222 | 
223 |         return $this;
224 |     }
225 | 
226 |     /**
227 |      * Set smoothing
228 |      *
229 |      * @param float $smoothing
230 |      * @return OpenRouteService
231 |      */
232 |     public function setSmoothing(float $smoothing): OpenRouteService
233 |     {
234 |         $this->body['smoothing'] = $smoothing;
235 | 
236 |         return $this;
237 |     }
238 | 
239 |     /**
240 |      * Set area units
241 |      *
242 |      * @param string $area_units
243 |      * @return OpenRouteService
244 |      * @throws Exception
245 |      */
246 |     public function setAreaUnits(string $area_units): OpenRouteService
247 |     {
248 |         if (!in_array($area_units, self::UNITS))
249 |         {
250 |             throw new Exception("{$area_units} is not valid unit.");
251 |         }
252 |         $this->body['area_units'] = $area_units;
253 | 
254 |         return $this;
255 |     }
256 | 
257 |     /**
258 |      * Set location type
259 |      *
260 |      * @param string $location_type
261 |      * @return OpenRouteService
262 |      * @throws Exception
263 |      */
264 |     public function setLocationType(string $location_type): OpenRouteService
265 |     {
266 |         if (!in_array($location_type, self::LOCATION_TYPE))
267 |         {
268 |             throw new Exception("{$location_type} is not valid location type.");
269 |         }
270 |         $this->body['location_type'] = $location_type;
271 | 
272 |         return $this;
273 |     }
274 | 
275 |     /**
276 |      * Set options
277 |      *
278 |      * @param array $options
279 |      * @return OpenRouteService
280 |      * @throws Exception
281 |      */
282 |     public function setOptions(array $options): OpenRouteService
283 |     {
284 |         $diff = array_diff(array_keys($options), self::OPTIONS);
285 |         if ($diff)
286 |         {
287 |             throw new Exception("'" . join("', '", $diff) . "' are not valid options.");
288 |         }
289 |         $this->body['options'] = $options;
290 | 
291 |         return $this;
292 |     }
293 | 
294 |     /**
295 |      * Set range type
296 |      *
297 |      * @param string $range_type
298 |      * @return OpenRouteService
299 |      * @throws Exception
300 |      */
301 |     public function setRangeType(string $range_type): OpenRouteService
302 |     {
303 |         if (!in_array($range_type, self::RANGE_TYPE))
304 |         {
305 |             throw new Exception("{$range_type} is not valid range type.");
306 |         }
307 |         $this->body['range_type'] = $range_type;
308 | 
309 |         return $this;
310 |     }
311 | 
312 |     /**
313 |      * Set units
314 |      *
315 |      * @param string $units
316 |      * @return OpenRouteService
317 |      * @throws Exception
318 |      */
319 |     public function setUnits(string $units): OpenRouteService
320 |     {
321 |         if (!in_array($units, self::UNITS))
322 |         {
323 |             throw new Exception("{$units} is not valid unit.");
324 |         }
325 |         $this->body['units'] = $units;
326 | 
327 |         return $this;
328 |     }
329 | }
330 | 


--------------------------------------------------------------------------------