Evacuation Simulation in Urban Fabrics
Columbia University, Datamining the city, Final presentation
Group: Chenyu Xu, Huanyu Chen, Ruilan Jia, Shuting Han, Yicheng Xu
(Names in alphabetical order)
1.Background
When facing unpredictable disasters such as hurricane, earthquake, fire, car accident and terror attack, human can be very vulnerable. For terror attack alone, there are 1076 attacks and 7396 fatalities worldwide in 2017 so far. In densely populated cities, there is a higher chance of accidents in hotspots resulting in relevantly more injuries and fatalities.
Lately, on May 18, 2017, a car was crashed in Times Square, New York City. One person was killed and 20 were injured.
On December 31, 2014, a deadly stampede occurred in Shanghai, near Chen Yi Square on the Bund, where around 300,000 people had gathered for the New Year celebration. 36 people were killed and there were 49 injured, 13 seriously.
Cities should contain complex refuge, acting as shelter from emergency. The roles of urban fabric and physical infrastructure are crucial not only in preventing terrorists’ actions but also determining the way of evacuating people. Even though security might be the most effective way to prevent accidents directly, city design should be improved to evacuate people as fast as possible to reduce the impact.
Among all the different types of disasters mentioned above, we assumed one extreme case when the damage source(Attacker) is mobile meaning that it could have huge potential impact, and visualized it in realistic urban areas.
This project uses an agent-based model to simulate human behaviors during an emergency in global cities, aiming to evaluate efficiency of evacuation in different types of urban fabric. The efficiency is assessed by quantifying fatalities over the same time.
2.Methodology
2.1 Define objects
def setup():
size(1000,1000)
frameRate(30)
# How many people you want in the simulation.
number_of_victim =200
number_of_threat = 1
global baseImage, allexitsonImage
baseImage = loadImage("Victorian_Harbour.jpg")
allexitsonImage = [{"location":(960,740),"count1":0,"count2":0},{"location":(40,790),"count1":0,"count2":0}]
# Add all the victim and threat behaviors and their parameters.
global victimSelfbehaviors,victimRespondbehaviors,threatbehaviors, victimbehaviors
victimbehaviors = Run1(speedlimit=2.0, turnratelimit=math.pi / 20)
victimSelfbehaviors = (
MoveTowardsExit(allexits=allexitsonImage,closeness_exit=300,weight=10),
TurnAwayFromWall_1(weight=10.0, BaseImage = baseImage),
Escape(EscapeRadius=60),
MoveTowardsCenterOfNearbyVictims1(closeness=50.0, threshold=25.0, speedfactor=100.0, weight=40.0),
TurnAwayFromClosestVictim(threshold=15.0, speedfactor=5.0, weight=20.0),
TurnToAverageDirection(closeness=50.0, weight=40.0),
WrapAroundWindowEdges1()
)
victimRespondbehaviors = (
Die(KillingRadius=20),
TurnAwayFromThreat(threshold=100, speedfactor=200, weight=2),
)
threatbehaviors = (
MoveTowardsCenterOfNearbyVictims2(closeness=200.0, threshold=0, speedfactor=50.0, weight=10.0),
TurnAwayFromWall_2(weight=10.0, BaseImage = baseImage),
Run2(speedlimit=8.0, turnratelimit=math.pi / 10.0),
WrapAroundWindowEdges2()
)# Make some people!
global allvictims, allthreats #allpeople
allvictims = []
allthreats = []
for i in xrange(0, number_of_victim):
allvictims.append(Victim(baseImage))
for j in xrange(0, number_of_threat):
allthreats.append(Threat(baseImage))def draw():
background(255)
image(baseImage,0,0)
for victim in allvictims:
victim.move()
victim.draw()
for threat in allthreats:
threat.move()
threat.draw()
for i in allexitsonImage:
#noFill()
fill(100,255,100)
#stroke(0,255,255)
noStroke()
rect(i['location'][0],i['location'][1],10,10)
filename="VH-{0}.jpg".format(frameCount)
print filename
saveFrame(filename)
if frameCount == 1200:
Dead=0
Escaped=0
Stillalive=0
for victim in allvictims:
if victim.life==0:
Dead+=1
elif victim.life==1:
Stillalive+=1
elif victim.life==2:
Escaped+=1
print Dead
print Escaped
print Stillalive
exit()class Victim(object):
def __init__(self, baseImage):
# Need the baseImage to position the victim on a white pixel
x = random.randrange(400, 600)
y = random.randrange(400, 600)
br = brightness(baseImage.get(x, y))
while br < 20:
x = random.randrange(0, width)
y = random.randrange(0, height)
br = brightness(baseImage.get(x, y))
self.position = [x, y]
self.speed = 0.01
self.direction = random.random() * 2.0 * math.pi - math.pi
self.turnrate = 0
self.victimcolor=[color(0,0,0),color(255,255,0)]
self.life = 1
self.exitsaround = []
self.exit_to_go=()def move(self):
if self.life == 0:
return
if self.life == 2:
return
global allvictims, victimSelfbehaviors,victimRespondbehaviors, allthreats, victimbehaviorsstate = {}# here defining the otherfishes, the "self" in behavior functions doesn't need to be states again when begin a function, so "self" here is read as "fish" in the behavior functions, "fish" here is read as "otherfish" in behavior functions
# This nested loop structure is really expensive. Don't do it
# if it's not strictly necessary for the behaviors of this agent.
for othervictim in allvictims:
if othervictim.life == 1:
for behavior in victimSelfbehaviors:
behavior.setup(self, othervictim, state)
victimbehaviors.setup(self, othervictim, state)for behavior in victimSelfbehaviors:
behavior.apply(self, state)
behavior.draw(self, state)
for threat in allthreats:
for behavior in victimRespondbehaviors:
behavior.setup(self, threat, state)
for behavior in victimRespondbehaviors:
behavior.apply(self, state)
#behavior.draw(self, state)
victimbehaviors.apply(self, state)
#victimbehaviors.draw(self,state)
def draw(self):
if self.life == 0:
pushMatrix()
stroke(0,0,0)
translate(*self.position)
rotate(self.direction)
fill(self.victimcolor[1])
triangle(-10,0,4,-4,4,4)
popMatrix()
if self.life == 1:
pushMatrix()
noStroke()
translate(*self.position)
rotate(self.direction)
fill(self.victimcolor[0])
triangle(-10,0,4,-4,4,4)
popMatrix()
if self.life == 2:
pass
'''pushMatrix()
translate(*self.position)
rotate(self.direction)
fill(self.victimcolor[1])
triangle(-10,0,4,-4,4,4)
popMatrix()'''class Threat(object):
def __init__(self, baseImage):
#self.position = [random.randrange(0, width), random.randrange(0, height)]
x = random.randrange(400, 600)
y = random.randrange(500, 700)
br = brightness(baseImage.get(x, y))
while br < 20:
x = random.randrange(450, 550)
y = random.randrange(450, 550)
br = brightness(baseImage.get(x, y))
self.position = [x,y]
self.speed = 0.02
self.direction = random.random() * 2.0 * math.pi - math.pi
self.turnrate = 0
self.threatcolor=color(255,0,0)
self.life=1def move(self):
global allthreats, threatbehaviors, allvictimsstate = {}
live=1
live2=2# here defining the otherfishes, the "self" in behavior functions doesn't need to be states again when begin a function, so "self" here is read as "fish" in the behavior functions, "fish" here is read as "otherfish" in behavior functions
for victim in allvictims:
if victim.life==1:
#print victim
for behavior3 in threatbehaviors:
behavior3.setup(self, victim, state)
for behavior in threatbehaviors:
behavior.apply(self, state)
behavior.draw(self, state)def draw(self):
pushMatrix()translate(*self.position)
rotate(self.direction)
fill(self.threatcolor)
triangle(-20,0,8,-8,8,8)
popMatrix()
2.2 Define Behavior
2.2 People Behaviors
2.2.1 Move Towards Center Of Nearby People
class MoveTowardsCenterOfNearbyVictims1(VictimSelfBehavior):
def setup(self, victim, othervictim, state):
if victim is othervictim:
return
if 'closecount' not in state:
state['closecount'] = 0.0
if 'center' not in state:
state['center'] = [0.0, 0.0]closeness = self.parameters['closeness']
distance_to_othervictim = dist(
othervictim.position[0], othervictim.position[1],
victim.position[0], victim.position[1]
)
if distance_to_othervictim < closeness:
if state['closecount'] == 0:
state['center'] = othervictim.position
state['closecount'] += 1.0
else:
state['center'][0] *= state['closecount']
state['center'][1] *= state['closecount']state['center'] = [
state['center'][0] + othervictim.position[0],
state['center'][1] + othervictim.position[1]
]state['closecount'] += 1.0state['center'][0] /= state['closecount']
state['center'][1] /= state['closecount']
def apply(self, victim, state):
if state['closecount'] == 0:
returncenter = state['center']
distance_to_center = dist(
center[0], center[1],
victim.position[0], victim.position[1]
)if distance_to_center > self.parameters['threshold']:
angle_to_center = math.atan2(
victim.position[1] - center[1],
victim.position[0] - center[0]
)
if state['closecount'] <= 20:
victim.turnrate += (angle_to_center - victim.direction) / self.parameters['weight']
victim.speed += distance_to_center / self.parameters['speedfactor']
if state['closecount'] > 20:
victim.turnrate -= (angle_to_center - victim.direction) / self.parameters['weight']
victim.speed += (self.parameters['closeness'] - distance_to_center) / self.parameters['speedfactor']def draw(self, victim, state):
closeness = self.parameters['closeness']
stroke(200, 200, 255)
noFill()
2.2.2 Turn Away From Closest Person
class TurnAwayFromClosestVictim(VictimSelfBehavior):
def setup(self, victim, othervictim, state):
if victim is othervictim:
return
if 'closest_victim' not in state:
state['closest_victim'] = None
if 'distance_to_closest_victim' not in state:
state['distance_to_closest_victim'] = 1000000distance_to_othervictim = dist(
othervictim.position[0], othervictim.position[1],
victim.position[0], victim.position[1]
)if distance_to_othervictim < state['distance_to_closest_victim']:
state['distance_to_closest_victim'] = distance_to_othervictim
state['closest_victim'] = othervictimdef apply(self, victim, state):
closest_victim = state['closest_victim']
if closest_victim is None:
returndistance_to_closest_victim = state['distance_to_closest_victim']
if distance_to_closest_victim < self.parameters['threshold']:
angle_to_closest_victim = math.atan2(
victim.position[1] - closest_victim.position[1],
victim.position[0] - closest_victim.position[0]
)
victim.turnrate -= (angle_to_closest_victim - victim.direction) / self.parameters['weight']
victim.speed += self.parameters['speedfactor'] / distance_to_closest_victimdef draw(self, victim, state):
stroke(100, 255, 100)
closest = state['closest_victim']
#line(victim.position[0], victim.position[1], closest.position[0], closest.position[1])
2.2.3 Turn To Average Direction
class TurnToAverageDirection(VictimSelfBehavior):
def setup(self, victim, othervictim, state):
if victim is othervictim:
return
if 'average_direction' not in state:
state['average_direction'] = 0.0
if 'closecount_for_avg' not in state:
state['closecount_for_avg'] = 0.0distance_to_othervictim = dist(
othervictim.position[0], othervictim.position[1],
victim.position[0], victim.position[1]
)closeness = self.parameters['closeness']
if distance_to_othervictim < closeness:
if state['closecount_for_avg'] == 0:
state['average_direction'] = othervictim.direction
state['closecount_for_avg'] += 1.0
else:
state['average_direction'] *= state['closecount_for_avg']
state['average_direction'] += othervictim.direction
state['closecount_for_avg'] += 1.0
state['average_direction'] /= state['closecount_for_avg']def apply(self, victim, state):
if state['closecount_for_avg'] == 0:
return
average_direction = state['average_direction']
victim.turnrate += (average_direction - victim.direction) / self.parameters['weight']
2.2.4 Move Towards Exit
class MoveTowardsExit(VictimSelfBehavior):
def setup(self, victim, othervictim, state):
if victim.life == 1:
closeness = self.parameters['closeness_exit']
for i in xrange(len(self.parameters['allexits'])):
distance_to_exit = dist(self.parameters['allexits'][i]['location'][0],self.parameters['allexits'][i]['location'][1],victim.position[0],victim.position[1])
if distance_to_exit < closeness:
self.parameters['allexits'][i]['count2'] += 1
victim.exitsaround.append(self.parameters['allexits'][i])
#print victim.exitsaround
if victim.exitsaround == []:
pass
else:
minivictim=1000
for exitaround in victim.exitsaround:
if exitaround['count1'] < minivictim:
minivictim=exitaround['count1']
victim.exit_to_go = exitaround['location']
def apply(self, victim, state):
for i in xrange(len(self.parameters['allexits'])):
self.parameters['allexits'][i]['count1']=self.parameters['allexits'][i]['count2']
self.parameters['allexits'][i]['count2']=0
if victim.exitsaround == []:
return
else:
distance_to_exit = dist(victim.position[0],victim.position[1],victim.exit_to_go[0],victim.exit_to_go[0])
angle_to_exit = math.atan2(victim.position[1]-victim.exit_to_go[1],victim.position[0]-victim.exit_to_go[0])
victim.turnrate += (angle_to_exit-victim.direction)/self.parameters['weight']
victim.speed += distance_to_exit / self.parameters['weight']
victim.exitsaround=[]
#victim.exit_to_go=()
def draw(self, victim, state):
#print victim.exit_to_go
if victim.exit_to_go == ():
pass
else:
#line (victim.position[0],victim.position[1],victim.exit_to_go[0],victim.exit_to_go[1])
pass
victim.exit_to_go=()class Escape(VictimSelfBehavior):
def setup(self, victim, othervictim, state):
Escaperadius = self.parameters['EscapeRadius']
if 'life' not in state:
state['life'] = 1
for allexit in victim.exitsaround:
distance_to_exit = dist(allexit['location'][0],allexit['location'][1],victim.position[0],victim.position[1])
if distance_to_exit <=60:
victim.life = 2
state['life'] = 2
def apply(self, victim, state):
if state['life'] == 2:
victim.life = 2
def draw(self,victim, state):
pass
2.2.5 Turn Away From Wall
def setup(self, victim, othervictim, state):
img=self.parameters['BaseImage']
# define location of antenna
if 'antenna' not in state:
state['antenna']=[1,1,1]
x = victim.position[0]
y = victim.position[1]
direction = victim.direction
direction1 = direction + math.pi / 12.0
direction2 = direction - math.pi / 12.0
distance = -10 # Because of a motion bug in the original example,
# the agents are actually moving backwards.
# Compensate by using a negative antenna length.
#antenna1
x0=floor(x+distance*cos(direction))
if x0 >= width:
x0 = x0-width
if x0 < 0:
x0 = width+x0
y0=floor(y+distance*sin(direction))
if y0 >= height:
y0 = y0-height
if y0 < 0:
y0 = height+y0
#antenna2
x1=floor(x+distance*cos(direction1))
if x1 >= width:
x1 = x1-width
if x1 < 0:
x1 = width+x1
y1=floor(y+distance*sin(direction1))
if y1 >= height:
y1 = y1-height
if y1 < 0:
y1 = height+y1
#antenna3
x2=floor(x+distance*cos(direction2))
if x2 >= width:
x2 = x2-width
if x2 < 0:
x2 = width+x2
y2=floor(y+distance*sin(direction2))
if y2 >= height:
y2 = y2-height
if y2 < 0:
y2 = height+y2
xs=[x0,x1,x2]
ys=[y0,y1,y2]# define if victims collide with walls
#img = loadImage("Times_Square.jpg")
#img.loadPixels()
for i in xrange(3):
# Draw the antenna for debugging
stroke(128, 0, 0)
point(xs[i], ys[i])
# extract the pixel color underneath the antenna
px = img.get(xs[i], ys[i])
b = brightness(px)
if b < 20:
state['antenna'][i] = 0
else:
state['antenna'][i] = 1
# turn direction
def apply(self, victim, state):
if 'antenna' not in state:
self.setup(victim, None, state)
if state['antenna'][0] == 0:
if state['antenna'][1] == 0 and state['antenna'][2] == 1:
victim.turnrate -= (math.pi/2) #/ self. parameters['weight']
elif state['antenna'][1] == 1 and state['antenna'][2] == 0:
victim.turnrate += (math.pi/2) #/ self. parameters['weight']
else:
'''t=random.randrange(0,2)
if t <=1:
factor=1
else:
factor=-1'''
victim.turnrate += (math.pi/2) #/ self. parameters['weight']
2.2.6 Turn Away From Attacker
class TurnAwayFromThreat(VictimRespondBehavior):
def setup(self, victim, threat, state):if 'closest_threat' not in state:
state['closest_threat'] = None
if 'distance_to_closest_threat' not in state:
state['distance_to_closest_threat'] = 1000000distance_to_threat = dist(
victim.position[0], victim.position[1],
threat.position[0], threat.position[1]
)if distance_to_threat < state['distance_to_closest_threat']:
state['distance_to_closest_threat'] = distance_to_threat
state['closest_threat'] = threatdef apply(self, victim, state):
closest_threat = state['closest_threat']
if closest_threat is None:
returndistance_to_closest_threat = state['distance_to_closest_threat']
if distance_to_closest_threat < self.parameters['threshold']:
angle_to_closest_threat = math.atan2(
victim.position[1]-closest_threat.position[1] ,
victim.position[0]-closest_threat.position[0]
)
victim.turnrate -= (angle_to_closest_threat - victim.direction) / self.parameters['weight']
victim.speed += self.parameters['speedfactor'] / distance_to_closest_threatdef draw(self, victim, state):
stroke(100, 255, 100)
closest = state['closest_threat']
line(victim.position[0], victim.position[1], closest.position[0], closest.position[1])
2.2.7 People Get Injured By Attacker
class Die(VictimRespondBehavior):
def setup(self, victim, threat, state):
killingradius = self.parameters['KillingRadius']
if 'life' not in state:
state['life'] = 1
distance_to_threat=dist(threat.position[0],threat.position[1],victim.position[0],victim.position[1])
if distance_to_threat <= killingradius:
state['life'] = 0
def apply(self, victim, state):
if state['life'] == 0:
victim.life = 0
def draw(self,victim, state):
pass
2.2.8 Speed Up (SelfBehavior)
class Run1(VictimSelfBehavior):
def setup(self, victim, othervictim, state):
victim.speed = 1
victim.turnrate = 0def apply(self, victim, state):
if victim.turnrate > self.parameters['turnratelimit']:
victim.turnrate = self.parameters['turnratelimit']
if victim.turnrate < -self.parameters['turnratelimit']:
victim.turnrate = -self.parameters['turnratelimit']
victim.direction += victim.turnrate
if victim.speed > self.parameters['speedlimit']:
victim.speed = self.parameters['speedlimit']
victim.position[0] -= math.cos(victim.direction) * victim.speed
victim.position[1] -= math.sin(victim.direction) * victim.speed
if victim.direction > math.pi:
victim.direction -= 2 * math.pi
if victim.direction < -math.pi:
victim.direction += 2 * math.pi
2.2.9 Speed Up (Interaction)
class Run3(VictimRespondBehavior):
def setup(self, victim, threat, state):
#if victim.speed is None:
victim.speed = 0
#if victim.turnrate is None:
victim.turnrate = 0def apply(self, victim, state):
if victim.turnrate > self.parameters['turnratelimit']:
victim.turnrate = self.parameters['turnratelimit']
if victim.turnrate < -self.parameters['turnratelimit']:
victim.turnrate = -self.parameters['turnratelimit']
victim.direction += victim.turnrate
if victim.speed > self.parameters['speedlimit']:
victim.speed = self.parameters['speedlimit']
victim.position[0] -= math.cos(victim.direction) * victim.speed
victim.position[1] -= math.sin(victim.direction) * victim.speed
if victim.direction > math.pi:
victim.direction -= 2 * math.pi
if victim.direction < -math.pi:
victim.direction += 2 * math.pi
2.3 Attacker Behaviors
2.3.1 Move Towards Center Of Nearby People
class MoveTowardsCenterOfNearbyVictims2(ThreatBehavior):
def setup(self, threat, victim, state):
if 'closecount' not in state:
state['closecount'] = 0.0
if 'center' not in state:
state['center'] = [0.0, 0.0]closeness = self.parameters['closeness']
distance_to_victim = dist(
victim.position[0], victim.position[1],
threat.position[0], threat.position[1]
)
if distance_to_victim < closeness:
if state['closecount'] == 0:
state['center'] = victim.position
state['closecount'] += 1.0
else:
state['center'][0] *= state['closecount']
state['center'][1] *= state['closecount']state['center'] = [
state['center'][0] + victim.position[0],
state['center'][1] + victim.position[1]
]state['closecount'] += 1.0state['center'][0] /= state['closecount']
state['center'][1] /= state['closecount']
def apply(self, threat, state):
if state['closecount'] == 0:
returncenter = state['center']
distance_to_center = dist(
center[0], center[1],
threat.position[0], threat.position[1]
)if distance_to_center > self.parameters['threshold']:
angle_to_center = math.atan2(
threat.position[1] - center[1],
threat.position[0] - center[0]
)
threat.turnrate += (angle_to_center - threat.direction) / self.parameters['weight']
threat.speed += distance_to_center / self.parameters['speedfactor']def draw(self, threat, state):
closeness = self.parameters['closeness']
stroke(200, 200, 255)
noFill()
ellipse(threat.position[0], threat.position[1], closeness * 2, closeness * 2)
#ellipse(otherfish.position[0], otherfish.position[1], closeness * 0.1, closeness * 0.1)
#print otherfish.position'''
2.3.2 Speed Up
class Run2(ThreatBehavior):
def setup(self, threat, victim, state):
threat.speed = 2
threat.turnrate = 0def apply(self, threat, state):
if threat.speed > self.parameters['speedlimit']:
threat.speed = self.parameters['speedlimit']
threat.position[0] -= math.cos(threat.direction) * threat.speed
threat.position[1] -= math.sin(threat.direction) * threat.speed
if threat.turnrate > self.parameters['turnratelimit']:
threat.turnrate = self.parameters['turnratelimit']
if threat.turnrate < -self.parameters['turnratelimit']:
threat.turnrate = -self.parameters['turnratelimit']
threat.direction += threat.turnrate
if threat.direction > math.pi:
threat.direction -= 2 * math.pi
if threat.direction < -math.pi:
threat.direction += 2 * math.pi
3.Site Context
We select 4 urban locations all highly populated in new year celebration from 4 typical urban gathering space.
I. Small-size street intersection square_Times Square, New York.
II. Large-size open square surrounded by urban roads_Trafalgar Square, London.
III. Large-size enclosed square surrounded by buildings_Piazza Del Campo, Siena.
IV. Urban linear open space_Victoria Harbour, Hong Kong.
4.Evacuation Stimulation in Processing
https://vimeo.com/247191582
5. Analysis and Conclusion
5.1 Analysis
A. Regular experiment
B. When double the exit closeness distance
I. Small-size street intersection square_Times Square, New York.
II. Large-size open square surrounded by urban roads_Trafalgar Square, London.
III. Large-size enclosed square surrounded by buildings_Piazza Del Campo, Siena.
IV. Urban linear open space_Victoria Harbour, Hong Kong.
5.2 Result Analysis
People are more likely to die in the large-size open square.
More people escaped from small-size street intersection square and urban linear space.
People are more likely to be trapped in large-size enclosed square.
5.3 Conclusion
Create more signage to help people clearly know their locations and correct direction in large-size squares.
Enhance exit capacity and efficiency in enclosed squares.