在Java中使用GeoTools解析POI数据并存储到PostGIS实战

目录

前言

一、POI数据相关介绍

1、原始数据说明

2、空间数据库表设计

二、POI数据存储的设计与实现

1、对应的数据模型对象的设计

2、属性表数据和空间信息的读取

3、实际运行结果

三、总结


前言

        POI点,全称为Point of Interest(兴趣点),是指一切被抽象为点要素的空间地理实体,尤其是与人们生活密切相关的地理要素,如小区、餐馆、商场、车站等。POI是地理信息数据的一部分,用于丰富地图内容,提供用户所需的地理信息,POI数据在地图应用、导航系统、位置服务、市场营销、城市规划等领域有着广泛应用。

        POI数据涵盖了丰富的地理信息和属性特征,能够反映出一个地区的商业、文化、交通等各方面的特色,每个POI点主要包含四方面的信息:名称、类别、坐标、分类。(1)名称:POI的名称或标题。(2)地址:POI的详细地址。(3)经纬度坐标:POI的地理位置坐标,通过经纬度坐标将现实世界中的地点与数字世界进行关联。(4)分类:POI所属的类别。(5)描述:关于POI的详细描述。(6)联系方式:如电话、网址等。(7)附加信息:如营业时间、用户评价、图片等。可以看出,POI数据的核心要素在于其地理位置信息和属性描述。POI数据的特点有多样性(涵盖多种类型的地理实体)、动态性(POI信息会随时间变化而更新)、空间性(具有明确的地理位置)、可查询性(用户可以根据需求检索特定的POI)。

        因此,可以看出,学会管理并正确的使用POI数据,对于我们进行城市规划、导航服务、位置服务、智慧旅游、智慧应急等方面有重要的应用。在我们应用这些数据之前,需要先将POI数据管理起来。本文即在这样的场景下产生。与GDAL的shp数据处理方式不同,在GeoTools中的处理方法有一定的不同。文章分享的方法可以在分布式环境中利用Mybatis-Plus这种ORM框架进行快速的空间数据批量插入。与GeoTools官方提供的PostGIS数据读写相比,本文分享的方法将更加方便,易于与其它项目进行集成。

一、POI数据相关介绍

        在讲解如何利用GeoTools进行数据管理时,我们先对POI数据进行简单的说明。POI数据不仅包含丰富的空间位置信息,同时包含很丰富的分类,以餐饮类的POI为例,我们可以分为大类、中类和小类。

1、原始数据说明

按照大类分为餐饮服务、中类分为中餐厅、西餐、烧烤,小类可以分为湘菜、川菜等。

        在这里,从行政区划上,我们把POI按照其归属进行了划分。在后续的统计中可以充分的利用这些数据。 这里的数据也是从互联网上抓取的数据,大多数的POI数据都是有标准的大类、中类、小类。当然,在拿到的部分POI数据,比如商业住宅的数据,

        在商务住宅的POI数据中,所有的分类数据都集中到了大类这个字段,而另外两个字段比如中类和小类则是空的。其它的数据都是正确的,因此我们需要对商务住宅这个大类的数据进行简单的分拆。然后对应到具体的大类、中类、小类上面。 

2、空间数据库表设计

        在上一篇博客中,我们对POI信息表的空间数据属性字段有了具体的了解。与Shapefile等空间数据表相同,在PostGIS空间数据库中,我们也需要设计对应的空间表来存储对应的空间数据。

        以上就是Shapefile中的属性字段信息,按照一一映射的原则,我们在PostGIS当中也同样的来设计对应的空间表。 

        大家可以使用自己熟悉的工具来进行表结构的设计,然后在数据库客户端软件中进行创建表结构即可。这里同样将数据表的表结构贴出来,供大家参考:

CREATE TABLE "public"."biz_poi_info" ("pk_id" int8 NOT NULL,"name" varchar(255) COLLATE "pg_catalog"."default","main_category" varchar(255) COLLATE "pg_catalog"."default","type" varchar(255) COLLATE "pg_catalog"."default","subtype" varchar(255) COLLATE "pg_catalog"."default","address" varchar(255) COLLATE "pg_catalog"."default","province_name" varchar(255) COLLATE "pg_catalog"."default","city_name" varchar(255) COLLATE "pg_catalog"."default","area_name" varchar(255) COLLATE "pg_catalog"."default","lon_wgs84" numeric(18,11),"lat_wgs84" numeric(18,11),"geom" "public"."geometry","year" int4,"create_by" int8,"create_time" timestamp(6),"update_by" int8,"update_time" timestamp(6),CONSTRAINT "pk_biz_poi_info" PRIMARY KEY ("pk_id")
);CREATE INDEX "idx_biz_poi_info_geom" ON "public"."biz_poi_info" USING gist ("geom" "public"."gist_geometry_ops_2d"
);
COMMENT ON COLUMN "public"."biz_poi_info"."pk_id" IS 'pk_id';
COMMENT ON COLUMN "public"."biz_poi_info"."name" IS '名称';
COMMENT ON COLUMN "public"."biz_poi_info"."main_category" IS '大类,比如:餐饮服务';
COMMENT ON COLUMN "public"."biz_poi_info"."type" IS '中类,比如:中餐';
COMMENT ON COLUMN "public"."biz_poi_info"."subtype" IS '小类';
COMMENT ON COLUMN "public"."biz_poi_info"."address" IS '地址';
COMMENT ON COLUMN "public"."biz_poi_info"."province_name" IS '省';
COMMENT ON COLUMN "public"."biz_poi_info"."city_name" IS '市';
COMMENT ON COLUMN "public"."biz_poi_info"."area_name" IS '区';
COMMENT ON COLUMN "public"."biz_poi_info"."year" IS '年份';
COMMENT ON COLUMN "public"."biz_poi_info"."create_by" IS '创建人';
COMMENT ON COLUMN "public"."biz_poi_info"."create_time" IS '创建时间';
COMMENT ON COLUMN "public"."biz_poi_info"."update_by" IS '更新人';
COMMENT ON COLUMN "public"."biz_poi_info"."update_time" IS '更新时间';
COMMENT ON TABLE "public"."biz_poi_info" IS '保存兴趣点信息表';

        以上就是对POI数据进行简单的介绍,以及对POI数据的时空数据表的物理模型和表结构进行了讲解。请注意,在进行空间数据库设计的时候,请务必安装PostGIS的扩展,否则上面的SQL将无法运行,为了在后面的空间分析和查询的服务中发挥出更好的性能,我们给Geometry字段建立空间索引。

二、POI数据存储的设计与实现

        在介绍完POI的属性表结构和空间数据库物理表模型之后,我们来具体讲解如何使用GeoTools来进行属性的读取,并调用Mybatis-Plus的批量入库代码,将2020年星城长沙的POI数据进行入库操作。

1、对应的数据模型对象的设计

        众所周知,在面向对象的设计中,我们需要给数据库模型的表设计一个对应的实体类,这里简称为实体类。一般字段与数据库物理表是一一对应的。这里我们直接给出原始的代码:

package com.yelang.project.extend.earthquake.domain;
import java.io.Serializable;
import java.math.BigDecimal;
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 com.yelang.framework.web.domain.BaseEntity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@TableName(value = "biz_poi_info", autoResultMap = true)
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
/*** - 兴趣点信息表实体类* @author 夜郎king**/
public class PoiInfo extends BaseEntity implements Serializable {private static final long serialVersionUID = -9163178655131959272L;@TableId(value = "pk_id")private Long pkId;// 主键private String name;// 名称@TableField(value = "main_category")private String mainCategory;// 大类,比如:餐饮服务private String type;// 中类,比如:中餐private String subtype;// 小类private String address;// 地址@TableField(value = "province_name")private String provinceName;// 省@TableField(value = "city_name")private String cityName;// 市@TableField(value = "area_name")private String areaName;// 区@TableField(value = "lon_wgs84")private BigDecimal lonWgs84;@TableField(value = "lat_wgs84")private BigDecimal latWgs84;@TableField(typeHandler = PgGeometryTypeHandler.class)private String geom;@TableField(exist = false)private String geomJson;private Integer year;// 年份public PoiInfo(String name, String mainCategory, String type, String subtype, String address, String provinceName,String cityName, String areaName, BigDecimal lonWgs84, BigDecimal latWgs84, String geom, Integer year) {super();this.name = name;this.mainCategory = mainCategory;this.type = type;this.subtype = subtype;this.address = address;this.provinceName = provinceName;this.cityName = cityName;this.areaName = areaName;this.lonWgs84 = lonWgs84;this.latWgs84 = latWgs84;this.geom = geom;this.year = year;}
}

        熟悉博主代码风格的小伙伴一定知道,在处理空间数据时,我们需要将Wkt格式的字符数据转为PostGIS认识的Geometry字段,因此在这里就需要自定义typeHandler来进行处理。在上面的代码中标识符如下:

@TableField(typeHandler = PgGeometryTypeHandler.class)

2、属性表数据和空间信息的读取

        在定义好模型实体之后,我们将介绍如何使用GeoTools来进行属性数据和空间信息的读取。通过这两个信息要素,构成完成的一条空间基本信息。在本文的例子中,我们需要指定属性来进行解析,比如需要将“地址”这个属性对应到address中,因此我们需要类似于Jdbc的ResultSet的处理方式,需要手动的进行数据的对应。下面贴出具体的解析代码:

@Test
public void Read2PostGIS() throws IOException, FactoryException {// 指定Shapefile的文件路径//String shpFile = "C:/BaiduDownload/长沙市2020年POI数据集/长沙市2020年POI数据集/长沙POI数据(.shp)/风景名胜.shp";String shpFile = "C:/BaiduDownload/长沙市2020年POI数据集/长沙市2020年POI数据集/长沙POI数据(.shp)/住宿服务.shp";//FileDataStore dataStore = FileDataStoreFinder.getDataStore(new File(shpFile));ShapefileDataStore shapefileDataStore = new ShapefileDataStore(new File(shpFile).toURI().toURL());shapefileDataStore.setCharset(Charset.forName("UTF-8"));// 设置中文字符编码// 获取特征类型SimpleFeatureType featureType = shapefileDataStore.getSchema(shapefileDataStore.getTypeNames()[0]);CoordinateReferenceSystem crs = featureType.getGeometryDescriptor().getCoordinateReferenceSystem();System.out.println("坐标参考系统:" + crs);Integer epsgCode = CRS.lookupEpsgCode(crs, true);SimpleFeatureSource featureSource = shapefileDataStore.getFeatureSource();SimpleFeatureCollection simpleFeatureCollection=featureSource.getFeatures();SimpleFeatureIterator itertor = simpleFeatureCollection.features();//遍历featurecollectionList<PoiInfo> list = new ArrayList<PoiInfo>();Date now = new Date();while (itertor.hasNext()){SimpleFeature feature = itertor.next();Property nameProperty = feature.getProperty("名称");String name = (String)nameProperty.getValue();Property mainCategoryProperty = feature.getProperty("大类");String mainCategory = (String) mainCategoryProperty.getValue();Property typeProperty = feature.getProperty("中类");String type = (String)typeProperty.getValue();Property subtypeProperty = feature.getProperty("小类");String subtype = subtypeProperty != null ? (String)subtypeProperty.getValue() : "";Property addressProperty = feature.getProperty("地址");String address = (String)addressProperty.getValue();Property provinceNameProperty = feature.getProperty("省");String provinceName = (String)provinceNameProperty.getValue();Property cityNameProperty = feature.getProperty("市");String cityName = (String)cityNameProperty.getValue();Property areaNameProperty = feature.getProperty("区");String areaName = (String)areaNameProperty.getValue();Property lonWgs84Property = feature.getProperty("WGS84_经");BigDecimal lonWgs84 = new BigDecimal(String.valueOf(lonWgs84Property.getValue()));Property latWgs84Property = feature.getProperty("WGS84_纬");BigDecimal latWgs84 = new BigDecimal(String.valueOf(latWgs84Property.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,实现动态写入PoiInfo poi = new PoiInfo(name, mainCategory, type, subtype, address, provinceName, cityName, areaName, lonWgs84, latWgs84, geom, 2020);poi.setCreateTime(now);poi.setUpdateTime(now);list.add(poi);}if(list.size() > 0) {// poiService.saveBatch(list,500);}System.out.println(list.size());
}

        在上面的代码中,请注意在Geometry字段的属性设置时,我们为了能动态的设置空间对象的SRID,需要动态将解析出来的空间参考编码设置到WKT字符串中,方便在数据处理时可以动态的设置。看到很多朋友在介绍相关的博客值,总是在设置方法将4326这个SRID设置为静态的,这样的处理方式不够灵活。

        在第一节中我们曾将讲过,在商务住宅这类POI中,数据的制作方将大类、中类、小类进行了合并,也因此导致了在数据中只有一列,这里举一个合并的例子:

商务住宅;楼宇;商住两用楼宇|商务住宅;楼宇;商务写字楼

        在上面的例子当中,我们就需要特殊处理,正常的大类、中类、小类三类组合起来都是三个长度的标准分类,上面的分类就不是,因此我们将最后的字符全部合并起来,当成当前分类的小类。毕竟这种情况不多,当然我们后续可以对数据进行一个集中的清理。数据转换的逻辑:

String mainCategory = (String) mainCategoryProperty.getValue();;
String [] splitMainCategory = mainCategory.split(";");
String type = "";
String subtype ="";
//商务住宅的POI要特殊处理、从大类中分解出中类和小类
if(splitMainCategory.length == 3) {mainCategory = splitMainCategory[0];type = splitMainCategory[1];subtype = splitMainCategory[2];
}else if(splitMainCategory.length > 3) {mainCategory = splitMainCategory[0];type = splitMainCategory[1];for(int i = 2;i <splitMainCategory.length;i++ ) {subtype += splitMainCategory[i];}
}else {Property typeProperty = feature.getProperty("中类");type = (String)typeProperty.getValue();Property subtypeProperty = feature.getProperty("小类");subtype = subtypeProperty != null ? (String)subtypeProperty.getValue() : "";
}

3、实际运行结果

        最后我们使用Junit来调用上述的代码实现POI数据的批量插入,由于篇幅有限,关于在Mybatis-Plus中如何批量插入数据的代码不再赘述。读取时的数据显示如下:

        可以看到数据已经成功的加载到内存中,等待批量录入的空间数据库中。下面我们来看下空间数据库中的情况。 在PgAdmin中执行查询语句可以看到如下的结果:

        如果能看到以上结果说明,数据已经成功的插入到数据库中,在PgAdmin当中,还可以直接看到空间数据的分布,可以点击查看属性信息。

        以上步骤就是如何在Java中调用GeoTools进行POI数据入库实例。

三、总结

        以上就是本文的主要内容,本文主要讲解在Java开发环境,如何使用Geotools来进行数据的解析与存储,与GDAL的shp数据处理方式不同,在GeoTools中的处理方法有一定的不同。文章分享的方法可以在分布式环境中利用Mybatis-Plus这种ORM框架进行快速的空间数据批量插入。与GeoTools官方提供的PostGIS数据读写相比,本文分享的方法将更加方便,易于与其它项目进行集成。行文仓促,定有不足之处,还恳请各位专家朋友不吝赐教,万分感谢。

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

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

相关文章

MySQL基础篇 part1

为什么使用数据库和数据库基本概念 想在vscode用markdown了&#xff0c;为什么不直接拿pdf版本呢&#xff1f; DB:数据库(Database) 即存储数据的“仓库”&#xff0c;其本质是一个文件系统。它保存了一系列有组织的数据。 DBMS:数据库管理系统(Database Management System)…

YOLO11震撼发布!

非常高兴地向大家介绍 Ultralytics YOLO系列的新模型&#xff1a; YOLO11&#xff01; YOLO11 在以往 YOLO 模型基础上带来了一系列强大的功能和优化&#xff0c;使其速度更快、更准确、用途更广泛。主要改进包括 增强了特征提取功能&#xff0c;从而可以更精确地捕捉细节以更…

二维环境下的TDOA测距定位的MATLAB代码,带中文注释

TDOA测距定位程序介绍 概述 本MATLAB程序实现了基于时间差到达&#xff08;TDOA&#xff09;技术的二维测距定位&#xff0c;能够处理4个或任意数量&#xff08;大于3个&#xff09;的锚节点。在无线定位和导航系统中&#xff0c;TDOA是一种常用的定位方法&#xff0c;通过测量…

论文精读--Two-Stream Convolutional Networks for Action Recognition in Videos

对于单张图片&#xff0c;丢进卷积和全连接层直接得出分类结果就行 但对于视频&#xff0c;早期的一些工作把视频中的一些关键帧抽取出来&#xff0c;把一个个帧通过网络&#xff0c;最后把结果合并&#xff0c;或者把帧叠起来&#xff0c;一起丢进网络。在网络中进行early fu…

基于Springboot+Vue的基于协同过滤算法的个性化音乐推荐系统 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统中…

【YOLO系列】YOLOv11正式发布!

Yolov11发布文档 代码链接 了解Ultralytics YOLO11的所有突破性功能&#xff0c;这是我们最新的人工智能模型&#xff0c;具有无与伦比的准确性和效率。 我们很高兴向大家介绍Ultralytics型号的下一次进化&#xff1a;YOLO11&#xff01;YOLO11建立在以前YOLO模型版本令人印象…

安装图片标识工具anylabeling

目录 下载压缩包 创建环境 安装opencv 安装第三方库 运行setup.py文件 安装过程可能会出现的错误&#xff1a; 错误1 错误2 安装完成 图标更换 之前提到的嵌入式开发】可编程4k蓝牙摄像头点击器还可以训练模型&#xff0c;使图像识别精度提高 现在讲解&#xff0c;如…

【人人保-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

TongESB7, TongGW, admin账号密码重置方式

停止控制台 修改系统库 identities 表 configuration字段中的password 重启manage

【C语言】指针详解(一)

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1.内存与地址2.指针变量与地址2.1 取地址操作符&2.2 指针变量2.3 指针类型2.4 解引用操作符2.5 指针变量的大小 3. 指针变量类型的意义3.1 指针的解引用 4. const修饰指针4.1 const修饰变量4.2 const修饰指针变量…

矿石运输船数据集、散货船数据集、普通货船数据集、集装箱船数据集、渔船数据集以及客船数据集

海船&#xff1a;用于船只检测的大规模精准标注数据集 我们很高兴地介绍一个新的大规模数据集——海船&#xff0c;该数据集专为训练和评估船只目标检测算法而设计。目前&#xff0c;这个数据集包含31,455张图像&#xff0c;并涵盖了六种常见的船只类型&#xff0c;包括矿石运…

如何使用ssm实现科技银行业务管理系统+vue

TOC ssm743科技银行业务管理系统vue 第一章 绪论 1.1 研究背景 在现在社会&#xff0c;对于信息处理方面&#xff0c;是有很高的要求的&#xff0c;因为信息的产生是无时无刻的&#xff0c;并且信息产生的数量是呈几何形式的增加&#xff0c;而增加的信息如何存储以及短时间…

网络通信——动态路由协议RIP

目录 一.动态路由协议分类 二.距离矢量路由协议 &#xff08;理解&#xff09; 三. 链路状态路由协议&#xff08;理解&#xff09; 四.RIP的工作原理 五.路由表的形成过程 六. RIP的度量值&#xff08;条数&#xff09;cost 七.RIP的版本&#xff08;v1和v2&#xff0…

springboot整合seata

一、准备 docker部署seata-server 1.5.2参考&#xff1a;docker安装各个组件的命令 二、springboot集成seata 2.1 引入依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId>&…

数据清洗第1篇章 - 处理缺失值和重复值

数据清洗是数据分析过程中至关重要的一步&#xff0c;它确保数据的准确性、一致性和完整性。这不仅有助于提高分析结果的可靠性和有效性&#xff0c;还能为算法建模决策提供高质量的数据基础。在进行数据分析和建模的过程中&#xff0c;大量的时间花在数据准备上&#xff1a;加…

【Linux服务器】git和github交互使用

前言&#xff1a;有时候pycharm连接不上github&#xff0c;还是得命令行操作 目录 1. 准备git2. 配置github账户3. 上传项目3.1 创建本地仓库3.2 提交本地代码3.3 上传到github 4. 注意 1. 准备git 下载链接&#xff1a;官网 下载后直接运行安装&#xff0c;cmd输入git --vers…

Redis篇(缓存机制 - 多级缓存)(持续更新迭代)

目录 一、传统缓存的问题 二、JVM进程缓存 1. 导入案例 2. 初识Caffeine 3. 实现JVM进程缓存 3.1. 需求 3.2. 实现 三、Lua语法入门 1. 初识Lua 2. HelloWorld 3. 变量和循环 3.1. Lua的数据类型 3.2. 声明变量 3.3. 循环 4. 条件控制、函数 4.1. 函数 4.2. 条…

set和map结构的使用

个人主页&#xff1a;敲上瘾-CSDN博客 个人专栏&#xff1a;游戏、数据结构、c语言基础、c学习、算法 目录 一、序列式容器和关联式容器 二、set和multiset 1.insert 2.erase 3.find 4.count 三、map和mapmulti 1.pair 2.insert 3.find 4.operator[ ] 5.erase 6.lo…

UE虚幻引擎云渲染汽车动画的优势!

在汽车广告和动画制作领域&#xff0c;虚幻引擎&#xff08;UE&#xff09;结合云渲染技术正掀起一场技术革命。这项技术以其高性能、成本效益和灵活性&#xff0c;为创作者提供了强大的工具&#xff0c;以实现更加逼真和高效的汽车动画制作。 一、为什么选择UE虚幻引擎制作汽车…

MATLAB案例 | Copula的密度函数和分布函数图

本文介绍各种类型&#xff08;Gaussian、t、Gumbel、Clayton、Frank&#xff09;Copula的密度函数和分布函数图的绘制 完整代码 clc close all clear%% ********************计算Copula的密度函数和分布函数图************************ [Udata,Vdata] meshgrid(linspace(0,1…