Files
Laca-City/backend/dist/modules/routing/routing.service.js
PhongPham c65cc97a33 🎯 MapView v2.0 - Global Deployment Ready
 MAJOR FEATURES:
• Auto-zoom intelligence với smart bounds fitting
• Enhanced 3D GPS markers với pulsing effects
• Professional route display với 6-layer rendering
• Status-based parking icons với availability indicators
• Production-ready build optimizations

🗺️ AUTO-ZOOM FEATURES:
• Smart bounds fitting cho GPS + selected parking
• Adaptive padding (50px) cho visual balance
• Max zoom control (level 16) để tránh quá gần
• Dynamic centering khi không có selection

🎨 ENHANCED VISUALS:
• 3D GPS marker với multi-layer pulse effects
• Advanced parking icons với status colors
• Selection highlighting với animation
• Dimming system cho non-selected items

🛣️ ROUTE SYSTEM:
• OpenRouteService API integration
• Multi-layer route rendering (glow, shadow, main, animated)
• Real-time distance & duration calculation
• Visual route info trong popup

📱 PRODUCTION READY:
• SSR safe với dynamic imports
• Build errors resolved
• Global deployment via Vercel
• Optimized performance

🌍 DEPLOYMENT:
• Vercel: https://whatever-ctk2auuxr-phong12hexdockworks-projects.vercel.app
• Bundle size: 22.8 kB optimized
• Global CDN distribution
• HTTPS enabled

💾 VERSION CONTROL:
• MapView-v2.0.tsx backup created
• MAPVIEW_VERSIONS.md documentation
• Full version history tracking
2025-07-20 19:52:16 +07:00

206 lines
7.8 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 RoutingService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RoutingService = void 0;
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
const axios_1 = require("axios");
let RoutingService = RoutingService_1 = class RoutingService {
constructor(configService) {
this.configService = configService;
this.logger = new common_1.Logger(RoutingService_1.name);
this.valhallaUrl = this.configService.get('VALHALLA_URL') || 'http://valhalla:8002';
this.valhallaClient = axios_1.default.create({
baseURL: this.valhallaUrl,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
});
}
async calculateRoute(dto) {
try {
this.logger.debug(`Calculating route from ${dto.originLat},${dto.originLng} to ${dto.destinationLat},${dto.destinationLng}`);
const requestId = this.generateRequestId();
const valhallaRequest = this.buildValhallaRequest(dto);
const response = await this.valhallaClient.post('/route', valhallaRequest);
if (!response.data || !response.data.trip) {
throw new Error('Invalid response from Valhalla routing engine');
}
const routes = this.parseValhallaResponse(response.data);
return {
routes,
origin: { lat: dto.originLat, lng: dto.originLng },
destination: { lat: dto.destinationLat, lng: dto.destinationLng },
requestId,
};
}
catch (error) {
this.logger.error('Failed to calculate route', error);
if (error.response?.status === 400) {
throw new common_1.HttpException('Invalid route request parameters', common_1.HttpStatus.BAD_REQUEST);
}
if (error.response?.status === 404) {
throw new common_1.HttpException('No route found between the specified locations', common_1.HttpStatus.NOT_FOUND);
}
throw new common_1.HttpException('Route calculation service unavailable', common_1.HttpStatus.SERVICE_UNAVAILABLE);
}
}
buildValhallaRequest(dto) {
const locations = [
{ lat: dto.originLat, lon: dto.originLng },
{ lat: dto.destinationLat, lon: dto.destinationLng },
];
const costingOptions = this.getCostingOptions(dto);
return {
locations,
costing: dto.costing,
costing_options: costingOptions,
directions_options: {
units: 'kilometers',
language: 'en-US',
narrative: true,
alternates: dto.alternatives || 1,
},
format: 'json',
shape_match: 'edge_walk',
encoded_polyline: true,
};
}
getCostingOptions(dto) {
const options = {};
if (dto.costing === 'auto') {
options.auto = {
maneuver_penalty: 5,
gate_cost: 30,
gate_penalty: 300,
private_access_penalty: 450,
toll_booth_cost: 15,
toll_booth_penalty: 0,
ferry_cost: 300,
use_ferry: dto.avoidTolls ? 0 : 1,
use_highways: dto.avoidHighways ? 0 : 1,
use_tolls: dto.avoidTolls ? 0 : 1,
};
}
else if (dto.costing === 'bicycle') {
options.bicycle = {
maneuver_penalty: 5,
gate_penalty: 300,
use_roads: 0.5,
use_hills: 0.5,
use_ferry: 1,
avoid_bad_surfaces: 0.25,
};
}
else if (dto.costing === 'pedestrian') {
options.pedestrian = {
walking_speed: 5.1,
walkway_factor: 1,
sidewalk_factor: 1,
alley_factor: 2,
driveway_factor: 5,
step_penalty: 0,
use_ferry: 1,
use_living_streets: 0.6,
};
}
return options;
}
parseValhallaResponse(data) {
const trip = data.trip;
if (!trip || !trip.legs || trip.legs.length === 0) {
return [];
}
const route = {
summary: {
distance: Math.round(trip.summary.length * 100) / 100,
time: Math.round(trip.summary.time / 60 * 100) / 100,
cost: this.estimateFuelCost(trip.summary.length, 'auto'),
},
geometry: this.decodePolyline(trip.shape),
steps: this.parseManeuvers(trip.legs[0].maneuvers),
confidence: 0.95,
};
return [route];
}
parseManeuvers(maneuvers) {
return maneuvers.map(maneuver => ({
instruction: maneuver.instruction,
distance: Math.round(maneuver.length * 1000),
time: maneuver.time,
type: maneuver.type?.toString() || 'unknown',
geometry: [],
}));
}
decodePolyline(encoded) {
const points = [];
let index = 0;
let lat = 0;
let lng = 0;
while (index < encoded.length) {
let result = 1;
let shift = 0;
let b;
do {
b = encoded.charCodeAt(index++) - 63 - 1;
result += b << shift;
shift += 5;
} while (b >= 0x1f);
lat += (result & 1) !== 0 ? ~(result >> 1) : (result >> 1);
result = 1;
shift = 0;
do {
b = encoded.charCodeAt(index++) - 63 - 1;
result += b << shift;
shift += 5;
} while (b >= 0x1f);
lng += (result & 1) !== 0 ? ~(result >> 1) : (result >> 1);
points.push({
lat: lat / 1e5,
lng: lng / 1e5,
});
}
return points;
}
estimateFuelCost(distanceKm, costing) {
if (costing !== 'auto')
return 0;
const fuelEfficiency = 10;
const fuelPricePerLiter = 1.5;
return Math.round((distanceKm / fuelEfficiency) * fuelPricePerLiter * 100) / 100;
}
generateRequestId() {
return `route_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
async getServiceStatus() {
try {
const response = await this.valhallaClient.get('/status');
return {
status: 'healthy',
version: response.data?.version,
};
}
catch (error) {
this.logger.error('Valhalla service health check failed', error);
return {
status: 'unhealthy',
};
}
}
};
exports.RoutingService = RoutingService;
exports.RoutingService = RoutingService = RoutingService_1 = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [config_1.ConfigService])
], RoutingService);
//# sourceMappingURL=routing.service.js.map