基于SpringBoot和PostGIS的OSM时空路网数据入库实践

目录

前言

一、空间表的设计

1、属性信息

2、空间表结构设计

二、路网数据入库

1、实体类设计

2、路网数据写入

3、pgAdmin数据查询

三、总结


前言

        在当今数字化时代,随着信息技术的飞速发展,地理空间数据的应用范围越来越广泛,尤其是在交通领域,路网数据的管理和分析对于智能交通系统、城市规划、导航服务等方面具有至关重要的作用。而时空路网数据作为交通领域的核心数据类型,其不仅包含空间位置信息,还涵盖了时间维度的变化特征,对于准确地反映交通动态、优化交通资源配置以及提高交通管理效率等方面都具有不可或缺的价值。然而,时空路网数据的处理和管理面临着诸多挑战。一方面,路网数据规模庞大,且随着时间的推移不断累积,传统的数据处理和存储方式难以满足高效存储和快速查询的需求。另一方面,数据来源多样,包括卫星遥感、道路传感器、车辆GPS等,不同来源的数据在格式、精度、更新频率等方面存在很大差异,如何对这些异构数据进行有效整合和规范化处理,是构建高质量路网数据库的关键问题。同时,时空数据具有复杂的空间和关系时间序列特性,需要具备强大的空间分析能力和时间序列数据处理能力,才能充分发挥其在交通分析和决策中的作用。

        在此背景下,本实践旨在探索基于SpringBoot和PostGIS的时空路网数据入库解决方案。SpringBoot作为一个流行的Java开发框架,以其简洁、高效的开发模式和丰富的生态体系,为后端应用开发提供了强大的支持,能够方便地进行数据接入、业务逻辑处理以及与前端的交互PostGIS作为PostgreSQL的空间数据库扩展,具备强大的空间数据存储和分析能力,能够有效地处理地理空间数据的复杂几何关系和拓扑结构,为时空路网数据的存储、查询和分析提供可靠的支撑。通过结合SpringBoot和PostGIS的优势,可以构建一个高效、稳定且易于扩展的时空路网数据管理系统,实现对海量时空路网数据的高效入库、存储和管理,为后续的交通分析、模拟和决策提供坚实的数据基础,推动交通领域的数字化转型和智能化发展。

        本文以OSM的路网数据为例,详细讲解如何在PostGIS数据库中进行空间表的设计,以及基于SpringBoot实现路网数据的空间导入,通过Geotools来实现数据的读取,通过实战型的代码讲解让大家对路网的入库和空间检索有一定的了解。

一、空间表的设计

        本节将重点介绍针对OSM路网数据的空间表设计。不仅再次对路网关键属性信息进行介绍,同时给出了路网空间表的物理结构和建表语句。

1、属性信息

序号名称数据类型长度说明备注
1osm_idString12oms标识
2codeInteger4code
3fclassString28道路类型这个字段是最重要的字段,他表示的是道路的类型,一共有27个分类,比如高速路、自行车道等,我们在下文fclass道路类型会详细介绍这27个分类,一般情况下我们都是根据道路
分类来进行数据可视化和数据分析的
4nameString100道路名称道路的名称,比如大广高速,该字段数据缺失比较多,name道路名称。大部分道路没有名字
5refString20道路编号道路的编号,例如大广高速的编号是G45,该字段数据缺ref道路编号失比较多,也就是大部分道路没有编号
6onewayString1是否为单行道有F和T两个值,其中F代表不是单行道,T代表是单行道
7maxspeedInteger3最大速度
8layerInteger12
9bridgeString1是否为桥梁有F和T两个值,其中F代表不是桥梁,T代表是桥梁
10tunnelString1是否为隧道有F和T两个值,其中F代表不是隧道,T代表是隧道

         请注意,上面的这些字段和具体的字段的含义非常有意义,以后在进行数据分析时会重点用得到。 当然,由于数据较多,在OSM的路网数据中,道路分类不一定都有这些数据。

2、空间表结构设计

        依据前面的的矢量数据属性信息,为了在空间表里也能实现相应的数据模型查询,我们设计的路网表的属性与上面表几乎是一致,仅仅是多了两个字段,第一个是业务主键,第二个是空间属性geom。如下图所示:

        对应的路网物理表结构SQL语句如下:

CREATE TABLE "biz_road_network" ("pk_id" int8 NOT NULL,"osm_id" varchar(12) COLLATE "default" NOT NULL,"code" int4,"fclass" varchar(28) COLLATE "default" NOT NULL DEFAULT ''::character varying,"name" varchar(100) COLLATE "default" NOT NULL DEFAULT ''::character varying,"ref" varchar(20) COLLATE "default" NOT NULL DEFAULT ''::character varying,"oneway" char(1) COLLATE "default" NOT NULL,"maxspeed" int4 NOT NULL DEFAULT 0,"layer" int8,"bridge" char(1) COLLATE "default" DEFAULT ''::bpchar,"tunnel" char(1) COLLATE "default" DEFAULT ''::bpchar,"geom" geometry,CONSTRAINT "pk_biz_road_network" PRIMARY KEY ("pk_id")
);
CREATE INDEX "biz_road_network_fclass" ON "public"."biz_road_network" USING btree ("fclass" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
);
CREATE INDEX "idx_biz_road_network_geom" ON "public"."biz_road_network" USING gist ("geom" "public"."gist_geometry_ops_2d"
);
CREATE INDEX "idx_biz_road_network_name" ON "public"."biz_road_network" USING btree ("name" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
);
COMMENT ON COLUMN "public"."biz_road_network"."pk_id" IS '主键';
COMMENT ON COLUMN "public"."biz_road_network"."osm_id" IS 'OSM主键';
COMMENT ON COLUMN "public"."biz_road_network"."code" IS 'code';
COMMENT ON COLUMN "public"."biz_road_network"."fclass" IS '道路类型,这个字段是最重要的字段,他表示的是道路的类型,一共有27个分类,比如高速路、自行车道等';
COMMENT ON COLUMN "public"."biz_road_network"."name" IS '道路名称';
COMMENT ON COLUMN "public"."biz_road_network"."ref" IS '道路的编号,例如大广高速的编号是G45';
COMMENT ON COLUMN "public"."biz_road_network"."oneway" IS '是否为单行道';
COMMENT ON COLUMN "public"."biz_road_network"."maxspeed" IS '最大速度';
COMMENT ON COLUMN"public"."biz_road_network"."layer" IS 'layer';
COMMENT ON COLUMN "public"."biz_road_network"."bridge" IS '是否为桥梁';
COMMENT ON COLUMN "public"."biz_road_network"."tunnel" IS '是否为隧道';
COMMENT ON COLUMN "public"."biz_road_network"."geom" IS 'geom';
COMMENT ON TABLE "public"."biz_road_network" IS '路网信息表';

        以上就是对路网的空间表的结构和物理表语句进行简单的说明,这是实现数据入库的基础。

二、路网数据入库

        在前面的内容中已经对路网表的设计,接下来我们在SpringBoot环境中使用Geotools来读取路网矢量数据,并将数据插入到PostGIS中。最后为了验证数据是否成功插入,基于pgAdmin来对岳麓区的所有路网进行查询并进行可视化。让大家对如何使用Java程序来进行GIS应用开发有更好的了解。Java应用程序采用MVC模式开发,这里讲解数据的读取和写入,演示模型层和业务层的业务逻辑。

1、实体类设计

        实体类比较简单,主要采用的是MybatiesPlus的方式来操作,因此只需要定义一个跟数据库物理表结构类似的实体类,核心代码如下:

package com.yelang.project.extend.earthquake.domain;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.yelang.framework.handler.PgGeometryTypeHandler;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/*** - 路网信息表* @author 夜郎king*/
@TableName(value = "biz_road_network", autoResultMap = true)
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class RoadNetwork implements Serializable{private static final long serialVersionUID = -6520680287497074840L;@TableId(value="pk_id")private Long pkId;//@TableField(value="osm_id")private String osmId;private Integer code;private String fclass;//道路类型private String name;//道路类型@TableField(value="ref")private String ref;private String oneway;//private Integer maxspeed;//限速private Long layer;private String bridge;private String tunnel;@TableField(typeHandler = PgGeometryTypeHandler.class)private String geom;@TableField(exist=false)private String geomJson;public RoadNetwork(String osmId, Integer code, String fclass, String name, String ref, String oneway,Integer maxspeed, Long layer, String bridge, String tunnel, String geom) {super();this.osmId = osmId;this.code = code;this.fclass = fclass;this.name = name;this.ref = ref;this.oneway = oneway;this.maxspeed = maxspeed;this.layer = layer;this.bridge = bridge;this.tunnel = tunnel;this.geom = geom;}
}

2、路网数据写入

        众所周知,在MybatisPlus中要实现数据的写入,除了有实体类,还需要有Mapper和Servcie类。目前的路网相关Mapper和Service类都比较简单,没有复杂的自定义方法。因此这里暂且忽略,如果确实需要实例代码的,可以单独在评论区留言或者发私信,仅限于Mapper和Service类。为了验证数据的导入性,这里我们使用Junit测试程序来进行导入。核心测试方法如下:

package com.yelang.project.geotools.vectorroad;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.referencing.CRS;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.locationtech.jts.io.WKTWriter;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.yelang.project.extend.earthquake.domain.RoadNetwork;
import com.yelang.project.extend.earthquake.service.IRoadNetworkService;
@SpringBootTest
@RunWith(SpringRunner.class)
public class RoadToPostGIS {// 指定Shapefile的文件路径private static final String ROAD_SHP_FILE = "F:/shpfilepath/湖南路网2024.shp";@Autowiredprivate IRoadNetworkService roadNetworkService;@Testpublic void shpData2DB() throws IOException, FactoryException {ShapefileDataStore shapefileDataStore = new ShapefileDataStore(new File(ROAD_SHP_FILE).toURI().toURL());shapefileDataStore.setCharset(Charset.forName("GBK"));// 设置中文字符编码// 获取特征类型SimpleFeatureType featureType = shapefileDataStore.getSchema(shapefileDataStore.getTypeNames()[0]);CoordinateReferenceSystem crs = featureType.getGeometryDescriptor().getCoordinateReferenceSystem();Integer epsgCode = 0;if(crs != null) {epsgCode = CRS.lookupEpsgCode(crs, true);}SimpleFeatureSource featureSource = shapefileDataStore.getFeatureSource();SimpleFeatureCollection simpleFeatureCollection=featureSource.getFeatures();SimpleFeatureIterator itertor = simpleFeatureCollection.features();//遍历featurecollectionList<RoadNetwork> list = new ArrayList<RoadNetwork>();while (itertor.hasNext()){SimpleFeature feature = itertor.next();Property osmIdProperty = feature.getProperty("osm_id");String osmId = (String)osmIdProperty.getValue();Property codeProperty = feature.getProperty("code");Integer code = (Integer)codeProperty.getValue();Property fclassProperty = feature.getProperty("fclass");String fclass = (String) fclassProperty.getValue();Property nameProperty = feature.getProperty("name");String name = (String)nameProperty.getValue();Property refProperty = feature.getProperty("ref");String ref = (String)refProperty.getValue();Property onewayProperty = feature.getProperty("oneway");String oneway = (String)onewayProperty.getValue();Property maxspeedProperty = feature.getProperty("maxspeed");Integer maxspeed = (Integer)maxspeedProperty.getValue();Property layerProperty = feature.getProperty("layer");Long layer = (Long)layerProperty.getValue();Property bridgeProperty = feature.getProperty("bridge");String bridge = (String)bridgeProperty.getValue();Property tunnelProperty = feature.getProperty("tunnel");String tunnel = (String)tunnelProperty.getValue();// 获取空间字段org.locationtech.jts.geom.Geometry geometry = (org.locationtech.jts.geom.Geometry) feature.getDefaultGeometry();// 创建WKTWriter对象WKTWriter wktWriter = new WKTWriter();// 将Geometry对象转换为WKT格式的字符串String wkt = wktWriter.write(geometry);String geom = "SRID=" + epsgCode +";" + wkt;//拼接srid,实现动态写入RoadNetwork road = new RoadNetwork(osmId, code, fclass, name, ref, oneway, maxspeed, layer, bridge, tunnel, geom);list.add(road);}if(list.size() > 0) {roadNetworkService.saveBatch(list,500);}}
}

3、pgAdmin数据查询

        运行上面的程序就实现了OSM路网数据的导入,路网的范围是湖南省范围内的,为了验证数据是否完全导入,这里我们使用pgAdmin查询界面来验证,之所以要用这个工具,并不是navicat不能用,而是这个工具自带了一个地图可视化的界面,可以辅助查看数据的可视化效果。下面以查询长沙市岳麓区的路网信息为例,执行以下sql:

select r.* from biz_road_network r,biz_area t 
where st_contains(t.geom, r.geom) and t.area_name = '岳麓区';

         执行之后在客户端可以看到以下界面:

        发现岳麓区有 5876条路线信息,拖动滚动条到最后,点击预览按钮可以查看地图的可视化效果,界面如下所示:

        说明我们的路网数据已经成功的导入到PostGIS中。 

三、总结

        以上就是本文的主要内容,本实践旨在探索基于SpringBoot和PostGIS的时空路网数据入库解决方案。通过结合SpringBoot和PostGIS的优势,可以构建一个高效、稳定且易于扩展的时空路网数据管理系统,实现对海量时空路网数据的高效入库、存储和管理,为后续的交通分析、模拟和决策提供坚实的数据基础,推动交通领域的数字化转型和智能化发展。本文通过空间数据库相关表的设计与后台代码实现,为大家详细介绍了如何进行路网数据的入库。行文仓促,难免有许多不足之处,如有不足,还恳请各位专家博主在评论区批评指出,不胜感激。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/83739.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代付入账是什么意思?怎么操作?

代付入账就是指商户委托银行通过企业银行账户向指定持卡人账户划付款项&#xff0c;款项划入指定账户即为入账。 具体操作流程如下&#xff1a; 1. 向第三方支付公司指定账户充值加款。 2. 通过操作后台提交代付银行卡信息。 3. 第三方支付公司受理业务申请。 4. 第三方审…

数学复习笔记 27

前言 太难受了。因为一些事情。和朋友倾诉了一下&#xff0c;也没啥用&#xff0c;几年之后不知道自己再想到的时候&#xff0c;会怎么考虑呢。另外&#xff0c;笔记还是有框架一点比较好&#xff0c;这样比较有逻辑感受。不然太乱了。这篇笔记是关于线代第五章&#xff0c;特…

第四十五天打卡

知识点回顾&#xff1a; tensorboard的发展历史和原理 tensorboard的常见操作 tensorboard在cifar上的实战&#xff1a;MLP和CNN模型 效果展示如下&#xff0c;很适合拿去组会汇报撑页数&#xff1a; 作业&#xff1a;对resnet18在cifar10上采用微调策略下&#xff0c;用tensor…

使用高斯朴素贝叶斯算法对鸢尾花数据集进行分类

高斯朴素贝叶斯算法通常用于特征变量是连续变量&#xff0c;符合高素分布的情况。 使用高斯朴素贝叶斯算法对鸢尾花数据集进行分类 """ 使用高斯贝叶斯堆鸢尾花进行分类 """ #导入需要的库 from sklearn.datasets import load_iris from skle…

【docker】Windows安装docker

环境及工具&#xff08;点击下载&#xff09; Docker Desktop Installer.exe &#xff08;windows 环境下运行docker的一款产品&#xff09; wsl_update_x64 &#xff08;Linux 内核包&#xff09; 前期准备 系统要求2&#xff1a; Windows 11&#xff1a;64 位系统&am…

量化Quantization初步之--带量化(QAT)的XOR异或pyTorch版250501

量化(Quantization)这词儿听着玄&#xff0c;经常和量化交易Quantitative Trading (量化交易)混淆。 其实机器学习(深度学习)领域的量化Quantization是和节约内存、提高运算效率相关的概念&#xff08;因大模型的普及&#xff0c;这个量化问题尤为迫切&#xff09;。 揭秘机器…

【Redis】zset 类型

zset 一. zset 类型介绍二. zset 命令zaddzcard、zcountzrange、zrevrange、zrangebyscorezpopmax、zpopminzrank、zrevrank、zscorezrem、zremrangebyrank、zremrangebyscorezincrby阻塞版本命令&#xff1a;bzpopmax、bzpopmin集合间操作&#xff1a;zinterstore、zunionstor…

Mermaid 绘图--以企业权限视图为例

文章目录 一、示例代码二、基础结构设计2.1 组织架构树2.2 权限视图设计 三、销售数据权限系统四、关键语法技巧汇总 一、示例代码 在企业管理系统开发中&#xff0c;清晰的权限视图设计至关重要。本文将分享如何使用 Mermaid 绘制直观的企业权限关系图&#xff0c;复制以下代…

[pdf、epub]300道《软件方法》强化自测题业务建模需求分析共257页(202505更新)

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 在本账号CSDN资源下载&#xff0c;或者访问链接&#xff1a; http://www.umlchina.com/url/quizad.html 如果需要提取码&#xff1a;umlc 文件夹中的“300道软件方法强化自测题2025…

std__map,std__unordered_map,protobuf__map之间的性能比较

简单比较下 std::map、std::unordered_map 和 protobuf::Map 的性能&#xff0c;主要关注在 插入、查找 和 删除 操作上的效率以及内存管理的差异。 std::map 底层实现&#xff1a;std::map 使用红黑树作为底层数据结构&#xff0c;红黑树是一种平衡二叉查找树的变体结构&…

文档处理组件Aspose.Words 25.5全新发布 :六大新功能与性能深度优化

在数字化办公日益普及的今天&#xff0c;文档处理的效率与质量直接影响到企业的运营效率。Aspose.Words 作为业界领先的文档处理控件&#xff0c;其最新发布的 25.5 版本带来了六大新功能和多项性能优化&#xff0c;旨在为开发者和企业用户提供更强大、高效的文档处理能力。 六…

Three.js + Vue3 加载GLB模型项目代码详解

本说明结合 src/App.vue 代码,详细解释如何在 Vue3 项目中用 three.js 加载并显示 glb 模型。 1. 依赖与插件导入 import {onMounted, onUnmounted } from vue import * as THREE from three import Stats from stats.js import {OrbitControls } from three/examples/jsm/co…

Flutter如何支持原生View

在 Flutter 中集成原生 View&#xff08;如 Android 的 SurfaceView、iOS 的 WKWebView&#xff09;是通过 平台视图&#xff08;Platform View&#xff09; 实现的。这一机制允许在 Flutter UI 中嵌入原生组件&#xff0c;解决了某些场景下 Flutter 自身渲染能力的不足&#x…

vue-11(命名路由和命名视图)

命名路由和命名视图 命名路由和命名视图提供了组织和导航 Vue.js 应用程序的强大方法&#xff0c;尤其是在它们的复杂性增加时。它们提供了一种语义更合理、可维护的路由方法&#xff0c;使您的代码更易于理解和修改。命名路由允许您按名称引用路由&#xff0c;而不是依赖 URL…

微软认证考试科目众多?该如何选择?

在云计算、人工智能、数据分析等技术快速发展的今天&#xff0c;微软认证&#xff08;Microsoft Certification&#xff09;已成为IT从业者、开发者、数据分析师提升竞争力的重要凭证。但面对众多考试科目&#xff0c;很多人不知道如何选择。本文将详细介绍微软认证的考试方向、…

视频汇聚平台EasyCVR“明厨亮灶”方案筑牢旅游景区餐饮安全品质防线

一、背景分析​ 1&#xff09;政策监管刚性需求​&#xff1a;国家食品安全战略及 2024年《关于深化智慧城市发展的指导意见》要求构建智慧餐饮场景&#xff0c;推动数字化监管。多地将“AI明厨亮灶”纳入十四五规划考核&#xff0c;要求餐饮单位操作可视化并具备风险预警能力…

Mysql莫名奇妙重启

收到客户反馈有时接口报504&#xff0c;查看应用日志发现故障期间数据库连接失败 com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failureThe last packet sent successfully to the server was 0 milliseconds ago. The driver has not receive…

半监督学习:低密度分离假设 (Low-Density Separation Assumption)

半监督学习(SSL)的目标是借助未标记数据辅助训练&#xff0c;以期获得比仅用带标签的监督学习范式更好的效果。但是&#xff0c;SSL的前提是数据分布需满足某些假设。否则&#xff0c;SSL可能无法提升监督学习的效果&#xff0c;甚至会因误导性推断降低预测准确性。 半监督学习…

Python Day44

Task&#xff1a; 1.预训练的概念 2.常见的分类预训练模型 3.图像预训练模型的发展史 4.预训练的策略 5.预训练代码实战&#xff1a;resnet18 1. 预训练的概念 预训练&#xff08;Pre-training&#xff09;是指在大规模数据集上&#xff0c;先训练模型以学习通用的特征表示&am…

vue3 eslint ts 关闭多单词命名检查

无效做法 import { globalIgnores } from eslint/config import {defineConfigWithVueTs,vueTsConfigs, } from vue/eslint-config-typescript import pluginVue from eslint-plugin-vue import skipFormatting from vue/eslint-config-prettier/skip-formatting// To allow m…