基本思路

基本的思路就是:

Django 框架下,收到 request 之后,运行相应函数,异步运行 simulation.py 以及返回相应的 html 文件。

simulation.py 会进行 sumo 仿真,并且每一个仿真时间内都会将该时间内每一辆车的经纬度信息储存到数据库中。同时 html 文件中的 javascript 会利用 ajax 联合 python 访问数据库中的信息并返回。(ajax 图方便关闭了异步功能,等我学明白 javascript 就写递归)

获得信息后就将数据点加入 cesium 中,然后 cesium 可以异步进行模型的运动模拟。

分步介绍

1 sumo 文件

就是很简单的三件套,路网文件、路由文件以及 sumocfg 文件。只是多了一个仿真文件 simulation.py 而已。

这个仿真文件不仅仅是通过 traci 来实现仿真,同时在每个访问时间内都会把上一个访问时间内的每辆车的经纬度存放在 MySQL 数据库中。

当然,在这之前要记得创建数据库,并且在数据库中创建一个名为 data 的表格。这个表格应该要有四列:

TIMEID (int)CARID (archer(20))LONGITUDE (double)LATITUDE (double)(别骂了我知道还要有 height,下次一定)

def run():

db = pymysql.connect(host="localhost",

port=xxxx,

user="root", password="xxxxxxxxxx",

database="mission",

charset="utf8")

cursor = db.cursor()

sql = """truncate data"""

try:

cursor.execute(sql)

db.commit()

except:

db.rollback()

step = 0

nett = net.readNet('/Users/gankutsuou/Desktop/项目/sync/app/static/sumo/test.net.xml')

while traci.simulation.getMinExpectedNumber() > 0:

traci.simulationStep()

id_list = traci.vehicle.getIDList()

for i in id_list:

tt = traci.vehicle.getPosition(i)

sql = "insert into data(TIMEID, CARID, LONGITUDE, LATITUDE) VALUES(%s, '%s', %s, %s)" % (step, i, tt[0], tt[1])

try:

cursor.execute(sql)

db.commit()

except:

db.rollback()

step += 1

cursor.close()

db.close()

traci.close()

sys.stdout.flush()

2 Django

Django 是一个由 Python 编写的一个开放源代码的 Web 应用框架。为什么选择他呢?首先因为它是 Python 编写的,而这个语言我会。另外就是感觉要比 Node 要稍微简单一些。

2.1 创建标准 Django 文件

Django 可以通过 pip 和 conda 来进行安装,非常简单,不多赘述。然后打开 PyCharm专业版,可以直接创建 Django 项目文件夹。删除掉文件夹中的 template 文件夹,并在 setting.py 中删除那个文件夹的路径信息。

2.2 创建 app 文件

在主页面中打开终端,输入

django-admin startapp [name of your app]

在这个页面创建 static 和 template 文件夹,分别用来储存静态文件和 html 页面。把 cesium文件夹一股脑放进 static 文件夹里面,顺便再在里面再创建一个 sumo 文件夹,里面存放 sumo 相关的文件。然后在 template 文件夹里面创建一个我们的 html 文件。

这样就完成了基本文件夹的创建。

2.3 编写函数

首先在在文件夹中的 url.py 中编写:

from django.urls import path

from app import views

urlpatterns = [

path('index/', views.index),

]

然后在 app 文件中的 views.py 中编写函数:

def index(request):

subprocess.Popen("python /Users/gankutsuou/Desktop/项目/sync/app/static/sumo/simulation.py", shell=True)

time.sleep(2)

return render(request, "HelloWorld.html")

基本原理就是浏览器在访问 xxx.com/index/ 的时候会发回 request,然后就回运行 index 函数。

subprocess 是我找到的一个可以非阻塞运行 py 文件的小奇技淫巧。这样就可以不用非要等着仿真跑完再返回页面了!

3 cesium 方面

我 tm 发现 javascript 居然是不能够直接通过库来访问数据库的! 垃圾语言啊

没有办法,那就只好借助一下 Ajax了。

3.1 Ajax

在 Django 中实现 Ajax,那就要再处理一下 url.py 和 views.py 了。

urlpatterns = [

path('index/', views.index),

path('ajax/get/', views.ajax_get),

path('ajax/get2/', views.ajax_get2),

]

def ajax_get(request):

a = request.GET.get("a")

b = request.GET.get("b")

db = pymysql.connect(host="localhost",

port=3306,

user="root", password="xxxxxxxxxx",

database="mission",

charset="utf8")

cursor = db.cursor()

sql = "select LONGITUDE, LATITUDE from data where TIMEID = %s and CARID = 'veh%s'" % (a, b)

try:

cursor.execute(sql)

db.commit()

except:

db.rollback()

c = cursor.fetchone()

cursor.close()

db.close()

return HttpResponse(c[0])

def ajax_get2(request):

a = request.GET.get("a")

b = request.GET.get("b")

db = pymysql.connect(host="localhost",

port=3306,

user="root", password="xxxxxxxxxx",

database="mission",

charset="utf8")

cursor = db.cursor()

sql = "select LONGITUDE, LATITUDE from data where TIMEID = %s and CARID = 'veh%s'" % (a, b)

try:

cursor.execute(sql)

db.commit()

except:

db.rollback()

c = cursor.fetchone()

cursor.close()

db.close()

return HttpResponse(c[1])

这么处理之后,就可以在 html 文件里使用 Ajax 啦。

3.2 script

for (let i = 0; i < 20; i++) {

var k = 0;

var kk = 0;

$.ajax({

type: 'GET',

url: "/ajax/get/",

data: { 'a': i, 'b': 0 },

async: false,

success: function(ret) {

{#$('#result_get').html(ret);#}

k = Number(ret);

}

});

$.ajax({

type: 'GET',

url: "/ajax/get2/",

data: { 'a': i, 'b': 0 },

async: false,

success: function(ret) {

{#$('#result_get').html(ret);#}

kk = Number(ret);

}

});

var kkk = new Cesium.Cartesian3(k, kk, 6357068.77482456663);

var kkkk = Cesium.Cartographic.fromCartesian(kkk)

const dataPoint = { longitude: kkkk.longitude, latitude: kkkk.latitude, height: kkkk.height };

不要在意我的命名啦!

而且现在由于我还没学怎么导入多辆车,所以现在就只用了一辆车,就没有 b 的循环捏。

而且因为我的 sumo 地图是乱画的,所以坐标是没有办法转化为经纬度的。所以高程我就先给他内定了。然后在这里我把笛卡尔坐标系转化为了经纬度,放入 dataPoint 里面。

其他的就自己随便写写就好啦。我写的是芳 carla 式的视角跟随。

const time = Cesium.JulianDate.addSeconds(start, i * timeStepInSeconds, new Cesium.JulianDate());

const position = Cesium.Cartesian3.fromDegrees(dataPoint.longitude, dataPoint.latitude, dataPoint.height);

// Store the position along with its timestamp.

// Here we add the positions all upfront, but these can be added at run-time as samples are received from a server.

positionProperty.addSample(time, position);

viewer.entities.add({

description: `Location: (${dataPoint.longitude}, ${dataPoint.latitude}, ${dataPoint.height})`,

position: position,

point: { pixelSize: 10, color: Cesium.Color.RED }

});

}

// STEP 4 CODE (green circle entity)

// Create an entity to both visualize the entire radar sample series with a line and add a point that moves along the samples.

async function loadModel() {

// Load the glTF model from Cesium ion.

const airplaneUri = await Cesium.IonResource.fromAssetId(1232489);

const airplaneEntity = viewer.entities.add({

availability: new Cesium.TimeIntervalCollection([ new Cesium.TimeInterval({ start: start, stop: stop }) ]),

position: positionProperty,

// Attach the 3D model instead of the green point.

model: { uri: airplaneUri },

// Automatically compute the orientation from the position.

orientation: new Cesium.VelocityOrientationProperty(positionProperty),

path: new Cesium.PathGraphics({ width: 3 })

});

viewer.trackedEntity = airplaneEntity;

}

以上就是本次项目的基本思路写法,细节就没有赘述。

项目地址

查看原文