@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 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
- // For the selected filters, request the filter values
44
- if (filterTypes.includes('agency-ids'))
45
- context.filters.agency_ids = await promptFilterByAgencyIds();
46
- if (filterTypes.includes('line-ids'))
47
- context.filters.line_ids = await promptFilterByLineIds();
48
- if (filterTypes.includes('pattern-ids'))
49
- context.filters.pattern_ids = await promptFilterByPatternIds();
50
- if (filterTypes.includes('stop-ids'))
51
- context.filters.stop_ids = await promptFilterByStopIds();
52
- if (filterTypes.includes('vehicle-ids'))
53
- context.filters.vehicle_ids = await promptFilterByVehicleIds();
54
- context.dates = await promptFilterByDates();
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,4 @@
1
+ import { type TaskProps } from '../../types.js';
2
+ export declare function exportHashedShapesGeoJSON({ context, hashedShapeIds, message }: TaskProps & {
3
+ hashedShapeIds: string[];
4
+ }): Promise<void>;
@@ -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,3 @@
1
+ import { HashedShape } from '@tmlmobilidade/types';
2
+ import { FeatureCollection, LineString } from 'geojson';
3
+ export declare function hashedShapesToFeatureCollection(hashedShapes: HashedShape | HashedShape[]): FeatureCollection<LineString, GeoJSON.GeoJsonProperties>;
@@ -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
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tmlmobilidade/export-data",
3
3
  "description": "CLI tool to export data from GO.",
4
- "version": "20260103.2047.14",
4
+ "version": "20260107.1017.45",
5
5
  "author": {
6
6
  "email": "iso@tmlmobilidade.pt",
7
7
  "name": "TML-ISO"