"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var TrafficGridService_1; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.TrafficGridService = void 0; const common_1 = require("@nestjs/common"); const typeorm_1 = require("typeorm"); let TrafficGridService = TrafficGridService_1 = class TrafficGridService { dataSource; logger = new common_1.Logger(TrafficGridService_1.name); grid = new Map(); GEOHASH_PRECISION = 6; constructor(dataSource) { this.dataSource = dataSource; } async onModuleInit() { await this.refreshGrid(); } async refreshGrid() { this.logger.log('📡 Rebuilding Traffic Grid v3 (In-Memory Lookup)...'); try { const startTime = Date.now(); const data = await this.dataSource.query(` SELECT ST_X(ST_Centroid(geometry::geometry)) as lng, ST_Y(ST_Centroid(geometry::geometry)) as lat, p."hourOfDay", p."dayOfWeek", p."averageSpeed", s."averageSpeed" as base_speed FROM road_speed_profiles p JOIN road_segment_stats s ON p."segmentId" = s."segmentId" WHERE s.geometry IS NOT NULL `); this.grid.clear(); let count = 0; for (const row of data) { const gh = this.encodeGeohash(row.lat, row.lng, this.GEOHASH_PRECISION); const factor = row.base_speed / Math.max(row.averageSpeed, 1); const key = `${gh}:${row.hourOfDay}:${row.dayOfWeek}`; this.grid.set(key, factor); count++; } this.logger.log(`✅ Grid rebuilt in ${Date.now() - startTime}ms. Cached ${count} cells (${this.grid.size} unique keys).`); } catch (e) { this.logger.error('❌ Failed to refresh traffic grid:', e.message); } } getTrafficFactor(coords, hr, dow) { if (this.grid.size === 0) return 1.0; let totalFactor = 0; let matches = 0; for (let i = 0; i < coords.length; i += 2) { const [lng, lat] = coords[i]; const gh = this.encodeGeohash(lat, lng, this.GEOHASH_PRECISION); const key = `${gh}:${hr}:${dow}`; const factor = this.grid.get(key); if (factor) { totalFactor += factor; matches++; } } if (matches === 0) return 1.0; let finalFactor = totalFactor / matches; return Math.min(Math.max(finalFactor, 0.8), 3.0); } encodeGeohash(lat, lng, precision) { const BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz"; let minLat = -90, maxLat = 90; let minLng = -180, maxLng = 180; let result = ""; let isEven = true; let bit = 0; let ch = 0; while (result.length < precision) { if (isEven) { let mid = (minLng + maxLng) / 2; if (lng > mid) { ch |= (1 << (4 - bit)); minLng = mid; } else { maxLng = mid; } } else { let mid = (minLat + maxLat) / 2; if (lat > mid) { ch |= (1 << (4 - bit)); minLat = mid; } else { maxLat = mid; } } isEven = !isEven; if (bit < 4) { bit++; } else { result += BASE32[ch]; bit = 0; ch = 0; } } return result; } }; exports.TrafficGridService = TrafficGridService; exports.TrafficGridService = TrafficGridService = TrafficGridService_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [typeof (_a = typeof typeorm_1.DataSource !== "undefined" && typeorm_1.DataSource) === "function" ? _a : Object]) ], TrafficGridService); //# sourceMappingURL=traffic-grid.service.js.map