2026-04-14-8 auth and commercial
This commit is contained in:
+6
-2
@@ -2,12 +2,16 @@ import { MapsService } from './maps.service';
|
||||
export declare class MapsController {
|
||||
private readonly mapsService;
|
||||
constructor(mapsService: MapsService);
|
||||
getRoute(fromLat: number, fromLng: number, toLat: number, toLng: number): Promise<{
|
||||
getRoute(query: any): Promise<{
|
||||
distance: any;
|
||||
duration: number;
|
||||
trafficAwareDuration: number;
|
||||
trafficFactor: number;
|
||||
startName: string;
|
||||
endName: string;
|
||||
points: any;
|
||||
bbox: any;
|
||||
congestionAware: boolean;
|
||||
alternatives: any;
|
||||
}>;
|
||||
getConfig(): Promise<{
|
||||
center: number[];
|
||||
|
||||
+27
-8
@@ -22,8 +22,30 @@ let MapsController = class MapsController {
|
||||
constructor(mapsService) {
|
||||
this.mapsService = mapsService;
|
||||
}
|
||||
async getRoute(fromLat, fromLng, toLat, toLng) {
|
||||
return this.mapsService.getRoute([fromLat, fromLng], [toLat, toLng]);
|
||||
async getRoute(query) {
|
||||
const waypoints = [];
|
||||
if (query.fromLat && query.fromLng) {
|
||||
waypoints.push([parseFloat(query.fromLat), parseFloat(query.fromLng)]);
|
||||
}
|
||||
const stopKeys = Object.keys(query)
|
||||
.filter(key => key.startsWith('stop') && key.endsWith('Lat'))
|
||||
.sort((a, b) => {
|
||||
const numA = parseInt(a.replace('stop', '').replace('Lat', ''), 10);
|
||||
const numB = parseInt(b.replace('stop', '').replace('Lat', ''), 10);
|
||||
return numA - numB;
|
||||
});
|
||||
for (const latKey of stopKeys) {
|
||||
const prefix = latKey.replace('Lat', '');
|
||||
const lngKey = `${prefix}Lng`;
|
||||
if (query[latKey] && query[lngKey]) {
|
||||
waypoints.push([parseFloat(query[latKey]), parseFloat(query[lngKey])]);
|
||||
}
|
||||
}
|
||||
if (query.toLat && query.toLng) {
|
||||
waypoints.push([parseFloat(query.toLat), parseFloat(query.toLng)]);
|
||||
}
|
||||
const profile = query.profile || 'car';
|
||||
return this.mapsService.getRoute(waypoints, profile);
|
||||
}
|
||||
async getConfig() {
|
||||
return this.mapsService.getMapConfig();
|
||||
@@ -32,13 +54,10 @@ let MapsController = class MapsController {
|
||||
exports.MapsController = MapsController;
|
||||
__decorate([
|
||||
(0, common_1.Get)('route'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Calculate a route between two points 🚗' }),
|
||||
__param(0, (0, common_1.Query)('fromLat')),
|
||||
__param(1, (0, common_1.Query)('fromLng')),
|
||||
__param(2, (0, common_1.Query)('toLat')),
|
||||
__param(3, (0, common_1.Query)('toLng')),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Calculate a route with dynamic waypoints 🚗' }),
|
||||
__param(0, (0, common_1.Query)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Number, Number, Number, Number]),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], MapsController.prototype, "getRoute", null);
|
||||
__decorate([
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"maps.controller.js","sourceRoot":"","sources":["../../src/maps/maps.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAmE;AACnE,6CAAwD;AACxD,iDAA6C;AAC7C,kEAA6D;AAKtD,IAAM,cAAc,GAApB,MAAM,cAAc;IACI;IAA7B,YAA6B,WAAwB;QAAxB,gBAAW,GAAX,WAAW,CAAa;IAAG,CAAC;IAInD,AAAN,KAAK,CAAC,QAAQ,CACM,OAAe,EACf,OAAe,EACjB,KAAa,EACb,KAAa;QAE7B,OAAO,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IACvE,CAAC;IAIK,AAAN,KAAK,CAAC,SAAS;QACb,OAAO,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC;IACzC,CAAC;CACF,CAAA;AAnBY,wCAAc;AAKnB;IAFL,IAAA,YAAG,EAAC,OAAO,CAAC;IACZ,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,yCAAyC,EAAE,CAAC;IAElE,WAAA,IAAA,cAAK,EAAC,SAAS,CAAC,CAAA;IAChB,WAAA,IAAA,cAAK,EAAC,SAAS,CAAC,CAAA;IAChB,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;IACd,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;;;;8CAGhB;AAIK;IAFL,IAAA,YAAG,EAAC,QAAQ,CAAC;IACb,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,0CAA0C,EAAE,CAAC;;;;+CAGrE;yBAlBU,cAAc;IAH1B,IAAA,iBAAO,EAAC,MAAM,CAAC;IACf,IAAA,mBAAU,EAAC,MAAM,CAAC;IAClB,IAAA,kBAAS,EAAC,2BAAW,CAAC;qCAEqB,0BAAW;GAD1C,cAAc,CAmB1B"}
|
||||
{"version":3,"file":"maps.controller.js","sourceRoot":"","sources":["../../src/maps/maps.controller.ts"],"names":[],"mappings":"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"}
|
||||
Vendored
+5
-2
@@ -10,10 +10,12 @@ exports.MapsModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const maps_service_1 = require("./maps.service");
|
||||
const traffic_grid_service_1 = require("./traffic-grid.service");
|
||||
const maps_controller_1 = require("./maps.controller");
|
||||
const road_stat_entity_1 = require("./road-stat.entity");
|
||||
const candidate_road_entity_1 = require("./candidate-road.entity");
|
||||
const redis_module_1 = require("../common/redis.module");
|
||||
const geocoding_module_1 = require("../geocoding/geocoding.module");
|
||||
let MapsModule = class MapsModule {
|
||||
};
|
||||
exports.MapsModule = MapsModule;
|
||||
@@ -22,10 +24,11 @@ exports.MapsModule = MapsModule = __decorate([
|
||||
imports: [
|
||||
typeorm_1.TypeOrmModule.forFeature([road_stat_entity_1.RoadSegmentStat, candidate_road_entity_1.CandidateRoad]),
|
||||
redis_module_1.RedisModule,
|
||||
geocoding_module_1.GeocodingModule,
|
||||
],
|
||||
controllers: [maps_controller_1.MapsController],
|
||||
providers: [maps_service_1.MapsService],
|
||||
exports: [maps_service_1.MapsService],
|
||||
providers: [maps_service_1.MapsService, traffic_grid_service_1.TrafficGridService],
|
||||
exports: [maps_service_1.MapsService, traffic_grid_service_1.TrafficGridService],
|
||||
})
|
||||
], MapsModule);
|
||||
//# sourceMappingURL=maps.module.js.map
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"maps.module.js","sourceRoot":"","sources":["../../src/maps/maps.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,iDAA6C;AAC7C,uDAAmD;AACnD,yDAAqD;AACrD,mEAAwD;AACxD,yDAAqD;AAW9C,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IATtB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,UAAU,CAAC,CAAC,kCAAe,EAAE,qCAAa,CAAC,CAAC;YAC1D,0BAAW;SACZ;QACD,WAAW,EAAE,CAAC,gCAAc,CAAC;QAC7B,SAAS,EAAE,CAAC,0BAAW,CAAC;QACxB,OAAO,EAAE,CAAC,0BAAW,CAAC;KACvB,CAAC;GACW,UAAU,CAAG"}
|
||||
{"version":3,"file":"maps.module.js","sourceRoot":"","sources":["../../src/maps/maps.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,iDAA6C;AAC7C,iEAA4D;AAC5D,uDAAmD;AACnD,yDAAqD;AACrD,mEAAwD;AACxD,yDAAqD;AACrD,oEAAgE;AAYzD,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IAVtB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,UAAU,CAAC,CAAC,kCAAe,EAAE,qCAAa,CAAC,CAAC;YAC1D,0BAAW;YACX,kCAAe;SAChB;QACD,WAAW,EAAE,CAAC,gCAAc,CAAC;QAC7B,SAAS,EAAE,CAAC,0BAAW,EAAE,yCAAkB,CAAC;QAC5C,OAAO,EAAE,CAAC,0BAAW,EAAE,yCAAkB,CAAC;KAC3C,CAAC;GACW,UAAU,CAAG"}
|
||||
Vendored
+13
-8
@@ -1,22 +1,27 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Repository, DataSource } from 'typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { RoadSegmentStat } from './road-stat.entity';
|
||||
import { RedisService } from '../common/redis.service';
|
||||
import { TrafficGridService } from './traffic-grid.service';
|
||||
import { GeocodingService } from '../geocoding/geocoding.service';
|
||||
export declare class MapsService {
|
||||
private configService;
|
||||
private roadStatRepo;
|
||||
private dataSource;
|
||||
private redisService;
|
||||
private trafficGrid;
|
||||
private geocodingService;
|
||||
private readonly graphHopperUrl;
|
||||
private readonly TRAFFIC_CACHE_KEY;
|
||||
constructor(configService: ConfigService, roadStatRepo: Repository<RoadSegmentStat>, dataSource: DataSource, redisService: RedisService);
|
||||
getRoute(from: [number, number], to: [number, number]): Promise<{
|
||||
constructor(configService: ConfigService, roadStatRepo: Repository<RoadSegmentStat>, trafficGrid: TrafficGridService, geocodingService: GeocodingService);
|
||||
getRoute(waypoints: [number, number][], profile?: string): Promise<{
|
||||
distance: any;
|
||||
duration: number;
|
||||
trafficAwareDuration: number;
|
||||
trafficFactor: number;
|
||||
startName: string;
|
||||
endName: string;
|
||||
points: any;
|
||||
bbox: any;
|
||||
congestionAware: boolean;
|
||||
alternatives: any;
|
||||
}>;
|
||||
private decodePolyline;
|
||||
getMapConfig(): Promise<{
|
||||
center: number[];
|
||||
zoom: number;
|
||||
|
||||
Vendored
+101
-72
@@ -20,100 +20,129 @@ const common_1 = require("@nestjs/common");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const typeorm_2 = require("typeorm");
|
||||
const road_stat_entity_1 = require("./road-stat.entity");
|
||||
const redis_service_1 = require("../common/redis.service");
|
||||
const axios_1 = __importDefault(require("axios"));
|
||||
const road_stat_entity_1 = require("./road-stat.entity");
|
||||
const traffic_grid_service_1 = require("./traffic-grid.service");
|
||||
const geocoding_service_1 = require("../geocoding/geocoding.service");
|
||||
let MapsService = class MapsService {
|
||||
configService;
|
||||
roadStatRepo;
|
||||
dataSource;
|
||||
redisService;
|
||||
trafficGrid;
|
||||
geocodingService;
|
||||
graphHopperUrl;
|
||||
TRAFFIC_CACHE_KEY = 'traffic_snapshot';
|
||||
constructor(configService, roadStatRepo, dataSource, redisService) {
|
||||
constructor(configService, roadStatRepo, trafficGrid, geocodingService) {
|
||||
this.configService = configService;
|
||||
this.roadStatRepo = roadStatRepo;
|
||||
this.dataSource = dataSource;
|
||||
this.redisService = redisService;
|
||||
this.graphHopperUrl = this.configService.get('GRAPH_HOPPER_URL', 'http://map-routing:8080');
|
||||
this.trafficGrid = trafficGrid;
|
||||
this.geocodingService = geocodingService;
|
||||
this.graphHopperUrl = this.configService.get('GRAPH_HOPPER_URL', 'http://routing:8080');
|
||||
}
|
||||
async getRoute(from, to) {
|
||||
const congestedSegments = await this.redisService.get(this.TRAFFIC_CACHE_KEY) || [];
|
||||
const buffer = 0.05;
|
||||
const minLat = Math.min(from[0], to[0]) - buffer;
|
||||
const maxLat = Math.max(from[0], to[0]) + buffer;
|
||||
const minLng = Math.min(from[1], to[1]) - buffer;
|
||||
const maxLng = Math.max(from[1], to[1]) + buffer;
|
||||
const nearbyCongestion = congestedSegments.filter(seg => {
|
||||
const geom = JSON.parse(typeof seg.geojson === 'string' ? seg.geojson : JSON.stringify(seg.geojson));
|
||||
return geom.coordinates.some(([lng, lat]) => lat >= minLat && lat <= maxLat && lng >= minLng && lng <= maxLng);
|
||||
}).slice(0, 50);
|
||||
const customModel = {
|
||||
speed: [],
|
||||
areas: {},
|
||||
};
|
||||
nearbyCongestion.forEach((seg, index) => {
|
||||
const areaId = `cong_${index}`;
|
||||
const geojson = typeof seg.geojson === 'string' ? JSON.parse(seg.geojson) : seg.geojson;
|
||||
customModel.areas[areaId] = {
|
||||
type: 'Feature',
|
||||
geometry: geojson,
|
||||
properties: {}
|
||||
};
|
||||
customModel.speed.push({
|
||||
if: `in_${areaId}`,
|
||||
multiply_by: Math.max(0.1, 1 / seg.congestionFactor)
|
||||
});
|
||||
});
|
||||
async getRoute(waypoints, profile = 'car') {
|
||||
if (waypoints.length < 2) {
|
||||
throw new common_1.HttpException('At least two waypoints are required', common_1.HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
try {
|
||||
const params = {
|
||||
point: [`${from[0]},${from[1]}`, `${to[0]},${to[1]}`],
|
||||
profile: 'car',
|
||||
const ghPoints = waypoints.map(wp => [wp[1], wp[0]]);
|
||||
let startName = 'Unknown Location';
|
||||
let endName = 'Unknown Location';
|
||||
try {
|
||||
const startWp = waypoints[0];
|
||||
const endWp = waypoints[waypoints.length - 1];
|
||||
const [startRes, endRes] = await Promise.all([
|
||||
this.geocodingService.reverseGeocode(startWp[0], startWp[1]),
|
||||
this.geocodingService.reverseGeocode(endWp[0], endWp[1])
|
||||
]);
|
||||
const formatName = (r) => {
|
||||
const parts = [r.name_ar || r.name, r.neighbourhood, r.district, r.governorate].filter(Boolean);
|
||||
const uniqueParts = [...new Set(parts)];
|
||||
return uniqueParts.length > 0 ? uniqueParts.join('، ') : 'Unknown Location';
|
||||
};
|
||||
if (startRes && startRes.length > 0)
|
||||
startName = formatName(startRes[0]);
|
||||
if (endRes && endRes.length > 0)
|
||||
endName = formatName(endRes[0]);
|
||||
}
|
||||
catch (e) {
|
||||
console.warn('Geocoding internal error during routing:', e);
|
||||
}
|
||||
const payload = {
|
||||
points: ghPoints,
|
||||
profile: profile,
|
||||
locale: 'en',
|
||||
calc_points: true,
|
||||
points_encoded: true,
|
||||
elevation: false,
|
||||
type: 'json',
|
||||
};
|
||||
if (customModel.speed.length > 0) {
|
||||
params.custom_model = JSON.stringify(customModel);
|
||||
if (waypoints.length === 2) {
|
||||
payload.algorithm = 'alternative_route';
|
||||
payload['ch.disable'] = true;
|
||||
payload['alternative_route.max_paths'] = 2;
|
||||
payload['alternative_route.max_weight_factor'] = 1.6;
|
||||
payload['alternative_route.max_share_factor'] = 0.6;
|
||||
}
|
||||
const response = await axios_1.default.get(`${this.graphHopperUrl}/route`, {
|
||||
params,
|
||||
paramsSerializer: (params) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(v => searchParams.append(key, v));
|
||||
}
|
||||
else {
|
||||
searchParams.append(key, value);
|
||||
}
|
||||
});
|
||||
return searchParams.toString();
|
||||
},
|
||||
});
|
||||
console.log('Routing Successful with congestion:', customModel.speed.length, 'segments');
|
||||
const route = response.data.paths[0];
|
||||
if (!route) {
|
||||
console.log(`Routing Request: ${waypoints.length} points via ${profile} on ${this.graphHopperUrl}`);
|
||||
const response = await axios_1.default.post(`${this.graphHopperUrl}/route`, payload, { timeout: 10000 });
|
||||
console.log('Routing SUCCESS');
|
||||
const paths = response.data.paths;
|
||||
if (!paths || paths.length === 0)
|
||||
throw new common_1.HttpException('No route found', common_1.HttpStatus.NOT_FOUND);
|
||||
}
|
||||
const route = paths[0];
|
||||
const now = new Date();
|
||||
const hr = now.getHours();
|
||||
const dow = now.getDay();
|
||||
const coords = this.decodePolyline(route.points);
|
||||
const trafficFactor = this.trafficGrid.getTrafficFactor(coords, hr, dow);
|
||||
const baseDuration = route.time / 1000;
|
||||
const trafficAwareDuration = baseDuration * trafficFactor;
|
||||
const alternatives = paths.slice(1).map(alt => ({
|
||||
distance: alt.distance,
|
||||
duration: Math.round(alt.time / 1000),
|
||||
points: alt.points,
|
||||
bbox: alt.bbox
|
||||
}));
|
||||
return {
|
||||
distance: route.distance,
|
||||
duration: route.time / 1000,
|
||||
duration: Math.round(baseDuration),
|
||||
trafficAwareDuration: Math.round(trafficAwareDuration),
|
||||
trafficFactor: Math.round(trafficFactor * 100) / 100,
|
||||
startName,
|
||||
endName,
|
||||
points: route.points,
|
||||
bbox: route.bbox,
|
||||
congestionAware: customModel.speed.length > 0,
|
||||
alternatives: alternatives
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Routing Error:', error.message);
|
||||
if (error.response) {
|
||||
console.error('GraphHopper Response:', error.response.data);
|
||||
}
|
||||
throw new common_1.HttpException('Error calculating route', common_1.HttpStatus.BAD_GATEWAY);
|
||||
const msg = error.response ? `GH Error: ${JSON.stringify(error.response.data)}` : `DNS/Connection Error: ${error.message}`;
|
||||
console.error('CRITICAL ROUTING FAILURE:', msg);
|
||||
throw new common_1.HttpException(`Routing Failure: ${msg}`, common_1.HttpStatus.BAD_GATEWAY);
|
||||
}
|
||||
}
|
||||
decodePolyline(encoded) {
|
||||
const points = [];
|
||||
let index = 0, len = encoded.length;
|
||||
let lat = 0, lng = 0;
|
||||
while (index < len) {
|
||||
let b, shift = 0, result = 0;
|
||||
do {
|
||||
b = encoded.charCodeAt(index++) - 63;
|
||||
result |= (b & 0x1f) << shift;
|
||||
shift += 5;
|
||||
} while (b >= 0x20);
|
||||
let dlat = ((result & 1) ? ~(result >> 1) : (result >> 1));
|
||||
lat += dlat;
|
||||
shift = 0;
|
||||
result = 0;
|
||||
do {
|
||||
b = encoded.charCodeAt(index++) - 63;
|
||||
result |= (b & 0x1f) << shift;
|
||||
shift += 5;
|
||||
} while (b >= 0x20);
|
||||
let dlng = ((result & 1) ? ~(result >> 1) : (result >> 1));
|
||||
lng += dlng;
|
||||
points.push([lng * 1e-5, lat * 1e-5]);
|
||||
}
|
||||
return points;
|
||||
}
|
||||
async getMapConfig() {
|
||||
return {
|
||||
center: [31.95, 35.91],
|
||||
@@ -128,7 +157,7 @@ exports.MapsService = MapsService = __decorate([
|
||||
__param(1, (0, typeorm_1.InjectRepository)(road_stat_entity_1.RoadSegmentStat)),
|
||||
__metadata("design:paramtypes", [config_1.ConfigService,
|
||||
typeorm_2.Repository,
|
||||
typeorm_2.DataSource,
|
||||
redis_service_1.RedisService])
|
||||
traffic_grid_service_1.TrafficGridService,
|
||||
geocoding_service_1.GeocodingService])
|
||||
], MapsService);
|
||||
//# sourceMappingURL=maps.service.js.map
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
export declare class RoadSpeedProfile {
|
||||
segmentId: string;
|
||||
hourOfDay: number;
|
||||
dayOfWeek: number;
|
||||
averageSpeed: number;
|
||||
sampleCount: number;
|
||||
lastUpdated: Date;
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
"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);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.RoadSpeedProfile = void 0;
|
||||
const typeorm_1 = require("typeorm");
|
||||
let RoadSpeedProfile = class RoadSpeedProfile {
|
||||
segmentId;
|
||||
hourOfDay;
|
||||
dayOfWeek;
|
||||
averageSpeed;
|
||||
sampleCount;
|
||||
lastUpdated;
|
||||
};
|
||||
exports.RoadSpeedProfile = RoadSpeedProfile;
|
||||
__decorate([
|
||||
(0, typeorm_1.PrimaryColumn)(),
|
||||
__metadata("design:type", String)
|
||||
], RoadSpeedProfile.prototype, "segmentId", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.PrimaryColumn)(),
|
||||
__metadata("design:type", Number)
|
||||
], RoadSpeedProfile.prototype, "hourOfDay", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.PrimaryColumn)(),
|
||||
__metadata("design:type", Number)
|
||||
], RoadSpeedProfile.prototype, "dayOfWeek", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('float', { default: 0 }),
|
||||
__metadata("design:type", Number)
|
||||
], RoadSpeedProfile.prototype, "averageSpeed", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('int', { default: 0 }),
|
||||
__metadata("design:type", Number)
|
||||
], RoadSpeedProfile.prototype, "sampleCount", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.CreateDateColumn)(),
|
||||
__metadata("design:type", Date)
|
||||
], RoadSpeedProfile.prototype, "lastUpdated", void 0);
|
||||
exports.RoadSpeedProfile = RoadSpeedProfile = __decorate([
|
||||
(0, typeorm_1.Entity)('road_speed_profiles'),
|
||||
(0, typeorm_1.Index)(['segmentId', 'hourOfDay', 'dayOfWeek'], { unique: true })
|
||||
], RoadSpeedProfile);
|
||||
//# sourceMappingURL=road-speed-profile.entity.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"road-speed-profile.entity.js","sourceRoot":"","sources":["../../src/maps/road-speed-profile.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAiF;AAI1E,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAE3B,SAAS,CAAS;IAGlB,SAAS,CAAS;IAGlB,SAAS,CAAS;IAGlB,YAAY,CAAS;IAGrB,WAAW,CAAS;IAGpB,WAAW,CAAO;CACnB,CAAA;AAlBY,4CAAgB;AAE3B;IADC,IAAA,uBAAa,GAAE;;mDACE;AAGlB;IADC,IAAA,uBAAa,GAAE;;mDACE;AAGlB;IADC,IAAA,uBAAa,GAAE;;mDACE;AAGlB;IADC,IAAA,gBAAM,EAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;sDACX;AAGrB;IADC,IAAA,gBAAM,EAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;qDACV;AAGpB;IADC,IAAA,0BAAgB,GAAE;8BACN,IAAI;qDAAC;2BAjBP,gBAAgB;IAF5B,IAAA,gBAAM,EAAC,qBAAqB,CAAC;IAC7B,IAAA,eAAK,EAAC,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;GACpD,gBAAgB,CAkB5B"}
|
||||
+1
@@ -2,6 +2,7 @@ export declare class RoadSegmentStat {
|
||||
segmentId: string;
|
||||
averageSpeed: number;
|
||||
congestionFactor: number;
|
||||
isClosed: boolean;
|
||||
sampleCount: number;
|
||||
geometry: any;
|
||||
lastUpdated: Date;
|
||||
|
||||
+8
-1
@@ -15,6 +15,7 @@ let RoadSegmentStat = class RoadSegmentStat {
|
||||
segmentId;
|
||||
averageSpeed;
|
||||
congestionFactor;
|
||||
isClosed;
|
||||
sampleCount;
|
||||
geometry;
|
||||
lastUpdated;
|
||||
@@ -32,6 +33,11 @@ __decorate([
|
||||
(0, typeorm_1.Column)('float', { default: 1.0 }),
|
||||
__metadata("design:type", Number)
|
||||
], RoadSegmentStat.prototype, "congestionFactor", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('boolean', { default: false }),
|
||||
(0, typeorm_1.Index)(),
|
||||
__metadata("design:type", Boolean)
|
||||
], RoadSegmentStat.prototype, "isClosed", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('int', { default: 0 }),
|
||||
__metadata("design:type", Number)
|
||||
@@ -51,6 +57,7 @@ __decorate([
|
||||
__metadata("design:type", Date)
|
||||
], RoadSegmentStat.prototype, "lastUpdated", void 0);
|
||||
exports.RoadSegmentStat = RoadSegmentStat = __decorate([
|
||||
(0, typeorm_1.Entity)('road_segment_stats')
|
||||
(0, typeorm_1.Entity)('road_segment_stats'),
|
||||
(0, typeorm_1.Index)('idx_segment_congestion', ['congestionFactor'], { where: '"congestionFactor" > 1.1' })
|
||||
], RoadSegmentStat);
|
||||
//# sourceMappingURL=road-stat.entity.js.map
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"road-stat.entity.js","sourceRoot":"","sources":["../../src/maps/road-stat.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAA+D;AAGxD,IAAM,eAAe,GAArB,MAAM,eAAe;IAE1B,SAAS,CAAS;IAGlB,YAAY,CAAS;IAGrB,gBAAgB,CAAS;IAGzB,WAAW,CAAS;IASpB,QAAQ,CAAM;IAGd,WAAW,CAAO;CACnB,CAAA;AAxBY,0CAAe;AAE1B;IADC,IAAA,uBAAa,GAAE;;kDACE;AAGlB;IADC,IAAA,gBAAM,EAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;qDACX;AAGrB;IADC,IAAA,gBAAM,EAAC,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;;yDACT;AAGzB;IADC,IAAA,gBAAM,EAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;oDACV;AASpB;IAPC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,WAAW;QACjB,kBAAkB,EAAE,YAAY;QAChC,IAAI,EAAE,IAAI;QACV,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,eAAK,EAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;iDACX;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAE,CAAC;8BACrD,IAAI;oDAAC;0BAvBP,eAAe;IAD3B,IAAA,gBAAM,EAAC,oBAAoB,CAAC;GAChB,eAAe,CAwB3B"}
|
||||
{"version":3,"file":"road-stat.entity.js","sourceRoot":"","sources":["../../src/maps/road-stat.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAA+D;AAIxD,IAAM,eAAe,GAArB,MAAM,eAAe;IAE1B,SAAS,CAAS;IAGlB,YAAY,CAAS;IAGrB,gBAAgB,CAAS;IAIzB,QAAQ,CAAU;IAGlB,WAAW,CAAS;IASpB,QAAQ,CAAM;IAGd,WAAW,CAAO;CACnB,CAAA;AA5BY,0CAAe;AAE1B;IADC,IAAA,uBAAa,GAAE;;kDACE;AAGlB;IADC,IAAA,gBAAM,EAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;qDACX;AAGrB;IADC,IAAA,gBAAM,EAAC,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;;yDACT;AAIzB;IAFC,IAAA,gBAAM,EAAC,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACrC,IAAA,eAAK,GAAE;;iDACU;AAGlB;IADC,IAAA,gBAAM,EAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;oDACV;AASpB;IAPC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,WAAW;QACjB,kBAAkB,EAAE,YAAY;QAChC,IAAI,EAAE,IAAI;QACV,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,eAAK,EAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;iDACX;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAE,CAAC;8BACrD,IAAI;oDAAC;0BA3BP,eAAe;IAF3B,IAAA,gBAAM,EAAC,oBAAoB,CAAC;IAC5B,IAAA,eAAK,EAAC,wBAAwB,EAAE,CAAC,kBAAkB,CAAC,EAAE,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC;GAChF,eAAe,CA4B3B"}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { OnModuleInit } from '@nestjs/common';
|
||||
import { DataSource } from 'typeorm';
|
||||
export declare class TrafficGridService implements OnModuleInit {
|
||||
private dataSource;
|
||||
private readonly logger;
|
||||
private grid;
|
||||
private readonly GEOHASH_PRECISION;
|
||||
constructor(dataSource: DataSource);
|
||||
onModuleInit(): Promise<void>;
|
||||
refreshGrid(): Promise<void>;
|
||||
getTrafficFactor(coords: [number, number][], hr: number, dow: number): number;
|
||||
private encodeGeohash;
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
"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;
|
||||
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", [typeorm_1.DataSource])
|
||||
], TrafficGridService);
|
||||
//# sourceMappingURL=traffic-grid.service.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"traffic-grid.service.js","sourceRoot":"","sources":["../../src/maps/traffic-grid.service.ts"],"names":[],"mappings":"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"}
|
||||
Reference in New Issue
Block a user