postgis:更新车辆实时位置、使用聚合函数检测拥挤程度

一 内容和设计思想

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);
            }
        }

三 实际效果

相关推荐
可观测性用观测云8 分钟前
前端错误可观测最佳实践
前端
恋猫de小郭9 分钟前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
一枚前端小能手30 分钟前
「周更第3期」实用JS库推荐:Lodash
前端·javascript
艾小码30 分钟前
Vue组件到底怎么定义?全局注册和局部注册,我踩过的坑你别再踩了!
前端·javascript·vue.js
Cyan_RA937 分钟前
计算机网络面试题 — TCP连接如何确保可靠性?
前端·后端·面试
谢尔登37 分钟前
【CSS】层叠上下文和z-index
前端·css
鹏多多38 分钟前
前端复制功能的高效解决方案:copy-to-clipboard详解
前端·javascript
AryaNimbus40 分钟前
你不知道的 Cursor系列(三):再也不用死记硬背 Linux 命令,终端 Cmd+K 来帮你!
前端·ai编程·cursor
uhakadotcom42 分钟前
Rollup 从0到1:TypeScript打包完全指南
前端·javascript·面试
Mintopia1 小时前
实时语音转写 + AIGC:Web 端智能交互的技术链路
前端·javascript·aigc