From 45ab65587d790d71f760cd5040257f8be11b23be Mon Sep 17 00:00:00 2001 From: Bertrand PINEL <bpinel@ippon.fr> Date: Wed, 7 Oct 2020 14:42:31 +0200 Subject: [PATCH] Follow road using Mapbox WS API --- app/components/path-editor.js | 128 +++++++++++++++----------- app/services/mapbox-ws.js | 85 +++++++++++++++++ config/environment.js | 1 + package-lock.json | 11 ++- package.json | 3 + tests/unit/services/mapbox-ws-test.js | 12 +++ 6 files changed, 184 insertions(+), 56 deletions(-) create mode 100644 app/services/mapbox-ws.js create mode 100644 tests/unit/services/mapbox-ws-test.js diff --git a/app/components/path-editor.js b/app/components/path-editor.js index e9e6238..60f725b 100644 --- a/app/components/path-editor.js +++ b/app/components/path-editor.js @@ -1,58 +1,78 @@ import Component from '@glimmer/component'; -import { tracked } from '@glimmer/tracking'; -import { action } from '@ember/object'; +import {tracked} from '@glimmer/tracking'; +import {action} from '@ember/object'; import mapboxgl from 'mapbox-gl'; - + +import {inject as service} from '@ember/service'; + + export default class PathEditorComponent extends Component { - - @tracked lat = 0 - @tracked lng = 0 - @tracked polyline = [] - startPoint - geoJsonPolyline = { - "geometry": { - "coordinates": this.polyline, - "type": "LineString" - }, - "type": "Feature", - "properties": { - "name": "Cyclo path" - } - } - initLayer = false - - @action - async mapClicked({ target:map, point}) { - let clickCoord = map.unproject(point); - this.lat = clickCoord.lat; - this.lng = clickCoord.lng; - let newCoord = [this.lng, this.lat]; - this.polyline.push(newCoord); - - if (this.polyline.length == 1) { - // First point special processing --> add a marker as starting point - this.startPoint = new mapboxgl.Marker() - .setLngLat([this.lng, this.lat]).addTo(map); - } else { - if (!this.initLayer) { - map.addSource('cycling-path', { 'type': 'geojson', 'data': this.geoJsonPolyline }); - map.addLayer({ - 'id': 'Drawn_cycling_path', - 'type': 'line', - 'source': 'cycling-path', - 'layout': { - 'line-cap': 'round' - }, - 'paint': { - 'line-dasharray': [1, 2], - 'line-color': '#f77', - 'line-width': 3 - } - }); - this.initLayer=true; - } else { - map.getSource('cycling-path').setData(this.geoJsonPolyline); - } - } - } + + @service mapboxWs; + + @tracked lat = 0 + @tracked lng = 0 + @tracked polyline = [] + startPoint + geoJsonPolyline = { + "geometry": { + "coordinates": this.polyline, + "type": "LineString" + }, + "type": "Feature", + "properties": { + "name": "Cyclo path" + } + } + initLayer = false + + @action async mapClicked({target: map, point}) { + let clickCoord = map.unproject(point); + this.lat = clickCoord.lat; + this.lng = clickCoord.lng; + let newCoord = [this.lng, this.lat]; + this.polyline.push(newCoord); + + if (this.polyline.length == 1) { // First point special processing --> add a marker as starting point + this.startPoint = new mapboxgl.Marker().setLngLat([this.lng, this.lat]).addTo(map); + } else { + /**** Addition for following roads */ + // complete polyline by first retrieving new path though mapbox service + let previousCoord = this.polyline[this.polyline.length - 2]; + let path = await this.mapboxWs.direction("cycling", previousCoord, newCoord); + if (path.length > 0) { // Insert intermediate coord in the polyline + let last = this.polyline.pop(); + for (let i = 0; i < path.length; i++) { + this.polyline.push(path[i]); + } + } + // Don't put back last point that could be outside the road + // this.polyline.push(last); + /** End of addition */ + if (!this.initLayer) { + map.addSource('cycling-path', { + 'type': 'geojson', + 'data': this.geoJsonPolyline + }); + map.addLayer({ + 'id': 'Drawn_cycling_path', + 'type': 'line', + 'source': 'cycling-path', + 'layout': { + 'line-cap': 'round' + }, + 'paint': { + 'line-dasharray': [ + 1, 2 + ], + 'line-color': '#f77', + 'line-width': 3 + } + }); + this.initLayer = true; + } else { + map.getSource('cycling-path').setData(this.geoJsonPolyline); + } + } + } } diff --git a/app/services/mapbox-ws.js b/app/services/mapbox-ws.js new file mode 100644 index 0000000..ef7dafb --- /dev/null +++ b/app/services/mapbox-ws.js @@ -0,0 +1,85 @@ +import Service from '@ember/service'; +import axios from 'axios'; +import config from '../config/environment'; + +export default class MapboxWsService extends Service { + baseUrl = 'https://api.mapbox.com'; + + token = config.mapboxToken; + + init() { + super.init(...arguments); + + this.axios = axios.create(this.config()); + + this.load(this.axios); + + this.request = this.axios.get; + this.get = this.axios.get; + this.delete = this.axios.delete; + this.head = this.axios.head; + this.options = this.axios.options; + this.post = this.axios.post; + this.put = this.axios.put; + this.patch = this.axios.patch; + } + + client() { + return this.axios; + } + + async direction(profile, coord1, coord2) { + const coords = coord1[0]+','+coord1[1]+';'+coord2[0]+','+coord2[1] + const urlOptimize = this.baseUrl+'/directions/v5/mapbox/'+profile+'/'+coords+'?geometries=geojson&access_token='+this.token; + let response = await axios({ + method: 'get', + url: urlOptimize, + mode: 'no-cors', + headers: { + "Accept": "*/*", + "content-type": "text/plain", + "Access-Control-Allow-Origin": "*" + } + }); + if (response.status === 200 && response.data.code === 'Ok') { + return response.data.routes[0].geometry.coordinates; + } else { + return []; + } + } + + async optimize(profile, coord1, coord2) { + const coords = coord1[0]+','+coord1[1]+';'+coord2[0]+','+coord2[1] + const urlOptimize = this.baseUrl+'/optimized-trips/v1/mapbox/'+profile+'/'+coords+'?geometries=geojson&access_token='+this.token; + let response = await axios({ + method: 'get', + url: urlOptimize, + mode: 'no-cors', + headers: { + "Accept": "*/*", + "content-type": "text/plain", + "Access-Control-Allow-Origin": "*" + } + }); + if (response.status === 200 && response.data.code === 'Ok') { + return response.data.trips[0].geometry.coordinates; + } else { + return []; + } + } + + headers() { + return {}; + } + + config() { + return { + baseURL: this.baseUrl, + headers: this.headers() + }; + } + + load() { + return true; + } +} \ No newline at end of file diff --git a/config/environment.js b/config/environment.js index c1263c3..2503a7d 100644 --- a/config/environment.js +++ b/config/environment.js @@ -6,6 +6,7 @@ module.exports = function(environment) { environment, rootURL: '/', locationType: 'auto', + mapboxToken: process.env.MAPBOX_TOKEN, 'mapbox-gl': { accessToken: process.env.MAPBOX_TOKEN, map: { diff --git a/package-lock.json b/package-lock.json index 0f830c5..f196080 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3772,6 +3772,14 @@ "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", "dev": true }, + "axios": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz", + "integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -12535,8 +12543,7 @@ "follow-redirects": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", - "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", - "dev": true + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" }, "for-in": { "version": "1.0.2", diff --git a/package.json b/package.json index 30bc57f..b7da62e 100644 --- a/package.json +++ b/package.json @@ -57,5 +57,8 @@ }, "ember": { "edition": "octane" + }, + "dependencies": { + "axios": "^0.20.0" } } diff --git a/tests/unit/services/mapbox-ws-test.js b/tests/unit/services/mapbox-ws-test.js new file mode 100644 index 0000000..2e1e4e4 --- /dev/null +++ b/tests/unit/services/mapbox-ws-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Service | mapbox-ws', function(hooks) { + setupTest(hooks); + + // TODO: Replace this with your real tests. + test('it exists', function(assert) { + let service = this.owner.lookup('service:mapbox-ws'); + assert.ok(service); + }); +}); -- GitLab