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

三 实际效果

相关推荐
e***74958 分钟前
【JavaEE】Spring Web MVC
前端·spring·java-ee
AntBlack22 分钟前
Z-Image 发布了 ,赶紧体验了一把(配套 Modal执行脚本)
前端·后端·aigc
诸葛韩信1 小时前
前端工程化1——npm insatall背后的工作原理
前端·npm·node.js
k***12171 小时前
SpringBoot返回文件让前端下载的几种方式
前端·spring boot·后端
专注前端30年1 小时前
如何使用 HTML5 的 Canvas + JavaScript 实现炫酷的游戏得分特效?
前端·javascript·游戏·html5·canvas·canva可画
q***06291 小时前
解决 Tomcat 跨域问题 - Tomcat 配置静态文件和 Java Web 服务(Spring MVC Springboot)同时允许跨域
java·前端·spring
木易 士心2 小时前
深入理解 CSS 中的 !important
前端·css
行走的陀螺仪2 小时前
GitLab CI/CD 完整教学指南
前端·ci/cd·gitlab·团队开发·自动化测试部署
谢尔登3 小时前
Webpack高级之常用配置项
前端·webpack·node.js
helloyangkl3 小时前
前端——不同环境下配置env
前端·javascript·react.js