2018-07-19 11:01:31 -04:00
|
|
|
import 'ol/ol.css';
|
|
|
|
import {Map as olMap, View} from 'ol';
|
|
|
|
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
|
|
|
|
import {OSM, Vector as VectorSource} from 'ol/source';
|
|
|
|
import Feature from 'ol/Feature';
|
|
|
|
import {fromLonLat} from 'ol/proj';
|
|
|
|
import {Icon, Stroke, Style} from 'ol/style';
|
|
|
|
import Projection from 'ol/proj/Projection';
|
|
|
|
import {LineString, Point} from 'ol/geom';
|
|
|
|
|
|
|
|
import {readFileSync} from 'fs';
|
2018-07-19 11:49:55 -04:00
|
|
|
const packetLog = readFileSync(__dirname + '/../IS_packets.txt', 'utf-8');
|
2018-07-19 11:01:31 -04:00
|
|
|
|
|
|
|
import {APRSParser} from 'aprs-parser';
|
|
|
|
|
|
|
|
import icon from "./arrow.png";
|
|
|
|
|
|
|
|
let tile_layer = new TileLayer({source: new OSM()});
|
|
|
|
|
|
|
|
let map = new olMap({
|
|
|
|
target: 'map',
|
|
|
|
layers: [
|
2018-07-19 13:18:41 -04:00
|
|
|
tile_layer
|
2018-07-19 11:01:31 -04:00
|
|
|
],
|
|
|
|
view: new View({
|
|
|
|
center: fromLonLat([-72.15, 43.90]),
|
|
|
|
zoom: 10
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
let colorGen = {
|
|
|
|
hues: null,
|
|
|
|
get: function (totalNum) {
|
|
|
|
if (this.hues === null) {
|
|
|
|
let mult = Math.floor(360 / totalNum);
|
|
|
|
this.hues = Array.from(Array(totalNum).keys())
|
|
|
|
.map(x => x * mult);
|
|
|
|
|
|
|
|
// Shuffle (this is not a great shuffle, but I don't care)
|
|
|
|
this.hues.forEach((current, index, arr) => {
|
|
|
|
let randomIndex = Math.floor(Math.random() * index);
|
|
|
|
[arr[index], arr[randomIndex]] =
|
|
|
|
[arr[randomIndex], arr[index]];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return this.hues.pop();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-07-19 11:49:55 -04:00
|
|
|
function pathStyle(feature) {
|
|
|
|
let styles = [
|
|
|
|
new Style({stroke: new Stroke(
|
|
|
|
{color: 'hsl(' + feature.getProperties().hue + ', 75%, 50%)', width: 2}
|
|
|
|
)})
|
|
|
|
];
|
2018-07-19 11:01:31 -04:00
|
|
|
|
2018-07-19 11:49:55 -04:00
|
|
|
feature.getGeometry().forEachSegment((start, end) => {
|
|
|
|
let dx = end[0] - start[0];
|
|
|
|
let dy = end[1] - start[1];
|
|
|
|
let rotation = Math.atan2(dy, dx);
|
|
|
|
// arrows
|
|
|
|
styles.push(new Style({
|
|
|
|
geometry: new Point(end),
|
|
|
|
image: new Icon({
|
|
|
|
src: icon,
|
|
|
|
anchor: [0.75, 0.5],
|
|
|
|
rotateWithView: true,
|
|
|
|
rotation: -rotation
|
|
|
|
})
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
return styles;
|
|
|
|
}
|
|
|
|
|
|
|
|
function plotPaths(packets) {
|
2018-07-19 13:18:41 -04:00
|
|
|
let vector_layer = new VectorLayer({source: new VectorSource()});
|
|
|
|
map.addLayer(vector_layer);
|
|
|
|
|
2018-07-19 11:49:55 -04:00
|
|
|
packets
|
2018-07-19 13:18:41 -04:00
|
|
|
.filter(packet => packet.date > new Date("2018-07-14") && packet.date < new Date("2018-07-15"))
|
2018-07-19 11:49:55 -04:00
|
|
|
// filter by callsign
|
|
|
|
.filter(packet => (packet.from !== undefined) &&
|
|
|
|
(packet.from.toString() === "W1HS-9"))
|
|
|
|
// filter to just positional data
|
|
|
|
.filter(packet => 'data' in packet && 'latitude' in packet.data)
|
|
|
|
// join into Arrays of points
|
|
|
|
.reduce((acc, packet) => {
|
|
|
|
if (!acc.has(packet.from.toString())) acc.set(packet.from.toString(), []);
|
|
|
|
acc.get(packet.from.toString()).push([packet.data.longitude,
|
|
|
|
packet.data.latitude]);
|
|
|
|
return acc;
|
|
|
|
}, new Map())
|
|
|
|
// plot on map
|
|
|
|
.forEach((points, callsign, map) => {
|
|
|
|
let pathFeature = new Feature({
|
|
|
|
geometry: new LineString(points),
|
|
|
|
hue: colorGen.get(map.size)
|
|
|
|
});
|
2018-07-19 11:01:31 -04:00
|
|
|
|
2018-07-19 11:49:55 -04:00
|
|
|
pathFeature.setStyle(pathStyle);
|
2018-07-19 11:01:31 -04:00
|
|
|
|
2018-07-19 11:49:55 -04:00
|
|
|
vector_layer.getSource().addFeature(pathFeature);
|
|
|
|
pathFeature.getGeometry().transform(new Projection({code: "EPSG:4326"}),
|
|
|
|
tile_layer.getSource().getProjection());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-07-19 13:18:41 -04:00
|
|
|
function parsePackets(packetLog) {
|
|
|
|
let parser = new APRSParser();
|
|
|
|
return packetLog.trim().split("\n")
|
|
|
|
// parse to Date and APRS packet
|
|
|
|
.map(line => {
|
|
|
|
let packet = parser.parse(line.slice(29));
|
|
|
|
packet.date = new Date(line.slice(0,18));
|
|
|
|
return packet;
|
|
|
|
});
|
|
|
|
}
|
2018-07-19 11:01:31 -04:00
|
|
|
|
2018-07-19 13:18:41 -04:00
|
|
|
let packets = parsePackets(packetLog);
|
2018-07-19 11:49:55 -04:00
|
|
|
plotPaths(packets);
|