zerito commited on
Commit
a3ca6ea
·
1 Parent(s): d5e7276

Deploy backend update Mon Dec 1 15:20:30 CET 2025

Browse files
src/controllers/trackController.ts CHANGED
@@ -91,7 +91,16 @@ export class TrackController {
91
 
92
  static async create(req: Request, res: Response) {
93
  console.log('--- Start createTrack Controller ---');
94
- const { name, geojson, properties, difficulty, type } = req.body;
 
 
 
 
 
 
 
 
 
95
  console.log(`Attempting to create track: Name='${name}', Difficulty='${difficulty}', Type='${type}'.`);
96
 
97
  if (!geojson || !geojson.geometry || !geojson.geometry.coordinates) {
@@ -104,7 +113,7 @@ export class TrackController {
104
  const trackData = {
105
  name,
106
  geojson: geojson.geometry,
107
- properties,
108
  difficulty,
109
  type
110
  };
 
91
 
92
  static async create(req: Request, res: Response) {
93
  console.log('--- Start createTrack Controller ---');
94
+ let { name, geojson, properties, difficulty, type } = req.body;
95
+
96
+ // Extract difficulty and type from geojson.properties if not provided directly
97
+ if (!difficulty && geojson?.properties?.['piste:difficulty']) {
98
+ difficulty = geojson.properties['piste:difficulty'];
99
+ }
100
+ if (!type && geojson?.properties?.['piste:type']) {
101
+ type = geojson.properties['piste:type'];
102
+ }
103
+
104
  console.log(`Attempting to create track: Name='${name}', Difficulty='${difficulty}', Type='${type}'.`);
105
 
106
  if (!geojson || !geojson.geometry || !geojson.geometry.coordinates) {
 
113
  const trackData = {
114
  name,
115
  geojson: geojson.geometry,
116
+ properties: geojson.properties || properties,
117
  difficulty,
118
  type
119
  };
src/import_gpx_runs_with_names.ts ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import sql from './db';
4
+
5
+ const GPX_FILE_PATH = path.join(__dirname, '../gpx_data/alps_with_names.gpx');
6
+
7
+ async function importGpx() {
8
+ try {
9
+ // Ensure schema is up to date
10
+ console.log('Updating database schema...');
11
+ await sql`
12
+ CREATE TABLE IF NOT EXISTS routes (
13
+ id SERIAL PRIMARY KEY,
14
+ name VARCHAR(255),
15
+ description TEXT,
16
+ path JSONB,
17
+ properties JSONB,
18
+ difficulty VARCHAR(50),
19
+ type VARCHAR(50),
20
+ created_at TIMESTAMP DEFAULT NOW()
21
+ );
22
+ `;
23
+ try {
24
+ await sql`ALTER TABLE routes ADD COLUMN IF NOT EXISTS properties JSONB;`;
25
+ await sql`ALTER TABLE routes ADD COLUMN IF NOT EXISTS difficulty VARCHAR(50);`;
26
+ await sql`ALTER TABLE routes ADD COLUMN IF NOT EXISTS type VARCHAR(50);`;
27
+ } catch (e) {
28
+ console.log('Columns might already exist, continuing...');
29
+ }
30
+
31
+ // Clear existing data to ensure no duplicates from previous runs
32
+ console.log('Clearing existing routes...');
33
+ await sql`DELETE FROM routes`;
34
+
35
+ console.log('Reading GPX file...');
36
+ const gpxContent = fs.readFileSync(GPX_FILE_PATH, 'utf-8');
37
+
38
+ // Simple regex-based parser for this specific GPX format
39
+ // Note: A proper XML parser would be better for production, but this avoids deps.
40
+ const tracks = gpxContent.split('<trk>');
41
+ tracks.shift(); // Remove the header part before the first <trk>
42
+
43
+ console.log(`Found ${tracks.length} tracks. Processing...`);
44
+
45
+ const seenNames = new Set<string>();
46
+
47
+ for (const trackStr of tracks) {
48
+ const nameMatch = trackStr.match(/<name>(.*?)<\/name>/);
49
+ const name = nameMatch ? nameMatch[1] : 'Unknown Track';
50
+
51
+ // Filter out tracks with no name, "Unknown Track", or names starting with "way/"
52
+ if (!name || name === 'Unknown Track' || name.startsWith('way/')) {
53
+ // console.log(`Skipping unnamed or way/ track: ${name}`);
54
+ continue;
55
+ }
56
+
57
+ // Filter out duplicates
58
+ if (seenNames.has(name)) {
59
+ console.log(`Skipping duplicate track: ${name}`);
60
+ continue;
61
+ }
62
+ seenNames.add(name);
63
+
64
+ const descMatch = trackStr.match(/<desc>([\s\S]*?)<\/desc>/);
65
+ const desc = descMatch ? descMatch[1] : '';
66
+
67
+ // Parse properties from desc (key=value format)
68
+ const properties: any = {};
69
+ desc.split('\n').forEach(line => {
70
+ const parts = line.split('=');
71
+ if (parts.length >= 2) {
72
+ const key = parts[0].trim();
73
+ const value = parts.slice(1).join('=').trim();
74
+ properties[key] = value;
75
+ }
76
+ });
77
+
78
+ const difficulty = properties['piste:difficulty'] || null;
79
+ const type = properties['piste:type'] || null;
80
+
81
+ // Extract coordinates
82
+ const coordinates: number[][] = [];
83
+ const trkptRegex = /<trkpt lat="([\d.-]+)" lon="([\d.-]+)"[\s\S]*?(?:<ele>([\d.-]+)<\/ele>)?/g;
84
+ let match;
85
+ while ((match = trkptRegex.exec(trackStr)) !== null) {
86
+ const lat = parseFloat(match[1]);
87
+ const lon = parseFloat(match[2]);
88
+ const ele = match[3] ? parseFloat(match[3]) : 0;
89
+ coordinates.push([lon, lat, ele]); // GeoJSON uses [lon, lat, ele]
90
+ }
91
+
92
+ if (coordinates.length < 2) {
93
+ console.warn(`Skipping track "${name}" - not enough points.`);
94
+ continue;
95
+ }
96
+
97
+ const geojson = {
98
+ type: 'LineString',
99
+ coordinates: coordinates
100
+ };
101
+
102
+ // Insert into DB using postgres.js
103
+ await sql`
104
+ INSERT INTO routes (name, description, path, properties, difficulty, type)
105
+ VALUES (${name}, ${desc}, ${JSON.stringify(geojson)}, ${JSON.stringify(properties)}, ${difficulty}, ${type})
106
+ RETURNING id;
107
+ `;
108
+ // console.log(`Imported: ${name}`);
109
+ }
110
+
111
+ console.log('Import completed successfully!');
112
+ } catch (err) {
113
+ console.error('Error importing GPX:', err);
114
+ }
115
+ // Note: postgres.js automatically handles connection pooling and releases.
116
+ // Closing would terminate all connections and cause errors in the running server.
117
+ }
118
+
119
+ importGpx();