Only show movement trails for stations that are actually moving
- Add minimum movement threshold (100m) to skip stationary stations - Add max hop distance (10km) to break trails at igate-hopping artifacts - Require 3+ points per segment to filter noise from 2-point jumps
This commit is contained in:
parent
b875b7ab9e
commit
33799f3f33
2 changed files with 50 additions and 13 deletions
|
|
@ -48,6 +48,9 @@ export class TrailManager {
|
||||||
"#20B2AA", // Light Sea Green
|
"#20B2AA", // Light Sea Green
|
||||||
];
|
];
|
||||||
private proximityThreshold: number = 5.5; // kilometers
|
private proximityThreshold: number = 5.5; // kilometers
|
||||||
|
private minMovementThreshold: number = 0.1; // km - minimum total distance to show a trail
|
||||||
|
private maxHopDistance: number = 10; // km - max distance between consecutive points before breaking
|
||||||
|
private minSegmentPoints: number = 3; // minimum points in a segment to draw it
|
||||||
|
|
||||||
constructor(trailLayer: LayerGroup, trailDuration: number = 60 * 60 * 1000) {
|
constructor(trailLayer: LayerGroup, trailDuration: number = 60 * 60 * 1000) {
|
||||||
this.trailLayer = trailLayer;
|
this.trailLayer = trailLayer;
|
||||||
|
|
@ -320,9 +323,8 @@ export class TrailManager {
|
||||||
// Create new trail if we have at least 2 positions with valid coordinates
|
// Create new trail if we have at least 2 positions with valid coordinates
|
||||||
if (trailState.positions.length >= 2) {
|
if (trailState.positions.length >= 2) {
|
||||||
// Filter out positions with invalid coordinates and create coordinate pairs
|
// Filter out positions with invalid coordinates and create coordinate pairs
|
||||||
const latLngs: [number, number][] = trailState.positions
|
const validPositions: [number, number][] = trailState.positions
|
||||||
.filter((pos) => {
|
.filter((pos) => {
|
||||||
// Validate coordinates are finite numbers within valid ranges
|
|
||||||
return (
|
return (
|
||||||
pos &&
|
pos &&
|
||||||
typeof pos.lat === "number" &&
|
typeof pos.lat === "number" &&
|
||||||
|
|
@ -339,9 +341,47 @@ export class TrailManager {
|
||||||
})
|
})
|
||||||
.map((pos) => [pos.lat, pos.lng]);
|
.map((pos) => [pos.lat, pos.lng]);
|
||||||
|
|
||||||
// Only create trail if we still have at least 2 valid positions after filtering
|
// Calculate total path distance to determine if station is actually moving
|
||||||
if (latLngs.length >= 2) {
|
let totalDistance = 0;
|
||||||
// Assign color if not already assigned
|
for (let i = 1; i < validPositions.length; i++) {
|
||||||
|
totalDistance += this.calculateDistance(
|
||||||
|
validPositions[i - 1][0],
|
||||||
|
validPositions[i - 1][1],
|
||||||
|
validPositions[i][0],
|
||||||
|
validPositions[i][1],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip trail if station hasn't moved enough (stationary station)
|
||||||
|
if (totalDistance < this.minMovementThreshold) {
|
||||||
|
trailState.dots = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Break trail into segments at giant jumps (bad data / different igates)
|
||||||
|
const segments: [number, number][][] = [[]];
|
||||||
|
segments[0].push(validPositions[0]);
|
||||||
|
for (let i = 1; i < validPositions.length; i++) {
|
||||||
|
const hopDist = this.calculateDistance(
|
||||||
|
validPositions[i - 1][0],
|
||||||
|
validPositions[i - 1][1],
|
||||||
|
validPositions[i][0],
|
||||||
|
validPositions[i][1],
|
||||||
|
);
|
||||||
|
if (hopDist > this.maxHopDistance) {
|
||||||
|
// Start a new segment
|
||||||
|
segments.push([validPositions[i]]);
|
||||||
|
} else {
|
||||||
|
segments[segments.length - 1].push(validPositions[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw only segments with enough points to indicate real movement
|
||||||
|
const drawableSegments = segments.filter(
|
||||||
|
(seg) => seg.length >= this.minSegmentPoints,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (drawableSegments.length > 0) {
|
||||||
if (!trailState.color) {
|
if (!trailState.color) {
|
||||||
trailState.color = this.assignTrailColor(
|
trailState.color = this.assignTrailColor(
|
||||||
baseCallsign,
|
baseCallsign,
|
||||||
|
|
@ -349,8 +389,6 @@ export class TrailManager {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create polyline with assigned color
|
|
||||||
// For historical positions (immediate=true), use higher opacity for better visibility
|
|
||||||
const polylineOptions: PolylineOptions = {
|
const polylineOptions: PolylineOptions = {
|
||||||
color: trailState.color,
|
color: trailState.color,
|
||||||
weight: 3,
|
weight: 3,
|
||||||
|
|
@ -362,9 +400,11 @@ export class TrailManager {
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
trailState.trail = L.polyline(latLngs, polylineOptions).addTo(
|
// L.polyline accepts an array of arrays for multi-segment lines
|
||||||
this.trailLayer,
|
trailState.trail = L.polyline(
|
||||||
);
|
drawableSegments,
|
||||||
|
polylineOptions,
|
||||||
|
).addTo(this.trailLayer);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
"Error creating trail polyline for",
|
"Error creating trail polyline for",
|
||||||
|
|
@ -372,11 +412,9 @@ export class TrailManager {
|
||||||
":",
|
":",
|
||||||
error,
|
error,
|
||||||
);
|
);
|
||||||
console.error("Invalid coordinates:", latLngs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't create additional dots here since historical positions are now shown as markers
|
|
||||||
trailState.dots = [];
|
trailState.dots = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Slideover panel responsive styles */
|
/* Slideover panel responsive styles */
|
||||||
body.map-page .slideover-panel {
|
body.map-page .slideover-panel {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue