Moar federation
This commit is contained in:
parent
fe1b5f8be0
commit
889c5336bf
2 changed files with 91 additions and 105 deletions
|
|
@ -90,7 +90,8 @@ public class ActivityImageService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Draw the track outline from high-resolution track points.
|
||||
* Draw the track outline from high-resolution track points with privacy protection.
|
||||
* Fades in/out the first and last 300 meters to hide start/end locations.
|
||||
*/
|
||||
private void drawTrack(Graphics2D g2d, Activity activity, int width, int height) {
|
||||
List<Map<String, Object>> trackPoints = parseTrackPoints(activity.getTrackPointsJson());
|
||||
|
|
@ -98,6 +99,10 @@ public class ActivityImageService {
|
|||
return;
|
||||
}
|
||||
|
||||
// Calculate cumulative distances along the track
|
||||
double[] cumulativeDistances = calculateCumulativeDistances(trackPoints);
|
||||
double totalDistance = cumulativeDistances[cumulativeDistances.length - 1];
|
||||
|
||||
// Find bounds
|
||||
double minLat = Double.MAX_VALUE, maxLat = -Double.MAX_VALUE;
|
||||
double minLon = Double.MAX_VALUE, maxLon = -Double.MAX_VALUE;
|
||||
|
|
@ -129,38 +134,50 @@ public class ActivityImageService {
|
|||
double scaleY = trackHeight / (maxLat - minLat);
|
||||
double scale = Math.min(scaleX, scaleY);
|
||||
|
||||
// Create path
|
||||
Path2D.Double path = new Path2D.Double();
|
||||
boolean first = true;
|
||||
|
||||
for (Map<String, Object> point : trackPoints) {
|
||||
Double lat = getDouble(point, "latitude");
|
||||
Double lon = getDouble(point, "longitude");
|
||||
if (lat != null && lon != null) {
|
||||
double x = (lon - minLon) * scale;
|
||||
double y = trackHeight - (lat - minLat) * scale; // Flip Y axis
|
||||
|
||||
if (first) {
|
||||
path.moveTo(x, y);
|
||||
first = false;
|
||||
} else {
|
||||
path.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw track
|
||||
g2d.setColor(new Color(0, 180, 216)); // Bright blue
|
||||
// Draw track segments with privacy fade
|
||||
g2d.setStroke(new BasicStroke(4.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
|
||||
g2d.draw(path);
|
||||
|
||||
// Draw start and end markers
|
||||
if (!trackPoints.isEmpty()) {
|
||||
Map<String, Object> firstPoint = trackPoints.get(0);
|
||||
Map<String, Object> lastPoint = trackPoints.get(trackPoints.size() - 1);
|
||||
final double FADE_DISTANCE = 300.0; // 300 meters fade zone
|
||||
|
||||
drawMarker(g2d, firstPoint, minLat, minLon, scale, trackHeight, new Color(0, 255, 0)); // Green start
|
||||
drawMarker(g2d, lastPoint, minLat, minLon, scale, trackHeight, new Color(255, 0, 0)); // Red end
|
||||
for (int i = 0; i < trackPoints.size() - 1; i++) {
|
||||
Map<String, Object> point1 = trackPoints.get(i);
|
||||
Map<String, Object> point2 = trackPoints.get(i + 1);
|
||||
|
||||
Double lat1 = getDouble(point1, "latitude");
|
||||
Double lon1 = getDouble(point1, "longitude");
|
||||
Double lat2 = getDouble(point2, "latitude");
|
||||
Double lon2 = getDouble(point2, "longitude");
|
||||
|
||||
if (lat1 != null && lon1 != null && lat2 != null && lon2 != null) {
|
||||
double x1 = (lon1 - minLon) * scale;
|
||||
double y1 = trackHeight - (lat1 - minLat) * scale;
|
||||
double x2 = (lon2 - minLon) * scale;
|
||||
double y2 = trackHeight - (lat2 - minLat) * scale;
|
||||
|
||||
// Calculate opacity based on distance from start/end
|
||||
double distanceFromStart = cumulativeDistances[i];
|
||||
double distanceFromEnd = totalDistance - cumulativeDistances[i];
|
||||
|
||||
// Calculate fade opacity (0.0 to 1.0)
|
||||
float opacity = 1.0f;
|
||||
|
||||
if (distanceFromStart < FADE_DISTANCE) {
|
||||
// Fade in from start
|
||||
opacity = Math.min(opacity, (float) (distanceFromStart / FADE_DISTANCE));
|
||||
}
|
||||
|
||||
if (distanceFromEnd < FADE_DISTANCE) {
|
||||
// Fade out at end
|
||||
opacity = Math.min(opacity, (float) (distanceFromEnd / FADE_DISTANCE));
|
||||
}
|
||||
|
||||
// Apply opacity to track color
|
||||
int alpha = Math.max(0, Math.min(255, (int) (opacity * 255)));
|
||||
g2d.setColor(new Color(0, 180, 216, alpha)); // Bright blue with alpha
|
||||
|
||||
// Draw segment
|
||||
g2d.drawLine((int) x1, (int) y1, (int) x2, (int) y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -175,25 +192,50 @@ public class ActivityImageService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Draw a circular marker at a track point.
|
||||
* Calculate cumulative distances along the track using Haversine formula.
|
||||
* Returns an array where each element is the total distance from the start to that point.
|
||||
*/
|
||||
private void drawMarker(Graphics2D g2d, Map<String, Object> point, double minLat, double minLon,
|
||||
double scale, int trackHeight, Color color) {
|
||||
Double lat = getDouble(point, "latitude");
|
||||
Double lon = getDouble(point, "longitude");
|
||||
if (lat != null && lon != null) {
|
||||
double x = (lon - minLon) * scale;
|
||||
double y = trackHeight - (lat - minLat) * scale;
|
||||
private double[] calculateCumulativeDistances(List<Map<String, Object>> trackPoints) {
|
||||
double[] distances = new double[trackPoints.size()];
|
||||
distances[0] = 0.0;
|
||||
|
||||
g2d.setColor(color);
|
||||
int markerSize = 12;
|
||||
g2d.fillOval((int) x - markerSize / 2, (int) y - markerSize / 2, markerSize, markerSize);
|
||||
for (int i = 1; i < trackPoints.size(); i++) {
|
||||
Map<String, Object> point1 = trackPoints.get(i - 1);
|
||||
Map<String, Object> point2 = trackPoints.get(i);
|
||||
|
||||
// White outline
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.setStroke(new BasicStroke(2.0f));
|
||||
g2d.drawOval((int) x - markerSize / 2, (int) y - markerSize / 2, markerSize, markerSize);
|
||||
Double lat1 = getDouble(point1, "latitude");
|
||||
Double lon1 = getDouble(point1, "longitude");
|
||||
Double lat2 = getDouble(point2, "latitude");
|
||||
Double lon2 = getDouble(point2, "longitude");
|
||||
|
||||
if (lat1 != null && lon1 != null && lat2 != null && lon2 != null) {
|
||||
double segmentDistance = haversineDistance(lat1, lon1, lat2, lon2);
|
||||
distances[i] = distances[i - 1] + segmentDistance;
|
||||
} else {
|
||||
distances[i] = distances[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
return distances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate distance between two GPS coordinates using Haversine formula.
|
||||
* Returns distance in meters.
|
||||
*/
|
||||
private double haversineDistance(double lat1, double lon1, double lat2, double lon2) {
|
||||
final double EARTH_RADIUS = 6371000.0; // Earth radius in meters
|
||||
|
||||
double dLat = Math.toRadians(lat2 - lat1);
|
||||
double dLon = Math.toRadians(lon2 - lon1);
|
||||
|
||||
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
|
||||
Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||
|
||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
|
||||
return EARTH_RADIUS * c;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -270,71 +270,15 @@ function createActivityMap(containerId, geoJsonData, options = {}) {
|
|||
|
||||
/**
|
||||
* Add start and finish markers to the map
|
||||
* PRIVACY: This function is deprecated and does nothing.
|
||||
* Start/finish markers are no longer displayed to protect athlete privacy.
|
||||
*
|
||||
* @param {Object} map - Leaflet map instance
|
||||
* @param {Object} geoJsonData - GeoJSON track data
|
||||
*/
|
||||
function addStartFinishMarkers(map, geoJsonData) {
|
||||
if (!geoJsonData) {
|
||||
return;
|
||||
}
|
||||
|
||||
let coordinates;
|
||||
|
||||
// Handle both LineString and FeatureCollection
|
||||
if (geoJsonData.type === 'LineString') {
|
||||
coordinates = geoJsonData.coordinates;
|
||||
} else if (geoJsonData.type === 'Feature') {
|
||||
coordinates = geoJsonData.geometry.coordinates;
|
||||
} else if (geoJsonData.type === 'FeatureCollection' && geoJsonData.features && geoJsonData.features.length > 0) {
|
||||
coordinates = geoJsonData.features[0].geometry.coordinates;
|
||||
}
|
||||
|
||||
if (!coordinates || coordinates.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start marker (green)
|
||||
const startCoord = coordinates[0];
|
||||
const startMarker = L.marker([startCoord[1], startCoord[0]], {
|
||||
icon: L.divIcon({
|
||||
className: 'start-finish-marker',
|
||||
html: `<div style="
|
||||
background-color: #10b981;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid white;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
"></div>`,
|
||||
iconSize: [24, 24],
|
||||
iconAnchor: [12, 12]
|
||||
}),
|
||||
title: 'Start'
|
||||
}).addTo(map);
|
||||
|
||||
startMarker.bindPopup('<strong>Start</strong>');
|
||||
|
||||
// Finish marker (red)
|
||||
const finishCoord = coordinates[coordinates.length - 1];
|
||||
const finishMarker = L.marker([finishCoord[1], finishCoord[0]], {
|
||||
icon: L.divIcon({
|
||||
className: 'start-finish-marker',
|
||||
html: `<div style="
|
||||
background-color: #ef4444;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid white;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
"></div>`,
|
||||
iconSize: [24, 24],
|
||||
iconAnchor: [12, 12]
|
||||
}),
|
||||
title: 'Finish'
|
||||
}).addTo(map);
|
||||
|
||||
finishMarker.bindPopup('<strong>Finish</strong>');
|
||||
// Privacy protection: Do not show start/end markers to hide athlete home locations
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue