Render smoother findmydevice history paths

This commit is contained in:
seo
2026-06-17 19:42:59 +09:00
parent 8bbe43b8c4
commit 938868ea64
3 changed files with 173 additions and 79 deletions
+132
View File
@@ -221,6 +221,136 @@ if (isset($historyData[0])) {
}
}
function pathDistanceM(array $a, array $b): float
{
$earth = 6371000;
$lat1 = deg2rad((float)$a['lat']);
$lat2 = deg2rad((float)$b['lat']);
$dLat = $lat2 - $lat1;
$dLng = deg2rad((float)$b['lng'] - (float)$a['lng']);
$h = sin($dLat / 2) ** 2 + cos($lat1) * cos($lat2) * sin($dLng / 2) ** 2;
return 2 * $earth * asin(min(1, sqrt($h)));
}
function pointLineDistanceM(array $point, array $start, array $end): float
{
$latScale = 111320;
$lngScale = 111320 * cos(deg2rad((float)$point['lat']));
$px = (float)$point['lng'] * $lngScale;
$py = (float)$point['lat'] * $latScale;
$sx = (float)$start['lng'] * $lngScale;
$sy = (float)$start['lat'] * $latScale;
$ex = (float)$end['lng'] * $lngScale;
$ey = (float)$end['lat'] * $latScale;
$dx = $ex - $sx;
$dy = $ey - $sy;
if ($dx == 0.0 && $dy == 0.0) {
return sqrt(($px - $sx) ** 2 + ($py - $sy) ** 2);
}
$t = max(0, min(1, (($px - $sx) * $dx + ($py - $sy) * $dy) / ($dx ** 2 + $dy ** 2)));
$cx = $sx + $t * $dx;
$cy = $sy + $t * $dy;
return sqrt(($px - $cx) ** 2 + ($py - $cy) ** 2);
}
function douglasPeucker(array $points, float $epsilon): array
{
$count = count($points);
if ($count <= 2) {
return $points;
}
$maxDistance = 0.0;
$index = 0;
$last = $count - 1;
for ($i = 1; $i < $last; $i++) {
$distance = pointLineDistanceM($points[$i], $points[0], $points[$last]);
if ($distance > $maxDistance) {
$index = $i;
$maxDistance = $distance;
}
}
if ($maxDistance > $epsilon) {
$left = douglasPeucker(array_slice($points, 0, $index + 1), $epsilon);
$right = douglasPeucker(array_slice($points, $index), $epsilon);
return array_merge(array_slice($left, 0, -1), $right);
}
return [$points[0], $points[$last]];
}
function smoothPath(array $path): array
{
$count = count($path);
if ($count < 5) {
return $path;
}
$smoothed = [];
for ($i = 0; $i < $count; $i++) {
if ($i === 0 || $i === $count - 1) {
$smoothed[] = $path[$i];
continue;
}
$prev = $path[$i - 1];
$cur = $path[$i];
$next = $path[$i + 1];
$smoothed[] = [
'lat' => ((float)$prev['lat'] + ((float)$cur['lat'] * 2) + (float)$next['lat']) / 4,
'lng' => ((float)$prev['lng'] + ((float)$cur['lng'] * 2) + (float)$next['lng']) / 4,
'time' => $cur['time'],
];
}
return $smoothed;
}
function renderPath(array $path, int $renderLimit = 1200): array
{
$count = count($path);
if ($count <= 2) {
return $path;
}
$filtered = [];
$last = null;
foreach ($path as $point) {
if ($last === null || pathDistanceM($last, $point) >= 8) {
$filtered[] = $point;
$last = $point;
}
}
if (count($filtered) < 2) {
$filtered = [$path[0], $path[$count - 1]];
}
$epsilon = 6.0;
$render = $filtered;
do {
$render = douglasPeucker($filtered, $epsilon);
$epsilon *= 1.45;
} while (count($render) > $renderLimit && $epsilon < 500);
if (count($render) > $renderLimit) {
$sampled = [];
$lastIndex = count($render) - 1;
for ($i = 0; $i < $renderLimit; $i++) {
$sampled[] = $render[(int)round($i * $lastIndex / max(1, $renderLimit - 1))];
}
$render = $sampled;
}
return smoothPath($render);
}
$historyRawCount = count($path);
$path = renderPath($path);
$lang = $_GET['lang'] ?? 'en';
$activityMap = [
@@ -503,5 +633,7 @@ echo json_encode([
'connection' => $connectionMap[$lang][$connectionState] ?? $connectionState ?? null,
'sensors' => $sensorSections,
'history' => $includeHistory ? $path : null,
'historyRawCount' => $includeHistory ? $historyRawCount : null,
'historyRenderCount' => $includeHistory ? count($path) : null,
], JSON_UNESCAPED_UNICODE);
?>