├── .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 |
';
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 |
--------------------------------------------------------------------------------