차량 목적지 경로 미리보기 추가
This commit is contained in:
@@ -565,6 +565,14 @@ function publishSimulatorLocations() {
|
||||
function createSimulatorLocationBody(vehicle, nowMs) {
|
||||
const routeState = interpolateRoute(vehicle.route, nowMs, vehicle.loopMs, hashOffset(vehicle.driverId));
|
||||
const speedWave = Math.sin((nowMs + hashOffset(vehicle.vehicleNo)) / 17000) * 8;
|
||||
const speed = Math.max(18, Math.round(vehicle.speedBase + speedWave));
|
||||
const routePath = createSimulatedRoutePath(
|
||||
{ latitude: routeState.latitude, longitude: routeState.longitude },
|
||||
routeState.destinationPoint,
|
||||
vehicle.driverId
|
||||
);
|
||||
const remainingDistanceKm = roundNumber(calculatePathDistanceKm(routePath) * 1.08, 1);
|
||||
const etaMinutes = Math.max(3, Math.round((remainingDistanceKm / Math.max(speed, 24)) * 60));
|
||||
|
||||
return {
|
||||
driverId: vehicle.driverId,
|
||||
@@ -573,13 +581,20 @@ function createSimulatorLocationBody(vehicle, nowMs) {
|
||||
latitude: routeState.latitude,
|
||||
longitude: routeState.longitude,
|
||||
accuracy: 8,
|
||||
speed: Math.max(18, Math.round(vehicle.speedBase + speedWave)),
|
||||
speed,
|
||||
heading: routeState.heading,
|
||||
cargoName: vehicle.cargoName,
|
||||
cargoQuantity: vehicle.cargoQuantity,
|
||||
cargoWeight: vehicle.cargoWeight,
|
||||
origin: routeState.origin,
|
||||
destination: routeState.destination,
|
||||
destinationLatitude: routeState.destinationPoint.latitude,
|
||||
destinationLongitude: routeState.destinationPoint.longitude,
|
||||
routePath,
|
||||
routeSource: "simulated",
|
||||
remainingDistanceKm,
|
||||
etaMinutes,
|
||||
estimatedArrivalAt: new Date(nowMs + etaMinutes * 60 * 1000).toISOString(),
|
||||
provider: "simulator",
|
||||
simulated: true,
|
||||
recordedAt: new Date(nowMs).toISOString()
|
||||
@@ -599,7 +614,8 @@ function interpolateRoute(route, nowMs, loopMs, offsetMs) {
|
||||
longitude: interpolateNumber(from.longitude, to.longitude, localProgress),
|
||||
heading: Math.round(calculateBearing(from, to)),
|
||||
origin: from.name,
|
||||
destination: to.name
|
||||
destination: to.name,
|
||||
destinationPoint: to
|
||||
};
|
||||
}
|
||||
|
||||
@@ -751,6 +767,13 @@ function createLocationRecord(body, driver) {
|
||||
cargoWeight: optionalString(body.cargoWeight || body.weight, 40),
|
||||
origin: optionalString(body.origin || body.departure, 80),
|
||||
destination: optionalString(body.destination || body.arrival, 80),
|
||||
destinationLatitude: optionalNumber(body.destinationLatitude || body.destinationLat || body.arrivalLatitude || body.arrivalLat),
|
||||
destinationLongitude: optionalNumber(body.destinationLongitude || body.destinationLng || body.destinationLon || body.arrivalLongitude || body.arrivalLng || body.arrivalLon),
|
||||
routePath: normalizeRoutePath(body.routePath || body.path || body.polyline),
|
||||
routeSource: optionalString(body.routeSource || body.routingProvider, 40),
|
||||
remainingDistanceKm: optionalNumber(body.remainingDistanceKm || body.distanceKm || body.routeDistanceKm),
|
||||
etaMinutes: optionalNumber(body.etaMinutes || body.estimatedMinutes || body.remainingMinutes),
|
||||
estimatedArrivalAt: normalizeOptionalDate(body.estimatedArrivalAt || body.etaAt || body.arrivalAt),
|
||||
provider: body.provider ? String(body.provider) : "",
|
||||
simulated: Boolean(body.simulated || driver.simulated),
|
||||
recordedAt: normalizeDate(body.recordedAt),
|
||||
@@ -767,6 +790,81 @@ function point(name, latitude, longitude) {
|
||||
return { name, latitude, longitude };
|
||||
}
|
||||
|
||||
function createSimulatedRoutePath(from, to, seed) {
|
||||
const deltaLatitude = to.latitude - from.latitude;
|
||||
const deltaLongitude = to.longitude - from.longitude;
|
||||
const length = Math.sqrt(deltaLatitude ** 2 + deltaLongitude ** 2) || 1;
|
||||
const offsetDirection = hashOffset(seed) % 2 === 0 ? 1 : -1;
|
||||
const offsetSize = Math.min(0.32, Math.max(0.025, length * 0.14));
|
||||
const perpendicularLatitude = (-deltaLongitude / length) * offsetSize * offsetDirection;
|
||||
const perpendicularLongitude = (deltaLatitude / length) * offsetSize * offsetDirection;
|
||||
|
||||
return [
|
||||
normalizeRoutePoint(from),
|
||||
normalizeRoutePoint({
|
||||
latitude: interpolateNumber(from.latitude, to.latitude, 0.35) + perpendicularLatitude,
|
||||
longitude: interpolateNumber(from.longitude, to.longitude, 0.35) + perpendicularLongitude
|
||||
}),
|
||||
normalizeRoutePoint({
|
||||
latitude: interpolateNumber(from.latitude, to.latitude, 0.72) - perpendicularLatitude * 0.35,
|
||||
longitude: interpolateNumber(from.longitude, to.longitude, 0.72) - perpendicularLongitude * 0.35
|
||||
}),
|
||||
normalizeRoutePoint(to)
|
||||
];
|
||||
}
|
||||
|
||||
function normalizeRoutePath(value) {
|
||||
if (!Array.isArray(value)) return [];
|
||||
|
||||
return value
|
||||
.map((pointValue) => normalizeRoutePoint(pointValue))
|
||||
.filter(Boolean)
|
||||
.slice(0, 48);
|
||||
}
|
||||
|
||||
function normalizeRoutePoint(value) {
|
||||
if (!value) return null;
|
||||
|
||||
const latitude = Array.isArray(value)
|
||||
? Number(value[0])
|
||||
: Number(value.latitude ?? value.lat);
|
||||
const longitude = Array.isArray(value)
|
||||
? Number(value[1])
|
||||
: Number(value.longitude ?? value.lng ?? value.lon);
|
||||
|
||||
if (!Number.isFinite(latitude) || latitude < -90 || latitude > 90) return null;
|
||||
if (!Number.isFinite(longitude) || longitude < -180 || longitude > 180) return null;
|
||||
|
||||
return {
|
||||
latitude: roundNumber(latitude, 6),
|
||||
longitude: roundNumber(longitude, 6)
|
||||
};
|
||||
}
|
||||
|
||||
function calculatePathDistanceKm(routePath) {
|
||||
let distance = 0;
|
||||
for (let index = 1; index < routePath.length; index += 1) {
|
||||
distance += calculateDistanceKm(routePath[index - 1], routePath[index]);
|
||||
}
|
||||
return distance;
|
||||
}
|
||||
|
||||
function calculateDistanceKm(from, to) {
|
||||
const earthRadiusKm = 6371;
|
||||
const deltaLatitude = toRadians(to.latitude - from.latitude);
|
||||
const deltaLongitude = toRadians(to.longitude - from.longitude);
|
||||
const fromLatitude = toRadians(from.latitude);
|
||||
const toLatitude = toRadians(to.latitude);
|
||||
const a = Math.sin(deltaLatitude / 2) ** 2 +
|
||||
Math.cos(fromLatitude) * Math.cos(toLatitude) * Math.sin(deltaLongitude / 2) ** 2;
|
||||
return earthRadiusKm * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
}
|
||||
|
||||
function roundNumber(value, decimals) {
|
||||
const multiplier = 10 ** decimals;
|
||||
return Math.round(value * multiplier) / multiplier;
|
||||
}
|
||||
|
||||
function smoothStep(value) {
|
||||
return value * value * (3 - 2 * value);
|
||||
}
|
||||
@@ -833,6 +931,12 @@ function normalizeDate(value) {
|
||||
return Number.isNaN(parsed.getTime()) ? new Date().toISOString() : parsed.toISOString();
|
||||
}
|
||||
|
||||
function normalizeOptionalDate(value) {
|
||||
if (!value) return null;
|
||||
const parsed = new Date(value);
|
||||
return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString();
|
||||
}
|
||||
|
||||
function readHistory({ driverId, limit }) {
|
||||
if (!fs.existsSync(HISTORY_FILE)) return [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user