11import React , { useRef , useMemo , useEffect , useState , useContext } from "react" ;
22import * as d3 from "d3" ;
33
4- import { MapContext , SimpleDataContext } from "@mapcomponents/react-maplibre" ;
4+ import {
5+ MapContext ,
6+ SimpleDataContext ,
7+ useMapState ,
8+ } from "@mapcomponents/react-maplibre" ;
59
610import { MapboxLayer } from "@deck.gl/mapbox" ;
711import { IconLayer } from "@deck.gl/layers" ;
812
913import DeckGlContext from "../../deckgl_components/DeckGlContext" ;
1014import getShipType from "./utils/getShipType" ;
11- import Ships from "./assets/Ships_v2.png" ;
12-
15+ import Ships from "./assets/Ships.png" ;
1316
1417const navStats = {
1518 0 : "under way using engine" ,
@@ -27,7 +30,15 @@ const navStats = {
2730 15 : "default" ,
2831} ;
2932
30- const MlIconLayer = ( props ) => {
33+ const MlIconLayer = ( {
34+ setOpenSidebar,
35+ setSidebarInfo,
36+ showMovingVessels,
37+ showNotMovingVessels,
38+ selectedVessel,
39+ setSelectedVessel,
40+ ...props
41+ } ) => {
3142 // Use a useRef hook to reference the layer object to be able to access it later inside useEffect hooks
3243 // without the requirement of adding it to the dependency list (ignore the false eslint exhaustive deps warning)
3344 const mapContext = useContext ( MapContext ) ;
@@ -45,9 +56,12 @@ const MlIconLayer = (props) => {
4556 const [ data , setData ] = useState ( [ ] ) ;
4657
4758 const [ hoverInfo , setHoverInfo ] = useState ( { } ) ;
48-
4959 const [ vesselInfo , setVesselInfo ] = useState ( ) ;
5060
61+ const [ size , setSize ] = useState ( 30 ) ;
62+ const mapState = useMapState ( { mapId : undefined , watch : { viewport : true } } ) ;
63+ // console.log(mapState?.viewport?.zoom);
64+
5165 const getVesselInfo = ( mmsi ) => {
5266 fetch ( "https://meri.digitraffic.fi/api/ais/v1/vessels/" + mmsi )
5367 . then ( ( response ) => {
@@ -58,6 +72,7 @@ const MlIconLayer = (props) => {
5872 } )
5973 . then ( ( data ) => {
6074 setVesselInfo ( data ) ;
75+ setSidebarInfo ( { hoverInfo, vesselInfo : data , navStats } ) ;
6176 } )
6277 . catch ( ( error ) => {
6378 console . error (
@@ -84,6 +99,39 @@ const MlIconLayer = (props) => {
8499 startAnimation ( ) ;
85100 } , [ simpleDataContext . data ] ) ;
86101
102+ const onClickHandler = ( ev ) => {
103+ setOpenSidebar ( true ) ;
104+ getVesselInfo ( ev . object . mmsi ) ;
105+ setSelectedVessel ( ev . object ) ;
106+ setHoverInfo ( { } ) ; // Hide tooltip
107+
108+ if ( mapContext . map ) {
109+ const currentZoom = mapState ?. viewport ?. zoom ;
110+
111+ // Only set zoom to 9 if the current zoom is less than or equal to 9
112+ const zoomLevel = currentZoom <= 9 ? 9 : currentZoom ;
113+
114+ mapContext . map . flyTo ( {
115+ center : [ ev . object . longitude , ev . object . latitude ] ,
116+ zoom : zoomLevel ,
117+ speed : 1 ,
118+ } ) ;
119+ }
120+ } ;
121+
122+ useEffect ( ( ) => {
123+ const zoom = mapState ?. viewport ?. zoom ;
124+ if ( zoom <= 6 ) {
125+ setSize ( 30 ) ;
126+ } else if ( zoom <= 11 ) {
127+ setSize ( 50 ) ;
128+ } else if ( zoom <= 15 ) {
129+ setSize ( 75 ) ;
130+ } else {
131+ setSize ( 100 ) ;
132+ }
133+ } , [ mapState ?. viewport ?. zoom ] ) ;
134+
87135 const deckLayerProps = useMemo ( ( ) => {
88136 return {
89137 id : layerName ,
@@ -98,14 +146,20 @@ const MlIconLayer = (props) => {
98146 width : 512 ,
99147 height : 512 ,
100148 } ,
101- other : {
149+ notmoving : {
102150 x : 512 ,
103151 y : 0 ,
104152 width : 512 ,
105153 height : 512 ,
106154 } ,
155+ selected : {
156+ x : 1024 ,
157+ y : 0 ,
158+ width : 512 ,
159+ height : 512 ,
160+ } ,
107161 } ,
108- sizeScale : 30 ,
162+ sizeScale : size ,
109163 autoHighlight : true ,
110164 onHover : ( d ) => {
111165 if ( d . picked ) {
@@ -116,13 +170,41 @@ const MlIconLayer = (props) => {
116170 }
117171 } ,
118172 getPosition : ( d ) => [ d . longitude , d . latitude ] ,
119- onClick : ( ev ) => getVesselInfo ( ev . object . mmsi ) ,
173+ onClick : onClickHandler ,
120174 getIcon : ( d ) => {
121- return d . navStat === 0 ? "moving" : "other" ;
175+ if (
176+ ( d . velocity === 0 && ! showNotMovingVessels ) ||
177+ ( d . velocity > 0 && ! showMovingVessels )
178+ ) {
179+ return null ;
180+ }
181+
182+ if ( selectedVessel && d . mmsi === selectedVessel . mmsi ) {
183+ return "selected" ;
184+ }
185+ return d . velocity === 0 ? "notmoving" : "moving" ;
122186 } ,
123187 getAngle : ( d ) => - d . true_track ,
188+ getTooltip : ( d ) => {
189+ if (
190+ ( d . velocity === 0 && ! showNotMovingVessels ) ||
191+ ( d . velocity > 1 && ! showMovingVessels )
192+ ) {
193+ return null ;
194+ }
195+
196+ return renderTooltip ( d ) ;
197+ } ,
124198 } ;
125- } , [ data ] ) ;
199+ } , [
200+ data ,
201+ selectedVessel ,
202+ hoverInfo ,
203+ vesselInfo ,
204+ size ,
205+ showMovingVessels ,
206+ showNotMovingVessels ,
207+ ] ) ;
126208
127209 const animationFrame = ( ) => {
128210 if ( ! simpleDataContext . data ) return ;
@@ -228,6 +310,21 @@ const MlIconLayer = (props) => {
228310 return null ;
229311 }
230312
313+ // Adjusting the tooltip position at the edges of the screen
314+ const tooltipWidth = 300 ;
315+ const tooltipHeight = 200 ;
316+
317+ const screenWidth = window . innerWidth ;
318+ const screenHeight = window . innerHeight ;
319+
320+ if ( x + tooltipWidth > screenWidth ) {
321+ x = screenWidth - tooltipWidth - 20 ;
322+ }
323+
324+ if ( y + tooltipHeight > screenHeight ) {
325+ y = screenHeight - tooltipHeight - 20 ;
326+ }
327+
231328 return (
232329 < div
233330 className = "tooltip"
@@ -242,13 +339,12 @@ const MlIconLayer = (props) => {
242339 opacity : 1 ,
243340 left : x ,
244341 top : y ,
245- minWidth : "180px" ,
246342 marginTop : "20px" ,
247343 marginLeft : "20px" ,
248344 display : "flex" ,
249345 } }
250346 >
251- < div style = { { paddingRight : "10px" } } >
347+ < div style = { { paddingRight : "10px" } } >
252348 < b > MMSI:</ b >
253349 { object . mmsi }
254350 < br />
@@ -265,14 +361,14 @@ const MlIconLayer = (props) => {
265361 < br />
266362 </ >
267363 ) }
268- < b > Speed:</ b >
364+ < b > Speed: </ b >
269365 { object . velocity } kn (
270366 { Math . round ( object . velocity * 1.852 * 100 ) / 100 } km/h)
271367 < br />
272368 < b > Position accuracy: </ b >
273369 { object . accurancy ? "high" : "low" }
274370 < br />
275- < br />
371+ < br />
276372 { ! vesselInfo ? (
277373 < b > click on ship to get more info...</ b >
278374 ) : (
0 commit comments