cambu-book
Description
This small square book tells the story of my summer in Los Angeles in 2014. After sophomore year of high school, I flew from Toronto, ON to live with extended family in LA while working at a small ‘startup’ company. The data to plot these moments of my summer was retrieved from Google’s location tracking database (https://myactivity.google.com) and chosen in a semi-computational manner that involved a non-perfect human input system.
Process
Going into this project, I knew I wanted to work with maps and location data. I’d been playing around with Mapbox Studio and I wanted an excuse to really dig into it. The next ingredient was to use my personal location data from Google My Activity — this made it easy for me to export all of my location data as a massive JSON blob. My initial idea revolved around using that data in conjunction with some longitude/latitude math to trace a line through the earth and create a book that had my location(s) and the projected location(s) of a doppelganger version of me in a different city.
I decided against this direction in the end because I wasn’t convinced there was any particular meaning to tracing a direct (or even canted/distorted line) through the planet. In the end, I decided to focus more on the exploration of my own data instead of trying to make artificial juxtapositions. See the below image for more process of the project.
Self-Evaluation
For me personally, looking at the book has a lot of meaning and feels very ‘deja vu’-ish because I can viscerally remember being at all of the highlighted locations. It also has a deep ‘uncanny valley’ feeling because, of course, the images are from Google street view and not things I personally captured — but, I can imagine having captured something similar. After all, I was there!
That said, something I didn’t account for when starting with the idea of using my personal location data was that it wouldn’t have the same meaning to other people. All of the psychological triggers that are working on me just aren’t being experienced the same way by other people. If I were to iterate further on this concept, I would create a fully automated pipeline for taking anyone’s location data + selected date range and converting that into their own book. I think then people would be able to feel the same way I did about my book.
Video
Here’s a video of Professor Levin flipping through my book:
Full Book
Code
python node transformer app:
import json
from datetime import datetime as dt
# JSON LOAD
myFile = open("LocationHistory.json")
# myFile = open("testLocHist.json")
js = json.load(myFile)
#js = [2012,2013,2014,2015,2016,2017]
# Vars
nLocations = len(js["locations"])
deleteIndexes = set() #must be a set so it be be easily indexed, gautam bose (andrew: gbose) helped me with the logic behind switching this from a list to a set
print("years in main loop: ")
for i in range(nLocations):
curTimeStamp = js["locations"][i]["timestampMs"]
#print(curTimeStamp)
## converting milliseconds to seconds
humanDate = dt.fromtimestamp(float(curTimeStamp)/1000.0)
#print(humanDate)
curYear = humanDate.year
curMon = humanDate.month
curDay = humanDate.day
#print(str(curMon) + " " + str(curDay))
if curYear == 2014: # if the condition is met, the items will be deleted
# keep these
# print(curYear)
if curMon >= 7 and curMon <= 8: #print(curYear) if curMon == 7: if curDay >= 12:
pass
#print("locked")
#print(humanDate)
else:
deleteIndexes.add(i)
if curMon == 8:
if curDay <= 18:
pass
#print("locked")
#print(humanDate)
else:
deleteIndexes.add(i)
else:
deleteIndexes.add(i)
else:
deleteIndexes.add(i)
#@Cam added this conditional to remove a brief trip to SF
if js["locations"][i]["latitudeE7"] < 350000000: pass else: # print('case1') deleteIndexes.add(i) # print("## start delete loop from ##") # print(deleteIndexes) # print("w/ LENGTH OF ---> ")
# print(len(deleteIndexes))
# print("______")
### PERCENTAGE GUESSOR
# print(nLocations)
# print("&&")
# print(deleteIndexes)
for x in range(nLocations, 0, -1):
print(x)
if x in deleteIndexes:
del js["locations"][x]
##### CHECKER LOOP
# for y in range(len(js["locations"])):
# curAgTimeStamp = js["locations"][y]["timestampMs"]
# # print(curAgTimeStamp)
# print(dt.fromtimestamp(float(curAgTimeStamp)/1000.0))
open("updated-file.json", "w").write(
json.dumps(js, sort_keys=True, indent=4, separators=(',', ': '))
)
# print(deleteIndexes)
# print(js["locations"])
# date = {}
# date = datetime.fromtimestamp(int("unixTimeVar"))
# year = date.year
# hour = date.hou∫∫r∫
##date.year
##etc. unpack
# #print("a total of" + nLocations + "exist in this JSON File") #TypeError: Can't convert 'int' object to str implicitly
# print("Locs:")
# print(nLocations)
#print(curYear)
#print(huma nDate.year)ss
# year = humanDate.year
# print(js["locations"][0]["timestampMs"])
# print(js["locations"][1]["timestampMs"])
javascript web app to call APIs and download images to local directory:
A Simple Map
//Load JSON File
var locations = [] // all data
window.onload = function rdy() {
newMapImage(34.1187624,-118.2751063,12,640,640)
newStreetImage(34.1187624,-118.2751063,640,640, 90, 235, 10)
}
var i = 0;
function again(jumpVal, newHeading) {
var longitude = locations[i][0];
var latitude = locations[i][1];
var zoom = 12 //locations[0][2]
var width = 640;
var height = 640;
var fov = 90;
var S = null;
var pitch = 10;
if (newHeading == null) {
var heading = 235;
} else {
var heading = newHeading;
}
if (S == null) {
newMapImage(longitude,latitude,zoom, width, height, 0);
newStreetImage(longitude,latitude, width, height, fov, heading, pitch, 0);
} else {
newMapImage(longitude,latitude,zoom, width, height, 1);
newStreetImage(longitude,latitude, width, height, fov, heading, pitch, 1);
}
console.log(longitude + " " + latitude)
console.log(i + " / " + locations.length)
//setTimeout(function(){alert(i)}, 500);
// newMapImage(longitude,latitude,zoom, width, height, save);
// newStreetImage(longitude,latitude, width, height, fov, heading, pitch, save);
i = i + jumpVal;
}
function newMapImage(long, lat, zoom, width, height, save) { // map image
var linkMap = "https://api.mapbox.com/styles/v1/mapbox/streets-v9/static/" + lat + "," + long + "," + zoom + "/" + width + "x" + height + "?access_token=pk.eyJ1Ijoic3VwZXJjZ2VlayIsImEiOiJjaWZxMzV6NnFhb3pjaXVseDQ1dm84Z2RkIn0.T5qZqiB_JanRezs012Zppw";
document.getElementById('myMap_image').src = linkMap;
document.getElementById('myMap_link').href = linkMap;
// document.getElementById('myMap_link').download = 1
if (save == 1){document.getElementById('myStreetView_link').click();}
}
function newStreetImage(long, lat, width, height, fov, heading, pitch, save) { // street image
var link = "https://maps.googleapis.com/maps/api/streetview?size=" + width + "x" + height + "&location=" + long + "," + lat + "&fov=" + fov + "&heading=" + heading + "&pitch=" + pitch + "&key=AIzaSyAAhrTirgQBQJH88rpw6LpOfp3oMRTMzqg";
document.getElementById('myStreetView_image').src = link;
document.getElementById('myStreetView_link').href = link;
// document.getElementById('plugBoi2').download = 1
if (save == 1){document.getElementById('myStreetView_link').click();}
}
map
street