115 lines
3.2 KiB
TypeScript
115 lines
3.2 KiB
TypeScript
import React, { useRef } from 'react';
|
|
import { View, Text, StyleSheet, TouchableOpacity, Platform } from 'react-native';
|
|
import MapView, { Marker, Polyline, type Region } from 'react-native-maps';
|
|
import { useDataStore } from '../../src/stores/dataStore';
|
|
import { formatCoord } from '../../src/utils/format';
|
|
|
|
export default function MapScreen() {
|
|
const history = useDataStore((s) => s.history);
|
|
const mapRef = useRef<MapView>(null);
|
|
|
|
const points = history
|
|
.filter((f) => f.meta && f.meta.latitude !== 0 && f.meta.longitude !== 0)
|
|
.map((f) => ({
|
|
latitude: f.meta!.latitude,
|
|
longitude: f.meta!.longitude,
|
|
frameId: f.frameId,
|
|
}))
|
|
.reverse(); // oldest first for polyline
|
|
|
|
const latest = points[points.length - 1];
|
|
|
|
const centerOnCurrent = () => {
|
|
if (!latest || !mapRef.current) return;
|
|
mapRef.current.animateToRegion(
|
|
{ latitude: latest.latitude, longitude: latest.longitude, latitudeDelta: 0.005, longitudeDelta: 0.005 },
|
|
600,
|
|
);
|
|
};
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
<MapView
|
|
ref={mapRef}
|
|
style={styles.map}
|
|
mapType="satellite"
|
|
showsUserLocation
|
|
initialRegion={
|
|
latest
|
|
? { latitude: latest.latitude, longitude: latest.longitude, latitudeDelta: 0.01, longitudeDelta: 0.01 }
|
|
: { latitude: 30, longitude: 115, latitudeDelta: 10, longitudeDelta: 10 }
|
|
}
|
|
>
|
|
{/* GPS track polyline */}
|
|
{points.length > 1 && (
|
|
<Polyline coordinates={points} strokeColor="#4a9eff" strokeWidth={2} />
|
|
)}
|
|
|
|
{/* Measurement point markers */}
|
|
{points.map((p) => (
|
|
<Marker
|
|
key={p.frameId}
|
|
coordinate={{ latitude: p.latitude, longitude: p.longitude }}
|
|
anchor={{ x: 0.5, y: 0.5 }}
|
|
title={`帧 #${p.frameId}`}
|
|
>
|
|
<View style={styles.markerDot} />
|
|
</Marker>
|
|
))}
|
|
</MapView>
|
|
|
|
{/* Overlay info */}
|
|
<View style={styles.overlay}>
|
|
<Text style={styles.overlayText}>测点数: {points.length}</Text>
|
|
{latest && (
|
|
<Text style={styles.overlayText}>
|
|
{formatCoord(latest.latitude, false)} {formatCoord(latest.longitude, true)}
|
|
</Text>
|
|
)}
|
|
</View>
|
|
|
|
{/* Center button */}
|
|
<TouchableOpacity style={styles.centerBtn} onPress={centerOnCurrent}>
|
|
<Text style={styles.centerBtnText}>⌖</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: { flex: 1 },
|
|
map: { flex: 1 },
|
|
markerDot: {
|
|
width: 10,
|
|
height: 10,
|
|
borderRadius: 5,
|
|
backgroundColor: '#4a9eff',
|
|
borderWidth: 1,
|
|
borderColor: '#fff',
|
|
},
|
|
overlay: {
|
|
position: 'absolute',
|
|
top: 12,
|
|
left: 12,
|
|
backgroundColor: 'rgba(0,0,0,0.6)',
|
|
borderRadius: 8,
|
|
padding: 8,
|
|
gap: 2,
|
|
},
|
|
overlayText: { color: '#ddd', fontSize: 11 },
|
|
centerBtn: {
|
|
position: 'absolute',
|
|
bottom: 24,
|
|
right: 16,
|
|
width: 44,
|
|
height: 44,
|
|
borderRadius: 22,
|
|
backgroundColor: 'rgba(10,10,10,0.85)',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
borderWidth: 1,
|
|
borderColor: '#444',
|
|
},
|
|
centerBtnText: { color: '#4a9eff', fontSize: 22 },
|
|
});
|