首页 >web前端 >js教程 >使用 Google 地图 API 的 React Native 中基于位置的应用程序的基本功能

使用 Google 地图 API 的 React Native 中基于位置的应用程序的基本功能

Linda Hamilton
Linda Hamilton原创
2024-12-07 20:15:14130浏览

Essential Functions for Location-Based Apps in React Native Using Google Maps APIs

在以位置为中心的应用程序中,实现强大的地理位置、路线和票价估算功能至关重要。以下是可以实现的关键实用程序的细分:


1.获取纬度、经度和地址

  • 函数:getLatLong(placeId)
  • 用途:使用 Google Places API 检索地理坐标(纬度和经度)以及给定 placeId 的地址。
  • 用例:可用于根据唯一的 placeId 识别确切位置(例如,地图标记)。
  • 示例:对于 placeId,该函数以结构化格式返回纬度、经度和地址。

2.反向地理编码

  • 函数:reverseGeocode(纬度,经度)
  • 用途:使用 Google Geocoding API 将地理坐标转换为人类可读的地址。
  • 用例:显示用户所选位置的地址或 GPS 坐标。
  • 示例:为给定坐标提供用户友好的地址,例如“澳大利亚新南威尔士州悉尼”。

3.放置自动完成建议

  • 函数:getPlacesSuggestions(query)
  • 用途:使用 Google Places Autocomplete API 根据用户输入获取位置建议。
  • 用例:通过提供位置建议下拉菜单来增强搜索功能。
  • 示例:当用户输入“悉尼”时建议“悉尼歌剧院”或“悉尼机场”。

4.计算距离

  • 函数:计算距离(lat1, lon1, lat2, lon2)
  • 用途:使用半正弦公式计算两个地理点之间的距离。
  • 用例:非常适合估计用户当前位置与其目的地之间的距离。
  • 示例:计算两组坐标之间的距离为20.56公里。

5.动态票价估算

  • 函数:计算票价(距离)
  • 用途:根据行驶距离计算不同车辆类型(自行车、汽车、经济型出租车、高级出租车)的票价。
  • 用例:对于叫车应用程序或送货服务动态显示票价估算很有用。
  • 示例:提供票价详细信息,例如 10 公里行程的自行车 50 卢比或经济型出租车 100 卢比。

6。使用贝塞尔曲线生成平滑路线

  • 函数:getPoints(地点)
  • 目的:使用二次贝塞尔曲线在两点之间创建一条平滑、具有视觉吸引力的路线。
  • 用例:在导航应用程序的地图上添加精美的路线可视化。
  • 示例:沿着两个位置之间的曲线生成 100 个点以创建平滑的折线。

7.车辆图标管理

  • 实用程序:车辆图标
  • 目的:将车辆类型(例如自行车、汽车、出租车经济)映射到各自的图标,以获得一致且动态的 UI。
  • 用例:根据乘车或送货应用程序中所选的车辆类型显示适当的图标。
  • 示例:动态获取“bike”或“cabPremium”的图标。

完整代码:

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
};

结论

这些实用程序使开发人员能够:

  1. 集成无缝的基于位置的服务。
  2. 通过实时数据和直观的视觉效果增强用户体验。
  3. 使用 Google Maps API 构建可扩展且动态的 React Native 应用程序。

通过结合地理位置、路线可视化和票价估算,您可以显着提升应用程序的功能并为用户提供更多价值。

以上是使用 Google 地图 API 的 React Native 中基于位置的应用程序的基本功能的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn