import { PolygonWithLabel } from "@/components/map/PolygonWithLabel"; import { PolylineWithLabel } from "@/components/map/PolylineWithLabel"; import { ENTITY, EVENT_ALARM_DATA, EVENT_BANZONE_DATA, EVENT_ENTITY_DATA, EVENT_GPS_DATA, EVENT_TRACK_POINTS_DATA, IOS_PLATFORM, LIGHT_THEME, } from "@/constants"; import { useColorScheme } from "@/hooks/use-color-scheme.web"; import { usePlatform } from "@/hooks/use-platform"; import { getAlarmEventBus, getBanzonesEventBus, getEntitiesEventBus, getGpsEventBus, getTrackPointsEventBus, } from "@/services/device_events"; import { getShipIcon } from "@/services/map_service"; import eventBus from "@/utils/eventBus"; import { convertWKTLineStringToLatLngArray, convertWKTtoLatLngString, } from "@/utils/geom"; import { useEffect, useRef, useState } from "react"; import { Animated, Image as RNImage, StyleSheet, Text, TouchableOpacity, View, } from "react-native"; import MapView, { Circle, Marker } from "react-native-maps"; import { SafeAreaView } from "react-native-safe-area-context"; export default function HomeScreen() { const [gpsData, setGpsData] = useState( undefined ); const [alarmData, setAlarmData] = useState(null); const [entityData, setEntityData] = useState< Model.TransformedEntity[] | null >(null); const [banzoneData, setBanzoneData] = useState(null); const [trackPointsData, setTrackPointsData] = useState< Model.ShipTrackPoint[] | null >(null); const [circleRadius, setCircleRadius] = useState(100); const [zoomLevel, setZoomLevel] = useState(10); const [isFirstLoad, setIsFirstLoad] = useState(true); const [polylineCoordinates, setPolylineCoordinates] = useState< number[][] | undefined >(undefined); const [polygonCoordinates, setPolygonCoordinates] = useState< number[][][] | undefined >(undefined); const platform = usePlatform(); const theme = useColorScheme(); const scale = useRef(new Animated.Value(0)).current; const opacity = useRef(new Animated.Value(1)).current; // console.log("Platform: ", platform); // console.log("Theme: ", theme); // const [number, setNumber] = useState(0); useEffect(() => { getGpsEventBus(); getAlarmEventBus(); getEntitiesEventBus(); getBanzonesEventBus(); getTrackPointsEventBus(); const queryGpsData = (gpsData: Model.GPSResonse) => { setGpsData(gpsData); }; const queryAlarmData = (alarmData: Model.AlarmResponse) => { // console.log("Alarm Data: ", alarmData.alarms.length); setAlarmData(alarmData); }; const queryEntityData = (entityData: Model.TransformedEntity[]) => { // console.log("Entities Length Data: ", entityData.length); setEntityData(entityData); }; const queryBanzonesData = (banzoneData: Model.Zone[]) => { // console.log("Banzone Data: ", banzoneData.length); setBanzoneData(banzoneData); }; const queryTrackPointsData = (TrackPointsData: Model.ShipTrackPoint[]) => { // console.log("TrackPoints Data: ", TrackPointsData.length); setTrackPointsData(TrackPointsData); }; eventBus.on(EVENT_GPS_DATA, queryGpsData); console.log("Registering event handlers in HomeScreen"); eventBus.on(EVENT_GPS_DATA, queryGpsData); console.log("Subscribed to EVENT_GPS_DATA"); eventBus.on(EVENT_ALARM_DATA, queryAlarmData); console.log("Subscribed to EVENT_ALARM_DATA"); eventBus.on(EVENT_ENTITY_DATA, queryEntityData); console.log("Subscribed to EVENT_ENTITY_DATA"); eventBus.on(EVENT_TRACK_POINTS_DATA, queryTrackPointsData); console.log("Subscribed to EVENT_TRACK_POINTS_DATA"); eventBus.once(EVENT_BANZONE_DATA, queryBanzonesData); console.log("Subscribed once to EVENT_BANZONE_DATA"); return () => { console.log("Unregistering event handlers in HomeScreen"); eventBus.off(EVENT_GPS_DATA, queryGpsData); console.log("Unsubscribed EVENT_GPS_DATA"); eventBus.off(EVENT_ALARM_DATA, queryAlarmData); console.log("Unsubscribed EVENT_ALARM_DATA"); eventBus.off(EVENT_ENTITY_DATA, queryEntityData); console.log("Unsubscribed EVENT_ENTITY_DATA"); eventBus.off(EVENT_TRACK_POINTS_DATA, queryTrackPointsData); console.log("Unsubscribed EVENT_TRACK_POINTS_DATA"); }; }, []); useEffect(() => { if (polylineCoordinates !== undefined) { console.log("Polyline Khac null"); } else { console.log("Polyline null"); } }, [polylineCoordinates]); useEffect(() => { setPolylineCoordinates(undefined); setPolygonCoordinates(undefined); if (!entityData) return; if (!banzoneData) return; for (const entity of entityData) { if (entity.id !== ENTITY.ZONE_ALARM_LIST) { continue; } let zones: any[] = []; try { zones = entity.valueString ? JSON.parse(entity.valueString) : []; } catch (parseError) { console.error("Error parsing zone list:", parseError); continue; } // Nếu danh sách zone rỗng, clear tất cả if (zones.length === 0) { setPolylineCoordinates(undefined); setPolygonCoordinates(undefined); return; } for (const zone of zones) { console.log("Zone Data: ", zone); const geom = banzoneData.find((b) => b.id === zone.zone_id); if (!geom) { continue; } const { geom_type, geom_lines, geom_poly } = geom.geom || {}; if (typeof geom_type !== "number") { continue; } if (geom_type === 2) { // if(oldEntityData.find(e => e.id === )) // foundPolyline = true; const coordinates = convertWKTLineStringToLatLngArray( geom_lines || "" ); if (coordinates.length > 0) { setPolylineCoordinates(coordinates); } } else if (geom_type === 1) { // foundPolygon = true; const coordinates = convertWKTtoLatLngString(geom_poly || ""); if (coordinates.length > 0) { console.log("Polygon Coordinate: ", coordinates); setPolygonCoordinates(coordinates); } } } } }, [banzoneData, entityData]); // Hàm tính radius cố định khi zoom change const calculateRadiusFromZoom = (zoom: number) => { const baseZoom = 10; const baseRadius = 100; const zoomDifference = baseZoom - zoom; const calculatedRadius = baseRadius * Math.pow(2, zoomDifference); // console.log("Caculate Radius: ", calculatedRadius); return Math.max(calculatedRadius, 50); }; // Xử lý khi region (zoom) thay đổi const handleRegionChangeComplete = (newRegion: any) => { // Tính zoom level từ latitudeDelta // zoom = log2(360 / (latitudeDelta * 2)) + 8 const zoom = Math.round(Math.log2(360 / (newRegion.latitudeDelta * 2)) + 8); const newRadius = calculateRadiusFromZoom(zoom); setCircleRadius(newRadius); setZoomLevel(zoom); // console.log("Zoom level:", zoom, "Circle radius:", newRadius); }; // Tính toán region để focus vào marker của tàu (chỉ lần đầu tiên) const getMapRegion = () => { if (!isFirstLoad) { // Sau lần đầu, return undefined để không force region return undefined; } if (!gpsData) { return { latitude: 15.70581, longitude: 116.152685, latitudeDelta: 0.05, longitudeDelta: 0.05, }; } return { latitude: gpsData.lat, longitude: gpsData.lon, latitudeDelta: 0.05, longitudeDelta: 0.05, }; }; const handleMapReady = () => { setTimeout(() => { setIsFirstLoad(false); }, 2000); }; useEffect(() => { if (alarmData?.level === 3) { const loop = Animated.loop( Animated.sequence([ Animated.parallel([ Animated.timing(scale, { toValue: 3, // nở to 3 lần duration: 1500, useNativeDriver: true, }), Animated.timing(opacity, { toValue: 0, // mờ dần duration: 1500, useNativeDriver: true, }), ]), Animated.parallel([ Animated.timing(scale, { toValue: 0, duration: 0, useNativeDriver: true, }), Animated.timing(opacity, { toValue: 1, duration: 0, useNativeDriver: true, }), ]), ]) ); loop.start(); return () => loop.stop(); } }, [alarmData?.level, scale, opacity]); return ( {trackPointsData && trackPointsData.length > 0 && trackPointsData.map((point, index) => { // console.log(`Rendering circle ${index}:`, point); return ( ); })} {polylineCoordinates !== undefined && ( ({ latitude: coord[0], longitude: coord[1], }))} label="Tuyến bờ" strokeColor="#FF5733" strokeWidth={4} showDistance={false} zIndex={50} /> )} {polygonCoordinates !== undefined && ( <> {polygonCoordinates.map((polygon, index) => { // Tạo key ổn định từ tọa độ đầu tiên của polygon const polygonKey = polygon.length > 0 ? `polygon-${polygon[0][0]}-${polygon[0][1]}-${index}` : `polygon-${index}`; return ( // ({ // latitude: coords[0], // longitude: coords[1], // }))} // fillColor="rgba(16, 85, 201, 0.6)" // strokeColor="rgba(16, 85, 201, 0.8)" // strokeWidth={2} // zIndex={50} // /> ({ latitude: coords[0], longitude: coords[1], }))} label="Test khu đánh bắt" content="Thời gian cấm (từ tháng 1 đến tháng 12)" fillColor="rgba(16, 85, 201, 0.6)" strokeColor="rgba(16, 85, 201, 0.8)" strokeWidth={2} zIndex={50} zoomLevel={zoomLevel} /> ); })} )} {gpsData !== undefined && ( {alarmData?.level === 3 && ( )} { const icon = getShipIcon( alarmData?.level || 0, gpsData.fishing ); return typeof icon === "string" ? { uri: icon } : icon; })()} style={{ width: 32, height: 32, transform: [ { rotate: `${ typeof gpsData.h === "number" && !isNaN(gpsData.h) ? gpsData.h : 0 }deg`, }, ], }} /> )} { setPolygonCoordinates(undefined); setPolylineCoordinates(undefined); }} > Get GPS Data ); } const styles = StyleSheet.create({ container: { flex: 1, }, map: { flex: 1, }, button: { // display: "none", 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", }, pingContainer: { width: 32, height: 32, alignItems: "center", justifyContent: "center", overflow: "visible", }, pingCircle: { position: "absolute", width: 40, height: 40, borderRadius: 20, backgroundColor: "#ED3F27", }, centerDot: { width: 20, height: 20, borderRadius: 10, backgroundColor: "#0096FF", }, });