241 lines
6.3 KiB
TypeScript
241 lines
6.3 KiB
TypeScript
import { showToastError } from "@/config";
|
|
import {
|
|
queryAlarm,
|
|
queryGpsData,
|
|
queryTrackPoints,
|
|
} from "@/controller/DeviceController";
|
|
import { getShipIcon } from "@/services/map_service";
|
|
import { Image as ExpoImage } from "expo-image";
|
|
import { useState } from "react";
|
|
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
|
|
|
import MapView, { Circle, Marker } from "react-native-maps";
|
|
import { SafeAreaProvider } from "react-native-safe-area-context";
|
|
|
|
export default function HomeScreen() {
|
|
const [gpsData, setGpsData] = useState<Model.GPSResonse | null>(null);
|
|
const [alarmData, setAlarmData] = useState<Model.AlarmResponse | null>(null);
|
|
const [trackPoints, setTrackPoints] = useState<Model.ShipTrackPoint[] | null>(
|
|
null
|
|
);
|
|
const getGpsData = async () => {
|
|
try {
|
|
const response = await queryGpsData();
|
|
console.log("GpsData: ", response.data);
|
|
console.log(
|
|
"Heading value:",
|
|
response.data?.h,
|
|
"Type:",
|
|
typeof response.data?.h
|
|
);
|
|
setGpsData(response.data);
|
|
} catch (error) {
|
|
console.error("Error fetching GPS data:", error);
|
|
showToastError("Lỗi", "Không thể lấy dữ liệu GPS");
|
|
}
|
|
};
|
|
|
|
const getAlarmData = async () => {
|
|
try {
|
|
const response = await queryAlarm();
|
|
console.log("AlarmData: ", response.data);
|
|
setAlarmData(response.data);
|
|
} catch (error) {
|
|
console.error("Error fetching Alarm Data: ", error);
|
|
showToastError("Lỗi", "Không thể lấy dữ liệu báo động");
|
|
}
|
|
};
|
|
|
|
const getShipTrackPoints = async () => {
|
|
try {
|
|
const response = await queryTrackPoints();
|
|
console.log(
|
|
"TrackPoints Data: ",
|
|
response.data[response.data.length - 1]
|
|
);
|
|
setTrackPoints(response.data);
|
|
} catch (error) {
|
|
console.error("Error fetching TrackPoints Data: ", error);
|
|
showToastError("Lỗi", "Không thể lấy lịch sử di chuyển");
|
|
}
|
|
};
|
|
|
|
const handleMapReady = () => {
|
|
console.log("Map loaded successfully!");
|
|
getGpsData();
|
|
getAlarmData();
|
|
getShipTrackPoints();
|
|
};
|
|
|
|
// Tính toán region để bao phủ cả GPS và track points
|
|
const getMapRegion = () => {
|
|
if (!gpsData && (!trackPoints || trackPoints.length === 0)) {
|
|
return {
|
|
latitude: 15.70581,
|
|
longitude: 116.152685,
|
|
latitudeDelta: 2,
|
|
longitudeDelta: 2,
|
|
};
|
|
}
|
|
|
|
let minLat = gpsData?.lat ?? 90;
|
|
let maxLat = gpsData?.lat ?? -90;
|
|
let minLon = gpsData?.lon ?? 180;
|
|
let maxLon = gpsData?.lon ?? -180;
|
|
|
|
// Bao gồm track points
|
|
if (trackPoints) {
|
|
trackPoints.forEach((point) => {
|
|
minLat = Math.min(minLat, point.lat);
|
|
maxLat = Math.max(maxLat, point.lat);
|
|
minLon = Math.min(minLon, point.lon);
|
|
maxLon = Math.max(maxLon, point.lon);
|
|
});
|
|
}
|
|
|
|
const latDelta = Math.max(maxLat - minLat, 0.01) * 1.2; // Padding 20%
|
|
const lonDelta = Math.max(maxLon - minLon, 0.01) * 1.2;
|
|
|
|
console.log("Map region:", {
|
|
minLat,
|
|
maxLat,
|
|
minLon,
|
|
maxLon,
|
|
latDelta,
|
|
lonDelta,
|
|
});
|
|
|
|
return {
|
|
latitude: (minLat + maxLat) / 2,
|
|
longitude: (minLon + maxLon) / 2,
|
|
latitudeDelta: latDelta,
|
|
longitudeDelta: lonDelta,
|
|
};
|
|
};
|
|
|
|
return (
|
|
<SafeAreaProvider style={styles.container}>
|
|
<MapView
|
|
onMapReady={handleMapReady}
|
|
onPoiClick={(point) => {
|
|
console.log("Poi clicked: ", point.nativeEvent);
|
|
}}
|
|
style={styles.map}
|
|
initialRegion={{
|
|
latitude: 15.70581,
|
|
longitude: 116.152685,
|
|
latitudeDelta: 2,
|
|
longitudeDelta: 2,
|
|
}}
|
|
region={getMapRegion()}
|
|
// userInterfaceStyle="dark"
|
|
showsBuildings={false}
|
|
showsIndoors={false}
|
|
loadingEnabled={true}
|
|
mapType="standard"
|
|
>
|
|
{trackPoints &&
|
|
trackPoints.length > 0 &&
|
|
trackPoints.map((point, index) => {
|
|
// console.log(`Rendering circle ${index}:`, point);
|
|
return (
|
|
<Circle
|
|
key={index}
|
|
center={{
|
|
latitude: point.lat,
|
|
longitude: point.lon,
|
|
}}
|
|
zIndex={50}
|
|
radius={20} // Tăng từ 50 → 1000m
|
|
fillColor="rgba(241, 12, 65, 0.8)" // Tăng opacity từ 0.06 → 0.8
|
|
strokeColor="rgba(221, 240, 15, 0.8)"
|
|
strokeWidth={2}
|
|
/>
|
|
);
|
|
})}
|
|
{gpsData && (
|
|
<Marker
|
|
coordinate={{
|
|
latitude: gpsData.lat,
|
|
longitude: gpsData.lon,
|
|
}}
|
|
title="Tàu của mình"
|
|
zIndex={100}
|
|
>
|
|
<View
|
|
style={{
|
|
transform: [
|
|
{
|
|
rotate: `${
|
|
typeof gpsData.h === "number" && !isNaN(gpsData.h)
|
|
? gpsData.h
|
|
: 0
|
|
}deg`,
|
|
},
|
|
],
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
}}
|
|
>
|
|
<ExpoImage
|
|
source={getShipIcon(alarmData?.level || 0, gpsData.fishing)}
|
|
style={{ width: 32, height: 32 }}
|
|
/>
|
|
</View>
|
|
</Marker>
|
|
)}
|
|
</MapView>
|
|
<TouchableOpacity style={styles.button} onPress={handleMapReady}>
|
|
<Text style={styles.buttonText}>Get GPS Data</Text>
|
|
</TouchableOpacity>
|
|
</SafeAreaProvider>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
},
|
|
map: {
|
|
flex: 1,
|
|
},
|
|
button: {
|
|
position: "absolute",
|
|
top: 50,
|
|
right: 20,
|
|
backgroundColor: "#007AFF",
|
|
paddingHorizontal: 16,
|
|
paddingVertical: 12,
|
|
borderRadius: 8,
|
|
elevation: 5,
|
|
shadowColor: "#000",
|
|
shadowOffset: {
|
|
width: 0,
|
|
height: 2,
|
|
},
|
|
shadowOpacity: 0.25,
|
|
shadowRadius: 3.84,
|
|
},
|
|
buttonText: {
|
|
color: "#fff",
|
|
fontSize: 16,
|
|
fontWeight: "600",
|
|
},
|
|
titleContainer: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
gap: 8,
|
|
},
|
|
stepContainer: {
|
|
gap: 8,
|
|
marginBottom: 8,
|
|
},
|
|
reactLogo: {
|
|
height: 178,
|
|
width: 290,
|
|
bottom: 0,
|
|
left: 0,
|
|
position: "absolute",
|
|
},
|
|
});
|