Skip to content

Commit

Permalink
nice map and vessels
Browse files Browse the repository at this point in the history
  • Loading branch information
mikkelklitlund committed Oct 16, 2024
1 parent 926b8f6 commit 8c7b533
Show file tree
Hide file tree
Showing 14 changed files with 1,281 additions and 120 deletions.
1,064 changes: 1,055 additions & 9 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,31 @@
"@grpc-web/proxy": "^0.1.0",
"@improbable-eng/grpc-web": "^0.15.0",
"concurrently": "^9.0.1",
"leaflet": "^1.9.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-leaflet": "^4.2.1",
"react-router-dom": "^6.26.2",
"rxjs": "^7.8.1",
"ts-proto": "^2.2.3"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
"@types/geojson": "^7946.0.14",
"@types/leaflet": "^1.9.12",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-leaflet": "^2.8.3",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.20",
"eslint": "^9.9.0",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.9",
"globals": "^15.9.0",
"postcss": "^8.4.47",
"tailwindcss": "^3.4.14",
"typescript": "^5.5.3",
"typescript-eslint": "^8.0.1",
"vite": "^5.4.1"
}
}
}
6 changes: 6 additions & 0 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
5 changes: 5 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,8 @@
.read-the-docs {
color: #888;
}

.rotate-90 {
position: absolute;
transform: rotate(90deg) !important;
}
1 change: 1 addition & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AppContextProvider, useAppContext } from './contexts/appcontext'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import VesselMapPage from './pages/vesselMapPage'
import { VesselGuiContextProvider } from './contexts/vesselGuiContext'
import 'leaflet/dist/leaflet.css'

function App() {
const [count, setCount] = useState(0)
Expand Down
42 changes: 42 additions & 0 deletions src/components/map.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { MapContainer, TileLayer, Marker, Popup, useMap } from 'react-leaflet'

import L from 'leaflet'

const ComponentResize = () => {
const map = useMap()

setTimeout(() => {
map.invalidateSize()
}, 0)

return null
}

interface IMap {
children: React.ReactNode
}

const LMap = ({ children }: IMap) => {
return (
<MapContainer
style={{
height: '100%',
width: '100%',
}}
center={[56.15674, 10.21076]}
attributionControl={true}
zoom={8}
minZoom={3}
scrollWheelZoom={true}
>
<ComponentResize />
<TileLayer
attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{children}
</MapContainer>
)
}

export default LMap
24 changes: 12 additions & 12 deletions src/components/popup.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { IDetailedVessel } from '../models/detailedVessel';
import { IDetailedVessel } from '../models/detailedVessel'

interface IPopupProps {
vessel: IDetailedVessel;
vessel: IDetailedVessel
}

export default function Popup({ vessel }: IPopupProps) {
return (
<div>
<h2>ID: {vessel.id}</h2>
<p>Name: {vessel.name}</p>
<p>Callsign: {vessel.callSign}</p>
<p>Length: {vessel.length}</p>
<p>pos fixing device: {vessel.positionFixingDevice}</p>
<p>MMSI: {vessel.mmsi}</p>
</div>
);
return (
<div>
<h2>ID: {vessel.id}</h2>
<p>Name: {vessel.name}</p>
<p>Callsign: {vessel.callSign}</p>
<p>Length: {vessel.length}</p>
<p>pos fixing device: {vessel.positionFixingDevice}</p>
<p>MMSI: {vessel.mmsi}</p>
</div>
)
}
34 changes: 34 additions & 0 deletions src/components/vesselMarker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ISimpleVessel } from '../models/simpleVessel'
import L from 'leaflet'
import { MapContainer, TileLayer, Marker, Popup as LPopup, useMap } from 'react-leaflet'
import Popup from './popup'
import IVesselDetail from '../models/detailedVessel'
import { useState } from 'react'

interface IVesselMarker {
vessel: ISimpleVessel
}

export default function VesselMarker({ vessel }: IVesselMarker) {
const [vesselDetails, setVesselDetails] = useState<IVesselDetail | undefined>(undefined)

const icon = L.divIcon({
className: 'custom-div-icon',
html: `<div style='width: 0; height: 0; border-left: 10px solid transparent; border-right: 10px solid transparent; border-bottom: 40px solid #c30b82; transform: rotate(${
vessel.location.heading || 0
}deg) translate(-5px, -20px);'></div>
`,
iconAnchor: [0, 0],
popupAnchor: [0, -25],
})

return (
<Marker position={[vessel.location.point.lat, vessel.location.point.lon]} icon={icon}>
{vesselDetails && (
<LPopup>
<Popup vessel={vesselDetails} />
</LPopup>
)}
</Marker>
)
}
113 changes: 50 additions & 63 deletions src/contexts/vesselGuiContext.tsx
Original file line number Diff line number Diff line change
@@ -1,75 +1,62 @@
import { createContext, useContext, useState } from 'react';
import { ISelectionArea } from '../models/selectionArea';
import { createContext, useContext, useState } from 'react'
import { ISelectionArea } from '../models/selectionArea'

enum ActiveGuiTool {
ZoomIn,
ZoomOut,
Rectangle,
Lasso,
Mouse
ZoomIn,
ZoomOut,
Rectangle,
Lasso,
Mouse,
}

interface IVesselGuiContext {
pathHistory: boolean;
setPathHistory: React.Dispatch<React.SetStateAction<boolean>>;
pathForecast: boolean;
setPathForecast: React.Dispatch<React.SetStateAction<boolean>>;
activeTool: ActiveGuiTool;
setActiveTool: React.Dispatch<React.SetStateAction<ActiveGuiTool>>;
activeTime: Date;
setActiveTime: React.Dispatch<React.SetStateAction<Date>>;
selectionArea?: ISelectionArea;
setSelectionArea: React.Dispatch<
React.SetStateAction<ISelectionArea | undefined>
>;
pathHistory: boolean
setPathHistory: React.Dispatch<React.SetStateAction<boolean>>
pathForecast: boolean
setPathForecast: React.Dispatch<React.SetStateAction<boolean>>
activeTool: ActiveGuiTool
setActiveTool: React.Dispatch<React.SetStateAction<ActiveGuiTool>>
activeTime: Date
setActiveTime: React.Dispatch<React.SetStateAction<Date>>
selectionArea?: ISelectionArea
setSelectionArea: React.Dispatch<React.SetStateAction<ISelectionArea | undefined>>
}

const VesselGuiContext = createContext<IVesselGuiContext | undefined>(
undefined
);
const VesselGuiContext = createContext<IVesselGuiContext | undefined>(undefined)

export const useVesselGuiContext = () => {
const context = useContext(VesselGuiContext);
if (context === undefined) {
throw new Error(
'useVesselGuiContext must be used within a AppContextProvider'
);
}
return context;
};
const context = useContext(VesselGuiContext)
if (context === undefined) {
throw new Error('useVesselGuiContext must be used within a AppContextProvider')
}
return context
}

export default VesselGuiContext;
export default VesselGuiContext

export const VesselGuiContextProvider = ({
children
}: {
children: React.ReactNode;
}) => {
const [pathHistory, setPathHistory] = useState<boolean>(false);
const [pathForecast, setPathForecast] = useState<boolean>(false);
const [activeTool, setActiveTool] = useState<ActiveGuiTool>(
ActiveGuiTool.Mouse
);
const [activeTime, setActiveTime] = useState<Date>(new Date());
const [selectionArea, setSelectionArea] = useState<
ISelectionArea | undefined
>();
export const VesselGuiContextProvider = ({ children }: { children: React.ReactNode }) => {
const [pathHistory, setPathHistory] = useState<boolean>(false)
const [pathForecast, setPathForecast] = useState<boolean>(false)
const [activeTool, setActiveTool] = useState<ActiveGuiTool>(ActiveGuiTool.Mouse)
const [activeTime, setActiveTime] = useState<Date>(new Date())
const [selectionArea, setSelectionArea] = useState<ISelectionArea | undefined>()

return (
<VesselGuiContext.Provider
value={{
pathHistory,
setPathHistory,
pathForecast,
setPathForecast,
activeTool,
setActiveTool,
activeTime,
setActiveTime,
selectionArea,
setSelectionArea
}}>
{children}
</VesselGuiContext.Provider>
);
};
return (
<VesselGuiContext.Provider
value={{
pathHistory,
setPathHistory,
pathForecast,
setPathForecast,
activeTool,
setActiveTool,
activeTime,
setActiveTime,
selectionArea,
setSelectionArea,
}}
>
{children}
</VesselGuiContext.Provider>
)
}
4 changes: 4 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
Expand Down
8 changes: 5 additions & 3 deletions src/models/monitoredVessel.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
export interface IMonitoredVessel {
mmsi: number
import { ILocation } from "./location"
import { ISimpleVessel } from "./simpleVessel"

export interface IMonitoredVessel extends ISimpleVessel {
trustwortiness: number
reason?: string
}

export default class MonitoredVessel implements IMonitoredVessel {
constructor(public mmsi: number, public trustwortiness: number, public reason?: string) {}
constructor(public mmsi: number, public trustwortiness: number, public location: ILocation, public reason?: string) { }
}
3 changes: 0 additions & 3 deletions src/models/ship.ts

This file was deleted.

Loading

0 comments on commit 8c7b533

Please sign in to comment.