163 lines
7.4 KiB
JavaScript
163 lines
7.4 KiB
JavaScript
"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 __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
};
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.MapsService = void 0;
|
|
const common_1 = require("@nestjs/common");
|
|
const config_1 = require("@nestjs/config");
|
|
const typeorm_1 = require("@nestjs/typeorm");
|
|
const typeorm_2 = require("typeorm");
|
|
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;
|
|
trafficGrid;
|
|
geocodingService;
|
|
graphHopperUrl;
|
|
constructor(configService, roadStatRepo, trafficGrid, geocodingService) {
|
|
this.configService = configService;
|
|
this.roadStatRepo = roadStatRepo;
|
|
this.trafficGrid = trafficGrid;
|
|
this.geocodingService = geocodingService;
|
|
this.graphHopperUrl = this.configService.get('GRAPH_HOPPER_URL', 'http://routing:8080');
|
|
}
|
|
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 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,
|
|
};
|
|
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;
|
|
}
|
|
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: Math.round(baseDuration),
|
|
trafficAwareDuration: Math.round(trafficAwareDuration),
|
|
trafficFactor: Math.round(trafficFactor * 100) / 100,
|
|
startName,
|
|
endName,
|
|
points: route.points,
|
|
bbox: route.bbox,
|
|
alternatives: alternatives
|
|
};
|
|
}
|
|
catch (error) {
|
|
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],
|
|
zoom: 12,
|
|
tileServerUrl: this.configService.get('TILE_SERVER_URL', 'http://localhost:3001'),
|
|
};
|
|
}
|
|
};
|
|
exports.MapsService = MapsService;
|
|
exports.MapsService = MapsService = __decorate([
|
|
(0, common_1.Injectable)(),
|
|
__param(1, (0, typeorm_1.InjectRepository)(road_stat_entity_1.RoadSegmentStat)),
|
|
__metadata("design:paramtypes", [config_1.ConfigService,
|
|
typeorm_2.Repository,
|
|
traffic_grid_service_1.TrafficGridService,
|
|
geocoding_service_1.GeocodingService])
|
|
], MapsService);
|
|
//# sourceMappingURL=maps.service.js.map
|