carthorse
Version:
A geospatial trail data processing pipeline for building 3D trail databases with elevation data
212 lines • 7.04 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RoutingQueries = void 0;
// Routing graph SQL queries
exports.RoutingQueries = {
// Intersection detection
detectIntersections: (schemaName, tolerance) => `
SELECT * FROM detect_trail_intersections($1, 'trails', $2)
`,
// Node generation
generateNodes: (schemaName, tolerance) => `
INSERT INTO ${schemaName}.routing_nodes (id, node_uuid, lat, lng, elevation, node_type, connected_trails, trail_ids, created_at)
WITH valid_trails AS (
SELECT app_uuid, name, geometry
FROM ${schemaName}.trails
WHERE geometry IS NOT NULL
AND ST_IsValid(geometry)
AND ST_Length(geometry) > 0
),
trail_endpoints AS (
SELECT
app_uuid,
name,
ST_StartPoint(geometry) as start_point,
ST_EndPoint(geometry) as end_point,
ST_Z(ST_StartPoint(geometry)) as start_elevation,
ST_Z(ST_EndPoint(geometry)) as end_elevation
FROM valid_trails
),
all_endpoints AS (
SELECT
app_uuid,
name,
start_point as point,
start_elevation as elevation,
'endpoint' as node_type,
name as connected_trails,
ARRAY[app_uuid] as trail_ids
FROM trail_endpoints
UNION ALL
SELECT
app_uuid,
name,
end_point as point,
end_elevation as elevation,
'endpoint' as node_type,
name as connected_trails,
ARRAY[app_uuid] as trail_ids
FROM trail_endpoints
),
intersection_points AS (
SELECT
ip.intersection_point as point,
COALESCE(ST_Z(ip.intersection_point_3d), 0) as elevation,
'intersection' as node_type,
array_to_string(ip.connected_trail_names, ',') as connected_trails,
array_agg(t.app_uuid) as trail_ids
FROM detect_trail_intersections($1, 'trails', $2) ip
JOIN ${schemaName}.trails t ON t.id = ANY(ip.connected_trail_ids)
WHERE array_length(ip.connected_trail_ids, 1) > 1
GROUP BY ip.intersection_point, ip.intersection_point_3d, ip.connected_trail_names
),
all_nodes AS (
SELECT point, elevation, node_type, connected_trails, trail_ids
FROM all_endpoints
WHERE point IS NOT NULL
UNION ALL
SELECT point, elevation, node_type, connected_trails, trail_ids
FROM intersection_points
WHERE point IS NOT NULL
),
unique_nodes AS (
SELECT DISTINCT
point,
elevation,
node_type,
connected_trails,
trail_ids
FROM all_nodes
WHERE point IS NOT NULL
),
clustered_nodes AS (
SELECT
point as clustered_point,
elevation,
node_type,
connected_trails,
trail_ids
FROM unique_nodes
WHERE point IS NOT NULL
)
SELECT
ROW_NUMBER() OVER (ORDER BY ST_X(clustered_point), ST_Y(clustered_point)) as id,
gen_random_uuid() as node_uuid,
ST_Y(clustered_point) as lat,
ST_X(clustered_point) as lng,
elevation,
node_type,
connected_trails,
trail_ids,
NOW() as created_at
FROM clustered_nodes
WHERE clustered_point IS NOT NULL
`,
// Edge generation
generateEdges: (schemaName, tolerance) => `
INSERT INTO ${schemaName}.routing_edges (source, target, trail_id, trail_name, length_km, elevation_gain, elevation_loss, geometry, geojson)
SELECT
start_node.id as source,
end_node.id as target,
t.app_uuid as trail_id,
t.name as trail_name,
t.length_km as length_km,
t.elevation_gain,
t.elevation_loss,
ST_MakeLine(
ST_SetSRID(ST_MakePoint(start_node.lng, start_node.lat), 4326),
ST_SetSRID(ST_MakePoint(end_node.lng, end_node.lat), 4326)
) as geometry,
ST_AsGeoJSON(
ST_MakeLine(
ST_SetSRID(ST_MakePoint(start_node.lng, start_node.lat), 4326),
ST_SetSRID(ST_MakePoint(end_node.lng, end_node.lat), 4326)
), 6, 0
) as geojson
FROM ${schemaName}.trails t
JOIN LATERAL (
SELECT n.id, n.lng, n.lat
FROM ${schemaName}.routing_nodes n
WHERE t.app_uuid = ANY(n.trail_ids)
ORDER BY ST_Distance(ST_StartPoint(t.geometry), ST_SetSRID(ST_MakePoint(n.lng, n.lat), 4326))
LIMIT 1
) start_node ON true
JOIN LATERAL (
SELECT n.id, n.lng, n.lat
FROM ${schemaName}.routing_nodes n
WHERE t.app_uuid = ANY(n.trail_ids)
ORDER BY ST_Distance(ST_EndPoint(t.geometry), ST_SetSRID(ST_MakePoint(n.lng, n.lat), 4326))
LIMIT 1
) end_node ON true
WHERE t.geometry IS NOT NULL
AND ST_IsValid(t.geometry)
AND t.length_km > 0
AND start_node.id IS NOT NULL
AND end_node.id IS NOT NULL
AND start_node.id <> end_node.id
AND NOT EXISTS (
SELECT 1 FROM ${schemaName}.routing_edges e
WHERE e.trail_id = t.app_uuid
)
`,
// Network validation
validateNetwork: (schemaName) => `
WITH node_degrees AS (
SELECT
n.id,
n.node_type,
COUNT(DISTINCT e.source) + COUNT(DISTINCT e.target) as degree
FROM ${schemaName}.routing_nodes n
LEFT JOIN ${schemaName}.routing_edges e ON n.id = e.source OR n.id = e.target
GROUP BY n.id, n.node_type
)
SELECT
node_type,
COUNT(*) as total_nodes,
COUNT(CASE WHEN degree = 0 THEN 1 END) as isolated_nodes,
COUNT(CASE WHEN degree = 1 THEN 1 END) as leaf_nodes,
COUNT(CASE WHEN degree > 1 THEN 1 END) as connected_nodes,
AVG(degree) as avg_degree
FROM node_degrees
GROUP BY node_type
`,
// Cleanup orphaned nodes
cleanupOrphanedNodes: (schemaName) => `
DELETE FROM ${schemaName}.routing_nodes
WHERE id NOT IN (
SELECT DISTINCT source FROM ${schemaName}.routing_edges
UNION
SELECT DISTINCT target FROM ${schemaName}.routing_edges
)
`,
// Cleanup orphaned edges
cleanupOrphanedEdges: (schemaName) => `
DELETE FROM ${schemaName}.routing_edges
WHERE source NOT IN (SELECT id FROM ${schemaName}.routing_nodes)
OR target NOT IN (SELECT id FROM ${schemaName}.routing_nodes)
`,
// Get node type breakdown
getNodeTypeBreakdown: (schemaName) => `
SELECT node_type, COUNT(*) as count
FROM ${schemaName}.routing_nodes
GROUP BY node_type
`,
// Check for isolated nodes
checkIsolatedNodes: (schemaName) => `
SELECT COUNT(*) as count
FROM ${schemaName}.routing_nodes n
WHERE n.id NOT IN (
SELECT DISTINCT source FROM ${schemaName}.routing_edges
UNION
SELECT DISTINCT target FROM ${schemaName}.routing_edges
)
`,
// Check for orphaned edges
checkOrphanedEdges: (schemaName) => `
SELECT COUNT(*) as count
FROM ${schemaName}.routing_edges e
WHERE e.source NOT IN (SELECT id FROM ${schemaName}.routing_nodes)
OR e.target NOT IN (SELECT id FROM ${schemaName}.routing_nodes)
`
};
//# sourceMappingURL=routing-queries.js.map