updates on the changes google.maps.DistanceMatrixService to orsm
This commit is contained in:
@@ -26,6 +26,7 @@ import {
|
|||||||
useMediaQuery
|
useMediaQuery
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
|
import { calculateDrivingDistance, calculateTotalCharge } from '../../../utils/distance';
|
||||||
import { Empty } from 'antd';
|
import { Empty } from 'antd';
|
||||||
import { FaPhoneAlt, FaBox, FaBoxes, FaTruck, FaArrowRight, FaArrowLeft, FaCheck, FaRoute, FaMoneyBillWave, FaChartLine, FaReceipt, FaPaperPlane } from 'react-icons/fa';
|
import { FaPhoneAlt, FaBox, FaBoxes, FaTruck, FaArrowRight, FaArrowLeft, FaCheck, FaRoute, FaMoneyBillWave, FaChartLine, FaReceipt, FaPaperPlane } from 'react-icons/fa';
|
||||||
import { GiDoorHandle } from 'react-icons/gi';
|
import { GiDoorHandle } from 'react-icons/gi';
|
||||||
@@ -365,130 +366,25 @@ const Createorder1 = () => {
|
|||||||
}, [startPoint, endPoint]);
|
}, [startPoint, endPoint]);
|
||||||
|
|
||||||
// google distance matrix logic
|
// google distance matrix logic
|
||||||
/*
|
|
||||||
const calculateDistance = async (pickup, drop) => {
|
const calculateDistance = async (pickup, drop) => {
|
||||||
const service = new google.maps.DistanceMatrixService();
|
|
||||||
const getDistanceMatrix = (origins, destinations, travelMode, unitSystem) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
service.getDistanceMatrix(
|
|
||||||
{
|
|
||||||
origins: [new google.maps.LatLng(origins.latitude, origins.longitude)],
|
|
||||||
destinations: [new google.maps.LatLng(destinations.latitude, destinations.longitude)],
|
|
||||||
travelMode: travelMode,
|
|
||||||
unitSystem: unitSystem
|
|
||||||
},
|
|
||||||
(response, status) => {
|
|
||||||
if (status === 'OK') {
|
|
||||||
resolve(response);
|
|
||||||
} else {
|
|
||||||
reject(new Error(`Error calculating distance: ${status}`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use await to wait for the promise to resolve
|
const roundedDistance = await calculateDrivingDistance(pickup, drop);
|
||||||
const response = await getDistanceMatrix(pickup, drop, 'DRIVING', google.maps.UnitSystem.METRIC);
|
console.log('calculated distance in km:', roundedDistance);
|
||||||
|
|
||||||
// Handle the response
|
|
||||||
const results = response.rows[0].elements;
|
|
||||||
for (let i = 0; i < results.length; i++) {
|
|
||||||
const element = results[i];
|
|
||||||
|
|
||||||
// Extract the numerical value of the distance
|
|
||||||
|
|
||||||
const distance = element.distance.value;
|
|
||||||
console.log('distance in m ', distance);
|
|
||||||
const distanceInKm = (distance / 1000).toFixed(2);
|
|
||||||
console.log('distance in km ', distanceInKm);
|
|
||||||
const roundedDistance = Math.round(distanceInKm);
|
|
||||||
console.log('roundedDistance', roundedDistance);
|
|
||||||
setDistance(roundedDistance);
|
setDistance(roundedDistance);
|
||||||
if (roundedDistance < minKm) {
|
|
||||||
setTotalCharge(basePrice);
|
const total = calculateTotalCharge(roundedDistance, basePrice, pricePerKm, minKm);
|
||||||
} else {
|
console.log('total charge:', total);
|
||||||
console.log('minKm', minKm);
|
|
||||||
console.log('pricePerKm', pricePerKm);
|
|
||||||
console.log('basePrice', basePrice);
|
|
||||||
const total = (roundedDistance - minKm) * pricePerKm + basePrice;
|
|
||||||
console.log('total', total);
|
|
||||||
setTotalCharge(total);
|
setTotalCharge(total);
|
||||||
}
|
|
||||||
setShowDistance(true);
|
setShowDistance(true);
|
||||||
if (roundedDistance > appLocaRadius) {
|
if (roundedDistance > appLocaRadius) {
|
||||||
setShowDistance(true);
|
setShowDistance(true);
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the numerical value of the duration
|
// ⏱️ Approximate duration
|
||||||
const durationMatch = element.duration.text.match(/([\d.]+)/);
|
|
||||||
const duration = durationMatch ? parseInt(durationMatch[0]) : null;
|
|
||||||
|
|
||||||
// Display only the numerical values
|
|
||||||
console.log(`Distance: ${roundedDistance}, Duration: ${duration}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error calculating distance:', error);
|
|
||||||
}
|
|
||||||
};*/
|
|
||||||
// Haversine + 1.3
|
|
||||||
const calculateDistance = async (pickup, drop) => {
|
|
||||||
// Haversine formula
|
|
||||||
const haversineDistance = (lat1, lon1, lat2, lon2) => {
|
|
||||||
const toRad = (value) => (value * Math.PI) / 180;
|
|
||||||
|
|
||||||
const R = 6371; // Earth radius in KM
|
|
||||||
const dLat = toRad(lat2 - lat1);
|
|
||||||
const dLon = toRad(lon2 - lon1);
|
|
||||||
|
|
||||||
const a =
|
|
||||||
Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
|
||||||
|
|
||||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
||||||
return R * c; // distance in KM
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 1. Get aerial distance
|
|
||||||
const aerialDistance = haversineDistance(pickup.latitude, pickup.longitude, drop.latitude, drop.longitude);
|
|
||||||
|
|
||||||
// 2. Convert to road approximation (1.3x)
|
|
||||||
const distanceInKm = (aerialDistance * 1.3).toFixed(2);
|
|
||||||
|
|
||||||
console.log('distance in km ', distanceInKm);
|
|
||||||
|
|
||||||
const roundedDistance = Math.round(distanceInKm);
|
|
||||||
console.log('roundedDistance', roundedDistance);
|
|
||||||
|
|
||||||
// 🔽 SAME AS YOUR EXISTING LOGIC (UNCHANGED)
|
|
||||||
setDistance(roundedDistance);
|
|
||||||
|
|
||||||
if (roundedDistance < minKm) {
|
|
||||||
setTotalCharge(basePrice);
|
|
||||||
} else {
|
|
||||||
console.log('minKm', minKm);
|
|
||||||
console.log('pricePerKm', pricePerKm);
|
|
||||||
console.log('basePrice', basePrice);
|
|
||||||
|
|
||||||
const total = (roundedDistance - minKm) * pricePerKm + basePrice;
|
|
||||||
|
|
||||||
console.log('total', total);
|
|
||||||
setTotalCharge(total);
|
|
||||||
}
|
|
||||||
|
|
||||||
setShowDistance(true);
|
|
||||||
|
|
||||||
if (roundedDistance > appLocaRadius) {
|
|
||||||
setShowDistance(true);
|
|
||||||
setOpen(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ⏱️ Approximate duration (optional, since no API)
|
|
||||||
const avgSpeed = 40; // km/h (adjust if needed)
|
const avgSpeed = 40; // km/h (adjust if needed)
|
||||||
const duration = Math.round((roundedDistance / avgSpeed) * 60); // minutes
|
const duration = Math.round((roundedDistance / avgSpeed) * 60); // minutes
|
||||||
|
|
||||||
console.log(`Distance: ${roundedDistance}, Duration: ${duration}`);
|
console.log(`Distance: ${roundedDistance}, Duration: ${duration}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error calculating distance:', error);
|
console.error('Error calculating distance:', error);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useNavigate } from 'react-router';
|
|||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||||
import { enqueueSnackbar } from 'notistack';
|
import { enqueueSnackbar } from 'notistack';
|
||||||
|
import { calculateDrivingDistance, calculateTotalCharge } from '../../../utils/distance';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -296,53 +297,17 @@ const MultipleOrders = () => {
|
|||||||
settotalCash(a4);
|
settotalCash(a4);
|
||||||
}, [dropCust]);
|
}, [dropCust]);
|
||||||
|
|
||||||
// ============================== distance (Google) ==============================
|
// ============================== distance (OSRM/Haversine) ==============================
|
||||||
const calculateDistance = async (customer) => {
|
const calculateDistance = async (customer) => {
|
||||||
if (typeof window === 'undefined' || !window.google?.maps?.DistanceMatrixService) {
|
|
||||||
throw new Error('Google Maps not loaded');
|
|
||||||
}
|
|
||||||
const service = new window.google.maps.DistanceMatrixService();
|
|
||||||
|
|
||||||
const getDistanceMatrix = (origins, destinations) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
try {
|
|
||||||
if (!origins || !destinations) return reject(new Error('Origin or destination data missing.'));
|
|
||||||
service.getDistanceMatrix(
|
|
||||||
{
|
|
||||||
origins: [new window.google.maps.LatLng(origins.latitude, origins.longitude)],
|
|
||||||
destinations: [new window.google.maps.LatLng(destinations.latitude, destinations.longitude)],
|
|
||||||
travelMode: 'DRIVING',
|
|
||||||
unitSystem: window.google.maps.UnitSystem.METRIC
|
|
||||||
},
|
|
||||||
(response, status) => {
|
|
||||||
if (status === 'OK') resolve(response);
|
|
||||||
else reject(new Error(`Google API error: ${status}`));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
reject(new Error(`Unexpected error inside DistanceMatrixService: ${err.message}`));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!customer || typeof customer !== 'object') throw new Error('Invalid customer data.');
|
if (!customer || typeof customer !== 'object') throw new Error('Invalid customer data.');
|
||||||
if (!pickCust || typeof pickCust !== 'object') throw new Error('Origin (pickCust) data missing or invalid.');
|
if (!pickCust || typeof pickCust !== 'object') throw new Error('Origin (pickCust) data missing or invalid.');
|
||||||
|
|
||||||
const response = await getDistanceMatrix(pickCust, customer);
|
const roundedDistance = await calculateDrivingDistance(pickCust, customer);
|
||||||
const distVal = response?.rows?.[0]?.elements?.[0]?.distance?.value;
|
const totalcharge = calculateTotalCharge(roundedDistance, basePrice, pricePerKm, minKm);
|
||||||
if (distVal == null) throw new Error('Malformed Distance Matrix response: missing distance value.');
|
|
||||||
|
|
||||||
const km = distVal / 1000;
|
|
||||||
const roundedDistance = Math.round(km);
|
|
||||||
const totalcharge =
|
|
||||||
roundedDistance < minKm ? basePrice : (roundedDistance - minKm) * pricePerKm + basePrice;
|
|
||||||
return { roundedDistance, totalcharge };
|
return { roundedDistance, totalcharge };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.message.includes('Google API')) {
|
if (error.message.includes('Origin') || error.message.includes('customer') || error.message.includes('coordinates')) {
|
||||||
OpenToast('Invalid coordinates or Google API error.', 'error', 3000);
|
|
||||||
} else if (error.message.includes('Malformed Distance Matrix')) {
|
|
||||||
OpenToast('Google Distance Matrix returned invalid data.', 'error', 3000);
|
|
||||||
} else if (error.message.includes('Origin') || error.message.includes('customer')) {
|
|
||||||
OpenToast('Missing or invalid input data for distance calculation.', 'warning', 3000);
|
OpenToast('Missing or invalid input data for distance calculation.', 'warning', 3000);
|
||||||
} else {
|
} else {
|
||||||
OpenToast('Unexpected error during distance calculation.', 'error', 3000);
|
OpenToast('Unexpected error during distance calculation.', 'error', 3000);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ var utc = require('dayjs/plugin/utc');
|
|||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
import { enqueueSnackbar } from 'notistack';
|
import { enqueueSnackbar } from 'notistack';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
|
import { calculateDrivingDistance, calculateTotalCharge } from '../../../utils/distance';
|
||||||
import Papa from 'papaparse';
|
import Papa from 'papaparse';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -344,47 +345,6 @@ const MultipleOrders = () => {
|
|||||||
|
|
||||||
// 🔹 Main distance calculation function
|
// 🔹 Main distance calculation function
|
||||||
const calculateDistance = async (customer) => {
|
const calculateDistance = async (customer) => {
|
||||||
const service = new google.maps.DistanceMatrixService();
|
|
||||||
|
|
||||||
// Helper: safely get distance matrix
|
|
||||||
const getDistanceMatrix = (origins, destinations) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
2;
|
|
||||||
try {
|
|
||||||
if (!origins || !destinations) {
|
|
||||||
return reject(new Error('Origin or destination data missing.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (
|
|
||||||
// typeof origins.latitude !== 'number' ||
|
|
||||||
// typeof origins.longitude !== 'number' ||
|
|
||||||
// typeof destinations.latitude !== 'number' ||
|
|
||||||
// typeof destinations.longitude !== 'number'
|
|
||||||
// ) {
|
|
||||||
// return reject(new Error('Invalid coordinates format. Expected numeric latitude/longitude.'));
|
|
||||||
// }
|
|
||||||
|
|
||||||
service.getDistanceMatrix(
|
|
||||||
{
|
|
||||||
origins: [new google.maps.LatLng(origins.latitude, origins.longitude)],
|
|
||||||
destinations: [new google.maps.LatLng(destinations.latitude, destinations.longitude)],
|
|
||||||
travelMode: 'DRIVING',
|
|
||||||
unitSystem: google.maps.UnitSystem.METRIC
|
|
||||||
},
|
|
||||||
(response, status) => {
|
|
||||||
if (status === 'OK') {
|
|
||||||
resolve(response);
|
|
||||||
} else {
|
|
||||||
reject(new Error(`Google API error: ${status}`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
reject(new Error(`Unexpected error inside DistanceMatrixService: ${err.message}`));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// --- Input validation ---
|
// --- Input validation ---
|
||||||
if (!customer || typeof customer !== 'object') {
|
if (!customer || typeof customer !== 'object') {
|
||||||
@@ -395,40 +355,19 @@ const MultipleOrders = () => {
|
|||||||
throw new Error('Origin (pickCust) data missing or invalid.');
|
throw new Error('Origin (pickCust) data missing or invalid.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Call Google Maps API ---
|
|
||||||
const response = await getDistanceMatrix(pickCust, customer);
|
|
||||||
|
|
||||||
// --- Validate response structure ---
|
|
||||||
if (!response.rows?.[0]?.elements?.[0] || !response.rows[0].elements[0].distance?.value) {
|
|
||||||
throw new Error('Malformed Distance Matrix response: missing distance value.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Compute distance ---
|
// --- Compute distance ---
|
||||||
const distanceInMeters = response.rows[0].elements[0].distance.value;
|
const roundedDistance = await calculateDrivingDistance(pickCust, customer);
|
||||||
const distanceInKilometers = distanceInMeters / 1000;
|
|
||||||
const roundedDistance = Math.round(distanceInKilometers);
|
|
||||||
|
|
||||||
// --- Calculate total charge ---
|
// --- Calculate total charge ---
|
||||||
let totalcharge;
|
const totalcharge = calculateTotalCharge(roundedDistance, basePrice, pricePerKm, minKm);
|
||||||
if (roundedDistance < minKm) {
|
|
||||||
totalcharge = basePrice;
|
|
||||||
} else {
|
|
||||||
totalcharge = (roundedDistance - minKm) * pricePerKm + basePrice;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTotalCharge(totalcharge);
|
setTotalCharge(totalcharge);
|
||||||
return { roundedDistance, totalcharge };
|
return { roundedDistance, totalcharge };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// --- Categorized smart error handling ---
|
// --- Categorized smart error handling ---
|
||||||
if (error.message.includes('Google API')) {
|
if (error.message.includes('Invalid coordinates') || error.message.includes('Invalid coordinate')) {
|
||||||
console.error('🚨 Google Maps API Error:', error.message);
|
|
||||||
OpenToastSmart('Google Maps API Error: ' + error.message, 'error');
|
|
||||||
} else if (error.message.includes('Invalid coordinates')) {
|
|
||||||
console.error('📍 Invalid coordinate format:', error.message);
|
console.error('📍 Invalid coordinate format:', error.message);
|
||||||
OpenToastSmart('Invalid coordinate format. Check location data.', 'warning');
|
OpenToastSmart('Invalid coordinate format. Check location data.', 'warning');
|
||||||
} else if (error.message.includes('Malformed Distance Matrix')) {
|
|
||||||
console.error('⚠️ Unexpected Google response structure:', error.message);
|
|
||||||
OpenToastSmart('Google Distance Matrix returned invalid data.', 'error');
|
|
||||||
} else if (error.message.includes('Origin') || error.message.includes('customer')) {
|
} else if (error.message.includes('Origin') || error.message.includes('customer')) {
|
||||||
console.error('❌ Missing or invalid input data:', error.message);
|
console.error('❌ Missing or invalid input data:', error.message);
|
||||||
OpenToastSmart('Missing or invalid input data for distance calculation.', 'warning');
|
OpenToastSmart('Missing or invalid input data for distance calculation.', 'warning');
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import { Empty } from 'antd';
|
import { Empty } from 'antd';
|
||||||
import { FaPhoneAlt } from 'react-icons/fa';
|
import { FaPhoneAlt } from 'react-icons/fa';
|
||||||
|
import { calculateDrivingDistance, calculateTotalCharge } from '../../../utils/distance';
|
||||||
import { GiDoorHandle } from 'react-icons/gi';
|
import { GiDoorHandle } from 'react-icons/gi';
|
||||||
import { FaLandmarkDome } from 'react-icons/fa6';
|
import { FaLandmarkDome } from 'react-icons/fa6';
|
||||||
import ClearIcon from '@mui/icons-material/Clear';
|
import ClearIcon from '@mui/icons-material/Clear';
|
||||||
@@ -470,68 +471,20 @@ const Createorder1 = () => {
|
|||||||
}, [startPoint, endPoint]);
|
}, [startPoint, endPoint]);
|
||||||
|
|
||||||
const calculateDistance = async (pickup, drop) => {
|
const calculateDistance = async (pickup, drop) => {
|
||||||
const service = new google.maps.DistanceMatrixService();
|
|
||||||
const getDistanceMatrix = (origins, destinations, travelMode, unitSystem) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
service.getDistanceMatrix(
|
|
||||||
{
|
|
||||||
origins: [new google.maps.LatLng(origins.latitude, origins.longitude)],
|
|
||||||
destinations: [new google.maps.LatLng(destinations.latitude, destinations.longitude)],
|
|
||||||
travelMode: travelMode,
|
|
||||||
unitSystem: unitSystem
|
|
||||||
},
|
|
||||||
(response, status) => {
|
|
||||||
if (status === 'OK') {
|
|
||||||
resolve(response);
|
|
||||||
} else {
|
|
||||||
reject(new Error(`Error calculating distance: ${status}`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use await to wait for the promise to resolve
|
const roundedDistance = await calculateDrivingDistance(pickup, drop);
|
||||||
const response = await getDistanceMatrix(pickup, drop, 'DRIVING', google.maps.UnitSystem.METRIC);
|
console.log('calculated distance in km:', roundedDistance);
|
||||||
|
|
||||||
// Handle the response
|
|
||||||
const results = response.rows[0].elements;
|
|
||||||
for (let i = 0; i < results.length; i++) {
|
|
||||||
const element = results[i];
|
|
||||||
|
|
||||||
// Extract the numerical value of the distance
|
|
||||||
|
|
||||||
const distance = element.distance.value;
|
|
||||||
console.log('distance in m ', distance);
|
|
||||||
const distanceInKm = (distance / 1000).toFixed(2);
|
|
||||||
console.log('distance in km ', distanceInKm);
|
|
||||||
const roundedDistance = Math.round(distanceInKm);
|
|
||||||
console.log('roundedDistance', roundedDistance);
|
|
||||||
setDistance(roundedDistance);
|
setDistance(roundedDistance);
|
||||||
if (roundedDistance < minKm) {
|
|
||||||
setTotalCharge(basePrice);
|
const total = calculateTotalCharge(roundedDistance, basePrice, pricePerKm, minKm);
|
||||||
} else {
|
console.log('total charge:', total);
|
||||||
console.log('minKm', minKm);
|
|
||||||
console.log('pricePerKm', pricePerKm);
|
|
||||||
console.log('basePrice', basePrice);
|
|
||||||
const total = (roundedDistance - minKm) * pricePerKm + basePrice;
|
|
||||||
console.log('total', total);
|
|
||||||
setTotalCharge(total);
|
setTotalCharge(total);
|
||||||
}
|
|
||||||
setShowDistance(true);
|
setShowDistance(true);
|
||||||
if (roundedDistance > appLocaRadius) {
|
if (roundedDistance > appLocaRadius) {
|
||||||
setShowDistance(true);
|
setShowDistance(true);
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the numerical value of the duration
|
|
||||||
const durationMatch = element.duration.text.match(/([\d.]+)/);
|
|
||||||
const duration = durationMatch ? parseInt(durationMatch[0]) : null;
|
|
||||||
|
|
||||||
// Display only the numerical values
|
|
||||||
console.log(`Distance: ${roundedDistance}, Duration: ${duration}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error calculating distance:', error);
|
console.error('Error calculating distance:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
65
src/utils/distance.js
Normal file
65
src/utils/distance.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* Calculates distance (in km) between origin and destination.
|
||||||
|
* Uses OSRM as primary driving distance source, and falls back to Haversine with a 1.3 multiplier.
|
||||||
|
*
|
||||||
|
* @param {Object} origin - { latitude, longitude }
|
||||||
|
* @param {Object} destination - { latitude, longitude }
|
||||||
|
* @returns {Promise<number>} - Round distance in KM
|
||||||
|
*/
|
||||||
|
export const calculateDrivingDistance = async (origin, destination) => {
|
||||||
|
const lat1 = origin?.latitude;
|
||||||
|
const lon1 = origin?.longitude;
|
||||||
|
const lat2 = destination?.latitude;
|
||||||
|
const lon2 = destination?.longitude;
|
||||||
|
|
||||||
|
if (lat1 == null || lon1 == null || lat2 == null || lon2 == null) {
|
||||||
|
throw new Error("Invalid coordinates");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Try OSRM API (either self-hosted or public demo server for fallback)
|
||||||
|
const osrmBaseUrl = process.env.REACT_APP_OSRM_URL || "https://router.project-osrm.org";
|
||||||
|
try {
|
||||||
|
const url = `${osrmBaseUrl}/route/v1/driving/${lon1},${lat1};${lon2},${lat2}?overview=false`;
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.routes && data.routes.length > 0) {
|
||||||
|
const distanceInMeters = data.routes[0].distance;
|
||||||
|
const distanceInKm = distanceInMeters / 1000;
|
||||||
|
return Math.round(distanceInKm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("OSRM API failed, falling back to Haversine math:", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Fallback: Haversine Formula with 1.3x multiplier
|
||||||
|
const toRad = (val) => (val * Math.PI) / 180;
|
||||||
|
const R = 6371; // Earth radius in km
|
||||||
|
const dLat = toRad(lat2 - lat1);
|
||||||
|
const dLon = toRad(lon2 - lon1);
|
||||||
|
const a =
|
||||||
|
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||||
|
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
|
||||||
|
Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||||
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
const aerialDistance = R * c;
|
||||||
|
|
||||||
|
return Math.round(aerialDistance * 1.3);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates total charge based on distance and pricing tier
|
||||||
|
*
|
||||||
|
* @param {number} distanceKm
|
||||||
|
* @param {number} basePrice
|
||||||
|
* @param {number} pricePerKm
|
||||||
|
* @param {number} minKm
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
export const calculateTotalCharge = (distanceKm, basePrice, pricePerKm, minKm) => {
|
||||||
|
if (distanceKm < minKm) {
|
||||||
|
return basePrice;
|
||||||
|
}
|
||||||
|
return (distanceKm - minKm) * pricePerKm + basePrice;
|
||||||
|
};
|
||||||
@@ -6572,11 +6572,6 @@ fs.realpath@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
||||||
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
||||||
|
|
||||||
fsevents@^2.3.2, fsevents@~2.3.2:
|
|
||||||
version "2.3.3"
|
|
||||||
resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz"
|
|
||||||
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
|
||||||
|
|
||||||
function-bind@^1.1.2:
|
function-bind@^1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
|
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
|
||||||
|
|||||||
Reference in New Issue
Block a user