Skip to content

Commit 6c3d9d5

Browse files
authored
Merge pull request #15 from mapcomponents/design/MlIconLayer
Design/ml icon layer
2 parents a630db2 + 6841a09 commit 6c3d9d5

File tree

9 files changed

+776
-27
lines changed

9 files changed

+776
-27
lines changed

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@
4040
"@fontsource/roboto": "^4.5.8",
4141
"@mapbox/mapbox-gl-draw": "^1.2.0",
4242
"@mapbox/mapbox-gl-sync-move": "^0.3.0",
43-
"@mapcomponents/react-maplibre": "^0.1.87",
43+
"@mapcomponents/react-maplibre": "^1.0.9",
4444
"@material-ui/core": "^4.12.4",
4545
"@material-ui/icons": "^4.11.3",
46-
"@mui/icons-material": "5.11.0",
47-
"@mui/material": "5.11.6",
48-
"@mui/styles": "^5.13.2",
46+
"@mui/icons-material": "6.4.0",
47+
"@mui/material": "6.4.0",
48+
"@mui/styles": "^6.4.0",
4949
"@nivo/core": "^0.69.0",
5050
"@nivo/line": "^0.69.1",
5151
"@testing-library/jest-dom": "^5.11.4",

public/mapcomponents-logo.svg

Lines changed: 24 additions & 0 deletions
Loading

src/components/MlIconLayer/MlIconLayer.js

Lines changed: 110 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import React, { useRef, useMemo, useEffect, useState, useContext } from "react";
22
import * 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

610
import { MapboxLayer } from "@deck.gl/mapbox";
711
import { IconLayer } from "@deck.gl/layers";
812

913
import DeckGlContext from "../../deckgl_components/DeckGlContext";
1014
import getShipType from "./utils/getShipType";
11-
import Ships from "./assets/Ships_v2.png";
12-
15+
import Ships from "./assets/Ships.png";
1316

1417
const 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

Comments
 (0)