UNPKG

carthorse

Version:

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

182 lines 7.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ManualNetworkStrategy = void 0; class ManualNetworkStrategy { async createNetwork(pgClient, config) { console.log('🔄 Using manual network creation strategy (current approach)...'); try { const { stagingSchema, tolerances } = config; // Step 1: Create ways_noded table directly from ways without splitting console.log(`📋 Creating ways_noded table in ${stagingSchema}.ways_noded without further splitting...`); await pgClient.query(` CREATE TABLE ${stagingSchema}.ways_noded AS SELECT ROW_NUMBER() OVER (ORDER BY id) as id, id as old_id, 1 as sub_id, the_geom, app_uuid, name, length_km, elevation_gain, elevation_loss FROM ${stagingSchema}.ways `); console.log('✅ Created ways_noded table without splitting'); // Step 1.5: Populate trail_id_mapping table for UUID ↔ Integer ID conversion console.log('🔄 Populating trail_id_mapping table...'); await pgClient.query(` INSERT INTO ${stagingSchema}.trail_id_mapping (app_uuid, trail_id) SELECT app_uuid, id as trail_id FROM ${stagingSchema}.ways_noded ORDER BY id `); console.log('✅ Populated trail_id_mapping table'); // Step 2: Create vertices table with manual intersection detection console.log(`📍 Creating vertices in ${stagingSchema}.ways_noded_vertices_pgr from trail endpoints only...`); await pgClient.query(` CREATE TABLE ${stagingSchema}.ways_noded_vertices_pgr AS SELECT DISTINCT ROW_NUMBER() OVER (ORDER BY point) as id, point as the_geom, COUNT(*) as cnt, 'f' as chk, COUNT(CASE WHEN is_start THEN 1 END) as ein, COUNT(CASE WHEN is_end THEN 1 END) as eout, CASE WHEN COUNT(*) >= 2 THEN 'intersection' WHEN COUNT(*) = 1 THEN 'endpoint' ELSE 'endpoint' END as node_type FROM ( -- Start and end points of all trails SELECT ST_StartPoint(the_geom) as point, true as is_start, false as is_end FROM ${stagingSchema}.ways_noded UNION ALL SELECT ST_EndPoint(the_geom) as point, false as is_start, true as is_end FROM ${stagingSchema}.ways_noded ) points GROUP BY point `); console.log('✅ Created vertices table from trail endpoints'); // Step 3: Add source and target columns to ways_noded await pgClient.query(` ALTER TABLE ${stagingSchema}.ways_noded ADD COLUMN source INTEGER, ADD COLUMN target INTEGER `); // Step 4: Update source and target based on vertex proximity await pgClient.query(` UPDATE ${stagingSchema}.ways_noded wn SET source = ( SELECT v.id FROM ${stagingSchema}.ways_noded_vertices_pgr v WHERE ST_DWithin(ST_StartPoint(wn.the_geom), v.the_geom, ${tolerances.edgeToVertexTolerance}) LIMIT 1 ), target = ( SELECT v.id FROM ${stagingSchema}.ways_noded_vertices_pgr v WHERE ST_DWithin(ST_EndPoint(wn.the_geom), v.the_geom, ${tolerances.edgeToVertexTolerance}) LIMIT 1 ) `); // Step 5: Remove edges that couldn't be connected to vertices await pgClient.query(` DELETE FROM ${stagingSchema}.ways_noded WHERE source IS NULL OR target IS NULL `); console.log('✅ Connected edges to vertices'); // Step 6: Preserve true loop trails but remove problematic self-loops console.log('🔄 Preserving true loop trails...'); await pgClient.query(` ALTER TABLE ${stagingSchema}.ways_noded ADD COLUMN is_true_loop BOOLEAN DEFAULT FALSE `); await pgClient.query(` UPDATE ${stagingSchema}.ways_noded SET is_true_loop = TRUE WHERE ST_Distance(ST_StartPoint(the_geom)::geography, ST_EndPoint(the_geom)::geography) < ${tolerances.trueLoopTolerance} `); const selfLoopResult = await pgClient.query(` DELETE FROM ${stagingSchema}.ways_noded WHERE source = target AND NOT is_true_loop `); console.log(`✅ Removed ${selfLoopResult.rowCount} problematic self-loop edges, preserved true loops`); await pgClient.query(` ALTER TABLE ${stagingSchema}.ways_noded DROP COLUMN is_true_loop `); // Step 7: Create routing edges from ways_noded console.log(`🛤️ Creating routing edges in ${stagingSchema}.routing_edges...`); await pgClient.query(`DROP TABLE IF EXISTS ${stagingSchema}.routing_edges`); await pgClient.query(` CREATE TABLE ${stagingSchema}.routing_edges AS SELECT wn.id, wn.source, wn.target, wn.app_uuid as trail_id, -- Use app_uuid (UUID) instead of integer ID COALESCE(wn.name, 'Trail ' || wn.app_uuid) as trail_name, wn.length_km as length_km, wn.elevation_gain, COALESCE(wn.elevation_loss, 0) as elevation_loss, true as is_bidirectional, wn.the_geom as geometry, ST_AsGeoJSON(wn.the_geom) as geojson FROM ${stagingSchema}.ways_noded wn WHERE wn.source IS NOT NULL AND wn.target IS NOT NULL `); console.log('✅ Created routing edges'); // Step 8: Get statistics const nodeCountResult = await pgClient.query(`SELECT COUNT(*) FROM ${stagingSchema}.ways_noded_vertices_pgr`); const edgeCountResult = await pgClient.query(`SELECT COUNT(*) FROM ${stagingSchema}.ways_noded`); const isolatedNodesResult = await pgClient.query(` SELECT COUNT(*) as isolated_count FROM ${stagingSchema}.ways_noded_vertices_pgr n WHERE NOT EXISTS ( SELECT 1 FROM ${stagingSchema}.ways_noded e WHERE e.source = n.id OR e.target = n.id ) `); const orphanedEdgesResult = await pgClient.query(` SELECT COUNT(*) as orphaned_count FROM ${stagingSchema}.ways_noded e WHERE e.source NOT IN (SELECT id FROM ${stagingSchema}.ways_noded_vertices_pgr) OR e.target NOT IN (SELECT id FROM ${stagingSchema}.ways_noded_vertices_pgr) `); console.log('✅ Manual network creation completed successfully'); return { success: true, stats: { nodesCreated: parseInt(nodeCountResult.rows[0].count), edgesCreated: parseInt(edgeCountResult.rows[0].count), isolatedNodes: parseInt(isolatedNodesResult.rows[0].isolated_count), orphanedEdges: parseInt(orphanedEdgesResult.rows[0].orphaned_count) } }; } catch (error) { console.error('❌ Manual network creation failed:', error); return { success: false, error: error instanceof Error ? error.message : String(error), stats: { nodesCreated: 0, edgesCreated: 0, isolatedNodes: 0, orphanedEdges: 0 } }; } } } exports.ManualNetworkStrategy = ManualNetworkStrategy; //# sourceMappingURL=manual-network-strategy.js.map