In location-centric applications, implementing robust features for geolocation, routing, and fare estimation is crucial. Here's a breakdown of the key utilities that can be implemented:
π 1. Fetching Latitude, Longitude, and Address
-
Function:
getLatLong(placeId)
-
Purpose: Retrieves geographic coordinates (latitude and longitude) and the address for a given
placeId
using Google Places API. -
Use Case: Useful for identifying exact locations based on a unique
placeId
(e.g., for map markers). -
Example: For a
placeId
, the function returns the latitude, longitude, and address in a structured format.
π 2. Reverse Geocoding
-
Function:
reverseGeocode(latitude, longitude)
- Purpose: Converts geographic coordinates into a human-readable address using Google Geocoding API.
- Use Case: Display the address for a user's selected location or GPS coordinates.
- Example: Provide a user-friendly address like "Sydney NSW, Australia" for given coordinates.
π 3. Place Autocomplete Suggestions
-
Function:
getPlacesSuggestions(query)
- Purpose: Fetches location suggestions based on user input using Google Places Autocomplete API.
- Use Case: Enhances search functionality by providing a dropdown of location suggestions.
- Example: Suggests "Sydney Opera House" or "Sydney Airport" as the user types "Sydney."
π 4. Calculating Distance
-
Function:
calculateDistance(lat1, lon1, lat2, lon2)
- Purpose: Calculates the distance between two geographical points using the Haversine formula.
- Use Case: Ideal for estimating the distance between a user's current location and their destination.
- Example: Calculates 20.56 kilometers as the distance between two sets of coordinates.
π 5. Dynamic Fare Estimation
-
Function:
calculateFare(distance)
- Purpose: Computes fares for different vehicle types (bike, auto, economy cab, premium cab) based on the distance traveled.
- Use Case: Useful for ride-hailing apps or delivery services to dynamically display fare estimates.
- Example: Provides fare details like βΉ50 for a bike or βΉ100 for an economy cab for a 10-km trip.
β¨ 6. Generating Smooth Routes Using Bezier Curves
-
Function:
getPoints(places)
- Purpose: Creates a smooth, visually appealing route between two points using quadratic Bezier curves.
- Use Case: Adds polished route visualization on maps for navigation apps.
- Example: Generates 100 points along a curve between two locations to create a smooth polyline.
π 7. Vehicle Icon Management
-
Utility:
vehicleIcons
- Purpose: Maps vehicle types (e.g., bike, auto, cabEconomy) to their respective icons for a consistent and dynamic UI.
- Use Case: Display appropriate icons based on the selected vehicle type in ride-hailing or delivery apps.
- Example: Fetches the icon for a "bike" or "cabPremium" dynamically.
Full Code:
import axios from "axios";
import { useUserStore } from "@/store/userStore";
/**
* Fetch latitude, longitude, and address details for a given place ID.
* @param {string} placeId - The unique identifier for a place (e.g., "ChIJN1t_tDeuEmsRUsoyG83frY4").
* @returns {Promise<{latitude: number, longitude: number, address: string}>} Location data.
* Example response:
* {
* latitude: -33.8670522,
* longitude: 151.1957362,
* address: "Sydney NSW, Australia"
* }
*/
export const getLatLong = async (placeId: string) => {
try {
const response = await axios.get("https://maps.googleapis.com/maps/api/place/details/json", {
params: {
placeid: placeId, // Place ID for the location
key: process.env.EXPO_PUBLIC_MAP_API_KEY, // API key for authentication
},
});
const data = response.data;
// Validate response status and extract required fields
if (data.status === "OK" && data.result) {
const location = data.result.geometry.location; // Get latitude and longitude
const address = data.result.formatted_address; // Get formatted address
return {
latitude: location.lat,
longitude: location.lng,
address: address,
};
} else {
// Handle API response errors
throw new Error("Unable to fetch location details");
}
} catch (error) {
// Catch and throw any request or processing errors
throw new Error("Unable to fetch location details");
}
};
/**
* Reverse geocode latitude and longitude to fetch the address.
* @param {number} latitude - Latitude of the location (e.g., -33.8670522).
* @param {number} longitude - Longitude of the location (e.g., 151.1957362).
* @returns {Promise<string>} Address of the location.
* Example response: "Sydney NSW, Australia"
*/
export const reverseGeocode = async (latitude: number, longitude: number) => {
try {
const response = await axios.get(
`https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=${process.env.EXPO_PUBLIC_MAP_API_KEY}`
);
// Check if the response status is OK and extract the address
if (response.data.status === "OK") {
const address = response.data.results[0].formatted_address;
return address; // Return the formatted address
} else {
// Log failure details and return an empty string
console.log("Geocoding failed: ", response.data.status);
return "";
}
} catch (error) {
// Handle any request or processing errors
console.log("Error during reverse geocoding: ", error);
return "";
}
};
/**
* Extract relevant place data from API response.
* @param {Array} data - Array of place predictions from the API.
* @returns {Array<{place_id: string, title: string, description: string}>} Processed place data.
* Example response:
* [
* { place_id: "xyz123", title: "Sydney Opera House", description: "Iconic performing arts venue in Sydney" }
* ]
*/
function extractPlaceData(data: any) {
return data.map((item: any) => ({
place_id: item.place_id, // Unique identifier for the place
title: item.structured_formatting.main_text, // Main title of the place
description: item.description, // Detailed description
}));
}
/**
* Fetch autocomplete suggestions for places based on a query.
* @param {string} query - User's input for place suggestions (e.g., "Sydney").
* @returns {Promise<Array>} List of place suggestions.
* Example response:
* [
* { place_id: "xyz123", title: "Sydney Opera House", description: "Iconic performing arts venue in Sydney" }
* ]
*/
export const getPlacesSuggestions = async (query: string) => {
const { location } = useUserStore.getState(); // Get user's current location from the store
try {
const response = await axios.get(
`https://maps.googleapis.com/maps/api/place/autocomplete/json`, {
params: {
input: query, // Query string for suggestions
location: `${location?.latitude},${location?.longitude}`, // Use current location for proximity
radius: 50000, // Search within a 50km radius
components: "country:IN", // Restrict results to India
key: process.env.EXPO_PUBLIC_MAP_API_KEY, // API key for authentication
}
}
);
// Process and return extracted place data
return extractPlaceData(response.data.predictions);
} catch (error) {
// Log errors and return an empty array
console.error("Error fetching autocomplete suggestions:", error);
return [];
}
};
/**
* Calculate the distance between two geographic coordinates using the Haversine formula.
* @param {number} lat1 - Latitude of the first point (e.g., 28.7041).
* @param {number} lon1 - Longitude of the first point (e.g., 77.1025).
* @param {number} lat2 - Latitude of the second point (e.g., 28.5355).
* @param {number} lon2 - Longitude of the second point (e.g., 77.3910).
* @returns {number} Distance in kilometers.
* Example response: 20.56
*/
export const calculateDistance = (lat1: number, lon1: number, lat2: number, lon2: number) => {
const R = 6371; // Earth's radius in kilometers
const dLat = (lat2 - lat1) * (Math.PI / 180); // Latitude difference in radians
const dLon = (lon2 - lon1) * (Math.PI / 180); // Longitude difference in radians
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2); // Haversine formula
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); // Angular distance
return R * c; // Distance in kilometers
};
/**
* Calculate fare for different vehicle types based on the distance traveled.
* @param {number} distance - Distance traveled in kilometers (e.g., 15).
* @returns {Object} Fare breakdown for each vehicle type.
* Example response:
* {
* bike: 50,
* auto: 60,
* cabEconomy: 100,
* cabPremium: 150
* }
*/
export const calculateFare = (distance: number) => {
// Fare rates for different vehicle types
const rateStructure = {
bike: { baseFare: 10, perKmRate: 5, minimumFare: 25 },
auto: { baseFare: 15, perKmRate: 7, minimumFare: 30 },
cabEconomy: { baseFare: 20, perKmRate: 10, minimumFare: 50 },
cabPremium: { baseFare: 30, perKmRate: 15, minimumFare: 70 },
};
// Helper function to calculate fare
const fareCalculation = (baseFare: number, perKmRate: number, minimumFare: number) => {
const calculatedFare = baseFare + (distance * perKmRate); // Calculate fare based on distance
return Math.max(calculatedFare, minimumFare); // Ensure the fare meets the minimum
};
// Return fare details for each vehicle type
return {
bike: fareCalculation(rateStructure.bike.baseFare, rateStructure.bike.perKmRate, rateStructure.bike.minimumFare),
auto: fareCalculation(rateStructure.auto.baseFare, rateStructure.auto.perKmRate, rateStructure.auto.minimumFare),
cabEconomy: fareCalculation(rateStructure.cabEconomy.baseFare, rateStructure.cabEconomy.perKmRate, rateStructure.cabEconomy.minimumFare),
cabPremium: fareCalculation(rateStructure.cabPremium.baseFare, rateStructure.cabPremium.perKmRate, rateStructure.cabPremium.minimumFare),
};
};
/**
* Generate points along a quadratic Bezier curve between two points with a control point.
* @param {Array<number>} p1 - The starting point of the curve [latitude, longitude] (e.g., [28.7041, 77.1025]).
* @param {Array<number>} p2 - The ending point of the curve [latitude, longitude] (e.g., [28.5355, 77.3910]).
* @param {Array<number>} controlPoint - The control point for the curve [latitude, longitude] (e.g., [28.6, 77.25]).
* @param {number} numPoints - The number of points to generate along the curve.
* @returns {Array<{latitude: number, longitude: number}>} Array of coordinates forming the curve.
* Example response:
* [
* { latitude: 28.7041, longitude: 77.1025 },
* { latitude: 28.635, longitude: 77.175 },
* { latitude: 28.5355, longitude: 77.3910 }
* ]
*/
function quadraticBezierCurve(p1: any, p2: any, controlPoint: any, numPoints: any) {
const points = [];
const step = 1 / (numPoints - 1); // Step size for dividing the curve
for (let t = 0; t <= 1; t += step) {
const x =
(1 - t) ** 2 * p1[0] + // Contribution of starting point
2 * (1 - t) * t * controlPoint[0] + // Contribution of control point
t ** 2 * p2[0]; // Contribution of ending point
const y =
(1 - t) ** 2 * p1[1] +
2 * (1 - t) * t * controlPoint[1] +
t ** 2 * p2[1];
const coord = { latitude: x, longitude: y }; // Store as coordinate object
points.push(coord); // Add to the points array
}
return points; // Return array of points forming the curve
}
/**
* Calculate the control point for a quadratic Bezier curve between two points.
* @param {Array<number>} p1 - The starting point [latitude, longitude].
* @param {Array<number>} p2 - The ending point [latitude, longitude].
* @returns {Array<number>} The control point [latitude, longitude].
* Example response: [28.6, 77.25]
*/
const calculateControlPoint = (p1: any, p2: any) => {
const d = Math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2); // Distance between p1 and p2
const scale = 1; // Scale factor for bending the curve
const h = d * scale; // Adjusted distance from midpoint
const w = d / 2; // Halfway between points
const x_m = (p1[0] + p2[0]) / 2; // Midpoint x
const y_m = (p1[1] + p2[1]) / 2; // Midpoint y
const x_c =
x_m + ((h * (p2[1] - p1[1])) / (2 * Math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2))) * (w / d);
const y_c =
y_m - ((h * (p2[0] - p1[0])) / (2 * Math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2))) * (w / d);
return [x_c, y_c]; // Return calculated control point
};
/**
* Generate Bezier curve points for given locations.
* @param {Array<{latitude: number, longitude: number}>} places - Array containing at least two points.
* @returns {Array<{latitude: number, longitude: number}>} Array of coordinates forming the curve.
* Example response:
* [
* { latitude: 28.7041, longitude: 77.1025 },
* { latitude: 28.635, longitude: 77.175 },
* { latitude: 28.5355, longitude: 77.3910 }
* ]
*/
export const getPoints = (places: any) => {
const p1 = [places[0].latitude, places[0].longitude]; // Starting point
const p2 = [places[1].latitude, places[1].longitude]; // Ending point
const controlPoint = calculateControlPoint(p1, p2); // Calculate the control point
return quadraticBezierCurve(p1, p2, controlPoint, 100); // Generate 100 points along the curve
};
/**
* Map of vehicle types to their respective icons.
* @type {Record<'bike' | 'auto' | 'cabEconomy' | 'cabPremium', { icon: any }>}
* Example usage:
* vehicleIcons.bike.icon -> Path to bike icon
*/
export const vehicleIcons: Record<'bike' | 'auto' | 'cabEconomy' | 'cabPremium', { icon: any }> = {
bike: { icon: require('@/assets/icons/bike.png') }, // Icon for bike
auto: { icon: require('@/assets/icons/auto.png') }, // Icon for auto
cabEconomy: { icon: require('@/assets/icons/cab.png') }, // Icon for economy cab
cabPremium: { icon: require('@/assets/icons/cab_premium.png') }, // Icon for premium cab
};
π Conclusion
These utilities empower developers to:
- Integrate seamless location-based services.
- Enhance user experience with real-time data and intuitive visuals.
- Build scalable and dynamic React Native applications with Google Maps APIs.
By combining geolocation, route visualization, and fare estimation, you can significantly elevate your app's functionality and deliver more value to your users.
Top comments (0)