File : routeProviders/PublicTransportData.js

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
    -v4.3.2:
23
        - Issue #80:PublicTransportProvider don't take care of the stop_enter_only and stop_exit_only OSM roles
24
Doc reviewed 202208
25
 */
26
27
import theSphericalTrigonometry from '../core/lib/SphericalTrigonometry.js';
28
29
import { ZERO, NOT_FOUND, INVALID_OBJ_ID, ONE, TWO } from '../main/Constants.js';
30
31
/* ------------------------------------------------------------------------------------------------------------------------- */
32
/**
33
coming soon...
34
@ignore
35
*/
36
/* ------------------------------------------------------------------------------------------------------------------------- */
37
38
class PublicTransportData {
39
40
    #newId;
41
    #selectedRelationId;
42
    #waysMap;
43
    #nodesMap;
44
    #stopsMap;
45
    #nodes3WaysCounter;
46
47
    /**
48
    The constructor
49
    */
50
51
    constructor ( selectedRelationId ) {
52
        Object.freeze ( this );
53
        this.#newId = INVALID_OBJ_ID;
54
        this.#selectedRelationId = selectedRelationId;
55
        this.#waysMap = new Map ( );
56
        this.#nodesMap = new Map ( );
57
        this.#stopsMap = new Map ( );
58
        this.#nodes3WaysCounter = ZERO;
59
    }
60
61
    get nodes3WaysCounter ( ) { return this.#nodes3WaysCounter; }
62
    set nodes3WaysCounter ( nodes3WaysCounter ) { this.#nodes3WaysCounter = nodes3WaysCounter; }
63
64
    get waysMap ( ) { return this.#waysMap; }
65
    get nodesMap ( ) { return this.#nodesMap; }
66
    get stopsMap ( ) { return this.#stopsMap; }
67
    get newId ( ) { return this.#newId --; }
68
69
    /**
70
    */
71
72
    firstOf ( array ) {
73
        return array [ ZERO ];
74
    }
75
76
    /**
77
    */
78
79
    lastOf ( array ) {
80
        return array [ array.length - ONE ];
81
    }
82
83
    /**
84
    */
85
86
    removeFrom ( array, value ) {
87
        array.splice ( array.indexOf ( value ), ONE );
88
    }
89
90
    /**
91
    */
92
93
    #reverseWay ( way ) {
94
95
        const oldStartNode = this.#nodesMap.get ( this.firstOf ( way.nodesIds ) );
96
        const oldEndNode = this.#nodesMap.get ( this.lastOf ( way.nodesIds ) );
97
98
        this.removeFrom ( oldStartNode.startingWaysIds, way.id );
99
        oldStartNode.endingWaysIds.push ( way.id );
100
101
        this.removeFrom ( oldEndNode.endingWaysIds, way.id );
102
        oldEndNode.startingWaysIds.push ( way.id );
103
104
        way.nodesIds.reverse ( );
105
106
    }
107
108
    /**
109
    */
110
111
    mergeWays ( waysId1, waysId2 ) {
112
113
        const way1 = this.#waysMap.get ( waysId1 );
114
        const way2 = this.#waysMap.get ( waysId2 );
115
116
        // reversing some ways, so :
117
        // - the 2 ways have the same direction
118
        // - the starting node of the merged way is the starting node of way1
119
        // - the ending node of the merged way is the ending node of way2
120
        // - the removed node is the ending node of way1
121
122
        if ( this.lastOf ( way1.nodesIds ) === this.lastOf ( way2.nodesIds ) ) {
123
            this.#reverseWay ( way2 );
124
        }
125
        else if ( this.firstOf ( way1.nodesIds ) === this.firstOf ( way2.nodesIds ) ) {
126
            this.#reverseWay ( way1 );
127
        }
128
        else if ( this.firstOf ( way1.nodesIds ) === this.lastOf ( way2.nodesIds ) ) {
129
            this.#reverseWay ( way1 );
130
            this.#reverseWay ( way2 );
131
132
        }
133
134
        // removing the node at the merging node and all the starting or ending ways of the node
135
        const mergedNode = this.#nodesMap.get ( way1.nodesIds.pop ( ) );
136
        mergedNode.startingWaysIds = [];
137
        mergedNode.endingWaysIds = [];
138
139
        // and then merging the 2 ways
140
        way1.nodesIds = way1.nodesIds.concat ( way2.nodesIds );
141
        way1.distance += way2.distance;
142
143
        // and changing the ending ways in the last node
144
        const endNode = this.#nodesMap.get ( this.lastOf ( way1.nodesIds ) );
145
        this.removeFrom ( endNode.endingWaysIds, way2.id );
146
        endNode.endingWaysIds.push ( way1.id );
147
148
        // finally we remove the second way from the ways map
149
        this.#waysMap.delete ( way2.id );
150
151
        return way1.id;
152
    }
153
154
    /**
155
    */
156
157
    #cloneNode ( nodeId ) {
158
159
        const node = this.#nodesMap.get ( nodeId );
160
161
        const clonedNode = {
162
            id : this.newId,
163
            lat : node.lat,
164
            lon : node.lon,
165
            type : 'node',
166
            startingWaysIds : [],
167
            endingWaysIds : [],
168
            isNode3Ways : node.isNode3Ways
169
        };
170
171
        this.#nodesMap.set ( clonedNode.id, clonedNode );
172
173
        return clonedNode.id;
174
    }
175
176
    /**
177
    */
178
179
    cloneWay ( wayId ) {
180
181
        const way = this.#waysMap.get ( wayId );
182
183
        const clonedWay = {
184
            id : this.newId,
185
            type : 'way',
186
            nodesIds : [],
187
            distance : way.distance
188
        };
189
190
        way.nodesIds.forEach ( nodeId => clonedWay.nodesIds.push ( this.#cloneNode ( nodeId ) ) );
191
192
        this.#nodesMap.get ( this.firstOf ( clonedWay.nodesIds ) ).startingWaysIds.push ( clonedWay.id );
193
        this.#nodesMap.get ( this.lastOf ( clonedWay.nodesIds ) ).endingWaysIds.push ( clonedWay.id );
194
195
        this.#waysMap.set ( clonedWay.id, clonedWay );
196
197
        return clonedWay.id;
198
    }
199
200
    /**
201
    */
202
203
    createMaps ( elements ) {
204
205
        this.#waysMap.clear ( );
206
        this.#nodesMap.clear ( );
207
        this.#stopsMap.clear ( );
208
209
        // Elements are pushed in 2 maps: 1 for nodes and 1 for ways
210
        elements.forEach (
211
            element => {
212
                switch ( element.type ) {
213
                case 'way' :
214
215
                    // replacing the nodes property with the nodesId property to
216
                    // avoid confusion between nodes and nodesId. the element.nodes contains nodesIds!!
217
                    element.nodesIds = element.nodes;
218
                    delete element.nodes;
219
                    if ( TWO <= element.nodesIds.length ) {
220
                        element.distance = ZERO;
221
                        this.#waysMap.set ( element.id, element );
222
                    }
223
                    break;
224
                case 'node' :
225
                    element.startingWaysIds = [];
226
                    element.endingWaysIds = [];
227
                    element.isNode3Ways = false;
228
                    this.#nodesMap.set ( element.id, element );
229
                    break;
230
                case 'relation' :
231
                    element.members.forEach (
232
                        member => {
233
234
                            // extracting all nodes with role 'stop'
235
                            if ( 'node' === member.type && member.role && NOT_FOUND !== member.role.indexOf ( 'stop' ) ) {
236
                                this.#stopsMap.set ( member.ref, member.ref );
237
                            }
238
                        }
239
                    );
240
                    break;
241
                default :
242
                    break;
243
                }
244
            }
245
        );
246
247
        // The stop map contain only the nodeId
248
        // we replace the nodeId with the node when possible
249
        this.#stopsMap.forEach (
250
            nodeId => {
251
                const node = this.#nodesMap.get ( nodeId );
252
                if ( node ) {
253
                    this.#stopsMap.set ( nodeId, node );
254
                }
255
                else {
256
                    window.TaN.showInfo (
257
                        'the relation ' +
258
                        this.#selectedRelationId +
259
                        ' have nodes not positionned on the railway ( node ' +
260
                        nodeId +
261
                        ').' );
262
                    this.#stopsMap.delete ( nodeId );
263
                }
264
            }
265
        );
266
267
        // Starting and ending ways are added to each node and length computed
268
        this.#waysMap.forEach (
269
            way => {
270
                this.#nodesMap.get ( this.firstOf ( way.nodesIds ) ).startingWaysIds.push ( way.id );
271
                this.#nodesMap.get ( this.lastOf ( way.nodesIds ) ).endingWaysIds.push ( way.id );
272
                let previousNode = null;
273
                way.nodesIds.forEach (
274
                    nodeId => {
275
                        const node = this.#nodesMap.get ( nodeId );
276
                        if ( previousNode ) {
277
                            way.distance += theSphericalTrigonometry.pointsDistance (
278
                                [ node.lat, node.lon ], [ previousNode.lat, previousNode.lon ]
279
                            );
280
                        }
281
                        previousNode = node;
282
                    }
283
                );
284
            }
285
        );
286
    }
287
}
288
289
export default PublicTransportData;
290
291
/* --- End of file --------------------------------------------------------------------------------------------------------- */
292