Newer
Older
import { HttpService } from '@nestjs/axios';
import { Injectable, Logger } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { BusLine } from './interface/BusLine';
import { StopPoint } from './interface/StopPoint';
import { SubLine } from './interface/SubLine';
import { TramLine } from './interface/TramLine';
import { PgisCoord } from './schemas/pgisCoord.schema';
import { TclStopPoint, TclStopPointDocument } from './tclStopPoint.schema';
interface Lines {
busLines: string[];
subLines: string[];
tramLines: string[];
}
@Injectable()
export class TclStopPointService {
private readonly logger = new Logger(TclStopPointService.name);
private receivedStopPoints: StopPoint[];
private receivedBusLines: BusLine[];
private receivedSubLines: SubLine[];
private receivedTramLines: TramLine[];
constructor(
private http: HttpService,
@InjectModel(TclStopPoint.name) private tclStopPointModel: Model<TclStopPointDocument>
) {}
/**
* Clear 'tclstoppoint' and fill it with data from Data Grand Lyon
*/
public async updateStopPoints(): Promise<void> {
await this.getUpdatedData();
const newStopPoints = await this.processReceivedStopPoints(this.receivedStopPoints);
await this.tclStopPointModel.deleteMany({}).exec();
await this.tclStopPointModel.insertMany(newStopPoints);
}
/**
* Get all tcl data from Data Grand Lyon
*/
private async getUpdatedData(): Promise<void> {
this.receivedStopPoints = await this.http
.get(
// tslint:disable-next-line: max-line-length
'https://download.data.grandlyon.com/wfs/rdata?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=tcl_sytral.tclarret&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4326&startIndex=0'
)
.toPromise()
.then(async (res) => res.data.features);
this.receivedBusLines = await this.http
.get(`https://download.data.grandlyon.com/ws/rdata/tcl_sytral.tcllignebus_2_0_0/all.json`)
.toPromise()
.then(async (res) => res.data.values);
this.receivedSubLines = await this.http
.get(`https://download.data.grandlyon.com/ws/rdata/tcl_sytral.tcllignemf_2_0_0/all.json`)
.toPromise()
.then(async (res) => res.data.values);
this.receivedTramLines = await this.http
.get(`https://download.data.grandlyon.com/ws/rdata/tcl_sytral.tcllignetram_2_0_0/all.json`)
.toPromise()
.then(async (res) => res.data.values);
}
/**
* Get all lines names and remove duplications
*/
private async processReceivedStopPoints(receivedStopPoints: StopPoint[]): Promise<TclStopPoint[]> {
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
const newStopPoints: TclStopPoint[] = [];
for (const receivedStopPoint of receivedStopPoints) {
const lines: Lines = await this.processReceivedLines(receivedStopPoint.properties.desserte);
const newStopPoint = new TclStopPoint();
newStopPoint.id = parseInt(receivedStopPoint.properties.id, 10);
newStopPoint.name = receivedStopPoint.properties.nom;
newStopPoint.busLines = [...new Set(lines.busLines)];
newStopPoint.subLines = lines.subLines;
newStopPoint.tramLines = lines.tramLines;
newStopPoint.prm = JSON.parse(receivedStopPoint.properties.pmr);
newStopPoint.elevator = JSON.parse(receivedStopPoint.properties.ascenseur);
newStopPoint.escalator = JSON.parse(receivedStopPoint.properties.escalator);
newStopPoint.gid = parseInt(receivedStopPoint.properties.gid, 10);
newStopPoint.lastUpdate = new Date(receivedStopPoint.properties.last_update);
newStopPoint.lastUpdateFme = new Date(receivedStopPoint.properties.last_update_fme);
newStopPoint.pgisCoord = receivedStopPoint.geometry;
newStopPoints.push(newStopPoint);
}
return newStopPoints;
}
/**
* Based on received data, check type and get it's real name in order to sort it.
*/
private async processReceivedLines(receivedLines: string): Promise<Lines> {
const receivedLinesArray = receivedLines.split(',');
const lines: Lines = {
busLines: [],
subLines: [],
tramLines: [],
};
for (let line of receivedLinesArray) {
let cleanLine: string;
let lineType: string[];
line = line.split(':')[0];
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
if (this.isExceptionLine(line)) {
// Ne rien faire
} else if (this.isSubLine(line)) {
cleanLine = await this.getCleanSubLine(line);
lineType = lines.subLines;
} else if (this.isTramLine(line)) {
cleanLine = await this.getCleanTramLine(line);
lineType = lines.tramLines;
} else {
/* Les codes des lignes de bus ne respectant pas de logique générale,
on considère que toutes les lignes qui n'ont pas été interceptées au dessus
sont des lignes de bus */
cleanLine = await this.getCleanBusLine(line);
lineType = lines.busLines;
}
if (cleanLine) {
lineType.push(cleanLine);
}
}
return lines;
}
/**
* Return true if bus line code is : XXX11
*/
private isSubLine(line: string): boolean {
const regex = /^3\d{2}/; // NOSONAR
return regex.test(line);
}
/**
* Return true if bus line code is starting with a T
*/
private isTramLine(line: string): boolean {
const regex = /^T/; // NOSONAR
return regex.test(line);
}
/**
* Return true if it's a known exception (ex: Rhônexpress)
*/
private isExceptionLine(line: string): boolean {
const regex = /(^RX|^TGS|^BGS|^NAV|^PL)/; // NOSONAR
return regex.test(line);
}
/**
* Get back bus line name from TCL code in the corresponding table
*/
private async getCleanLine(line: string, receivedLines: BusLine[] | TramLine[] | SubLine[]): Promise<string> {
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
const foundLine = receivedLines.find((receivedLine) => receivedLine.code_ligne === line);
// Exception for line 132. Does'nt exist anymore
if (foundLine && foundLine.ligne && foundLine.ligne !== '132') {
return foundLine.ligne;
} else {
return '';
}
}
/**
* Get back bus line name from TCL code
*/
private async getCleanBusLine(line: string): Promise<string> {
return this.getCleanLine(line, this.receivedBusLines);
}
/**
* Get back bus subway name from TCL code
*/
private async getCleanSubLine(line: string): Promise<string> {
return this.getCleanLine(line, this.receivedSubLines);
}
/**
* Get back tram line name from TCL code
*/
private async getCleanTramLine(line: string): Promise<string> {
return this.getCleanLine(line, this.receivedTramLines);
}
/**
* Get TCL nearast point.
* If none is found, we increase the default search radius.
* The amount of stop return if defined in the function.
*/
public async getClosestStopPoints(pgisCoord: PgisCoord): Promise<TclStopPoint[]> {
const NUMBER_STOPS = 5;
const RADIUS_FIRST_TRY = 100;
const RADIUS_SECOND_TRY = 500;
let stopPoints = await this.getStopPointsByDistance(pgisCoord, RADIUS_FIRST_TRY);
if (!stopPoints.length) {
stopPoints = await this.getStopPointsByDistance(pgisCoord, RADIUS_SECOND_TRY);
}
stopPoints = this.groupStopPointsByName(stopPoints);
return stopPoints.slice(0, NUMBER_STOPS);
}
/**
* Aggregate stops
*/
private groupStopPointsByName(stopPoints: TclStopPoint[]): TclStopPoint[] {
const uniqueStopPoints: TclStopPoint[] = [];
for (const stopPoint of stopPoints) {
const stopPointIndex = uniqueStopPoints.findIndex((uniqueStopPoint) => uniqueStopPoint.name === stopPoint.name);
if (stopPointIndex > -1) {
uniqueStopPoints[stopPointIndex].busLines = this.getUniqueCombinedLines(
uniqueStopPoints[stopPointIndex].busLines,
stopPoint.busLines
);
uniqueStopPoints[stopPointIndex].subLines = this.getUniqueCombinedLines(
uniqueStopPoints[stopPointIndex].subLines,
stopPoint.subLines
);
uniqueStopPoints[stopPointIndex].tramLines = this.getUniqueCombinedLines(
uniqueStopPoints[stopPointIndex].tramLines,
stopPoint.tramLines
);
} else {
uniqueStopPoints.push(stopPoint);
}
}
return uniqueStopPoints;
}
/**
* Merge two lines array and return a map of unique lines ordered alphabetically
*/
private getUniqueCombinedLines(stop1Lines: string[], stop2Lines: string[]): string[] {
// Natural line order
// Ex : 69, 296, C7, C25 instead of 296, 69, C25, C7
const collator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: 'base',
});
return Array.from(new Set([...stop1Lines, ...stop2Lines])).sort(collator.compare);
}
/**
* Query collection to get nearest coord
*/
public async getStopPointsByDistance(pgisCoord: PgisCoord, maxDistance: number): Promise<TclStopPoint[]> {
return this.tclStopPointModel
.find({
pgisCoord: {
$near: {
$geometry: pgisCoord,
$maxDistance: maxDistance,
},
},
})
.sort('-distance')
.exec();
}
}