Java 기하학 공간 기하학 데이터 처리의 응용

1 Java Geometry 공간 기하학 데이터 처리 응용

WKT 는 벡터 기하학적 개체, 공간 참조 시스템 및 공간 참조 시스템 간의 변환을 나타내는 데 사용되는 텍스트 마크업 언어입니다. 이진 표현인 WKB(well-known binary)는 동일한 정보를 전송 및 데이터베이스에 저장하는 것보다 낫습니다. 이 형식은 OGC(Open Geospatial Consortium)에서 개발했습니다.

WKT로 표현할 수 있는 기하학적 개체에는 점, 선, 다각형, TIN( 삼각형 불규칙 네트워크 ) 및 다면체가 포함됩니다. 차원이 다른 기하학적 개체는 기하학적 컬렉션을 통해 나타낼 수 있습니다.
기하학적 개체의 좌표는 2D(x,y), 3D(x,y,z), 4D(x,y,z,m)에 선형 참조 시스템에 속하는 m 값이 될 수 있습니다.
다음은 샘플 기하학 WKT 문자열입니다.

  • 포인트(6 10)

  • 라인스트링(3 4,10 50,20 25)

  • 폴리곤((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2))

  • 멀티포인트(3.5 5.6, 4.8 10.5)

  • MULTILINESTRING((3 4,10 50,20 25),(-5 -8,-10 -8,-15 -4))

  • 다중 다각형(((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2)),((6 3,9 2,9 4,6 삼)))

  • GeometryCOLLECTION(POINT(4 6),LINESTRING(4 6,7 10))

  • 포인트 ZM (1 1 5 60)

  • 포인트엠 (1180)

  • 비어있는 포인트

  • 다중 다각형 비어 있음

2 공간 데이터베이스에 데이터 삽입

--GEOM是类型为Geometry的字段--
--我们向该字段新增了一条3D的多边形数据--
--geometry :: STGeomFromText () 是由SQLSERVER提供的函数,它能将WKT文本转换为数据库geometry类型的数据--
INSERT INTO [dbo].[TEST_GEO_TABLE] ( [GEOM] )
VALUES
    ( geometry :: STGeomFromText ( 
    'POLYGON ((
        113.507259000000005 22.24814946 8, 
        113.507188600000006 22.248088559999999 9, 
        113.507117399999998 22.24802743 10, 
        113.507046099999997 22.24796624 11, 
        113.507017300000001 22.247888209999999 12
        ))',4326 )
    );

즉, 좌표를 WKT 텍스트로 변환하여 공간 데이터를 삽입할 수 있습니다. 다음으로 고려해야 할 사항은 WKT 텍스트를 생성하는 방법입니다.

3 Java를 사용하여 Geometry 객체 생성

3.1 공통 기하학 Java API

wkt 텍스트는 단지 문자열일 뿐입니다. 좌표점을 WKT 형식을 준수하는 문자열로 직접 연결하는 것으로 충분하지 않습니까?
진리는 이 진리인데 잘하기가 어렵습니다.

  • 스플라이싱 작업량이 엄청납니다.
  • 접합 프로세스는 오류가 발생하기 쉽습니다.
  • 스플라이싱 결과가 합법적이지 않고 사용 가능하지 않을 수 있으므로
    데이터를 처리하기 위해 Geometry 객체를 쉽게 생성하고 지리 정보 그리기, 생성 및 확인과 같은 기능을 수행할 수 있는 JAVA API 세트가 필요합니다.

시중에 나와 있는 Common GeometryApi는

Esri는 Arcgis에서 제공하는 공식 javaSDK인데 아쉽게도 기능이 많지 않고 기본적인 공간 컴퓨팅 기능도 제공하지 못합니다.
jts는 더 완벽한 기능과 더 많은 정보를 가지고 있습니다.

3.2 JTS의 일부 API 사용 방법

    @Test
    public void geoTest() throws ParseException {
    
    
        /**
         * GeometryFactory工厂,参数一:数据精度 参数二空间参考系SRID
         */
        GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING), 4326);

        /**
         * 熟知文本WKT阅读器,可以将WKT文本转换为Geometry对象
         */
        WKTReader wktReader = new WKTReader(geometryFactory);

        /**
         * Geometry对象,包含Point、LineString、Polygon等子类
         */
        Geometry geometry = wktReader.read("POINT (113.53896635 22.36429837)");

        /**
         * 将二进制流的形式读取Geometry对象
         */
        WKBReader wkbReader = new WKBReader(geometryFactory);

        /**
         * 单纯的一个坐标点,单点可以创建Point,多点可以创建LineString、Polygon等
         */
        Coordinate coordinate = new Coordinate(1.00, 2.00);
        Point point = geometryFactory.createPoint(coordinate);

        Polygon polygon = geometryFactory.createPolygon(new Coordinate[]{
    
    
                new Coordinate(1, 2),
                new Coordinate(1, 2),
                new Coordinate(1, 2),
                new Coordinate(1, 2),
                new Coordinate(1, 2),
        });
        Geometry geometry1 = point;
        Geometry geometry2 = polygon;

        /**
         * WKT输出器,将Geometry对象写出为WKT文本
         */
        WKTWriter wktWriter = new WKTWriter();
        String write = wktWriter.write(point);
    }

3.3 JTS의 Geometry 데이터 유형 하위 클래스

이미지

4 JAVA를 사용하여 공간 데이터베이스에 데이터 추가

위의 테스트 클래스에서 Api 사용에 따라 몇 가지 사항을 요약해 보겠습니다.

  • 팩토리 클래스 객체는 한 번만 초기화하면 되며 구성 클래스에 배치하고 Spring 컨테이너에 주입해야 합니다.
  • 프런트 엔드 또는 Excel에서 관련 좌표 데이터를 가져와 Geometry 개체를 생성합니다.
  • SqlServer에 지오메트리 개체 유지

이 예에서는 Geometry 개체를 유지하기 위해 두 가지 방법이 권장됩니다.

  1. Geometry 개체의 WKT 텍스트를 가져온 다음 SqlServer에서 제공하는 함수를 사용하여 geometry :: STGeomFromText ()WKT 텍스트를 데이터베이스의 Geometry 유형으로 저장합니다.
  2. jts 패키지의 Geometry 개체를 SqlServer JDBC 패키지의 Geometry 개체로 변환하고 Geometry 개체를 이진 형식으로 데이터베이스에 유지합니다.

환경:
이 예제 코드는 JTS, SpringBoot, Mybatis-Plus, mssql-jdbc 환경을 기반으로 합니다.

TypeHandler5 매핑된 사용자 정의 개체 필드를 사용하여 기하학 데이터 삽입

5.1 커스텀 TypeHandler

Mybatis 프레임워크를 사용할 때 Mybatis는 특수 개체와 Sql 필드 간의 매핑 관계를 실현하기 위해 사용자 정의 유형 변환기 TypeHandler를 제공합니다.

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.WKTReader;

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

/**
 * @author wangqichang
 * @since 2019/8/28
 */
@Slf4j
@MappedTypes(value = {
    
    Geometry.class})
public class GeometryTypeHandler extends BaseTypeHandler<Geometry> {
    
    

    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Geometry geometry, JdbcType jdbcType) throws SQLException {
    
    
        /**
         * 获取jts包对象的wkt文本,再转换成sqlserver的Geometry对象
         * 调用ps的setBytes()方法,以二进制持久化该geometry对象
         */
        com.microsoft.sqlserver.jdbc.Geometry geo = com.microsoft.sqlserver.jdbc.Geometry.STGeomFromText(geometry.toText(), geometry.getSRID());
        preparedStatement.setBytes(i, geo.STAsBinary());
    }

    @Override
    public Geometry getNullableResult(ResultSet resultSet, String s) {
    
    
        try {
    
    
            /**
             * 从ResultSet中读取二进制转换为SqlServer的Geometry对象
             * 使用jts的WKTReader将wkt文本转成jts的Geometryd对象
             */
            com.microsoft.sqlserver.jdbc.Geometry geometry1 = com.microsoft.sqlserver.jdbc.Geometry.STGeomFromWKB(resultSet.getBytes(s));
            String s1 = geometry1.toString();
            WKTReader wktReader = SpringContextUtil.getBean(WKTReader.class);
            Geometry read = wktReader.read(s1);
            return read;
        } catch (Exception e) {
    
    
            log.error(e.getMessage());
            throw new ServiceException(e.getMessage());
        }
    }

    @Override
    public Geometry getNullableResult(ResultSet resultSet, int i) throws SQLException {
    
    
        return null;
    }

    @Override
    public Geometry getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
    
    
        return null;
    }
}

5.2 엔터티 개체

엔터티 개체는 다음과 같습니다.

  • objectid정수 유형, 비 자동 증분(이 필드는 Arcgis에서 유지 관리되며 수정할 수 없음) @TableId은 플러그인에 이 필드가 기본 키 필드이고 필드 이름이 OBJECT임을 알려주는 mybatis-plus 플러그인의 주석입니다. 기본 키 전략은 사용자 입력입니다.
  • shapejts용 기하학 객체(이 객체의 JSON 직렬화 결과는 매우 무섭기 때문에 @JsonIgnore데코레이션 사용)
  • @KeySequence개체가 사용해야 하는 기본 키 시퀀스 이름을 식별하는 데 사용되는 mybatis-plus의 플러그인이기도 합니다. IKeyGenerator여기에서는 데이터를 삽입하기 전에 기본 키를 채우기 위해 Oracle의 시퀀스 이름을 쿼리하는 것과 유사한 one 을 구현했습니다 .
@Data
@TableName("LINE_WELL")
@KeySequence(value = "LINE_WELL",clazz = Integer.class)
public class Well extends MyGeometry implements Serializable {
    
    

    @TableId(value = "OBJECTID", type = IdType.INPUT)
    private Integer objectid;

    @JsonIgnore
    protected Geometry shape;
}

5.3 맞춤형 기본 키 생성 전략

arcgis에서 공간 테이블의 기본 키 필드는 int이며 자체 증가하지 않으며 수정할 수 없습니다. Arcgis는 자체 증분으로 수정될 때 약간의 오류가 있습니다. 따라서 Java 백그라운드에 공간 데이터를 삽입하는 것은 자체적으로 기본 키의 쿼리 생성을 완료해야 합니다.
IKeyGeneratorMybatis-Plus에서 제공하는 인터페이스입니다. 이 구현의 기능은 기본 키 생성 전략이 지정되면 mp 프레임워크가 새 데이터를 추가하기 전에 이 구현을 호출하고 그 결과를 객체의 ID에 할당한다는 것입니다(오라클의 시퀀스와 유사). Spring
컨테이너 중간 에 주입

import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;

/**
 * @author wangqichang
 * @since 2019/8/30
 */
public class SqlServerKeyGenerator implements IKeyGenerator {
    
    
    @Override
    public String executeSql(String incrementerName) {
    
    
        return "select max(OBJECTID)+1 from " + incrementerName;
    }
}

5.4 기하학 객체 지속성

개체를 유지하기 위해 mybatis-plus에서 제공하는 메서드를 호출할 때

 String str = "POLYGON ((113.52048666400003 22.248443089000034, 113.5206744190001 22.24822462700007, 113.52082998700007 22.248343788000057, 113.52060468200011 22.248547355000028, 113.52048666400003 22.248443089000034))";
        Geometry read = null;
        try {
    
    
            /**
             * 这里使用wkt文本生成了一个jts包下的Geometry对象
             */
            read = SpringContextUtil.getBean(WKTReader.class).read(str);
        } catch (ParseException e) {
    
    
            e.printStackTrace();
        }
        Well well = new Well();
        well.setShape(read);
        //这里是Mybatis-Plus提供的save接口,调用其内部实现直接储存对象
        wellService.save(well);
        System.out.println("持久化成功");

실행 로그는 다음과 같다:
데이터를 삽입하기 전에 SqlServerKeyGenerator중간에 있는 SQL을 실행하여 기본 키를 획득하고
삽입 코드의 필드 모양은 Geometry 객체의 바이너리이다.

2019-08-30 15:54:23.541  INFO 8484 --- [nio-8905-exec-1] jdbc.sqltiming                           : SELECT max(OBJECTID) + 1 FROM LINE_WELL 
 {
    
    executed in 4 msec}
2019-08-30 15:54:23.631  INFO 8484 --- [nio-8905-exec-1] jdbc.sqltiming                           : INSERT INTO LINE_WELL (OBJECTID, shape) VALUES (3, '<byte[]>') 
 {
    
    executed in 17 msec}

6 손으로 쓴 XML 삽입 형상 데이터

SqlServer에서 제공하는 기능을 사용하여 geometry :: STGeomFromText( #{wktText},4326)Geometry를 WKT 텍스트로 변환하여 삽입

    <insert id="insertCorridorBySql" parameterType="com.zh.xxx.entity.xxx" useGeneratedKeys="true"
            keyProperty="objectid">
        INSERT INTO [LINE_CORRIDOR] (
         shape
        )
        values (
        geometry :: STGeomFromText( #{wktText},4326)
        )
    </insert>

wktText는 테이블 필드가 아닌 임시 필드입니다. 여기서 부모 클래스를 정의하고 Geometry를 포함하는 모든 공간 테이블 엔터티는 wkt 텍스트 처리를 위해 이 클래스를 상속합니다.

import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.WKTWriter;

import java.io.Serializable;

/**
 * 针对Geometry获取Wkt文本字段做处理的Geometry父类,getWktText替代getText,输出三维wkt文本
 * 针对sql_server无法识别POLYGON Z 语法,对wkt文本进行替换
 */
@Data
public class MyGeometry implements Serializable {
    
    

    /**
     * 三维wkt输出,默认为2D不带Z
     */
    @TableField(exist = false)
    @JsonIgnore
    private WKTWriter wktWriter = new WKTWriter(3);

    /**
     * sql_server 与 jts wkt不兼容问题
     */
    @TableField(exist = false)
    @JsonIgnore
    private static final String THREE_D_PRIFIX = "POLYGON Z";
    @TableField(exist = false)
    @JsonIgnore
    private static final String TWO_D_PRIFIX = "POLYGON";

    @JsonIgnore
    protected Geometry shape;


    @TableField(exist = false)
    @JsonIgnore
    private String wktText;

    public String getWktText() {
    
    
        if (StrUtil.isBlank(wktText)){
    
    
            if (getShape() != null) {
    
    
                String wkt = wktWriter.write(shape);
                if (wkt.startsWith(THREE_D_PRIFIX)) {
    
    
                    wktText = StrUtil.replace(wkt, THREE_D_PRIFIX, TWO_D_PRIFIX);
                } else {
    
    
                    wktText = wkt;
                }
            }
        }
        return wktText;
    }
}

7 피트 레코드

7.1 jts는 sqlserver에서 인식하는 wkt와 호환되지 않습니다.

[2019-07-01 16:40:20,637] [ERROR] [http-nio-8905-exec-5] jdbc.audit 111 7. PreparedStatement.execute() INSERT INTO [zhundergroundcableline].[dbo].[LINE_CORRIDOR] ( [Shape] ) values ( geometry :: STGeomFromText( 'POLYGON Z((113.5079365 22.24850034 
0, 113.5078521 22.24845659 0, 113.5077674 22.24841271 0, 113.5076826 22.24836872 0, 113.5075978 22.24832498 0))',4326) ) 

com.microsoft.sqlserver.jdbc.SQLServerException: 在执行用户定义例程或聚合“geometry”期间出现 .NET Framework 错误: 
System.FormatException: 24142: 在位置 8 处应为 "(",但输入中实际为 "Z"
System.FormatException: 
   在 Microsoft.SqlServer.Types.WellKnownTextReader.RecognizeToken(Char token)
   在 Microsoft.SqlServer.Types.SqlGeometry.GeometryFromText(OpenGisType type, SqlChars text, Int32 srid)

Supongo que te gusta

Origin blog.csdn.net/An1090239782/article/details/123509504
Recomendado
Clasificación