diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml
new file mode 100644
index 0000000..91f9558
--- /dev/null
+++ b/.idea/deviceManager.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/markdown.xml b/.idea/markdown.xml
new file mode 100644
index 0000000..c61ea33
--- /dev/null
+++ b/.idea/markdown.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app.json b/app.json
index 90255e3..5a8dfa8 100644
--- a/app.json
+++ b/app.json
@@ -1,7 +1,7 @@
{
"expo": {
- "name": "Bincio",
- "slug": "bincio",
+ "name": "Bincio Autarchive",
+ "slug": "bincio-autarchive",
"version": "0.1.0",
"orientation": "portrait",
"scheme": "bincio",
diff --git a/app/activity/[id].tsx b/app/activity/[id].tsx
index 7b3aeb9..c7ebdd6 100644
--- a/app/activity/[id].tsx
+++ b/app/activity/[id].tsx
@@ -21,6 +21,7 @@ type Timeseries = {
power_w?: (number | null)[] | null;
lat?: (number | null)[] | null;
lon?: (number | null)[] | null;
+ distance_m?: number[] | null;
};
// ── Screen ───────────────────────────────────────────────────────────────────
@@ -361,6 +362,10 @@ function MetricCharts({ timeseries, loading, accent }: { timeseries: Timeseries
const { color, unit, decimals } = TAB_META[tab];
const raw = seriesMap[tab]!;
+ const distances = timeseries.distance_m
+ ? timeseries.distance_m
+ : calculateDistanceFromCoords(timeseries.lat, timeseries.lon);
+
return (
{/* Tab row */}
@@ -378,15 +383,15 @@ function MetricCharts({ timeseries, loading, accent }: { timeseries: Timeseries
))}
{/* Chart */}
-
+
);
}
function MetricChart({
- times, values, color, unit, decimals,
+ distances, values, color, unit, decimals,
}: {
- times: number[];
+ distances: number[] | null;
values: (number | null)[];
color: string;
unit: string;
@@ -396,25 +401,28 @@ function MetricChart({
const H = 100;
const PAD = 4;
+ if (!distances) return null;
+
// Downsample to ≤300 points
const step = Math.max(1, Math.floor(values.length / 300));
- const ts = times.filter((_, i) => i % step === 0);
+ const ds = distances.filter((_, i) => i % step === 0);
const vs = values.filter((_, i) => i % step === 0).map(v => v ?? 0);
const minV = Math.min(...vs);
const maxV = Math.max(...vs);
const range = maxV - minV || 1;
- const maxT = ts[ts.length - 1] || 1;
+ const maxD = ds[ds.length - 1] || 1;
- const x = (t: number) => PAD + (t / maxT) * (W - PAD * 2);
+ const x = (d: number) => PAD + (d / maxD) * (W - PAD * 2);
const y = (v: number) => PAD + (1 - (v - minV) / range) * (H - PAD * 2);
- const pts = ts.map((t, i) => `${x(t).toFixed(1)},${y(vs[i]).toFixed(1)}`);
+ const pts = ds.map((d, i) => `${x(d).toFixed(1)},${y(vs[i]).toFixed(1)}`);
const linePath = `M ${pts.join(' L ')}`;
- const areaPath = `M ${x(ts[0])},${H} L ${pts.join(' L ')} L ${x(maxT)},${H} Z`;
+ const areaPath = `M ${x(ds[0])},${H} L ${pts.join(' L ')} L ${x(maxD)},${H} Z`;
const gradId = `grad-${color.replace('#', '')}`;
const fmt = (v: number) => decimals === 0 ? String(Math.round(v)) : v.toFixed(decimals);
+ const fmtDist = (d: number) => (d / 1000).toFixed(1);
return (
<>
@@ -429,13 +437,50 @@ function MetricChart({
- {fmt(minV)} {unit}
+
+ {fmt(minV)} {unit} • {fmtDist(maxD)} km
+
>
);
}
// ── Helpers ───────────────────────────────────────────────────────────────────
+function calculateDistanceFromCoords(
+ lats: (number | null)[] | null | undefined,
+ lons: (number | null)[] | null | undefined,
+): number[] {
+ if (!lats || !lons) return [];
+
+ const distances: number[] = [0];
+ let cumulative = 0;
+
+ for (let i = 1; i < lats.length; i++) {
+ const lat1 = lats[i - 1];
+ const lon1 = lons[i - 1];
+ const lat2 = lats[i];
+ const lon2 = lons[i];
+
+ if (lat1 == null || lon1 == null || lat2 == null || lon2 == null) {
+ distances.push(cumulative);
+ continue;
+ }
+
+ // Haversine formula
+ const R = 6371000; // Earth's radius in meters
+ const dLat = (lat2 - lat1) * Math.PI / 180;
+ const dLon = (lon2 - lon1) * Math.PI / 180;
+ const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
+ Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
+ Math.sin(dLon / 2) * Math.sin(dLon / 2);
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+ cumulative += R * c;
+ distances.push(cumulative);
+ }
+
+ return distances;
+}
+
// Returns [west, south, east, north] per LngLatBounds spec
function geoJsonBounds(gj: object): [number, number, number, number] | null {
const coords: [number, number][] = [];