spingboot+es+geometry实现地理数据检索

springboot在集成ES,存储空间数据jts的geometry数据类型,存进es后如何根据点坐标检索对应矢量数据,
检索到数据后将返回值映射成实体的时候报错了,如何解决
实体:

import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Polygon;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import javax.persistence.Id;
import java.util.Date;

@Data
@Document(indexName = "geojson")
public class Geojson {

    @Id
    @Field(type = FieldType.Long)
    private Long id;

    @Field(type = FieldType.Long)
    private Long fileId;

    @Field(type = FieldType.Object)
    @JsonDeserialize(using = GeometryDeserializer.class)
    private Geometry json;


    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8",locale = "zh_CN")
    @Field(type = FieldType.Date, name = "create_time", pattern = "yyyy-MM-dd HH:mm:ss",format = DateFormat.custom )
    private Date createTime;

}

  NativeSearchQuery query = new NativeSearchQueryBuilder()
                .withFilter(queryBuilder)
                .withPageable(PageRequest.of(0, 10))
                .build();

  SearchHits<Geojson> search = elasticsearchRestTemplate.search(query, Geojson.class);

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.locationtech.jts.geom.Polygon]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.locationtech.jts.geom.Polygon.()

你的实体类有问题,json字段的类型为Geometry,而Geometry是一个接口,没有默认的构造函数,所以在映射时无法实例化Geometry的具体实现类

你要么就改成具体的实现类,比如Polygon,Point,LineString

要么就用Spring Data Elasticsearch的自定义映射功能,并且配置合适的序列化和反序列化类来处理Geometry类型

采用chatgpt:
您遇到的错误与 JTS 库中的 Polygon 类没有默认构造函数有关。 这意味着 Elasticsearch 在从检索到的文档映射回数据时无法创建 Polygon 类的实例。

要解决此问题,您可以尝试使用自定义转换器来处理 Geojson 实体中 Geometry 字段的映射。 该转换器将处理 Geometry 对象的序列化和反序列化为 Elasticsearch 可以理解的格式。

以下是如何为几何字段实现自定义转换器的示例:

创建自定义转换器:

import org.locationtech.jts.geom.Geometry;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;

@ReadingConverter
@WritingConverter
public class GeometryConverter implements Converter<String, Geometry> {

    private final WKTReader wktReader = new WKTReader();

    @Override
    public Geometry convert(String source) {
        try {
            return wktReader.read(source);
        } catch (ParseException e) {
            throw new IllegalArgumentException("Invalid geometry format: " + source, e);
        }
    }
}

使用 Elasticsearch 映射注册转换器:

import org.locationtech.jts.geom.Geometry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;

@Configuration
@EnableElasticsearchRepositories(basePackages = "your.repository.package")
public class ElasticsearchConfig {

    @Bean
    public ElasticsearchCustomConversions elasticsearchCustomConversions() {
        return new ElasticsearchCustomConversions(Arrays.asList(new GeometryConverter()));
    }
}

确保将 your.repository.package 替换为 Elasticsearch 存储库类所在的实际包。

通过此设置,GeometryConverter 将处理 Geometry 字段与众所周知的文本 (WKT) 格式(几何对象的文本表示形式)之间的序列化和反序列化。 Elasticsearch 会将几何图形存储为 WKT 格式的字符串,自定义转换器在检索数据时会将其转换回 Geometry 对象。

这应该可以解决与缺少 Polygon 默认构造函数相关的 BeanInstantiationException 错误。

报错信息指出缺少Polygon的默认构造函数。为了解决这个问题,你需要为Polygon类添加一个默认构造函数。

在Geojson实体类中添加以下代码:


java
import org.locationtech.jts.geom.Polygon;

public class Polygon extends Polygon {

    public Polygon() {
        super(null, null);
    }
}

这样,你就为Polygon类添加了默认构造函数,解决了报错问题。

注意:由于你使用的是自定义的Polygon类和默认的Polygon类同名,可能会导致冲突。为了避免冲突,你可以将自定义的Polygon类命名为其他名称,例如CustomPolygon。


 "json": {
                        "_class": "org.locationtech.jts.geom.Polygon",
                        "shell": {
                            "points": {
                                "_class": "org.locationtech.jts.geom.impl.CoordinateArraySequence",
                                "dimension": 3,
                                "measures": 0,
                                "coordinates": [
                                    {
                                        "x": 121.2583329,
                                        "y": 28.6553629,
                                        "z": "NaN"
                                    },
                                    {
                                        "x": 121.2583329,
                                        "y": 28.4375079,
                                        "z": "NaN"
                                    },
                                    {
                                        "x": 121.6972175,
                                        "y": 28.4375079,
                                        "z": "NaN"
                                    },
                                    {
                                        "x": 121.6972175,
                                        "y": 28.6553629,
                                        "z": "NaN"
                                    },
                                    {
                                        "x": 121.2583329,
                                        "y": 28.6553629,
                                        "z": "NaN"
                                    }
                                ]
                            },
                            "factory": {
                                "precisionModel": {
                                    "modelType": {
                                        "name": "FLOATING"
                                    },
                                    "scale": 0.0
                                },
                                "coordinateSequenceFactory": {
                                    "_class": "org.locationtech.jts.geom.impl.CoordinateArraySequenceFactory"
                                },
                                "SRID": 0
                            },
                            "SRID": 0
                        },
                        "holes": [],
                        "factory": {
                            "precisionModel": {
                                "modelType": {
                                    "name": "FLOATING"
                                },
                                "scale": 0.0
                            },
                            "coordinateSequenceFactory": {
                                "_class": "org.locationtech.jts.geom.impl.CoordinateArraySequenceFactory"
                            },
                            "SRID": 0
                        },
                        "SRID": 0
                    }
这是ES存的json数据,反序列化这个json会出错

在反序列化的时候可以debug看一下他的类是长什么样的,他是怎么反序列化的。他是哪里出了问题,
我觉得这种应该去看一下官方文档怎么做的

参考gpt:
结合自己分析给你如下建议:
这是一个比较复杂的问题,可能涉及到多个方面的原因和解决方法。我尝试从以下几个方面来回答您:
存储jts的geometry数据类型到ES中。这需要将jts的geometry数据类型转换为ES支持的geo_shape数据类型,或者使用GeoJSON或WKT格式来表示geometry数据。ES的geo_shape数据类型可以支持多种几何形状,如点、线、多边形等,并且可以进行空间查询和聚合。GeoJSON和WKT是两种常用的几何对象表示格式,它们可以与jts的geometry数据类型相互转换。您可以使用一些工具类或者库来进行这些转换,例如org.locationtech.jts.io.WKTReader和WKTWriter,或者https://github.com/bedatadriven/jackson-datatype-jts。
根据点坐标检索对应矢量数据。这需要使用ES的geo_shape查询来根据点坐标来过滤出与之相交或者包含的矢量数据。您可以使用QueryBuilders.geoShapeQuery方法来构建一个GeoShapeQueryBuilder对象,并指定点坐标和关系类型(如intersects或within)。然后您可以使用ElasticsearchRestTemplate.search方法来执行查询,并得到SearchHits对象。
将返回值映射成实体。这需要使用jackson-datatype-jts库来实现jts的geometry数据类型和GeoJSON格式之间的序列化和反序列化。您可以在实体类中使用@JsonDeserialize(using = GeometryDeserializer.class)注解来指定反序列化器,或者在配置类中使用@Bean注解来注册JtsModule到ObjectMapper中。