UNPKG

carthorse

Version:

A geospatial trail data processing pipeline for building 3D trail databases with elevation data

170 lines 8.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PostgresTrailProcessor = void 0; const queries_1 = require("../sql/queries"); class PostgresTrailProcessor { constructor(databaseService) { this.databaseService = databaseService; } async processTrails(schemaName, region, bbox) { console.log(`🛤️ Processing trails for region '${region}'${bbox ? ' with bbox filter' : ''}`); try { // Validate trail data const validationResult = await this.validateTrailsForRouting(schemaName); if (!validationResult.isValid) { return { success: false, trailsProcessed: validationResult.stats.totalTrails, validTrails: validationResult.stats.validTrails, invalidTrails: validationResult.stats.invalidTrails, errors: validationResult.errors, warnings: validationResult.warnings }; } // Calculate trail statistics const stats = await this.calculateTrailStats(schemaName); console.log(`✅ Trail processing completed successfully`); console.log(`📊 Processed ${stats.totalTrails} trails`); console.log(` - Valid trails: ${stats.validTrails}`); console.log(` - Invalid trails: ${stats.invalidTrails}`); console.log(` - Average length: ${stats.avgLength.toFixed(2)}km`); console.log(` - Average elevation gain: ${stats.avgElevationGain.toFixed(1)}m`); console.log(` - Average elevation loss: ${stats.avgElevationLoss.toFixed(1)}m`); return { success: true, trailsProcessed: stats.totalTrails, validTrails: stats.validTrails, invalidTrails: stats.invalidTrails, errors: validationResult.errors, warnings: validationResult.warnings }; } catch (error) { console.error('❌ Trail processing failed:', error); return { success: false, trailsProcessed: 0, validTrails: 0, invalidTrails: 0, errors: [error instanceof Error ? error.message : String(error)], warnings: [] }; } } async validateTrailsForRouting(schemaName) { console.log(`🔍 Validating trails for routing in schema '${schemaName}'`); const result = await this.databaseService.executeQuery(queries_1.StagingQueries.validateTrailsForRouting(schemaName)); const stats = result.rows[0]; const validationStats = { totalTrails: parseInt(stats.total_trails), validTrails: 0, invalidTrails: 0, nullGeometry: parseInt(stats.null_geometry), invalidGeometry: parseInt(stats.invalid_geometry), zeroOrNullLength: parseInt(stats.zero_or_null_length), selfLoops: parseInt(stats.self_loops), zeroLengthGeometry: parseInt(stats.zero_length_geometry), singlePointGeometry: parseInt(stats.single_point_geometry), avgLength: 0, avgElevationGain: 0, avgElevationLoss: 0 }; // Calculate valid/invalid trails const criticalIssues = validationStats.nullGeometry + validationStats.invalidGeometry + validationStats.zeroLengthGeometry + validationStats.singlePointGeometry; validationStats.validTrails = validationStats.totalTrails - criticalIssues; validationStats.invalidTrails = criticalIssues; const errors = []; const warnings = []; // Check for critical issues if (validationStats.nullGeometry > 0) { errors.push(`${validationStats.nullGeometry} trails have null geometry`); } if (validationStats.invalidGeometry > 0) { errors.push(`${validationStats.invalidGeometry} trails have invalid geometry`); } if (validationStats.zeroLengthGeometry > 0) { errors.push(`${validationStats.zeroLengthGeometry} trails have zero length geometry`); } if (validationStats.singlePointGeometry > 0) { errors.push(`${validationStats.singlePointGeometry} trails are single points`); } // Check for warnings if (validationStats.zeroOrNullLength > 0) { warnings.push(`${validationStats.zeroOrNullLength} trails have zero or null length`); } if (validationStats.selfLoops > 0) { warnings.push(`${validationStats.selfLoops} trails are self-loops (start = end)`); } let isValid = errors.length === 0; console.log(`📊 Trail validation results:`); console.log(` Total trails: ${validationStats.totalTrails}`); console.log(` Valid trails: ${validationStats.validTrails}`); console.log(` Invalid trails: ${validationStats.invalidTrails}`); console.log(` Null geometry: ${validationStats.nullGeometry}`); console.log(` Invalid geometry: ${validationStats.invalidGeometry}`); console.log(` Zero/null length: ${validationStats.zeroOrNullLength}`); console.log(` Self-loops: ${validationStats.selfLoops}`); console.log(` Zero length geometry: ${validationStats.zeroLengthGeometry}`); console.log(` Single point geometry: ${validationStats.singlePointGeometry}`); if (errors.length > 0) { console.error(`❌ Validation errors: ${errors.join(', ')}`); } if (warnings.length > 0) { console.warn(`⚠️ Validation warnings: ${warnings.join(', ')}`); } // Fail if no valid trails remain if (validationStats.validTrails === 0) { errors.push(`No valid trails found for routing graph generation. All ${validationStats.totalTrails} trails have issues that prevent edge creation`); isValid = false; } return { isValid, errors, warnings, stats: validationStats }; } async calculateTrailStats(schemaName) { console.log(`📊 Calculating trail statistics for schema '${schemaName}'`); const result = await this.databaseService.executeQuery(` SELECT COUNT(*) as total_trails, COUNT(CASE WHEN geometry IS NOT NULL AND ST_IsValid(geometry) AND length_km > 0 THEN 1 END) as valid_trails, COUNT(CASE WHEN geometry IS NULL OR NOT ST_IsValid(geometry) OR length_km <= 0 THEN 1 END) as invalid_trails, COUNT(CASE WHEN geometry IS NULL THEN 1 END) as null_geometry, COUNT(CASE WHEN NOT ST_IsValid(geometry) THEN 1 END) as invalid_geometry, COUNT(CASE WHEN length_km IS NULL OR length_km <= 0 THEN 1 END) as zero_or_null_length, COUNT(CASE WHEN ST_StartPoint(geometry) = ST_EndPoint(geometry) THEN 1 END) as self_loops, COUNT(CASE WHEN ST_Length(geometry) = 0 THEN 1 END) as zero_length_geometry, COUNT(CASE WHEN ST_NumPoints(geometry) < 2 THEN 1 END) as single_point_geometry, AVG(length_km) as avg_length, AVG(elevation_gain) as avg_elevation_gain, AVG(elevation_loss) as avg_elevation_loss FROM ${schemaName}.trails `); const stats = result.rows[0]; return { totalTrails: parseInt(stats.total_trails), validTrails: parseInt(stats.valid_trails), invalidTrails: parseInt(stats.invalid_trails), nullGeometry: parseInt(stats.null_geometry), invalidGeometry: parseInt(stats.invalid_geometry), zeroOrNullLength: parseInt(stats.zero_or_null_length), selfLoops: parseInt(stats.self_loops), zeroLengthGeometry: parseInt(stats.zero_length_geometry), singlePointGeometry: parseInt(stats.single_point_geometry), avgLength: parseFloat(stats.avg_length) || 0, avgElevationGain: parseFloat(stats.avg_elevation_gain) || 0, avgElevationLoss: parseFloat(stats.avg_elevation_loss) || 0 }; } async getTrailDetails(schemaName, limit = 10) { console.log(`🔍 Getting trail details for schema '${schemaName}' (limit: ${limit})`); const result = await this.databaseService.executeQuery(queries_1.StagingQueries.getTrailDetails(schemaName, limit), [limit]); console.log(`✅ Retrieved ${result.rows.length} trail details`); return result.rows; } } exports.PostgresTrailProcessor = PostgresTrailProcessor; //# sourceMappingURL=TrailProcessor.js.map