![]() |
The data consist of activities and place visits. An activity looks like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"activitySegment": { | |
"startLocation": { | |
"latitudeE7": 0, | |
"longitudeE7": 0, | |
"sourceInfo": { | |
"deviceTag": -0 | |
} | |
}, | |
"endLocation": { | |
"latitudeE7": 0, | |
"longitudeE7": 0, | |
"sourceInfo": { | |
"deviceTag": -0 | |
} | |
}, | |
"duration": { | |
"startTimestamp": "2023-02-01T03:49:39.393Z", | |
"endTimestamp": "2023-02-01T03:57:21.480Z" | |
}, | |
"distance": 1110, | |
"activityType": "CYCLING", | |
"confidence": "HIGH", | |
"activities": [ | |
{ | |
"activityType": "CYCLING", | |
"probability": 89.22557234764099 | |
}, | |
{ | |
"activityType": "WALKING", | |
"probability": 6.305336207151413 | |
}, | |
{ | |
"activityType": "IN_PASSENGER_VEHICLE", | |
"probability": 1.5200317837297916 | |
}, | |
{ | |
"activityType": "IN_BUS", | |
"probability": 1.4393270947039127 | |
}, | |
{ | |
"activityType": "IN_SUBWAY", | |
"probability": 0.44777640141546726 | |
}, | |
{ | |
"activityType": "MOTORCYCLING", | |
"probability": 0.3105701180174947 | |
}, | |
{ | |
"activityType": "RUNNING", | |
"probability": 0.2760248025879264 | |
}, | |
{ | |
"activityType": "IN_TRAIN", | |
"probability": 0.17639625584706664 | |
}, | |
{ | |
"activityType": "IN_TRAM", | |
"probability": 0.15619536861777306 | |
}, | |
{ | |
"activityType": "STILL", | |
"probability": 0.1334176049567759 | |
}, | |
{ | |
"activityType": "IN_FERRY", | |
"probability": 0.0047270386858144775 | |
}, | |
{ | |
"activityType": "SKIING", | |
"probability": 0.003990190816693939 | |
}, | |
{ | |
"activityType": "SAILING", | |
"probability": 6.135768217063742e-4 | |
}, | |
{ | |
"activityType": "FLYING", | |
"probability": 1.9939299988891435e-5 | |
}, | |
{ | |
"activityType": "IN_VEHICLE", | |
"probability": 2.1501850647198673e-9 | |
} | |
], | |
"waypointPath": { | |
"waypoints": [ | |
{ | |
"latE7": 0, | |
"lngE7": 0 | |
}, | |
{ | |
"latE7": 0, | |
"lngE7": 0 | |
}, | |
{ | |
"latE7": 0, | |
"lngE7": 0 | |
} | |
], | |
"source": "INFERRED", | |
"roadSegment": [ | |
{ | |
"placeId": "someid", | |
"duration": "31s" | |
}, | |
{ | |
"placeId": "someid", | |
"duration": "16s" | |
}, | |
{ | |
"placeId": "someid", | |
"duration": "38s" | |
}, | |
{ | |
"placeId": "someid", | |
"duration": "28s" | |
}, | |
{ | |
"placeId": "someid", | |
"duration": "91s" | |
} | |
], | |
"distanceMeters": 1480.0706767917454, | |
"travelMode": "BICYCLE", | |
"confidence": 0.0 | |
} | |
} | |
} |
You can peek through the internals in a way since you can see which options for a given activity Google algorithms have considered.
A place visit, on the other hand, is:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"placeVisit": { | |
"location": { | |
"latitudeE7": 0, | |
"longitudeE7": 0, | |
"placeId": "ChIJC8gdEfd2jEYR-_H0G00KSq4", | |
"address": "Aninkaistenkatu, 20100 Turku, Suomi", | |
"name": "Linja-autoasema", | |
"semanticType": "TYPE_SEARCHED_ADDRESS", | |
"sourceInfo": { | |
"deviceTag": -0 | |
}, | |
"locationConfidence": 57.1172, | |
"calibratedProbability": 48.786476 | |
}, | |
"duration": { | |
"startTimestamp": "2023-01-01T03:37:21.480Z", | |
"endTimestamp": "2023-01-01T04:56:24.466Z" | |
}, | |
"placeConfidence": "MEDIUM_CONFIDENCE", | |
"centerLatE7": 0, | |
"centerLngE7": 0, | |
"visitConfidence": 96, | |
"otherCandidateLocations": [{ | |
"latitudeE7": 604571641, | |
"longitudeE7": 222681737, | |
"placeId": "ChIJc9Dc5Ph3jEYR8pWWIcfi0r4", | |
"address": "Läntinen Pitkäkatu 7, 20100 Turku, Suomi", | |
"name": "Turku Bus Station", | |
"locationConfidence": 13.238819, | |
"calibratedProbability": 11.307895 | |
}, { | |
"latitudeE7": 604567433, | |
"longitudeE7": 222656352, | |
"placeId": "ChIJH7NECvd2jEYRH4YOsng8Ofw", | |
"address": "20100 Turku, Suomi", | |
"name": "Turku bus station", | |
"locationConfidence": 10.178642, | |
"calibratedProbability": 8.694055 | |
}, { | |
"latitudeE7": 604564020, | |
"longitudeE7": 222666501, | |
"placeId": "ChIJPVBncfd2jEYRLLPS3XEXJ1I", | |
"address": "20100 Turku, Suomi", | |
"name": "Turku, bus station", | |
"locationConfidence": 9.436044, | |
"calibratedProbability": 8.059767 | |
}], | |
"editConfirmationStatus": "NOT_CONFIRMED", | |
"locationConfidence": 46, | |
"placeVisitType": "SINGLE_PLACE", | |
"placeVisitImportance": "MAIN" | |
} | |
} |
As you can see, the data contains not just the place you can see from Maps you have visited but actually the metadata and the confidence scores of where Google thinks you have been.
Using the dataset, it is pretty easy to find out which locations you have visited and how long you have stayed. I co-authored with ChatGPT this simple script that outputs how many hours and times you have visited a given place.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fs from 'fs' | |
import path from 'path'; | |
// https://stackoverflow.com/questions/19700283/how-to-convert-time-in-milliseconds-to-hours-min-sec-format-in-javascript | |
function parseMillisecondsIntoReadableTime(milliseconds){ | |
//Get hours from milliseconds | |
var hours = milliseconds / (1000*60*60); | |
var absoluteHours = Math.floor(hours); | |
var h = absoluteHours > 9 ? absoluteHours : '0' + absoluteHours; | |
//Get remainder from hours and convert to minutes | |
var minutes = (hours - absoluteHours) * 60; | |
var absoluteMinutes = Math.floor(minutes); | |
var m = absoluteMinutes > 9 ? absoluteMinutes : '0' + absoluteMinutes; | |
//Get remainder from minutes and convert to seconds | |
var seconds = (minutes - absoluteMinutes) * 60; | |
var absoluteSeconds = Math.floor(seconds); | |
var s = absoluteSeconds > 9 ? absoluteSeconds : '0' + absoluteSeconds; | |
return h + ':' + m + ':' + s; | |
} | |
let visits = 0; | |
let timeSpentMs = 0; | |
const placeNamePatterns = ['Interesting place', 'Interesting place with another name'] | |
const dataDirPath = './data'; | |
const yearsDirs = fs.readdirSync(dataDirPath); | |
yearsDirs.forEach((yearDir) => { | |
if (yearDir.length !== 4) { | |
return; | |
} | |
let visitsYear = 0; | |
let timeSpentYearMs = 0; | |
const yearDirPath = path.join(dataDirPath, yearDir); | |
const filesInYearDir = fs.readdirSync(yearDirPath); | |
filesInYearDir.forEach((filename) => { | |
const filePath = path.join(yearDirPath, filename); | |
const data = fs.readFileSync(filePath, 'utf8'); | |
const timelineObjects = JSON.parse(data).timelineObjects; | |
for (const timelineObject of timelineObjects) { | |
if (timelineObject.placeVisit) { | |
const placeVisit = timelineObject.placeVisit.location.name; | |
const otherCandidateLocations = timelineObject.placeVisit.otherCandidateLocations; | |
if (placeNamePatterns.some((pattern) => placeVisit?.includes(pattern)) || otherCandidateLocations?.some((location) => placeNamePatterns.some((pattern) => location.name?.includes(pattern)))) { | |
const duration = timelineObject.placeVisit.duration; | |
const start = new Date(duration.startTimestamp); | |
const end = new Date(duration.endTimestamp); | |
timeSpentYearMs = timeSpentYearMs + (end - start); | |
visitsYear++; | |
} | |
} | |
} | |
}); | |
visits = visits + visitsYear; | |
timeSpentMs = timeSpentMs + timeSpentYearMs; | |
if (visitsYear === 0) { | |
console.log(yearDir, 'no visits') | |
console.log('\n') | |
return; | |
} | |
console.log(yearDir) | |
console.log(visitsYear, 'visits') | |
console.log(parseMillisecondsIntoReadableTime(timeSpentYearMs), 'time spent') | |
const average = timeSpentYearMs / visitsYear; | |
console.log(parseMillisecondsIntoReadableTime(average), 'average time spent per visit') | |
console.log('\n') | |
}); | |
console.log('Total') | |
console.log(visits, 'visits') | |
console.log(parseMillisecondsIntoReadableTime(timeSpentMs), 'time spent') |
Comments
Post a Comment