-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathserver.js
More file actions
122 lines (104 loc) · 3.61 KB
/
server.js
File metadata and controls
122 lines (104 loc) · 3.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
const express = require('express');
const cors = require('cors');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(cors());
app.use(express.json());
app.use(express.static(__dirname));
app.get('/api/health', (req, res) => {
res.json({ ok: true });
});
// Simple SSE stream that emits ship positions along a predefined route (fallback demo)
app.get('/api/ship/stream', (req, res) => {
const mmsi = String(req.query.mmsi || '000000000');
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders && res.flushHeaders();
// MMSI-specific demo routes (water-safe corridors)
const routesByMmsi = {
'211331640': [
{ lat: 53.5461, lng: 9.9662 },
{ lat: 51.0, lng: 2.0 },
{ lat: 48.7, lng: -4.5 },
{ lat: 36.0, lng: -5.5 },
{ lat: 31.2, lng: 32.3 },
{ lat: 29.9, lng: 32.55 },
{ lat: 12.5, lng: 43.2 },
{ lat: 15.0, lng: 46.0 },
{ lat: 9.0, lng: 70.0 },
{ lat: 5.0, lng: 90.0 },
{ lat: 1.26, lng: 103.84 },
{ lat: 10.0, lng: 112.0 },
{ lat: 22.5, lng: 120.5 },
{ lat: 31.2304, lng: 121.4737 }
],
'366982000': [
{ lat: 33.7329, lng: -118.2710 },
{ lat: 25.0, lng: -112.0 },
{ lat: 8.95, lng: -79.55 },
{ lat: 9.35, lng: -79.9 },
{ lat: 20.0, lng: -78.0 },
{ lat: 25.7781, lng: -80.1794 },
{ lat: 31.0, lng: -76.0 },
{ lat: 40.6711, lng: -74.0456 }
],
'DEFAULT': [
{ lat: 35.6762, lng: 139.6503 },
{ lat: 35.0951, lng: 129.0390 },
{ lat: 22.3193, lng: 114.1694 },
{ lat: 1.2966, lng: 103.7764 },
{ lat: -15.0, lng: 70.0 },
{ lat: -36.0, lng: 20.0 },
{ lat: 51.4631, lng: 0.3336 }
]
};
const route = routesByMmsi[mmsi] || routesByMmsi.DEFAULT;
let legIndex = 0;
let t = 0; // 0..1 along current leg
const legDurationMs = 60000; // 60s per leg (slower)
let lastTick = Date.now();
const send = (obj) => {
res.write(`data: ${JSON.stringify(obj)}\n\n`);
};
send({ type: 'hello', mmsi });
const interval = setInterval(() => {
const now = Date.now();
const dt = now - lastTick;
lastTick = now;
const from = route[legIndex];
const to = route[legIndex + 1] || route[legIndex];
if (!to) return;
t += dt / legDurationMs;
if (t > 1) {
t = 0;
legIndex++;
if (legIndex >= route.length - 1) legIndex = 0; // loop
}
const lat = from.lat + (to.lat - from.lat) * t;
const lng = from.lng + (to.lng - from.lng) * t;
// Rough course and speed
const course = Math.atan2(
(to.lng - from.lng) * Math.cos((from.lat + to.lat) * Math.PI / 360),
(to.lat - from.lat)
) * 180 / Math.PI;
const speed = 18 + Math.sin(now / 5000) * 2; // ~18±2 knots demo
send({
type: 'position',
mmsi,
name: 'Live Vessel',
lat,
lng,
speed: Number(speed.toFixed(1)),
course: Math.round((course + 360) % 360),
timestamp: new Date().toISOString()
});
}, 1000);
req.on('close', () => {
clearInterval(interval);
res.end();
});
});
app.listen(PORT, () => {
console.log(`Server listening on http://localhost:${PORT}`);
});