首页 > 其他分享 >基于webGIS的大气监测系统设计

基于webGIS的大气监测系统设计

时间:2024-06-23 12:27:58浏览次数:25  
标签:大气 webGIS springframework task private org import 监测 id

文章目录

项目地址

gitee项目地址

项目介绍

背景介绍

随着工业化和城市化的快速发展,大气环境问题愈发严重,对人类健康和社会可持续发展构成了严重威胁。为了有效应对这一挑战,本文设计并实现了一个基于WebGIS的大气环境监测系统。该系统集成了WebGIS技术和可视化手段,为大气环境的管理、监测与预测提供了高效、便捷的工具和方法。
本系统基于WebGIS技术,采用B/S架构进行开发,旨在实现大气环境监测信息的高效管理和数据分析。系统通过实时采集大气监测站点的数据,并结合地理信息系统(GIS)技术,以地图形式直观地展示大气环境信息,从而实现对大气污染物的可视化监测。此外,系统还具备污染物预测功能,能够根据历史数据和当前环境参数,预测未来一段时间内污染物的浓度变化。
在空气质量指数的预测方面,考虑到现有预测模型往往忽视污染物浓度与空气质量指数之间的关联性以及地区差异性,本文提出了一种基于历史污染物浓度间接预测空气质量指数的新方法。该方法是对污染物浓度预测后计算AQI指数,能够将指数误差平均到每个污染物浓度中,从而减少误差。通过对比模型预测误差验证了该方法的可行性。同时,为解决地区差异性过大的问题,系统提供自定义模型训练功能,针对不同地区构建不同模型。

技术介绍

技术架构
前端方面包括:

html+css+js三件套
Vue3.0+Axios+Arcgis Api for Javascript+Echarts

后端方面包括:

权限管理:Sa-Token
工具包:Hutool
数据导出:EasyExcel
深度学习:Deeplearning4j
持久层:MybatisPlus

功能介绍

功能模块
具体功能模块如上所示

项目展示

界面展示

登录注册页面:
登录注册页面
监控中心页面
监控中心
在这里插入图片描述
在这里插入图片描述
后台管理页面:
在这里插入图片描述

核心功能展示

空间插值结果:
在这里插入图片描述
动态任务修改:
在这里插入图片描述
动态任务管理:在这里插入图片描述
数据迁移:
在这里插入图片描述
数据导出
在这里插入图片描述
模型训练:
在这里插入图片描述
模型预测:
在这里插入图片描述
模型关联:
在这里插入图片描述

项目亮点

1. 扩展SpringBoot定时任务模块自定义实现动态时间设置,动态任务的启动与关闭,避免引入第三方扩展增加复杂性。

package org.nimi317.web_gis.task;

import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.nimi317.web_gis.entity.Job;
import org.nimi317.web_gis.service.IJobService;
import org.nimi317.web_gis.service.IUpdateService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.Task;
import org.springframework.scheduling.config.TriggerTask;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author thunderobot
 */
@Component
@Slf4j
public class ScheduledTask implements SchedulingConfigurer {

    private ScheduledTaskRegistrar registrar;

    private final IJobService jobService;

    private final ApplicationContext context;

    private final ThreadPoolTaskExecutor executor;

    private final IUpdateService service;

    private final AirQualityRealTask task;

    private final Map<Integer, org.springframework.scheduling.config.ScheduledTask> taskMap = new LinkedHashMap<>();

    public ScheduledTask(@Lazy IJobService jobService, @Lazy ApplicationContext context, ThreadPoolTaskExecutor executor, IUpdateService service, @Lazy AirQualityRealTask task) {
        this.jobService = jobService;
        this.context = context;
        this.executor = executor;
        this.service = service;
        this.task = task;
    }

    @Override
    public void configureTasks(@NotNull ScheduledTaskRegistrar taskRegistrar) {
        this.registrar = taskRegistrar;
        this.init();
        log.info("初始化定时任务");
        this.after();
    }

    private void init() {
        List<Job> jobs = jobService.getAllJobs();
        for (Job job : jobs) {
            add(job.getId(), getTask(job));
        }
    }

    private TriggerTask getTask(Job job) {
        Object bean = context.getBean(job.getBean());
        Integer id = job.getId();
        return new TriggerTask(() -> {
            try {
                Method method = bean.getClass().getMethod(job.getMethod());
                method.setAccessible(true);
                executor.execute(() -> {
                    try {
                        method.invoke(bean);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        throw new RuntimeException(e);
                    }
                });
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }, triggerContext -> new CronTrigger(jobService.getJobById(id).getCron()).nextExecution(triggerContext));
    }


    /**
     * 添加定时任务
     */
    public void add(int id, TriggerTask task) {
        //如果已经存在 则暂停之前的
        org.springframework.scheduling.config.ScheduledTask before = this.taskMap.get(id);
        if (before != null) {
            before.cancel(false);
        }
        org.springframework.scheduling.config.ScheduledTask scheduledTask = this.registrar.scheduleTriggerTask(task);
        this.taskMap.put(id, scheduledTask);
        log.info("定时任务已添加, id:{}", id);
    }

    /**
     * 启动定时任务
     */
    public void start(int id) {
        org.springframework.scheduling.config.ScheduledTask task = this.taskMap.get(id);
        if (task != null) {
            Task taskTask = task.getTask();
            add(id, (TriggerTask) taskTask);
        } else {
            Job job = this.jobService.getJobById(id);
            add(job.getId(), getTask(job));
        }
        log.info("定时任务启动,id:{}", id);
    }

    /**
     * 停止定时任务
     */
    public void stop(int id) {
        org.springframework.scheduling.config.ScheduledTask task = this.taskMap.get(id);
        if (task != null) {
            task.cancel(false);
        }
        log.info("定时任务停止,id:{}", id);
    }

    public void remove(int id) {
        stop(id);
        this.taskMap.remove(id);
        log.info("定时任务移除,id:{}", id);
    }

    private void after() {
        if (!service.isUpdated()) {
            task.setUpdate(false);
            task.task();
        } else {
            task.setUpdate(true);
        }
    }
}

2. 借助浏览器Worker技术解决空间插值大量计算导致卡顿问题,提升流畅度。

import {train} from "@sakitam-gis/kriging";
import {interpolatePoly} from "@/assets/interpolate.ts";
import {grid_, IDW, idw_grid} from "@/utils/interpolate.ts";

onmessage = (e) => {
    const {status} = e.data
    status_[status](e)
}

const status_ = {
    0: kriging,
    1: idw
}

function kriging(e) {
    const {data, params, width, pipList, obj, id} = e.data
    // 训练模型
    let t = data.map(x => x.z);
    let x = data.map(x => x.x);
    let y = data.map(x => x.y);
    // 当我想要获取process对象 不需要对象而是直接使用方法
    process(id, "开始训练数据")
    let model = train(t, x, y, params.model, params.sigma2, params.alpha);
    process(id, "开始进行插值")
    let gridInfo = grid_(interpolatePoly, model, width, pipList);
    process(id, "插值完成")
    post({status: 0, gridInfo}, e)
}

function idw(e) {
    const {data, pipList, width, power, obj, distance, id} = e.data
    let values = data.map(x => x.values);
    let positions = data.map(x => x.positions);
    const idw = new IDW({positions, values});
    idw[`use${distance}`]()
    process(id, "开始进行插值")
    let grid = idw_grid(interpolatePoly, power, width, pipList, idw);
    let zlim = values.reduce((prev, curr) => {
        if (prev[0] > curr) prev[0] = curr;
        if (prev[1] < curr) prev[1] = curr;
        return prev;
    }, [Infinity, -Infinity])
    process(id, "插值完成")
    let gridInfo = Object.assign({}, grid, {zlim})
    post({gridInfo, status: 1}, e)
}

function post(args, e) {
    self.postMessage({...args, id: e.data.id, obj: e.data.obj})
}

function process(id, info) {
    self.postMessage({
        status: 2,
        id: id,
        method: "process",
        info: info
    })
}

3. 使用自定义线程池+阻塞队列实现固定大小模型训练任务,并通过Deeplearning4j神经网络模块构建模型。

package org.nimi317.web_gis.utils;

import cn.hutool.core.io.IoUtil;
import lombok.SneakyThrows;
import org.nimi317.web_gis.exception.E;
import org.nimi317.web_gis.exception.RException;
import org.nimi317.web_gis.form.post.ModelPost;
import org.nimi317.web_gis.runnable.ModelRunnable;
import org.nimi317.web_gis.service.ModelService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author thunderobot
 */
@Component
public class ModelUtils {

    private final ModelService modelService;

    private final static String basePath = "C:\\Users\\thunderobot\\Desktop\\毕业设计\\web_gis\\src\\main\\resources\\data";


    private final BlockingQueue<Runnable> trainingQueue;

    private final ModelRunnable modelRunnable;


    public ModelUtils(ModelService modelService, @Value("${model.maxWaitingSize}") Integer maxWaitingSize,
                      @Value("${model.numThreads}") Integer numThreads, @Lazy ModelRunnable modelRunnable) {
        this.modelService = modelService;
        // 创建一个具有最大等待队列大小的BlockingQueue
        this.trainingQueue = new ArrayBlockingQueue<>(maxWaitingSize == null ? 5 : maxWaitingSize);
        // 创建一个固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(numThreads == null ? 1 : numThreads);
        this.modelRunnable = modelRunnable;

        // 提交一个任务到线程池,它不断从BlockingQueue中获取任务并执行
        executorService.submit(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    // 从队列中获取任务,如果队列为空则阻塞
                    Runnable task = trainingQueue.take();
                    // 执行任务
                    task.run();
                } catch (InterruptedException e) {
                    // 线程被中断,退出循环
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        });
    }

    // 提交一个训练任务
    public boolean submitTrainingTask(Runnable trainingTask) {
        // 尝试将任务放入队列,如果队列已满则返回false
        return trainingQueue.offer(trainingTask);
    }

    /**
     * 保存csv文件
     */
    public String saveCsv(MultipartFile file) {
        String s = file.getOriginalFilename().split("\\.")[1];
        if (!"csv".equals(s)) {
            throw new RException(E.FileException);
        }
        String fileName = UUID.randomUUID() + ".csv";
        String out = basePath + "\\" + fileName;
        save(file, out);
        return out;
    }

    @SneakyThrows
    public void save(MultipartFile file, String out) {
        File newFile = new File(out);
        //检查如果路径文件夹不存在则创建
        File parentFile = newFile.getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }
        FileOutputStream stream = new FileOutputStream(newFile);
        stream.write(file.getBytes());
        IoUtil.close(stream);
    }

    public boolean train(Integer id, String url, ModelPost post) {
        return this.submitTrainingTask(() -> {
            try{
                modelRunnable.run(id, url, post);
            }catch (Exception e) {
                modelService.handleError(id);
            }
        });
    }
}

4. 自定义实现应用层的简单分表查询,通过实现MybatisPlus拦截器对sql拦截根据时间范围进行动态sql修改

package org.nimi317.web_gis.interceptor;

import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.TableNameParser;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.nimi317.web_gis.division.DivisionInterface;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;

import static org.nimi317.web_gis.data.AreaCodeList.getStrategy;

/**
 * @author thunderobot
 * 分表
 */
@Slf4j
public class MybatisInterceptor implements InnerInterceptor {

    private void extracted(PluginUtils.MPBoundSql boundSql) {
        String sql = boundSql.sql();
        TableNameParser tableNameParser = new TableNameParser(sql);
        /*
            当前的表名
         */
        List<TableNameParser.SqlToken> names = new ArrayList<>();
        tableNameParser.accept(names::add);
        /*
         * 获取分表策略
         */
        StringBuilder builder = new StringBuilder();
        int last = 0;
        for (TableNameParser.SqlToken name : names) {
            String value = name.getValue();
            DivisionInterface strategy = getStrategy(value);
            String s = ObjectUtils.isEmpty(strategy) ? value : strategy.divisionTableName(value, sql, name,boundSql);
            int start = name.getStart();
            if (start != last) {
                builder.append(sql, last, start);
                builder.append(s != null ? s : value);
            }
            last = name.getEnd();
        }
        if (last != sql.length()) {
            builder.append(sql.substring(last));
        }
        String string = builder.toString();
        boundSql.sql(string);
    }

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        MappedStatement ms = mpSh.mappedStatement();
        SqlCommandType sct = ms.getSqlCommandType();
        if (sct == SqlCommandType.SELECT || sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
            if (InterceptorIgnoreHelper.willIgnoreDynamicTableName(ms.getId())) {
                return;
            }
            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
            this.extracted(mpBs);
        }
    }
}

标签:大气,webGIS,springframework,task,private,org,import,监测,id
From: https://blog.csdn.net/m0_56282036/article/details/139896995

相关文章

  • 【单片机毕业设计选题24019】-基于STM32的安防监测灭火系统
    系统功能:1.水泵喷水灭火功能:当火焰传感器监测到火焰时,蜂鸣器报警,水泵工作实现灭火。2.风扇功能:当烟雾传感器检测到CO或温度传感器检测到温度超过阈值时,蜂鸣器报警,启动风扇进行驱散烟雾或降温。3.防盗功能介绍:采用红外传感器,当有人靠近时就会报警。4.通过蓝牙实现在......
  • webgis 之 地图投影
    地图投影什么是地图投影目的种类等角投影的分类墨卡托投影Web墨卡托投影参考小结为了更好地展示地球上的数据,需要将地球投影到一个平面上。地图投影是一个数学问题,按照一定的几何关系,将地球上的经纬度坐标映射到一个平面上的坐标。地球投影有很多种,每种投影都有自......
  • 基于django的灾害监测系统
    说明技术栈:开发框架:Django前端:boostrap、ajax、JavaScript后端:python数据库:redis、postgresql前期工作安装PostgreSQL安装Redis创建第一个页面创建Django项目运行测试pythonmanage.pyrunserver创建apppythonmanage.pystartappapp1将app1注册添加到setting......
  • 晓影舆情系统是如何进行短视频平台的舆情监测
    晓影舆情系统通过一系列先进的技术手段和方法来监测短视频平台上的舆情信息。以下是其主要的监测步骤和策略:1.数据收集:晓影舆情系统首先会从短视频平台获取全量的数据。这些数据不仅包括视频本身的内容,还涵盖视频下方的评论、点赞、分享等互动信息。通过与短视频平台的合作,晓......
  • 【单片机毕业设计选题24017】-基于STM32的禽舍环境监测控制系统(蓝牙版)
    系统功能:系统分为主机端和从机端,主机端主动向从机端发送信息和命令,从机端收到主机端的信息后回复温湿度氨气浓度和光照强度等信息。主要功能模块原理图:电源时钟烧录接口:单片机和按键输入电路:主机部分电路:从机部分电路:资料获取地址主从机部分代码:初......
  • 打造智能家居:用ESP32轻松实现无线控制与环境监测
    ESP32是一款集成了Wi-Fi和蓝牙功能的微控制器,广泛应用于物联网项目。它由EspressifSystems公司开发,具有强大的处理能力和丰富的外设接口。下面我们将详细介绍ESP32的基础功能和引脚功能,并通过具体的实例项目展示其应用。主要功能双核处理器:两个XtensaLX6处理器,主频高达2......
  • ​b站视频演示效果:【web前端特效源码】使用HTML5+CSS3+JavaScript十分钟快速制作一个
    b站视频演示效果:【网页设计期末大作业源代码】使用HTML5+CSS3+JavaScript十分钟快速制作一个简约大气卡通动漫静态网站|自制超简单的卡通类网页,响应式自适应新手友效果图:完整代码:<!DOCTYPEhtml><html><head><title>Home</title><metaname="viewpor......
  • 了解振弦采集仪在建筑物安全监测中的应用与研究
    了解振弦采集仪在建筑物安全监测中的应用与研究摘要:河北稳控科技振弦采集仪是一种常用的结构物安全监测设备,广泛应用于建筑物、桥梁、塔楼等工程结构的监测。本文将从振弦采集仪的原理、应用案例和研究进展等方面进行详细介绍,以便更好地了解振弦采集仪在建筑物安全监测中的应用与......
  • 日志监测与文件句柄数监控推送脚本
    点击查看代码#!/bin/bashecho`date`#获取最新的错误计数new_error_count_8080=$(grep"Toomanyopenfiles"/var/log/router/8080/error.log|wc-l)new_error_count_8181=$(grep"Toomanyopenfiles"/var/log/router/8181/error.log|wc-l)......
  • 基于振弦采集仪的地下综合管廊工程安全监测技术研究
    基于振弦采集仪的地下综合管廊工程安全监测技术研究地下综合管廊工程是一项重要的城市基础设施工程,承载着城市供水、供电、供热、排水等重要功能。为了确保地下综合管廊工程的安全运行,需要进行有效的安全监测。本文将重点研究基于振弦采集仪的地下综合管廊工程安全监测技术。 ......