The FOMO Map: Session B Final Project

Jujie Xu
Data Mining the City
6 min readDec 15, 2017

Team: Jingyuan Gao, Jujie Xu, Siman Huang

What does FOMO mean?

FOMO _Fear Of Missing Out

/ˈfōmō/

noun

anxiety that an exciting or interesting event may currently be happening elsewhere, often aroused by posts seen on a social media website.

eg. “I realized I was a lifelong sufferer of FOMO!”

FOMO!

Why a FOMO Map?

Nowadays, people are overwhelmed by large amount of information about ongoing events provided by social medias. It has been much harder to make choices in our daily life.

Have you had the experience of being interested in or being distracted by what is going on in the city and what your friends are doing? Have you suffered from the fear of missing out?

A FOMO map is badly needed to help relieve the Fear Of Missing Out (or exaggerate the fear in an ironic way) and guide people to decide when faced with numerous choices of ongoing events.

How does a FOMO Map work?

The fear of missing out is closely associated with time. People need to know the starting time, the ending time and the duration of the events to decide whether it is suitable to go to the events. The time of commuting is also important. So apart from representing geographic locations, the FOMO Map is also a time-based map.

DIAGRAM

We choose concentric circles to form the map. The center point is the user’s current location. All ongoing events are arranged on these concentric circles. The relative locations of the user’s and the events’ are based on longitudes and latitudes in reality. The radius of different circles represent the amount of time that the user will spend traveling from their current locations to the destined events’ locations. Larger radius indicates longer transit time. Different types of events are represented in various colors.

Also, the popularity of ongoing events differs, which may influence people’s attitudes towards these events. Actually, most people tend to participate in events that are more popular. In the Fomo map, the dots will be attracted towards the center and the circle will be distorted if the events the dots represent are more popular than others.

To help users visualize how much time is left for them to catch an event, from an event’s starting time to the ending time, the dot and its circle will become more and more transparent until it disappears in the end.

Data collecting

We collected data into a csv file from “meetup.com”, including the types of the events, titles, organizers, locations, starting time, ending time, and the amount of members in these organizations to represent popularity.

MEETUP WEBSITE
DATA CSV

Fomo Map

Each color represents one type of event, pink for film, yellow for wellness, blue for music.

As the mouse touches each dot, the call-out window shows all the information of that one specific event, including categories, names, transit times, and amount of time left before it ends.

From an event’s start time to the end time, the dot would become more and more transparent until it disappears in the end to helps users to visualize how much time left before they could catch an event.

GSAPP

As your location changes, the map visualizes different results.

Time Square
East Village
import spatialpixel.mapping.slippymapper as slippymapper
import spatialpixel.google.directions as directions
import csv
def keyPressed():
global currenttime
if keyPressed:
currenttime += 0.5
print currenttime
def mouseClicked():
global currenttime
if mouseClicked:
currenttime -= 0.5
print currenttime

def setup():
size (1000,1000)

translate(width/2, height/2)
global newyork
newyork = slippymapper.SlippyMapper(40.807993,-73.963833,12,'toner',width,height)

global locations
global latitudes
latitudes=[]
global longitudes
longitudes=[]
global starttimes
starttimes = []
global endtimes
endtimes = []
global kinds
kinds = []
global popularitys
popularitys = []

global names
names = []
locations=[latitudes, longitudes, names]
global currenttime


with open('FOMO_meetup_Dec_12th.csv') as f1:
reader = csv.reader(f1)
header = reader.next()

for row in reader:
latitude = float(row[4])
latitudes.append(latitude)
longitude = float(row[5])
longitudes.append(longitude)
name = str(row[1])
names.append(name)

starttime = float(row[8])
starttimes.append(starttime)
endtime = float(row[9])
endtimes.append(endtime)
kind = str(row[0])
kinds.append(kind)
popularity = int(row[11])
popularitys.append(popularity)

apikey= 'AIzaSyDZnQqipexHOChnX8iQucq-84OGF3LNpVk'
centre = (40.807993,-73.963833)


currenttime = 20.0

number=len(longitudes)

global duration
duration = []
for i in xrange(number):
location_n=(locations[0][i],locations[1][i]) #(latitude[i], longitude[i])
location_n_route=directions.RenderGoogleDirections(apikey)
testjson=location_n_route.request(centre,location_n,'bus')
data=location_n_route.data
duration.append(data['routes'][0]['legs'][0]['duration']['value'])
print(duration)

global info
info = {}
global relative_info
relative_info = {}
max_duration = max(duration)
# print(max_duration)
for i in range(len(names)):
info[names[i]] = [locations[0][i], locations[1][i]]
relative_info[names[i]] = [locations[0][i] - centre[0], locations[1][i] - centre[1], 450*duration[i]/max_duration]
#print(relative_info)
textSize(30)
noStroke()
fill(255)
text('YOU', -30, 13)
fill(229, 163, 163)

global longi_point
longi_point = []
global latti_point
latti_point = []
global drift
drift=[]
global r
global stroke_weight
global thita
global drift
global indexColor
indexColor = {}
for i in xrange(len(names)):
if currenttime + duration[i]/3600 < endtime:
strokeWeight(2.5)
noFill()
indexColor = {'Arts': color(153, 204, 255), 'Films': color(255, 153, 153), 'Health & Wellness': color(255, 204, 102), 'Sports & Fitness': color(225, 0, 0), 'Hobbies & Crafts': color(0, 102, 0), 'Music': color(0, 153, 204), 'Photography': color(153, 102, 255)}
left_time_degree = ((endtime-currenttime)/(endtime-starttime)*255)
print(left_time_degree)
stroke(indexColor[kinds[i]], left_time_degree)
ellipse(0, 0, 2*relative_info[names[i]][2], 2*relative_info[names[i]][2])
# print(relative_info[location][0], relative_info[location][1])
distance = (relative_info[names[i]][0]**2 + relative_info[names[i]][1]**2) ** 0.5
# print(distance)
longi_point.append(relative_info[names[i]][0] / distance * relative_info[names[i]][2])
latti_point.append(relative_info[names[i]][1] / distance * relative_info[names[i]][2])


def draw():
translate(width/2, height/2)

background(0)
fill(50, 50, 50)
textSize(580)
text('YOUR', -880, -280)
text('FOMO', -880, 220)
text('MAP', -620, 720)
noFill()
strokeWeight(4)
stroke(150, 150, 150)
ellipse(0, 0, 80, 80)
strokeWeight(4)
line(0, 40, 0, -40)
line(40, 0, -40, 0)
fill(190, 190, 190)
textSize(26)
text('N', -8, -50)
text('S', -8, 68)
text('W', -68, 10)
text('E', 48, 8)
textSize(30)
noStroke()
fill(255)
text('YOU', -30, 13)
textSize(34)
fill(255)
textSize(22)
text(currenttime,-60,-470)


for i in xrange(len(names)):
if currenttime + duration[i]/3600 < endtimes[i]:
strokeWeight(1.0)
noFill()
left_time_degree = ((endtimes[i]-currenttime)/(endtimes[i]-starttimes[i])*255)
# print(left_time_degree)
stroke(indexColor[kinds[i]], left_time_degree)
ellipse(0, 0, 2*relative_info[names[i]][2], 2*relative_info[names[i]][2])

r = (longi_point[i]**2 + latti_point[i]**2) ** 0.5
thita = atan(- longi_point[i] / latti_point[i]) + PI
alp = 15.0 /180.0 * PI
drift = popularitys[i] /120
#print drift
drift_ratio = 0.1
x = [r * cos(thita - alp), (r-drift_ratio * drift) * cos(thita - 0.5*alp), (r-drift) * cos(thita - 0.15*alp), (r-drift) * cos(thita + 0.15*alp), (r-drift_ratio * drift) * cos(thita + 0.5*alp), r * cos(thita + alp)]
y = [r * sin(thita - alp), (r-drift_ratio * drift) * sin(thita - 0.5*alp), (r-drift) * sin(thita - 0.15*alp), (r-drift) * sin(thita + 0.15*alp), (r-drift_ratio * drift) * sin(thita + 0.5*alp), r * sin(thita + alp)]
beginShape()
curveVertex(x[0],y[0])
curveVertex(x[0],y[0])
curveVertex(x[1],y[1])
curveVertex(x[2],y[2])
curveVertex(x[3],y[3])
curveVertex(x[4],y[4])
curveVertex(x[5],y[5])
curveVertex(x[5],y[5])
endShape()
stroke_weight = 10.0
strokeWeight(stroke_weight)
#indexColor = {'Arts': color(153, 204, 255), 'Films': color(255, 153, 153), 'Health & Wellness': color(255, 204, 102), 'Sports & Fitness': color(230, 230, 0), 'Hobbies & Crafts': color(153, 255, 102), 'Music': color(0, 153, 204), 'Photography': color(204, 204, 255)}
left_time_degree = (((endtimes[i]-currenttime)/(endtimes[i]-starttimes[i]))*255)
#print left_time_degree
stroke(indexColor[kinds[i]], left_time_degree)
ellipse((r - (drift - stroke_weight/2)) * cos(thita), (r - (drift - stroke_weight/2)) * sin(thita), 10, 10)
# print(longi_point[-1], latti_point[-1])

#line(mouseX-width/2, mouseY-height/2, 0, 0)
print(len(longi_point))
for i in xrange(len(longi_point)):
r = (longi_point[i]**2 + latti_point[i]**2) ** 0.5
thita = atan(- longi_point[i] / latti_point[i]) + PI
drift = popularitys[i] /120

mouseIsOverColumn =latti_point[i] - drift < mouseX-width/2 and mouseX-width/2 < latti_point[i] + drift
mouseIsOverRow = -longi_point[i] - drift < mouseY-height/2 and mouseY-height/2 < -longi_point[i] + drift
mouseIsOverTheRectangle = mouseIsOverColumn and mouseIsOverRow
if mouseIsOverTheRectangle:
left_time_degree = ((endtimes[i]-currenttime)/(endtimes[i]-starttimes[i])*50)
if left_time_degree < 0:
left_time_degree = 0
# print(currenttime)
# print ('left_time_degree', left_time_degree)
fill(indexColor[kinds[i]])
noStroke()
rect(-550, 410, 300, 200)
fill(255)
textSize(12)
text(names[i], -490, 430)
text('Transit Time: ', -490, 450)
text(duration[i]/60, -410, 450)
text('min', -380, 450)
text('Time Left: ', -490, 470)
timeLeft = int((endtimes[i]-currenttime)*60)
text((timeLeft), -410, 470)
text('min', -380, 470)
textSize(18)
text(kinds[i], -490, 490)

--

--