| 1 | /* |
| 2 | Copyright - 2017 2023 - wwwouaiebe - Contact: https://www.ouaie.be/ |
| 3 | |
| 4 | This program is free software; |
| 5 | you can redistribute it and/or modify it under the terms of the |
| 6 | GNU General Public License as published by the Free Software Foundation; |
| 7 | either version 3 of the License, or any later version. |
| 8 | |
| 9 | This program is distributed in the hope that it will be useful, |
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | GNU General Public License for more details. |
| 13 | |
| 14 | You should have received a copy of the GNU General Public License |
| 15 | along with this program; if not, write to the Free Software |
| 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 17 | */ |
| 18 | /* |
| 19 | Changes: |
| 20 | - v4.0.0: |
| 21 | - created from v3.6.0 |
| 22 | Doc reviewed 202208 |
| 23 | */ |
| 24 | |
| 25 | import { ZERO, LAT_LNG, HTTP_STATUS_OK } from '../main/Constants.js'; |
| 26 | import { SelectOptionData, SelectDialog } from '../dialogs/selectDialog/SelectDialog.js'; |
| 27 | import PublicTransportRouteBuilder from '../routeProviders/PublicTransportRouteBuilder.js'; |
| 28 | import BaseRouteProvider from '../routeProviders/BaseRouteProvider.js'; |
| 29 | |
| 30 | /* ------------------------------------------------------------------------------------------------------------------------- */ |
| 31 | /** |
| 32 | This class implements the BaseRouteProvider for PublicTransport. It's not possible to instanciate |
| 33 | this class because the class is not exported from the module. Only one instance is created and added to the list |
| 34 | of Providers of TravelNotes |
| 35 | */ |
| 36 | /* ------------------------------------------------------------------------------------------------------------------------- */ |
| 37 | |
| 38 | class PublicTransportRouteProvider extends BaseRouteProvider { |
| 39 | |
| 40 | /** |
| 41 | A reference to the edited route |
| 42 | @type {Route} |
| 43 | */ |
| 44 | |
| 45 | #route; |
| 46 | |
| 47 | /** |
| 48 | Parse the response from the provider and add the received itinerary to the route itinerary |
| 49 | @param {Object} waysNodes The ways and nodes received from OSM |
| 50 | @param {function} onOk a function to call when the response is parsed correctly |
| 51 | @param {function} onError a function to call when an error occurs |
| 52 | */ |
| 53 | |
| 54 | #parseResponse ( waysNodes, onOk, onError ) { |
| 55 | new PublicTransportRouteBuilder ( this.#route ).buildRoute ( waysNodes, onOk, onError ); |
| 56 | } |
| 57 | |
| 58 | /** |
| 59 | Get the url to obtains the ways and node Osm elements for the relation |
| 60 | @param {Number} relationId The osm relation id |
| 61 | @return {String} The complete url |
| 62 | */ |
| 63 | |
| 64 | #getWaysNodesUrl ( relationId ) { |
| 65 | return window.TaN.overpassApiUrl + '?data=[out:json];rel(' + |
| 66 | relationId.toFixed ( ZERO ) + |
| 67 | ');way(r)->.e;way.e["railway"="rail"];(._;>;rel(' + |
| 68 | relationId.toFixed ( ZERO ) + |
| 69 | '););out;'; |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | Show a SelectDialog with all the train relations between the start point and end point |
| 74 | @param {Array.<Object>} relations The relations received from OSM |
| 75 | @return {Promise} The Promise created by the selectDialog.show ( ) |
| 76 | */ |
| 77 | |
| 78 | #getDialogPromise ( relations ) { |
| 79 | |
| 80 | if ( ZERO === relations.elements.length ) { |
| 81 | return Promise.reject ( new Error ( 'No relations found' ) ); |
| 82 | } |
| 83 | |
| 84 | const selectOptionsData = []; |
| 85 | relations.elements.forEach ( |
| 86 | relationElement => { |
| 87 | selectOptionsData.push ( new SelectOptionData ( relationElement.tags.name, relationElement.id ) ); |
| 88 | } |
| 89 | ); |
| 90 | |
| 91 | const selectDialog = new SelectDialog ( |
| 92 | { |
| 93 | title : 'Relations', |
| 94 | text : 'select a relation : ', |
| 95 | selectOptionsData : selectOptionsData |
| 96 | } |
| 97 | ); |
| 98 | |
| 99 | // baseDialog.show ( ) return a Promise... |
| 100 | return selectDialog.show ( ); |
| 101 | |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | The url to use to have the relations between the start point and end point |
| 106 | @type {String} |
| 107 | */ |
| 108 | |
| 109 | get #relationsUrl ( ) { |
| 110 | return window.TaN.overpassApiUrl + |
| 111 | '?data=[out:json];node["public_transport"="stop_position"]["train"="yes"](around:400.0,' + |
| 112 | this.#route.wayPoints.first.lat.toFixed ( LAT_LNG.fixed ) + |
| 113 | ',' + |
| 114 | this.#route.wayPoints.first.lng.toFixed ( LAT_LNG.fixed ) + |
| 115 | ')->.s;rel(bn.s)->.s;node["public_transport"="stop_position"]["train"="yes"](around:400.0,' + |
| 116 | this.#route.wayPoints.last.lat.toFixed ( LAT_LNG.fixed ) + |
| 117 | ',' + |
| 118 | this.#route.wayPoints.last.lng.toFixed ( LAT_LNG.fixed ) + |
| 119 | ')->.e;rel(bn.e)->.e;rel.e.s;out tags;'; |
| 120 | } |
| 121 | |
| 122 | /** |
| 123 | call the provider, wait for the response and then parse the provider response. Notice that we have two calls to the |
| 124 | Provider: one for the relation list and one for the ways and nodes. Notice also the dialog box between the 2 calls. |
| 125 | @param {function} onOk a function to pass to the ourParseResponse |
| 126 | @param {function} onError a function to pass to ourParseResponse or to call when an error occurs |
| 127 | */ |
| 128 | |
| 129 | #getRoute ( onOk, onError ) { |
| 130 | fetch ( this.#relationsUrl ) |
| 131 | .then ( |
| 132 | responseRelations => { |
| 133 | if ( HTTP_STATUS_OK === responseRelations.status && responseRelations.ok ) { |
| 134 | responseRelations.json ( ) |
| 135 | .then ( relations => this.#getDialogPromise ( relations ) ) |
| 136 | .then ( relationId => fetch ( this.#getWaysNodesUrl ( relationId ) ) ) |
| 137 | .then ( |
| 138 | responseWaysNodes => { |
| 139 | if ( HTTP_STATUS_OK === responseWaysNodes.status && responseWaysNodes.ok ) { |
| 140 | responseWaysNodes.json ( ) |
| 141 | .then ( waysNodes => this.#parseResponse ( waysNodes, onOk, onError ) ); |
| 142 | } |
| 143 | else { |
| 144 | onError ( new Error ( 'An error occurs...' ) ); |
| 145 | } |
| 146 | } |
| 147 | ) |
| 148 | .catch ( ( ) => onError ( new Error ( 'An error occurs...' ) ) ); |
| 149 | } |
| 150 | else { |
| 151 | onError ( new Error ( 'An error occurs...' ) ); |
| 152 | } |
| 153 | } |
| 154 | ) |
| 155 | .catch ( |
| 156 | |
| 157 | // calling onError without parameters because fetch don't accecpt to add something as parameter :-(... |
| 158 | ( ) => { onError ( ); } |
| 159 | ); |
| 160 | |
| 161 | } |
| 162 | |
| 163 | /** |
| 164 | The constructor |
| 165 | */ |
| 166 | |
| 167 | constructor ( ) { |
| 168 | super ( ); |
| 169 | } |
| 170 | |
| 171 | /** |
| 172 | Call the provider, using the waypoints defined in the route and, on success, |
| 173 | complete the route with the data from the provider |
| 174 | @param {Route} route The route to witch the data will be added |
| 175 | @return {Promise} A Promise. On success, the Route is completed with the data given by the provider. |
| 176 | */ |
| 177 | |
| 178 | getPromiseRoute ( route ) { |
| 179 | this.#route = route; |
| 180 | return new Promise ( ( onOk, onError ) => this.#getRoute ( onOk, onError ) ); |
| 181 | } |
| 182 | |
| 183 | /** |
| 184 | The icon used in the ProviderToolbarUI. |
| 185 | Overload of the base class icon property |
| 186 | @type {String} |
| 187 | */ |
| 188 | |
| 189 | get icon ( ) { |
| 190 | return 'data:image/svg+xml;utf8,' + |
| 191 | '<svg viewBox="-3 -3 20 20" xmlns="http://www.w3.org/2000/svg"> <g fill="rgb(128,0,0)">' + |
| 192 | '<path d="M 5,0 C 3.025911,-0.0084 1,3 1,7 l 0,2 c 0,1 1,2 2,2 l 8,0 c 1,0 2,-1 2,-2 L 13,7 C 13,3 11,0 9,0 z m ' + |
| 193 | '-1,3 6,0 c 0,0 1,1 1,3 L 3.03125,6 C 2.994661,3.9916 4,3 4,3 z M 3,8 6,8 6,9 3,9 z m 5,0 3,0 0,1 -3,0 z m -6,4 ' + |
| 194 | '-1,2 3,0 1,-2 z m 7,0 1,2 3,0 -1,-2 z"/></g></svg>'; |
| 195 | } |
| 196 | |
| 197 | /** |
| 198 | The provider name. |
| 199 | Overload of the base class name property |
| 200 | @type {String} |
| 201 | */ |
| 202 | |
| 203 | get name ( ) { return 'PublicTransport'; } |
| 204 | |
| 205 | /** |
| 206 | The title to display in the ProviderToolbarUI button. |
| 207 | Overload of the base class title property |
| 208 | @type {String} |
| 209 | */ |
| 210 | |
| 211 | get title ( ) { return 'Public Transport on OpenStreetMap'; } |
| 212 | |
| 213 | /** |
| 214 | The possible transit modes for the provider. |
| 215 | Overload of the base class transitModes property |
| 216 | Must be a subarray of [ 'bike', 'pedestrian', 'car', 'train', 'line', 'circle' ] |
| 217 | @type {Array.<String>} |
| 218 | */ |
| 219 | |
| 220 | get transitModes ( ) { return [ 'train' ]; } |
| 221 | |
| 222 | /** |
| 223 | A boolean indicating when a provider key is needed for the provider. |
| 224 | Overload of the base class providerKeyNeeded property |
| 225 | @type {Boolean} |
| 226 | */ |
| 227 | |
| 228 | get providerKeyNeeded ( ) { return false; } |
| 229 | } |
| 230 | |
| 231 | window.TaN.addProvider ( PublicTransportRouteProvider ); |
| 232 | |
| 233 | /* --- End of file --------------------------------------------------------------------------------------------------------- */ |
| 234 |