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

三 实际效果

相关推荐
过期的H2O21 分钟前
【H2O2|全栈】关于CSS(4)CSS基础(四)
前端·css
纳尼亚awsl15 分钟前
无限滚动组件封装(vue+vant)
前端·javascript·vue.js
八了个戒20 分钟前
【TypeScript入坑】TypeScript 的复杂类型「Interface 接口、class类、Enum枚举、Generics泛型、类型断言」
开发语言·前端·javascript·面试·typescript
西瓜本瓜@22 分钟前
React + React Image支持图像的各种转换,如圆形、模糊等效果吗?
前端·react.js·前端框架
黄毛火烧雪下23 分钟前
React 的 useEffect 钩子,执行一些异步操作来加载基本信息
前端·chrome·react.js
蓝莓味柯基29 分钟前
React——点击事件函数调用问题
前端·javascript·react.js
资深前端之路30 分钟前
react jsx
前端·react.js·前端框架
cc蒲公英42 分钟前
vue2中使用vue-office库预览pdf /docx/excel文件
前端·vue.js
Sam90291 小时前
【Webpack--013】SourceMap源码映射设置
前端·webpack·node.js
小兔崽子去哪了1 小时前
Element plus 图片手动上传与回显
前端·javascript·vue.js