地理位置 #

一、插件安装 #

1.1 安装依赖 #

bash
npm install @capacitor/geolocation
npx cap sync

1.2 权限配置 #

iOS (Info.plist):

xml
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要获取您的位置信息以提供更好的服务</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>需要持续获取您的位置信息</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>需要获取您的位置信息以提供更好的服务</string>

Android (AndroidManifest.xml):

xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

二、基本使用 #

2.1 获取当前位置 #

typescript
import { Geolocation } from '@capacitor/geolocation';

async function getCurrentPosition() {
    const position = await Geolocation.getCurrentPosition({
        enableHighAccuracy: true,
        timeout: 10000,
        maximumAge: 0
    });
    
    console.log('Latitude:', position.coords.latitude);
    console.log('Longitude:', position.coords.longitude);
    console.log('Accuracy:', position.coords.accuracy);
    
    return position;
}

2.2 位置信息结构 #

typescript
interface Position {
    coords: {
        latitude: number;      // 纬度
        longitude: number;     // 经度
        accuracy: number;      // 精度(米)
        altitude: number | null;  // 海拔(米)
        altitudeAccuracy: number | null;  // 海拔精度
        heading: number | null;   // 方向(度)
        speed: number | null;     // 速度(米/秒)
    };
    timestamp: number;  // 时间戳
}

2.3 基本选项 #

typescript
interface GeolocationOptions {
    // 是否使用高精度定位
    enableHighAccuracy?: boolean;
    
    // 超时时间(毫秒)
    timeout?: number;
    
    // 缓存最大年龄(毫秒)
    maximumAge?: number;
}

// 使用示例
const position = await Geolocation.getCurrentPosition({
    enableHighAccuracy: true,  // 使用GPS
    timeout: 10000,            // 10秒超时
    maximumAge: 0              // 不使用缓存
});

三、位置监听 #

3.1 实时监听位置 #

typescript
import { Geolocation } from '@capacitor/geolocation';

async function startWatching() {
    const watchId = await Geolocation.watchPosition(
        {
            enableHighAccuracy: true,
            timeout: 10000,
            maximumAge: 0
        },
        (position, error) => {
            if (error) {
                console.error('Watch error:', error);
                return;
            }
            
            console.log('New position:', position);
            updateMap(position);
        }
    );
    
    return watchId;
}

// 停止监听
async function stopWatching(watchId: string) {
    await Geolocation.clearWatch({ id: watchId });
}

3.2 位置监听管理 #

typescript
class LocationWatcher {
    private watchId: string | null = null;
    private callbacks: Set<(position: any) => void> = new Set();
    
    async start(options = {}) {
        if (this.watchId) {
            await this.stop();
        }
        
        this.watchId = await Geolocation.watchPosition(
            {
                enableHighAccuracy: true,
                timeout: 10000,
                maximumAge: 0,
                ...options
            },
            (position, error) => {
                if (error) {
                    console.error('Location error:', error);
                    return;
                }
                
                this.callbacks.forEach(cb => cb(position));
            }
        );
    }
    
    async stop() {
        if (this.watchId) {
            await Geolocation.clearWatch({ id: this.watchId });
            this.watchId = null;
        }
    }
    
    subscribe(callback: (position: any) => void): () => void {
        this.callbacks.add(callback);
        return () => this.callbacks.delete(callback);
    }
}

export const locationWatcher = new LocationWatcher();

四、权限管理 #

4.1 检查权限 #

typescript
async function checkPermissions() {
    const permissions = await Geolocation.checkPermissions();
    
    console.log('Location:', permissions.location);
    console.log('Coarse Location:', permissions.coarseLocation);
    
    return permissions;
}

4.2 请求权限 #

typescript
async function requestPermissions() {
    const permissions = await Geolocation.requestPermissions();
    
    if (permissions.location === 'granted') {
        // 权限已授予
        return true;
    }
    
    return false;
}

4.3 完整权限处理 #

typescript
import { Geolocation } from '@capacitor/geolocation';
import { Dialog } from '@capacitor/dialog';
import { AppLauncher } from '@capacitor/app-launcher';

async function ensureLocationPermission(): Promise<boolean> {
    // 检查权限
    const permissions = await Geolocation.checkPermissions();
    
    if (permissions.location === 'granted') {
        return true;
    }
    
    // 请求权限
    const result = await Geolocation.requestPermissions();
    
    if (result.location === 'granted') {
        return true;
    }
    
    // 权限被拒绝
    if (result.location === 'denied') {
        const { value } = await Dialog.confirm({
            title: '位置权限',
            message: '需要位置权限才能使用此功能,是否前往设置开启?'
        });
        
        if (value) {
            // 打开应用设置
            await AppLauncher.openUrl({ url: 'app-settings:' });
        }
    }
    
    return false;
}

五、距离计算 #

5.1 Haversine公式 #

typescript
function calculateDistance(
    lat1: number,
    lon1: number,
    lat2: number,
    lon2: number
): number {
    const R = 6371e3; // 地球半径(米)
    const φ1 = lat1 * Math.PI / 180;
    const φ2 = lat2 * Math.PI / 180;
    const Δφ = (lat2 - lat1) * Math.PI / 180;
    const Δλ = (lon2 - lon1) * Math.PI / 180;
    
    const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
              Math.cos(φ1) * Math.cos(φ2) *
              Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
    
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    
    return R * c; // 返回距离(米)
}

// 使用示例
const distance = calculateDistance(
    39.9042, 116.4074,  // 北京
    31.2304, 121.4737   // 上海
);
console.log(`距离: ${(distance / 1000).toFixed(2)} 公里`);

5.2 距离工具类 #

typescript
class DistanceCalculator {
    private static R = 6371e3; // 地球半径
    
    static between(
        point1: { lat: number; lng: number },
        point2: { lat: number; lng: number }
    ): number {
        return this.calculate(point1.lat, point1.lng, point2.lat, point2.lng);
    }
    
    static calculate(lat1: number, lon1: number, lat2: number, lon2: number): number {
        const φ1 = this.toRad(lat1);
        const φ2 = this.toRad(lat2);
        const Δφ = this.toRad(lat2 - lat1);
        const Δλ = this.toRad(lon2 - lon1);
        
        const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
                  Math.cos(φ1) * Math.cos(φ2) *
                  Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
        
        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        
        return this.R * c;
    }
    
    static formatDistance(meters: number): string {
        if (meters < 1000) {
            return `${Math.round(meters)}米`;
        }
        return `${(meters / 1000).toFixed(1)}公里`;
    }
    
    private static toRad(deg: number): number {
        return deg * Math.PI / 180;
    }
}

// 使用示例
const distance = DistanceCalculator.between(
    { lat: 39.9042, lng: 116.4074 },
    { lat: 31.2304, lng: 121.4737 }
);
console.log(DistanceCalculator.formatDistance(distance));

六、地图集成 #

6.1 Google Maps集成 #

typescript
// 使用Google Maps JavaScript API
declare global {
    interface Window {
        google: any;
    }
}

async function initMap(elementId: string) {
    const position = await Geolocation.getCurrentPosition();
    
    const map = new window.google.maps.Map(
        document.getElementById(elementId),
        {
            center: {
                lat: position.coords.latitude,
                lng: position.coords.longitude
            },
            zoom: 15
        }
    );
    
    new window.google.maps.Marker({
        position: {
            lat: position.coords.latitude,
            lng: position.coords.longitude
        },
        map,
        title: '当前位置'
    });
    
    return map;
}

6.2 高德地图集成 #

typescript
// 使用高德地图JS API
declare global {
    interface Window {
        AMap: any;
    }
}

async function initAMap(elementId: string) {
    const position = await Geolocation.getCurrentPosition();
    
    const map = new window.AMap.Map(elementId, {
        zoom: 15,
        center: [position.coords.longitude, position.coords.latitude]
    });
    
    new window.AMap.Marker({
        position: [position.coords.longitude, position.coords.latitude],
        map
    });
    
    return map;
}

七、完整示例 #

7.1 React组件 #

tsx
import { useState, useEffect, useCallback } from 'react';
import { Geolocation, Position } from '@capacitor/geolocation';

function LocationTracker() {
    const [position, setPosition] = useState<Position | null>(null);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState<string | null>(null);
    const [watching, setWatching] = useState(false);
    const [watchId, setWatchId] = useState<string | null>(null);
    
    const getCurrentPosition = useCallback(async () => {
        setLoading(true);
        setError(null);
        
        try {
            const pos = await Geolocation.getCurrentPosition({
                enableHighAccuracy: true,
                timeout: 10000
            });
            
            setPosition(pos);
        } catch (e) {
            setError(e instanceof Error ? e.message : '获取位置失败');
        } finally {
            setLoading(false);
        }
    }, []);
    
    const startWatching = useCallback(async () => {
        setWatching(true);
        
        const id = await Geolocation.watchPosition(
            {
                enableHighAccuracy: true,
                timeout: 10000
            },
            (pos, err) => {
                if (err) {
                    setError(err.message);
                    return;
                }
                setPosition(pos);
            }
        );
        
        setWatchId(id);
    }, []);
    
    const stopWatching = useCallback(async () => {
        if (watchId) {
            await Geolocation.clearWatch({ id: watchId });
            setWatchId(null);
        }
        setWatching(false);
    }, [watchId]);
    
    useEffect(() => {
        return () => {
            if (watchId) {
                Geolocation.clearWatch({ id: watchId });
            }
        };
    }, [watchId]);
    
    return (
        <div className="location-tracker">
            <div className="actions">
                <button onClick={getCurrentPosition} disabled={loading}>
                    {loading ? '获取中...' : '获取当前位置'}
                </button>
                
                {!watching ? (
                    <button onClick={startWatching}>开始追踪</button>
                ) : (
                    <button onClick={stopWatching}>停止追踪</button>
                )}
            </div>
            
            {error && <div className="error">{error}</div>}
            
            {position && (
                <div className="position-info">
                    <p>纬度: {position.coords.latitude.toFixed(6)}</p>
                    <p>经度: {position.coords.longitude.toFixed(6)}</p>
                    <p>精度: {position.coords.accuracy.toFixed(0)}米</p>
                    {position.coords.altitude && (
                        <p>海拔: {position.coords.altitude.toFixed(0)}米</p>
                    )}
                    {position.coords.speed && (
                        <p>速度: {position.coords.speed.toFixed(1)}米/秒</p>
                    )}
                </div>
            )}
        </div>
    );
}

export default LocationTracker;

7.2 Vue组合式函数 #

typescript
// composables/useGeolocation.ts
import { ref, onUnmounted } from 'vue';
import { Geolocation, Position } from '@capacitor/geolocation';

export function useGeolocation() {
    const position = ref<Position | null>(null);
    const loading = ref(false);
    const error = ref<string | null>(null);
    const watching = ref(false);
    
    let watchId: string | null = null;
    
    const getCurrentPosition = async () => {
        loading.value = true;
        error.value = null;
        
        try {
            position.value = await Geolocation.getCurrentPosition({
                enableHighAccuracy: true,
                timeout: 10000
            });
        } catch (e) {
            error.value = e instanceof Error ? e.message : '获取位置失败';
        } finally {
            loading.value = false;
        }
    };
    
    const startWatching = async () => {
        watching.value = true;
        
        watchId = await Geolocation.watchPosition(
            {
                enableHighAccuracy: true,
                timeout: 10000
            },
            (pos, err) => {
                if (err) {
                    error.value = err.message;
                    return;
                }
                position.value = pos;
            }
        );
    };
    
    const stopWatching = async () => {
        if (watchId) {
            await Geolocation.clearWatch({ id: watchId });
            watchId = null;
        }
        watching.value = false;
    };
    
    onUnmounted(() => {
        if (watchId) {
            Geolocation.clearWatch({ id: watchId });
        }
    });
    
    return {
        position,
        loading,
        error,
        watching,
        getCurrentPosition,
        startWatching,
        stopWatching
    };
}

八、后台定位 #

8.1 iOS后台定位 #

xml
<!-- Info.plist -->
<key>UIBackgroundModes</key>
<array>
    <string>location</string>
</array>

8.2 Android后台定位 #

xml
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

九、常见问题 #

9.1 定位不准确 #

typescript
// 使用高精度定位
const position = await Geolocation.getCurrentPosition({
    enableHighAccuracy: true,
    timeout: 20000,
    maximumAge: 0
});

9.2 定位超时 #

typescript
// 增加超时时间
const position = await Geolocation.getCurrentPosition({
    enableHighAccuracy: true,
    timeout: 30000  // 30秒
});

9.3 室内定位 #

typescript
// 室内可能需要使用网络定位
const position = await Geolocation.getCurrentPosition({
    enableHighAccuracy: false  // 使用网络定位
});

十、总结 #

10.1 核心功能 #

功能 方法
获取位置 Geolocation.getCurrentPosition()
监听位置 Geolocation.watchPosition()
停止监听 Geolocation.clearWatch()
检查权限 Geolocation.checkPermissions()
请求权限 Geolocation.requestPermissions()

10.2 下一步 #

了解地理位置后,让我们学习 存储系统

最后更新:2026-03-28