PostGIS学习笔记

标签: Postgis 分类: Gis 创建时间:2021-04-07 05:26:19 更新时间:2023-10-20 11:23:26

1.前言
PostGIS无疑是GIS领域一个非常重要的项目。

2.Geojson转入postgis中

3.将shp文件导入到postgis中

在 mac 版本中的 shp2pgsql 工具路径在 “/Library/PostgreSQL/15/bin/shp2pgsql” 路径下。

1
shp2pgsql -c 路径/shp数据文件名 新建的数据表名 数据库名|psql -d 数据库名 -U 用户名

5.MybatisPlus 返回 geojson 数据

这里其实有个两种思路,使用sql语句,或者使用 java 进行拼接。

5.1.使用sql语句进行转换

一种就是直接使用 postgis 提供的sql语句,将数据库中的数据转为 geojson 字符串,然后通过 JSONObject 对象转成 json 对象返回给前端。

(1)定义mapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.openmap.disaster.mapper.RfdwdMapper">
<select id="queryGeojson" resultType="java.lang.String">
SELECT
row_to_json(fc) as geojson
FROM (
SELECT
'FeatureCollection' AS type
, array_to_json(array_agg(f)) AS features
FROM (
SELECT
'feature' AS type
, ST_AsGeoJSON(shape)::json as geometry
, (
SELECT
row_to_json(t)
FROM (
SELECT
id
) AS t
) AS properties
FROM map_rfdwd mr
) AS f
) AS fc
</select>
</mapper>

(2) 分别实现 Mapper、IService和 ServiceImpl 接口
这里我就简单的贴上 ServiceImpl 实现的方法,Mapper 和 IService 都只需要定义一个 String queryGeojson(); 方法就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class RfdwdServiceImpl extends ServiceImpl<RfdwdMapper, Rfdwd> implements IRfdwdService {

@Resource
private RfdwdMapper rfdwdMapper;
/**
* 获取全部数据的geojson格式
* @return
*/
public String queryGeojson(){
String result=rfdwdMapper.queryGeojson();
return result;
}
}

(3) 在 Controller 层实现 IService 接口的调用

1
2
3
4
5
public Result<JSONObject> queryGeojson() {
String rfdwd=rfdwdService.queryGeojson();
JSONObject result=JSONObject.parseObject(rfdwd);
return Result.OK(result);
}
参考文章:
1.postgis查询数据为geojson
2.Postgis实现空间数据转GeoJSON 虽然功能小,但是比较实用,通过本篇也能学到一些小知识点以及相关函数的使用,主要包括:ST_AsGeoJSON的使用;row_to_json使用;array_agg聚合函数使用;array_to_json使用;row的定义使用和缺点;
3.PostGIS空间表查询GeoJSON 这里用复杂的sql语句进行了geojson格式的转换。

5.2使用转换器进行转换

另外一种就是在使用 mybatis-plus 的使用,自定义一个类型转换器,将geom字段转换为 geojson 格式,然后在进行属性的拼接。

(1) pom.xml 中增加依赖
这里我稍微的做了变动,就是把依赖的版本提升了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<properties>
<geotools.version>29.2</geotools.version>
</properties>

<dependencies>
<!-- geotools相关jar包 -->
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geojson</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-swing</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-jdbc</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools.jdbc</groupId>
<artifactId>gt-jdbc-postgis</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-epsg-hsql</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-main</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-api</artifactId>
<version>20.5</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-opengis</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-data</artifactId>
<version>20.5</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-referencing</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>net.postgis</groupId>
<artifactId>postgis-jdbc</artifactId>
<version>2.5.0</version>
</dependency>
<repositories>
<repository>
<id>osgeo</id>
<name>OSGeo Release Repository</name>
<url>https://repo.osgeo.org/repository/release/</url>
<snapshots><enabled>false</enabled></snapshots>
<releases><enabled>true</enabled></releases>
</repository>
<repository>
<id>osgeo-snapshot</id>
<name>OSGeo Snapshot Repository</name>
<url>https://repo.osgeo.org/repository/snapshot/</url>
<snapshots><enabled>true</enabled></snapshots>
<releases><enabled>false</enabled></releases>
</repository>
</repositories>

(2) 编写转换器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package com.openmap.disaster.config;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;

import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.geometry.jts.WKBReader;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.ParseException;
import org.postgis.PGgeography;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

@MappedTypes({String.class})
public class GeographyTypeHandler extends BaseTypeHandler<String> {
//插入数据,转换,geoJson2EWKB
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
PGgeography pGgeography = new PGgeography(parameter);
ps.setObject(i, pGgeography);
}

@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
PGgeography pGgeography = new PGgeography(rs.getString(columnName));
if (pGgeography == null) {
return null;
}
return pGgeography.toString();
}

@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
PGgeography pGgeography = new PGgeography(rs.getString(columnIndex));
if (pGgeography == null) {
return null;
}
return pGgeography.toString();
}

@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
PGgeography pGgeography = new PGgeography(cs.getString(columnIndex));
if (pGgeography == null) {
return null;
}
return pGgeography.toString();
}

//取出数据转换,WKB->Geojson
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
String WKB = rs.getString(columnName);
if(WKB==null){
return null;
}
WKBReader reader = new WKBReader();
Geometry geometry = null;
try {
geometry = reader.read(WKBReader.hexToBytes(WKB));
} catch (ParseException e) {
//转换失败
return null;
}
// 设置保留15位小数,否则GeometryJSON默认保留4位小数
GeometryJSON geometryJson = new GeometryJSON(16);
return geometryJson.toString(geometry);
}
}

(3) 应用转换器
在实体类上需要指定转换器,并且实体类注解TableName 补充 autoResultMap = true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.openmap.disaster.entity;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;

import com.baomidou.mybatisplus.annotation.*;
import com.openmap.disaster.config.GeographyTypeHandler;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
* @Description: rfdws
* @Author: jeecg-boot
* @Date: 2023-08-21
* @Version: V1.0
*/
@Data
@TableName(value = "map_rfdwd",autoResultMap = true)
public class Rfdwd implements Serializable {
private static final long serialVersionUID = 1L;

/**shape,指定类型转换器*/
@TableField(typeHandler = GeographyTypeHandler.class)
private String shape;
}

(4) 结果
最终的结果,就是将这个 shape 字段转换成了geojson字符串。

1
2
3
{
"shape": '{"type":"Point","coordinates":[120.02095677700004,30.23700725300006,0.0]}'
}
参考文章:
1.Mybatis-plus读取(GeoJson)和保存Postgis geography数据 我发现这个地方用的也是jeecg框架,这里面的代码基本上可以运行的,稍微修改一下就好了。
2.mybatis-plus 支持postgresql数据库中的geometry类型 这里引入了 mybatis-typehandlers-postgis 这个库。
3.基于Mybatis-Plus实现Geometry字段在PostGis空间数据库中的使用 这里定义了mapper、services,并实现了调用,还提供了插入方法,返回的是实体类。
4.SpringBoot下Postgre GeoJson数据查询处理 这里也是用的 sql 语句进行查询的,但是提供了一个思路就是:这里考虑到GeoJson数据过多,且变化较慢。存到固定路径供前端直接调用,每日定时更新它就好了。
5.Mybatis-Plus 对 GeoTools Geometry 映射 PostGIS Geometry 的支持 geotools 和 postgis 关联,方便业务操作,以及配置jackson将geometry格式化成geojson返给前端。
6.maven的依赖包无法下载解决 这里就是解决 gt-api 这个依赖包找不到的问题。
7.MyBatis 配置 TypeHandler mybatis.type-handlers-packag 进行配置
8.mybaties plus实体类设置typeHandler不生效 实体类注解TableName 补充 autoResultMap = true

5.3.Error setting non null for parameter #18 with JdbcType null

在使用转换器进行将geojson转为数据库类型的时候,出现了这个问题: Could not set parameters for mapping: ParameterMapping{property=’et.shape’, mode=IN, javaType=class java.lang.Object, jdbcType=STRUCT, numericScale=null, resultMapId=’null’, jdbcTypeName=’null’, expression=’null’},Unknown type: {“type”:”Point”,”coordinates”:[120.00888900000008,30.283056000000045]}

1
2
3
4
@Excel(name = "shape", width = 15)
@ApiModelProperty(value = "shape")
@TableField(typeHandler = GeographyTypeHandler.class)
private String shape;

转换函数:

1
2
3
4
5
6
7
8
9
10
@MappedTypes({String.class})
public class GeographyTypeHandler extends BaseTypeHandler<String> {
private static final Logger logger= LoggerFactory.getLogger(GeographyTypeHandler.class);
//插入数据,转换,geoJson2EWKB
@Override
public void setNonNullParameter(@NotNull PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
PGgeography pGgeography = new PGgeography(s1);
ps.setObject(i, pGgeography);
}
}

【解决方案】
修改了转换方法,就是将geojson格式,读取为wkt格式,然后转换为 geometry 类型,更新到数据库中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void setNonNullParameter(@NotNull PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
try {
GeometryJSON geometryJson = new GeometryJSON(16);
Geometry geometry = geometryJson.read(parameter);
WKBWriter wkbWriter = new WKBWriter();
byte[] write = wkbWriter.write(geometry);
String s1 = WKBWriter.toHex(write);
PGgeometry pGgeometry=new PGgeometry(s1);
ps.setObject(i, pGgeometry);
}catch (Exception e){
logger.error("GeographyTypeHandler",e);
}
}
参考文章:
1.操作MyBatis引发Error setting null for parameter #with jdbcType other.无效的列类型
2.在java程序中如何读写带有Geometry对象的表
3.Mybatis-plus读取和保存Postgis geometry数据
4.java实现geojson数据与wkt格式互转
5.PostGIS总结 使用geography这种数据类型时,PostGIS的内部计算是基于实际地球球体来计算的;而使用geometry这种数据类型时,PostGIS的内部计算是基于平面来计算的。
小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。