总体思路
疫情的模拟除了有人,我们还希望将“人”置于一个更加真实的环境中,使人在疫情中的行为更加“合理”。于是我们构建了若干个虚拟的“城市”,城市中有各种建筑,有道路。在保证我们需要的城市功能完整的情况下,为了降低城市的复杂度,我们在每个城市中设置了home(住宅)、market(菜场)、workplace(工作场所)、hospital(医院)、station(高铁站)、cross(路口)六种地标。这些地标在程序中都会被实例化为继承自place的子类对象,每个place对象中都存有这个对象的唯一标识符index、坐标position、到其他各个场所的路径集合,以及一些其他信息。
这部分的工作大体可以分为两个部分:1.城市的视效设计,2.各个“PlaceSystem”(为了方便代码的管理与调试,我们为每种地标构建了继承自“PlaceSystem”的子类系统)的初始化工作。
城市视效设计
这里的城市地图为我们的第一版设计,城市的布局参考了中心城市与其周边卫星城市的布局关系。这种布局能够使得每个简化版的城市之间联系更加紧密。在程序建模过程中,我们则并没有以中心城市与周边城市这种关系去定义这些城市。事实上,这种关系在这些虚拟的“微型”城市中会被弱化。城市中道路的设计我们参考了多种城市布局,包括网格状、环形放射状等。在绘制过程中,我们对其风格上进行了统一化处理,使其看上去更像一个整体。在城市的复杂度方面我们进行了多次尝试,综合考量了屏幕大小,视觉效果,人的运动等多方面因素之后,绘制了如上图所示的最终效果。
PlaceSystem的构建与初始化
对城市中的每种地标,我们都构建了一个继承自PlaceSystem的子类,以存放城市中每一个Place对象。因此,我们首先设想并讨论了各个“place”类的设计方案:
Place(父类)
父类Place包含四个属性:1.地点的类别——class;2.唯一标识地点的标识符——index;3.Place对象的坐标——pos;4.停留时间——staytime。
除此以外,人要在地图中沿着道路有目的地地运动起来,每一个place对象中还应该包括通往其他各类(除cross以外的类别)地点的路径。为了避免一些不太合理的“行为”,比如买完菜去上班,从医院去菜场等,我们将路径信息单独写在了每一个子类中:
Home(Place)
1.way_to_hospital:去往医院的路;
2.way_to_market:去往菜场的路;
3.way_to_station:去往车站的路;
4.way_to_work:去往工作的路;
Hospital(Place)
1.way_to_home:去往家的路;
2.way_to_work:去往工作的路;
3.intake:医院容纳量;
4.patientnum:已容纳人数
Work(Place)
1.way_to_hospital:去往医院的路;
2.way_to_market:去往菜场的路;
3.way_to_station:去往车站的路;
4.way_to_home:去往家的路;
Market(Place)
1.way_to_home:去往家的路;
Station(Place)
1.way_to_hospital:去往医院的路;
2.way_to_market:去往菜场的路;
3.way_to_home:去往家的路;
4.way_to_work:去往工作的路;
初始化
有了上述的想法之后,需要完成各个地点的初始化工作。初始化工作主要包括三部分:1.坐标信息以及标识符等基础信息的记录;2.各个地点路径的生成;3.数据记录与转存。
初始化——标识符、坐标信息
标识符、坐标信息的初始化我们通过ps标点实现,并将其记录到csv格式的文件中。
在csv文件中,我们还记录了与每一个地点直接相邻的地点标识,这一步的目的是为了初始化路径。
初始化——基于广度优先搜索初始化路径
路径初始化步骤在python中实现:
1.文件读取,构建各地点的邻接表:
def get_road(file_path):
sheet = pd.read_excel(file_path)
label_list = list(sheet["label"])
label_class={}
label_set=set(list(sheet["class"]))
for sets in label_set:
label_class[sets]=[]
#print(label_class)
place_info={}
roads_tol = {}
for i in range(len(label_list)):
num_of_near = sheet.iloc[i, 4]
roads_tol[label_list[i]] = list(sheet.iloc[i, 5:5 + num_of_near])
classs=sheet.iloc[i,0]
label_class[classs].append(sheet.iloc[i, 1])
#place_info:
place_info[label_list[i]]={}
#index:
place_info[label_list[i]]['index']=label_list[i]
#position:
position_str=sheet.iloc[i,3][1:-1].split(',')
position=[float(pos) for pos in position_str]
place_info[label_list[i]]['position']=position
#class:
place_info[label_list[i]]['class'] = classs
#print(label_list,'\n',roads_tol,'\n',label_class,'\n',place_info)
return label_list,roads_tol,label_class,place_info
2.构建广度优先搜索类,并定义最短路径生成函数:
class BFS_ROAD():
def __init__(self, begin, label_list, roads_tol):
self.begin = begin
self.label_list = label_list
self.roads_tol = roads_tol
def init_dist(self):
dist={}
roads={}
visted={}
for label in self.label_list:
dist[label]=10000
roads[label]=[]
visted[label]=False
dist[self.begin]=1
roads[self.begin]=[self.begin]
return dist,roads,visted
def bfs(self,target,roads_tol):
#print(roads_tol)
dist,roads,visted=self.init_dist()
queue_save=[self.begin]
while len(queue_save):
place_out=queue_save[0]
visted[place_out]=True
queue_save=queue_save[1:]
for near in roads_tol[place_out]:
#print(near,visted)
if not visted[near]:
if dist[near] > dist[place_out] + 1:
dist[near] = dist[place_out] + 1
roads_tmp=copy.deepcopy(roads[place_out])
roads_tmp.append(near)
roads[near] = roads_tmp
queue_save.append(near)
#print(roads[target])
return roads[target]
3.上述函数只是实现了最短路径的搜索,但在实际情况中,两个地点之间的路径一般不止一条,且单一的路径选择显得真实性不高。为了解决这一问题, 我们的做法是首先找到最短路径,随后随机删除最短路径中的某一段通路,在对删除后的路径进行广度优先搜索得到另一条通路。
def road_fromto(begin,end,inputfile):
"""
:param begin: begin place
:param end: end class
:return:
"""
label_list, roads_tol ,road_set,place_info= get_road(inputfile)
road_ends=road_set[end]
#print(road_ends)
roada = BFS_ROAD(begin, label_list, roads_tol)
roads_to_places=[]
for road_end in road_ends:
#print(roada.label_list)
road1 = roada.bfs(road_end, roada.roads_tol)
roadss = [road1]
for i in range(len(road1) - 1):
roads_tol_tmp = copy.deepcopy(roada.roads_tol)
roads_tol_tmp[road1[i]].remove(road1[i + 1])
road2 = roada.bfs(road_end, roads_tol_tmp)
roadss.append(road2)
new_road = []
for road in roadss:
if road not in new_road:
new_road.append(road)
for road in new_road:
roads_to_places.append(road)
return roads_to_places
初始化——基于json对数据进行转存
python生成的路径信息最终需要转存到p5.js的程序中。起初我们尝试了使用csv文件对地点的基础属性以及路径信息进行转存,但发现效率较低,且在读取多张表时信息的对应问题难以解决。
于是我们尝试使用了json格式的文件进行数据转存,使得问题得以很好地解决:
在python中保存json数据:
def write_json(inputfile,outputfile):
label_list, roads_tol, road_set ,place_info= get_road(inputfile)
for place in road_set["hm"]:
place_info[place]['roads_otherplace']={}
roads_work = road_fromto(place, "w", inputfile)
roads_work=[road for road in roads_work if road!=[]]
roads_station = road_fromto(place, "s", inputfile)
roads_station = [road for road in roads_station if road != []]
roads_hospital=road_fromto(place, "ho", inputfile)
roads_hospital = [road for road in roads_hospital if road != []]
roads_market=road_fromto(place,'m',inputfile)
roads_market = [road for road in roads_market if road != []]
place_info[place]['roads_otherplace']['work']=roads_work
place_info[place]['roads_otherplace']['station'] = roads_station
place_info[place]['roads_otherplace']['hospital'] = roads_hospital
place_info[place]['roads_otherplace']['market'] = roads_market
for place in road_set["ho"]:
place_info[place]['roads_otherplace']={}
roads_work = road_fromto(place, "w", inputfile)
roads_work=[road for road in roads_work if road!=[]]
roads_home = road_fromto(place, "hm", inputfile)
roads_home = [road for road in roads_home if road != []]
place_info[place]['roads_otherplace']['work']=roads_work
place_info[place]['roads_otherplace']['home'] = roads_home
for place in road_set["w"]:
place_info[place]['roads_otherplace']={}
roads_home = road_fromto(place, "hm", inputfile)
roads_home=[road for road in roads_home if road!=[]]
roads_station = road_fromto(place, "s", inputfile)
roads_station = [road for road in roads_station if road != []]
roads_hospital=road_fromto(place, "ho", inputfile)
roads_hospital = [road for road in roads_hospital if road != []]
roads_market=road_fromto(place,'m',inputfile)
roads_market = [road for road in roads_market if road != []]
place_info[place]['roads_otherplace']['home']=roads_home
place_info[place]['roads_otherplace']['station'] = roads_station
place_info[place]['roads_otherplace']['hospital'] = roads_hospital
place_info[place]['roads_otherplace']['market'] = roads_market
for place in road_set["s"]:
place_info[place]['roads_otherplace']={}
roads_home = road_fromto(place, "hm", inputfile)
roads_home=[road for road in roads_home if road!=[]]
roads_work = road_fromto(place, "w", inputfile)
roads_work = [road for road in roads_work if road != []]
roads_hospital=road_fromto(place, "ho", inputfile)
roads_hospital = [road for road in roads_hospital if road != []]
roads_market=road_fromto(place,'m',inputfile)
roads_market = [road for road in roads_market if road != []]
place_info[place]['roads_otherplace']['home']=roads_home
place_info[place]['roads_otherplace']['work'] = roads_work
place_info[place]['roads_otherplace']['hospital'] = roads_hospital
place_info[place]['roads_otherplace']['market'] = roads_market
for place in road_set["m"]:
place_info[place]['roads_otherplace']={}
roads_home = road_fromto(place, "hm", inputfile)
roads_home=[road for road in roads_home if road!=[]]
place_info[place]['roads_otherplace']['home']=roads_home
print(place_info)
place_info_str=json.dumps(place_info,indent=4)
with open (outputfile,'w') as json_file:
json_file.write(place_info_str)
在p5.js中读取并初始化类:
function preload(){
city1_json=loadJSON("data/Place_info_json/city1_placeinfo.json")
city2_json=loadJSON("data/Place_info_json/city2_placeinfo.json")
city3_json=loadJSON("data/Place_info_json/city3_placeinfo.json")
city4_json=loadJSON("data/Place_info_json/city4_placeinfo.json")
city5_json=loadJSON("data/Place_info_json/city5_placeinfo.json")
city6_json=loadJSON("data/Place_info_json/city6_placeinfo.json")
}
function System_data_pre(data_json){
for (let key in data_json){
if(data_json[key].class=='hm'){
let place=[]
//index:
place.push(data_json[key].index)
//position:
place.push(createVector(data_json[key].position[0],data_json[key].position[0]))
//ways:
let way_tol=data_json[key].roads_otherplace
let ways=[]
let way_work=[]
let way_station=[]
let way_hospital=[]
let way_market=[]
for (let i in way_tol.work){
way=way_tol.work[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_work.push(way_tmp)
}
ways.push(way_work)
for (let i in way_tol.station){
way=way_tol.station[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_station.push(way_tmp)
}
ways.push(way_station)
for (let i in way_tol.hospital){
way=way_tol.hospital[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_hospital.push(way_tmp)
}
ways.push(way_hospital)
for (let i in way_tol.market){
way=way_tol.market[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_market.push(way_tmp)
}
ways.push(way_market)
place.push(ways)
homes.push(place)
}
else if(data_json[key].class=='ho'){
let place=[]
//index:
place.push(data_json[key].index)
//position:
place.push(createVector(data_json[key].position[0],data_json[key].position[0]))
//ways:
let way_tol=data_json[key].roads_otherplace
let ways=[]
let way_work=[]
let way_home=[]
for (let i in way_tol.work){
way=way_tol.work[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_work.push(way_tmp)
}
ways.push(way_work)
for (let i in way_tol.home){
way=way_tol.home[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_home.push(way_tmp)
}
ways.push(way_home)
place.push(ways)
hospitals.push(place)
}
else if(data_json[key].class=='w'){
let place=[]
//index:
place.push(data_json[key].index)
//position:
place.push(createVector(data_json[key].position[0],data_json[key].position[0]))
//ways:
let way_tol=data_json[key].roads_otherplace
let ways=[]
let way_home=[]
let way_station=[]
let way_hospital=[]
let way_market=[]
for (let i in way_tol.home){
way=way_tol.home[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_home.push(way_tmp)
}
ways.push(way_home)
for (let i in way_tol.station){
way=way_tol.station[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_station.push(way_tmp)
}
ways.push(way_station)
for (let i in way_tol.hospital){
way=way_tol.hospital[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_hospital.push(way_tmp)
}
ways.push(way_hospital)
for (let i in way_tol.market){
way=way_tol.market[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_market.push(way_tmp)
}
ways.push(way_market)
place.push(ways)
works.push(place)
}
else if(data_json[key].class=='m'){
let place=[]
//index:
place.push(data_json[key].index)
//position:
place.push(createVector(data_json[key].position[0],data_json[key].position[0]))
//ways:
let way_tol=data_json[key].roads_otherplace
let ways=[]
let way_home=[]
for (let i in way_tol.home){
way=way_tol.home[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_home.push(way_tmp)
}
ways.push(way_home)
place.push(ways)
markets.push(place)
}
else if(data_json[key].class=='s'){
let place=[]
//index:
place.push(data_json[key].index)
//position:
place.push(createVector(data_json[key].position[0],data_json[key].position[0]))
//ways:
let way_tol=data_json[key].roads_otherplace
let ways=[]
let way_work=[]
let way_home=[]
let way_hospital=[]
let way_market=[]
for (let i in way_tol.work){
way=way_tol.work[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_work.push(way_tmp)
}
ways.push(way_work)
for (let i in way_tol.home){
way=way_tol.home[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_home.push(way_tmp)
}
ways.push(way_home)
for (let i in way_tol.hospital){
way=way_tol.hospital[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_hospital.push(way_tmp)
}
ways.push(way_hospital)
for (let i in way_tol.market){
way=way_tol.market[i]
let way_tmp=[]
for (let j in way){
let place_one=way[j]
way_tmp.push(createVector(data_json[place_one].position[0],data_json[place_one].position[1]))
}
way_market.push(way_tmp)
}
ways.push(way_market)
place.push(ways)
stations.push(place)
}
}
}
function city_init(){
let city1=new City("city1",["s1"],0.38)
let city2=new City("city2",["s2"],0.67)
let city3=new City("city3",["s3"],0.71)
let city4=new City("city4",["s4"],0.89)
let city5=new City("city5",["s5"],0.54)
let city6=new City("city6",["s6","s7"],0.97)
let citys=[city1,city2,city3,city4,city5,city6]
return citys
}
function init_cityandwaytostation()
{
//current_cities
for(let i=0;i<6;i++){
statSys.places[i].currentCity=citySys.citys[i]
}
statSys.places[6].currentCity=citySys.citys[5]
//next_stations:
statSys.places[0].connectstations=[statSys.places[1],statSys.places[3]]
statSys.places[1].connectstations=[statSys.places[0],statSys.places[3],statSys.places[5],statSys.places[6]]
statSys.places[2].connectstations=[statSys.places[3],statSys.places[4]]
statSys.places[3].connectstations=[statSys.places[0],statSys.places[1],statSys.places[2]]
statSys.places[4].connectstations=[statSys.places[2],statSys.places[5],statSys.places[6]]
statSys.places[5].connectstations=[statSys.places[1],statSys.places[4],statSys.places[6]]
statSys.places[6].connectstations=[statSys.places[1],statSys.places[4],statSys.places[5]]
//way_to_stations:
//s1:
statSys.places[0].way_to_station=[[statSys.places[0].pos,createVector(4.29,8.34),createVector(4.64,8.64),createVector(7.53,8.20),statSys.places[1].pos],
[statSys.places[0].pos,createVector(5.70,4.75),statSys.places[3].pos]]
statSys.places[1].way_to_station=[[statSys.places[1].pos,createVector(7.53,8.20),createVector(4.64,8.64),createVector(4.29,8.34),statSys.places[0].pos],
[statSys.places[1].pos,statSys.places[3].pos],
[statSys.places[1].pos,statSys.places[5].pos],
[statSys.places[1].pos,statSys.places[6].pos]]
statSys.places[2].way_to_station=[[statSys.places[2].pos,createVector(8.61,2.66),statSys.places[3].pos],
[statSys.places[2].pos,createVector(11.12,3.85),statSys.places[4].pos]]
statSys.places[3].way_to_station=[[statSys.places[3].pos,createVector(5.70,4.75),statSys.places[0].pos],
[statSys.places[3].pos,statSys.places[1].pos],
[statSys.places[3].pos,createVector(8.61,2.66),statSys.places[2].pos]]
statSys.places[4].way_to_station=[[statSys.places[4].pos,createVector(11.12,3.85),statSys.places[2].pos],
[statSys.places[4].pos,createVector(14.35,4.85),statSys.places[5].pos],
[statSys.places[4].pos,statSys.places[6].pos]]
statSys.places[5].way_to_station=[[statSys.places[5].pos,statSys.places[1].pos],
[statSys.places[5].pos,createVector(14.35,4.85),statSys.places[4].pos]
[statSys.places[5].pos,statSys.places[6].pos]]
statSys.places[6].way_to_station=[[statSys.places[6].pos,statSys.places[1].pos],
[statSys.places[6].pos,statSys.places[4].pos],
[statSys.places[6].pos,statSys.places[5].pos]]
}
function initSystem_fromjson(){
citys=city_init()
citySys=new CitySystem(citys)
homes=[]
hospitals=[]
works=[]
markets=[]
stations=[]
System_data_pre(city1_json)
System_data_pre(city2_json)
System_data_pre(city3_json)
System_data_pre(city4_json)
System_data_pre(city5_json)
System_data_pre(city6_json)
homeSys=new HomeSystem(homes)
hospSys=new HospitalSystem(hospitals)
workSys=new WorkSystem(works)
markSys=new MarketSystem(markets)
statSys=new StationSystem(stations)
init_cityandwaytostation()
console.log(statSys)
console.log(homeSys)
console.log(hospSys)
console.log(workSys)
console.log(markSys)
console.log(citySys)
}
function setup() {
initSystem_fromjson()
createCanvas(640, 320)
colorMode(HSB, 360, 100, 100, 100)
}