@tmlmobilidade/export-data 20260103.2047.14 → 20260107.1017.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +36 -15
- package/dist/prompts/export-types.js +3 -0
- package/dist/prompts/hashedshape-ids.d.ts +1 -0
- package/dist/prompts/hashedshape-ids.js +22 -0
- package/dist/tasks/hashed-shapes/hashed-shapes-geojson.d.ts +4 -0
- package/dist/tasks/hashed-shapes/hashed-shapes-geojson.js +49 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.js +9 -0
- package/dist/utils/hashed-shapes-to-geojson.d.ts +3 -0
- package/dist/utils/hashed-shapes-to-geojson.js +29 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,15 +8,17 @@ import { promptFilterByPatternIds } from './prompts/filter-pattern-ids.js';
|
|
|
8
8
|
import { promptFilterByStopIds } from './prompts/filter-stop-ids.js';
|
|
9
9
|
import { promptFilterTypes } from './prompts/filter-types.js';
|
|
10
10
|
import { promptFilterByVehicleIds } from './prompts/filter-vehicle-ids.js';
|
|
11
|
+
import { promptHashedShapeIds } from './prompts/hashedshape-ids.js';
|
|
11
12
|
import { exportValidationsByLine } from './tasks/apex-validations/validations-by-line.js';
|
|
12
13
|
import { exportValidationsByPattern } from './tasks/apex-validations/validations-by-pattern.js';
|
|
13
14
|
import { exportValidationsByStopByPattern } from './tasks/apex-validations/validations-by-stop-by-pattern.js';
|
|
14
15
|
import { exportValidationsByStopByTrip } from './tasks/apex-validations/validations-by-stop-by-trip.js';
|
|
15
16
|
import { exportValidationsByStop } from './tasks/apex-validations/validations-by-stop.js';
|
|
16
17
|
import { exportValidationsRaw } from './tasks/apex-validations/validations-raw.js';
|
|
18
|
+
import { exportHashedShapesGeoJSON } from './tasks/hashed-shapes/hashed-shapes-geojson.js';
|
|
17
19
|
import { exportRidesRaw } from './tasks/rides/rides-raw.js';
|
|
18
20
|
import { exportVehicleEventsRaw } from './tasks/vehicle-events/vehicle-events-raw.js';
|
|
19
|
-
import { exportTypeLabels } from './types.js';
|
|
21
|
+
import { exportTypeLabels, exportTypesWithoutFilters } from './types.js';
|
|
20
22
|
import { initExportContext } from './utils/init-context.js';
|
|
21
23
|
import { intro, log, outro, tasks } from '@clack/prompts';
|
|
22
24
|
import { ASCII_CM_SHORT } from '@tmlmobilidade/consts';
|
|
@@ -28,7 +30,7 @@ import { ASCII_CM_SHORT } from '@tmlmobilidade/consts';
|
|
|
28
30
|
const context = initExportContext();
|
|
29
31
|
//
|
|
30
32
|
// Greet the user
|
|
31
|
-
console.log(ASCII_CM_SHORT);
|
|
33
|
+
console.log(ASCII_CM_SHORT.replace(/▒/g, '\x1b[33m▒\x1b[0m'));
|
|
32
34
|
intro('Bem-vindo ao exportador de dados da CM!');
|
|
33
35
|
log.info(`O ID desta exportação é: ${context._id}`);
|
|
34
36
|
log.info(`Todos os resultados serão guardados aqui: ${context.output}`);
|
|
@@ -38,20 +40,34 @@ import { ASCII_CM_SHORT } from '@tmlmobilidade/consts';
|
|
|
38
40
|
//
|
|
39
41
|
// Request the export types and which filters to apply
|
|
40
42
|
const exportTypes = await promptExportTypes();
|
|
41
|
-
const filterTypes = await promptFilterTypes();
|
|
42
43
|
//
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
44
|
+
// Check if all selected export types don't require filters
|
|
45
|
+
const selectedTypesWithoutFilters = exportTypes.filter(type => exportTypesWithoutFilters.includes(type));
|
|
46
|
+
const shouldSkipFilters = selectedTypesWithoutFilters.length === exportTypes.length && exportTypes.length > 0;
|
|
47
|
+
//
|
|
48
|
+
// For hashed_shapes export, prompt for hashedshape IDs
|
|
49
|
+
let hashedShapeIds = [];
|
|
50
|
+
if (exportTypes.includes('hashed-shapes-geojson')) {
|
|
51
|
+
hashedShapeIds = await promptHashedShapeIds();
|
|
52
|
+
}
|
|
53
|
+
//
|
|
54
|
+
// Skip filters and dates if all selected export types don't require them
|
|
55
|
+
if (!shouldSkipFilters) {
|
|
56
|
+
const filterTypes = await promptFilterTypes();
|
|
57
|
+
//
|
|
58
|
+
// For the selected filters, request the filter values
|
|
59
|
+
if (filterTypes.includes('agency-ids'))
|
|
60
|
+
context.filters.agency_ids = await promptFilterByAgencyIds();
|
|
61
|
+
if (filterTypes.includes('line-ids'))
|
|
62
|
+
context.filters.line_ids = await promptFilterByLineIds();
|
|
63
|
+
if (filterTypes.includes('pattern-ids'))
|
|
64
|
+
context.filters.pattern_ids = await promptFilterByPatternIds();
|
|
65
|
+
if (filterTypes.includes('stop-ids'))
|
|
66
|
+
context.filters.stop_ids = await promptFilterByStopIds();
|
|
67
|
+
if (filterTypes.includes('vehicle-ids'))
|
|
68
|
+
context.filters.vehicle_ids = await promptFilterByVehicleIds();
|
|
69
|
+
context.dates = await promptFilterByDates();
|
|
70
|
+
}
|
|
55
71
|
//
|
|
56
72
|
// Build the tasks array for the selected export types
|
|
57
73
|
await tasks([
|
|
@@ -95,6 +111,11 @@ import { ASCII_CM_SHORT } from '@tmlmobilidade/consts';
|
|
|
95
111
|
task: async (message) => await exportVehicleEventsRaw({ context, message }),
|
|
96
112
|
title: exportTypeLabels['vehicle-events-raw'],
|
|
97
113
|
},
|
|
114
|
+
{
|
|
115
|
+
enabled: exportTypes.includes('hashed-shapes-geojson'),
|
|
116
|
+
task: async (message) => await exportHashedShapesGeoJSON({ context, hashedShapeIds, message }),
|
|
117
|
+
title: exportTypeLabels['hashed-shapes-geojson'],
|
|
118
|
+
},
|
|
98
119
|
]);
|
|
99
120
|
//
|
|
100
121
|
// Terminate the process
|
|
@@ -21,6 +21,9 @@ export async function promptExportTypes() {
|
|
|
21
21
|
'3. Vehicle Events': [
|
|
22
22
|
{ label: exportTypeLabels['vehicle-events-raw'], value: 'vehicle-events-raw' },
|
|
23
23
|
],
|
|
24
|
+
'4. HashedShapes': [
|
|
25
|
+
{ label: exportTypeLabels['hashed-shapes-geojson'], value: 'hashed-shapes-geojson' },
|
|
26
|
+
],
|
|
24
27
|
},
|
|
25
28
|
required: true,
|
|
26
29
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function promptHashedShapeIds(): Promise<string[]>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { note, text } from '@clack/prompts';
|
|
3
|
+
/* * */
|
|
4
|
+
export async function promptHashedShapeIds() {
|
|
5
|
+
//
|
|
6
|
+
note('EXPORTAR HASHED SHAPES PARA GEOJSON:\n'
|
|
7
|
+
+ ' • Introduz os HashedShape IDs separados por vírgulas. Exemplo: id1,id2,etc...\n'
|
|
8
|
+
+ ' • Todos os IDs serão exportados para um ficheiro GeoJSON.');
|
|
9
|
+
const value = await text({
|
|
10
|
+
message: 'HashedShape IDs:',
|
|
11
|
+
placeholder: 'id1,id2,etc...',
|
|
12
|
+
validate(value) {
|
|
13
|
+
if (!value || value.trim().length === 0) {
|
|
14
|
+
return 'É necessário introduzir pelo menos um HashedShape ID.';
|
|
15
|
+
}
|
|
16
|
+
return;
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
if (!value)
|
|
20
|
+
return [];
|
|
21
|
+
return value.split(',').map(id => id.trim()).filter(id => id.length > 0);
|
|
22
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { hashedShapesToFeatureCollection } from '../../utils/hashed-shapes-to-geojson.js';
|
|
3
|
+
import { hashedShapes } from '@tmlmobilidade/interfaces';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
/* * */
|
|
6
|
+
const TASK_ID = 'hashed-shapes-geojson';
|
|
7
|
+
/* * */
|
|
8
|
+
export async function exportHashedShapesGeoJSON({ context, hashedShapeIds, message }) {
|
|
9
|
+
//
|
|
10
|
+
message('A iniciar a exportação de HashedShapes para GeoJSON...');
|
|
11
|
+
//
|
|
12
|
+
// Fetch all hashed_shapes by their IDs
|
|
13
|
+
message(`A procurar ${hashedShapeIds.length} HashedShape(s) na base de dados...`);
|
|
14
|
+
const foundHashedShapes = [];
|
|
15
|
+
const notFoundIds = [];
|
|
16
|
+
for (const id of hashedShapeIds) {
|
|
17
|
+
const hashedShape = await hashedShapes.findById(id);
|
|
18
|
+
if (hashedShape) {
|
|
19
|
+
foundHashedShapes.push(hashedShape);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
notFoundIds.push(id);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (notFoundIds.length > 0) {
|
|
26
|
+
message(`Aviso: ${notFoundIds.length} HashedShape(s) não foram encontrados: ${notFoundIds.join(', ')}`);
|
|
27
|
+
}
|
|
28
|
+
if (foundHashedShapes.length === 0) {
|
|
29
|
+
message('Erro: Nenhum HashedShape foi encontrado.');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
message(`Encontrados ${foundHashedShapes.length} HashedShape(s).`);
|
|
33
|
+
//
|
|
34
|
+
// Convert to GeoJSON FeatureCollection
|
|
35
|
+
message('A converter para GeoJSON...');
|
|
36
|
+
const featureCollection = hashedShapesToFeatureCollection(foundHashedShapes);
|
|
37
|
+
//
|
|
38
|
+
// Prepare the output directory
|
|
39
|
+
message(`A preparar a pasta para guardar os resultados...`);
|
|
40
|
+
if (!fs.existsSync(context.output))
|
|
41
|
+
fs.mkdirSync(context.output, { recursive: true });
|
|
42
|
+
//
|
|
43
|
+
// Write the GeoJSON file
|
|
44
|
+
const outputFileName = `${context.output}/${TASK_ID}-${hashedShapeIds.join('-')}.geojson`;
|
|
45
|
+
message(`A escrever o ficheiro GeoJSON: ${outputFileName}`);
|
|
46
|
+
fs.writeFileSync(outputFileName, JSON.stringify(featureCollection, null, 2), 'utf-8');
|
|
47
|
+
message(`Exportação concluída! Ficheiro guardado em: ${outputFileName}`);
|
|
48
|
+
//
|
|
49
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -8,8 +8,14 @@ export declare const exportTypeLabels: {
|
|
|
8
8
|
readonly 'validations-by-stop-by-trip': "1.1. Validações por Stop ID, por Trip ID";
|
|
9
9
|
readonly 'validations-raw': "1.0. Validações em bruto";
|
|
10
10
|
readonly 'vehicle-events-raw': "3.0. Vehicle Events em bruto";
|
|
11
|
+
readonly 'hashed-shapes-geojson': "4.0. HashedShapes para GeoJSON";
|
|
11
12
|
};
|
|
12
13
|
export type ExportType = keyof typeof exportTypeLabels;
|
|
14
|
+
/**
|
|
15
|
+
* Export types that don't require filters or dates.
|
|
16
|
+
* These exports use their own specific input methods (e.g., IDs).
|
|
17
|
+
*/
|
|
18
|
+
export declare const exportTypesWithoutFilters: ExportType[];
|
|
13
19
|
export interface ExportContext {
|
|
14
20
|
_id: string;
|
|
15
21
|
dates: {
|
package/dist/types.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable perfectionist/sort-objects */
|
|
1
2
|
/* * */
|
|
2
3
|
/* * */
|
|
3
4
|
export const exportTypeLabels = {
|
|
@@ -9,4 +10,12 @@ export const exportTypeLabels = {
|
|
|
9
10
|
'validations-by-stop-by-trip': '1.1. Validações por Stop ID, por Trip ID',
|
|
10
11
|
'validations-raw': '1.0. Validações em bruto',
|
|
11
12
|
'vehicle-events-raw': '3.0. Vehicle Events em bruto',
|
|
13
|
+
'hashed-shapes-geojson': '4.0. HashedShapes para GeoJSON',
|
|
12
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Export types that don't require filters or dates.
|
|
17
|
+
* These exports use their own specific input methods (e.g., IDs).
|
|
18
|
+
*/
|
|
19
|
+
export const exportTypesWithoutFilters = [
|
|
20
|
+
'hashed-shapes-geojson',
|
|
21
|
+
];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
/* * */
|
|
3
|
+
export function hashedShapesToFeatureCollection(hashedShapes) {
|
|
4
|
+
function toFeature(shape) {
|
|
5
|
+
return {
|
|
6
|
+
geometry: {
|
|
7
|
+
coordinates: shape.points.map(point => [point.shape_pt_lon, point.shape_pt_lat]),
|
|
8
|
+
type: 'LineString',
|
|
9
|
+
},
|
|
10
|
+
properties: {
|
|
11
|
+
agency_id: shape.agency_id,
|
|
12
|
+
shape_id: shape._id,
|
|
13
|
+
},
|
|
14
|
+
type: 'Feature',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (Array.isArray(hashedShapes)) {
|
|
18
|
+
return {
|
|
19
|
+
features: hashedShapes.map(toFeature),
|
|
20
|
+
type: 'FeatureCollection',
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
return {
|
|
25
|
+
features: [toFeature(hashedShapes)],
|
|
26
|
+
type: 'FeatureCollection',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|