一 内容和设计思想
1,获取车辆实时位置,将位置信息通过node实时更新到数据库中
2,构造数据库触发器,监测数据库数据变化,当实时位置更新到数据库时,通过postgis聚类函数查找离车辆指定距离车辆数目,数目越大、密度越大,道路越拥堵。
3,根据聚类分析后的密度判断道路拥挤程度
二 代码实现(leaflet+node+postgis)
1,构造触发器
js
CREATE OR REPLACE FUNCTION changes()
RETURNS TRIGGER AS
$$
BEGIN
-- 发送 JSON 格式的变化数据到名为 'spatial_table_changes' 的通道
PERFORM pg_notify('car_changes', row_to_json(NEW)::text);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER after_change
AFTER INSERT OR UPDATE OR DELETE
ON car
FOR EACH ROW
EXECUTE FUNCTION changes();
2,node接受请求进行查询
js
const express = require('express');
const router = express.Router();
const pool = require('../db'); // 引入数据库连接配置
//更新小车实时位置
router.post('/data', async (req, res) => {
const { param1, param2 } = req.body;
try {
// 将 param1 和 param2 合并为一个点
const pointText = `POINT(${param2} ${param1})`;
// 在这里执行数据库更新,假设你的数据库表使用 geometry 类型的字段
const result = await pool.query('UPDATE car SET geom = ST_GeomFromText($1, 4326) where id=5834 RETURNING *', [pointText]);
res.json(result.rows[0]);
} catch (error) {
console.error('更新数据时出错:', error);
res.status(500).json({ error: '更新数据时出错' });
}
});
// 设置要监听的通道名称
const channelName = 'car_changes';
// 在应用程序启动时设置监听器
const setupNotificationListener = async () => {
const client = await pool.connect();
// 在通道上添加监听器
await client.query(`LISTEN ${channelName}`);
// 处理接收到的通知
client.on('notification', async () => {
router.get('/clusterResult', async (req, res) => {
try {
// 在通知事件处理程序中执行 SQL 查询
const queryResult = await pool.query(`
WITH ClusteredData AS (
SELECT
id,
ST_ClusterDBSCAN(st_transform(geom, 3857), eps := 100, minpoints := 2) OVER () AS cid
FROM
car
)
SELECT
COUNT(*) AS num_points_in_cluster
FROM
ClusteredData
WHERE
cid = (SELECT cid FROM ClusteredData WHERE id = 5834)
`);
res.json({ result: queryResult.rows });
} catch (error) {
console.error('Error executing SQL query:', error);
}
});
});
// 在应用程序关闭时释放连接
process.on('exit', () => {
client.release();
});
// 在捕获到未处理的 Promise 拒绝时退出
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
process.exit(1);
});
};
// 调用设置监听器的函数
setupNotificationListener();
3,前端可视化展示
js
//获取所有车辆位置
axios.get('http://localhost:3000/allMarker')
.then((response) => {
const data = response.data;
const icon = L.icon({
iconUrl: 'https://openlayers.org/en/v8.2.0/examples/data/icon.png',
iconSize: [20, 25],
})
for (var i = 0; i < data.length; i++) {
L.marker([data[i].y, data[i].x],{icon:icon}).addTo(geoMap);
}
})
.catch((error) => {
console.error("Error fetching data:", error);
});
//加载路线网络
axios.get('http://localhost:3000/route')
.then((response) => {
// 请求成功时的处理
const geojsonMarkerOptions = {
radius: 4,
weight: 3,
};
const data = response.data;
const vectorLayer = L.geoJSON(data, {
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, geojsonMarkerOptions);
},
});
vectorLayer.addTo(geoMap)
})
.catch((error) => {
// 请求失败时的处理
console.error('请求失败:', error);
});
//将小车实时坐标更新到数据库并设置触发器,通过聚合函数检测离车100m内的车数,得出车辆密度判断道路拥挤程度。
async function sendData(data1, data2) {
try {
const data = {
param1: data1,
param2: data2
};
const response = await axios.post('http://localhost:3000/data', data);
axios.get('http://localhost:3000/clusterResult')
.then((response) => {
// 请求成功时的处理
var value;
const data = response.data;
if (data.result[0].num_points_in_cluster <= 3) {
value='畅通'
} else if (data.result[0].num_points_in_cluster <= 6) {
value='一般'
} else {
value='拥堵'
}
document.getElementById('trafficValue').textContent=value;
})
} catch (error) {
console.error('发送到后端时出错:', error);
}
}
三 实际效果
