Ottenere due deliverable dal repository:
-
mappit-core— Una libreria JavaScript/TypeScript pura (senza dipendenze UI) che:- Carica, normalizza e filtra dati di geolocalizzazione Google
- Supporta tutti i formati esistenti: Records.json (legacy), Timeline.json (Standard, Semantic, iOS), file mensili Takeout (YYYY_MONTH.json)
- È utilizzabile sia come modulo programmatico (
import { loadData, filterByDate } from 'mappit-core') sia da riga di comando (npx mappit-core --load ./data --filter-date 2024-01-01 2024-06-30 --export output.json) - Espone un modello dati unificato, indipendente dal formato sorgente
-
mappit-app— Un'app Electron che:- Importa
mappit-corecome dipendenza - Implementa l'interfaccia di visualizzazione attualmente presente in
timeline.html(deck.gl + Mapbox, timeline sidebar, filtri, ricerca, summary, export KML) - Supporta tutti i formati tramite la libreria core
- Sostituisce sia la vecchia app Electron+Plotly sia il viewer HTML standalone
- Importa
| Componente | File | Cosa fa | Problemi |
|---|---|---|---|
| App Electron legacy | src/main.js, src/data.js, src/plot.js, src/utils.js, index.html |
Carica Records.json, filtra per data, plotta con Plotly/Mapbox | Electron 9, nodeIntegration: true, solo formato Records.json, nessun test |
| Timeline Viewer | timeline.html |
Viewer completo del nuovo formato Takeout con Google Maps | File monolitico ~6000 righe, non modulare, API key hardcoded, nessuna integrazione con il core |
| CLI/test | test_data.js |
Script di test manuale | Non è un vero test framework |
| Formato | Sorgente | Struttura |
|---|---|---|
| Records.json (legacy) | Google Takeout pre-2024 | { locations: [{ timestamp, latitudeE7, longitudeE7, velocity, activity, ... }] } |
| Timeline.json Standard | Google Takeout | { timelineObjects: [{ placeVisit | activitySegment }] } |
| Timeline.json Semantic | Google Takeout nuovo | { semanticSegments: [{ visit | activity, startTime, endTime, timelinePath }] } |
| Timeline.json iOS | Google Maps iOS export | Array<{ startTime, endTime, visit | activity, timelinePath }> |
| YYYY_MONTH.json | Google Takeout (cartelle mensili) | Come Standard, un file per mese |
Il core deve normalizzare tutti i formati in un unico modello interno:
// Un singolo record di posizione (per Records.json / scatter plot)
interface LocationPoint {
timestamp: string; // ISO 8601
lat: number; // gradi decimali
lng: number; // gradi decimali
accuracy?: number;
velocity?: number; // m/s
heading?: number;
altitude?: number;
source?: string;
activityType?: string; // tipo di attività normalizzato (gruppo)
activityConfidence?: number;
}
// Una visita a un luogo
interface PlaceVisit {
type: 'visit';
startTime: string;
endTime: string;
lat: number;
lng: number;
placeId?: string;
name?: string;
semanticType?: string;
editConfirmationStatus?: string;
}
// Un segmento di attività (spostamento)
interface ActivitySegment {
type: 'activity';
startTime: string;
endTime: string;
activityType: string; // normalizzato tramite activityGroupMapping
distanceMeters?: number;
startLocation: { lat: number; lng: number };
endLocation: { lat: number; lng: number };
path: Array<{ lat: number; lng: number; timestamp?: string }>;
}
// Elemento della timeline (unione delle due tipologie)
type TimelineEntry = PlaceVisit | ActivitySegment;
// Dataset completo caricato
interface MappitDataset {
source:
| 'records'
| 'timeline-standard'
| 'timeline-semantic'
| 'timeline-ios'
| 'takeout-monthly';
dateRange: { min: string; max: string };
// Per Records.json: array di punti grezzi
points: LocationPoint[];
// Per tutti i formati Timeline: eventi strutturati
timeline: TimelineEntry[];
}Obiettivo: struttura monorepo, tooling moderno, nessuna funzionalità nuova.
- 0.1 Inizializzare un monorepo con workspace npm
mappit-core/ ├── packages/ │ ├── core/ ← libreria │ │ ├── src/ │ │ ├── package.json │ │ └── tsconfig.json │ └── app/ ← app Electron │ ├── src/ │ ├── package.json │ └── tsconfig.json ├── package.json ← workspace root └── tsconfig.base.json - 0.2 Configurare TypeScript per entrambi i package (target ES2020+, strict mode)
- 0.3 Configurare ESLint + Prettier condivisi
- 0.4 Configurare Vitest come test runner per il core
- 0.5 Aggiungere script di build (
tsc) per il core → output CommonJS - 0.6 Creare cartella
fixtures/con dati di test per tutti i formati (records, standard, semantic, iOS, monthly)
Deliverable: repo compilabile, nessuna funzionalità ancora migrata.
Obiettivo: il core sa caricare e normalizzare tutti e 5 i formati in
MappitDataset.
- 1.1 Portare
src/utils.js→packages/core/src/geo.ts(haversine, degToRad, mean, normalize) - 1.2 Portare
src/defaults.js→packages/core/src/constants.ts(colorscale, activity mapping) - 1.3 Creare
packages/core/src/types.tscon le interfacce del modello dati unificato - 1.4 Creare
packages/core/src/activity-mapping.ts— estrarreactivityGroupMappingegetGroupedActivityTypedatimeline.html - 1.5 Creare
packages/core/src/loaders/records.ts- Carica Records.json (streaming con big-json per file grandi, JSON.parse per file piccoli)
- Normalizza ogni location in
LocationPoint - Produce un
MappitDatasetconsource: 'records'e il campopointspopolato
- 1.6 Creare
packages/core/src/loaders/timeline-standard.ts- Carica
{ timelineObjects: [...] } - Normalizza
placeVisit→PlaceVisit,activitySegment→ActivitySegment - Popola
timelinenelMappitDataset
- Carica
- 1.7 Creare
packages/core/src/loaders/timeline-semantic.ts- Gestisce
{ semanticSegments: [...] } - Parsifica le coordinate
"45.123°, 9.456°"e latimelinePath - Associa i punti del path ai segmenti di attività (logica del pointer ottimizzato già presente in timeline.html)
- Gestisce
- 1.8 Creare
packages/core/src/loaders/timeline-ios.ts- Gestisce il formato array iOS
- Preprocessa
timelinePathcon offset in minuti - Trasforma
visit/activitynel modello unificato
- 1.9 Creare
packages/core/src/loaders/takeout-monthly.ts- Scansiona una directory per file
YYYY_MONTH.json - Riutilizza il loader Standard per ciascun file
- Concatena i risultati
- Scansiona una directory per file
- 1.10 Creare
packages/core/src/loaders/auto-detect.ts- Dato un file o directory, rileva automaticamente il formato
- Se è un singolo JSON: ispeziona la struttura (array?
timelineObjects?semanticSegments?locations?) - Se è una directory: cerca
Timeline.jsonal top level, altrimenti scansiona per file mensili - Restituisce il
MappitDatasetappropriato
- 1.11 Scrivere test unitari per ogni loader con fixture dei rispettivi formati
- 1.12 Aggiornare
packages/core/src/index.ts— export pubblico della libreria
Deliverable: import { loadDataset } from 'mappit-core' funziona con tutti i formati.
Obiettivo: il core offre operazioni di filtro, statistiche e export.
- 2.1
packages/core/src/filters.tsfilterByDateRange(dataset, start, end)— filtra siapointschetimelinefilterByArea(dataset, bounds)— bounding box geografico (il--filterspaceattualmente TODO)filterByActivityType(dataset, types[])— filtra per tipo di attività
- 2.2
packages/core/src/transforms.tssimplifyDataset(dataset)— versione evoluta disimplifyDataattualetimelineToPoints(dataset)— esplode i segmenti della timeline in punti individuali (per scatter/heatmap)- (fase futura)
recordsToTimeline(dataset)— clustering temporale/spaziale per convertire punti GPS grezzi in pseudo-visite e pseudo-segmenti
- 2.3
packages/core/src/stats.tscomputeSummary(dataset)— calcola statistiche: distanza totale, numero visite, distribuzione attività, date min/maxcomputeYearlySummary(dataset)/computeMonthlySummary(dataset)— estrarre la logica dacalculateSummaries()in timeline.html
- 2.4
packages/core/src/exporters/json-exporter.ts— esporta dataset filtrato in JSON semplificato - 2.5
packages/core/src/exporters/kml-exporter.ts— estrarre la logica diexportToKML()da timeline.html - 2.6 Test unitari per filtri, trasformazioni e exporter
- 2.7 Aggiornare
packages/core/src/index.tscon tutti gli export
Deliverable: la libreria è completa come modulo programmatico.
Obiettivo: la libreria è utilizzabile da terminale.
- 3.1 Creare
packages/core/src/cli.tscon yargsmappit-core load <path> # auto-detect formato mappit-core load <path> --format records mappit-core load <path> --filter-date 2024-01-01 2024-06-30 mappit-core load <path> --filter-area 45.0,9.0,46.0,10.0 mappit-core load <path> --filter-activity WALKING,CYCLING mappit-core load <path> --export output.json mappit-core load <path> --export output.kml mappit-core load <path> --stats - 3.2 Campo
bininpackages/core/package.json(già presente dal Phase 0) - 3.3 Aggiungere spinner (ora@5) per operazioni di caricamento, filtro ed export
- 3.4 Test e2e per la CLI (17 test in
tests/cli.test.ts) - 3.5 Documentare la migrazione dai comandi legacy (
--loadfile,--filterdate,--plot,--writeOutput) alla nuova CLI nel README
Deliverable: npx mappit-core load ./Takeout --stats funziona. ✅
Obiettivo: nuova app Electron con architettura sicura che usa il core.
- 4.1 Configurare Electron recente (v33+) in
packages/app/contextIsolation: true,nodeIntegration: false- Preload script con
contextBridge - IPC channels tipizzati per comunicazione main↔renderer
- 4.2 Scegliere un framework per il renderer:
- Vanilla JS + bundler (più vicino all'attuale timeline.html, migrazione più semplice)
- 4.3 Configurare Vite come bundler per il renderer process
- 4.4 Main process: usare
mappit-coreper tutte le operazioni datiipc.handle('load-dataset', (path) => loadDataset(path))ipc.handle('filter', (dataset, filters) => filterByDateRange(...))ipc.handle('export-kml', (dataset, range) => exportToKML(...))ipc.handle('compute-stats', (dataset) => computeSummary(...))
- 4.5 Renderer process: gestione stato dei dati caricati
- Riceve
MappitDatasetserializzato dal main process via IPC - Mantiene stato locale per filtri attivi, date selezionate, vista corrente
- Riceve
- 4.6 Implementare caricamento dati
- Seleziona cartella/file tramite dialog nativo di Electron (sostituisce File System Access API del browser)
- Il main process usa
mappit-coreper caricare e inviare i dati al renderer - Supporto sia per directory Takeout che per singoli file
Deliverable: app che carica dati da qualsiasi formato e li mostra in console/log. ✅
Obiettivo: replicare la UI di timeline.html nell'app Electron.
- 5.1 Integrare deck.gl API nel renderer
- maplibre-gl come base map (gratuito, nessuna API key)
- deck.gl MapboxOverlay con ScatterplotLayer (visite) e PathLayer (attività)
- Tile CartoDB Dark Matter per coerenza col tema scuro dell'app
- 5.2 Sidebar con timeline
- Date picker (range) con navigazione prev/next e filtro GO
- Lista cronologica di visite e attività con intestazioni per data
- Dot colorati per tipo attività (no emoji — cross-platform)
- Click per evidenziare su mappa e viceversa
- 5.3 Filtri attività (checkbox per tipo, mostra/nascondi visite e spostamenti)
- 5.4 Rendering mappa
- ScatterplotLayer per le visite (cerchi ambra, highlight blu)
- PathLayer per gli spostamenti (colore per tipo attività)
- Auto-zoom/fit bounds al caricamento
- Tooltip su hover con nome luogo / tipo attività
- 5.5 Interazione mappa↔sidebar
- Click marker → evidenzia sidebar item e scroll automatico
- Click sidebar item → flyTo/fitBounds sulla mappa
- 5.6 Place Details (opzionale/progressivo)
- Fetch via Google Places API per arricchire i marker (disponibile dopo migrazione a Google Maps)
- Cache su filesystem: file JSON in
app.getPath('userData'), lettura/scrittura gestita dal main process via IPC - Toggle per disabilitare chiamate API
Deliverable: l'app è comparabile a timeline.html per la vista mappa+timeline. ✅
Obiettivo: parità completa con timeline.html + miglioramenti.
- 6.1 Summary view con statistiche annuali/mensili
- Grafici con Chart.js (distanze per attività, visite/attività per periodo)
- Navigazione Overview → Yearly → Monthly tramite tab
- 6.2 Ricerca luoghi
- Ricerca offline nel dataset caricato per nome/placeId (fuzzy match con scoring)
- Navigazione ai risultati sulla mappa (flyTo)
- 6.3 Area search (trova visite nell'area visibile della mappa)
- Usa
filterByAreadel core conmap.getBounds() - Filtra il dataset alle entry nel viewport corrente
- Usa
- 6.4 Export KML/JSON (usa
mappit-coreexporter)- Dialog nativo per salvare file con filtro KML/JSON/All
- 6.5 Impostazioni: (rimandato a Fase 7 — non critico per parità)
- 6.6 Layout responsive / mobile-friendly
- CSS media queries per ≤768px (sidebar collassabile) e ≤480px (sidebar full-width)
- 6.7 Supporto modalità scatter/heatmap per dati Records.json
HeatmapLayerdi@deck.gl/aggregation-layerscon color ramp 6 colori- Toggle heatmap on/off dalla toolbar mappa
Deliverable: l'app Electron sostituisce completamente sia il vecchio Electron+Plotly sia timeline.html.
Obiettivo: codice production-ready.
- 7.1 Rimuovere i file legacy (
src/main.js,src/data.js,src/plot.js,src/utils.js,src/defaults.js,index.html,test_data.js,timeline.html) - 7.2 Documentazione API per
mappit-core(JSDoc + TypeDoc) - 7.3 README aggiornato con istruzioni per entrambi i package
- 7.4 CI/CD: GitHub Actions per lint, test, build
- 7.5 Pubblicazione npm del core (preparata con
prepublishOnly, workflow publish.yml) - 7.6 Build dell'app Electron per distribuzione con electron-builder
┌─────────────────────────────────────────────────┐
│ mappit-app │
│ (Electron + deck.gl + Mapbox) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ Main │ │ Renderer │ │ Preload │ │
│ │ Process │──│ Process │──│ (contextBridge│ │
│ │ │ │ (UI/Map) │ │ + IPC) │ │
│ └─────┬─────┘ └──────────┘ └───────────────┘ │
│ │ │
│ │ import │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ mappit-core │ ◄── anche utilizzabile│
│ │ │ standalone via CLI│
│ │ • Loaders (5 formati) │ o come libreria │
│ │ • Filtri │ │
│ │ • Trasformazioni │ │
│ │ • Statistiche │ │
│ │ • Exporters │ │
│ │ • CLI │ │
│ └─────────────────────────┘ │
└─────────────────────────────────────────────────┘
| Fase | Effort stimato | Note |
|---|---|---|
| 0 — Setup | 1-2 giorni | Boilerplate, configurazione |
| 1 — Loaders | 3-5 giorni | Parte più critica: 5 formati, test |
| 2 — Filtri/Export | 2-3 giorni | Logica relativamente semplice |
| 3 — CLI | 1-2 giorni | Wrapping della libreria |
| 4 — Electron scaffold | 2-3 giorni | Architettura, IPC, build |
| 5 — UI mappa+timeline | 5-7 giorni | Migrazione da timeline.html, la parte più grossa |
| 6 — Funzionalità avanzate | 3-5 giorni | Summary, search, area, export |
| 7 — Pulizia | 1-2 giorni | Docs, CI, cleanup |
| Totale | ~18-29 giorni | Lavoro individuale, full-time |
- TypeScript: TypeScript consigliato per il core (type safety sui modelli dati). L'app può partire in JS e poi migrare.
- Framework UI per il renderer: Vanilla (più facile da migrare da timeline.html). Vanilla + Vite per la fase iniziale. In futuro Vue.js.
- Mappa: deck.gl + Mapbox per la fase iniziale (nessuna API key obbligatoria). Migrazione a Google Maps in una fase successiva per aggiungere Place Details e autocomplete Places API.
- Monorepo tool: npm workspaces (zero config)
- Streaming per file grandi: migrare a
stream-json(più mantenuto, già nelle dipendenze). - Nome dei package:
mappit-core+mappit-app(senza scope npm org). - Cache Place Details: file JSON su filesystem nella app data directory (
app.getPath('userData')), gestita dal main process. - Distribuzione: electron-builder.
recordsToTimeline: demandata a una fase futura; non bloccante per le prime release.