当前位置: 首页 > 知识库问答 >
问题:

冬眠空间PostGIS中的谓词,以纬度/经度搜索半径内的英里

朱宏爽
2023-03-14

我试图使用Hibernate Spatial中的谓词在X英里(用户定义)的半径范围内进行搜索。

虽然这类问题以前被问过,但在谓词中只问过一次,而且可能不是最新的,因为它不起作用。我必须能够使用谓词,而不是HQL。

我通过另一个答案找到了如何为PostGIS的WITHIN关键字设置谓词,但无法运行查询。

安装结束后,我声明错误输出和包版本。

以下是我的模式中的内容:

CREATE EXTENSION Postgis;

CREATE TABLE "user" (
    id               bigint  NOT NULL,
    search_radius    int,
    latitude         double precision,
    longitude        double precision,
    location         geography(point, 4326),

    PRIMARY KEY (id)
);

在我的模型中:

import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import org.hibernate.annotations.Type;

@Entity
@Table(name = "`user`")
public class User implements UserDetails {
    @Id
    private Long id;
    private Integer searchRadius = 125;
    private Double latitude;
    private Double longitude;
    @Column(name = "location", columnDefinition = "geography(Point, 4326)", nullable = true)
    @Type(type = "jts_geometry")
    private Point location;

    ...

    private void updateLocation() {
        if (latitude != null && longitude != null) {
            try {
                location = (Point) new WKTReader().read("POINT(" + latitude + " " + longitude + ")");
                location.setSRID(Location.SRID);
            } catch (ParseException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    }
}

其中更新位置在lat/lng的setters中被调用

谓词,这是我从使用JPA标准Api和Hibernate空间4一起获得的

import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Point;
import java.io.Serializable;
import javax.persistence.criteria.Expression;
import org.hibernate.jpa.criteria.CriteriaBuilderImpl;
import org.hibernate.jpa.criteria.ParameterRegistry;
import org.hibernate.jpa.criteria.Renderable;
import org.hibernate.jpa.criteria.compile.RenderingContext;
import org.hibernate.jpa.criteria.predicate.AbstractSimplePredicate;

public class WithinPredicate extends AbstractSimplePredicate implements Expression<Boolean>, Serializable {
    private final Expression<Point> matchExpression;
    private final Expression<Geometry> area;

    public WithinPredicate(CriteriaBuilderImpl criteriaBuilder, Expression<Point> matchExpression, Expression<Geometry> area) {
        super(criteriaBuilder);
        this.matchExpression = matchExpression;
        this.area = area;
    }

    public Expression<Point> getMatchExpression() {
        return matchExpression;
    }

    public Expression<Geometry> getArea() {
        return area;
    }

    public void registerParameters(ParameterRegistry registry) {
        // Nothing to register
    }

    @Override
    public String render(boolean isNegated, RenderingContext renderingContext) {
        StringBuilder buffer = new StringBuilder();
        buffer.append(" within(")
            .append(((Renderable) getMatchExpression()).render(renderingContext))
            .append(", ")
            .append(((Renderable) getArea()).render(renderingContext))
            .append(" ) = true ");
        String bo = buffer.toString();

        return bo;
    }
}

以及使用WithinPredicate和circle工厂创建查询。

import com.xxxx.repository.WithinPredicate;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.util.GeometricShapeFactory;

@Service
class ... {
    public List<User> fetchFor(User user) {
        ...
        Geometry radius = createCircle(user.getLatitude(), user.getLongitude(), user.getSearchRadius());
        radius.setSRID(Location.SRID);
        p = b.and(p, new WithinPredicate(
            (CriteriaBuilderImpl) b,
            u.get(User_.location),
            new LiteralExpression<Geometry>((CriteriaBuilderImpl) b, radius)
        ));
    }

    private static Geometry createCircle(double x, double y, final double RADIUS) {
        GeometricShapeFactory shapeFactory = new GeometricShapeFactory();
        shapeFactory.setNumPoints(32);
        shapeFactory.setCentre(new Coordinate(x, y));//there are your coordinates
        shapeFactory.setSize(RADIUS * 2);//this is how you set the radius
        return shapeFactory.createCircle().getBoundary();
    }
}

不幸的是,我在输出上得到的错误是:

2016-09-09 14:03:30.862  WARN 56393 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 0, SQLState: 42883
2016-09-09 14:03:30.862 ERROR 56393 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : ERROR: function st_within(geography, bytea) does not exist
  Hint: No function matches the given name and argument types. You might need to add explicit type casts.
  Position: 336

gradle中的Hibernate空间版本:

compile('org.hibernate:hibernate-spatial:5.0.9.Final')

我怀疑问题可能是由于模型/模式被设置为“Geography(Point,4326)”类型,而shapeFactory创建了“Geometry(Point,4326)”类型的半径。

非常感谢你在这方面的帮助。谢谢

共有1个答案

狄玉书
2023-03-14

我只读了你问题的一部分,但是有几件事引起了一些危险信号:

  • 您需要将轴顺序翻转到(X Y)或(经纬度)
  • ST_Within只存在于几何类型,而不存在于地理
  • 要在区域内查找点,不要创建不完美的缓冲点
  • 你会发现ST_DWithin更快,并为地理类型提供了很好的支持;对于半径,只需将您的英里隐藏到米
 类似资料:
  • 问题内容: 我有一个目前约有1万行的数据库。它们都基于邮政编码的中心具有经度/纬度。我发现了一个我正在尝试扩展的查询,但是总的来说它在某种程度上运行良好。但是,在下面的示例中,我试图找到半径25英里以内的事物,这似乎在大多数情况下都是可行的。我的大部分结果都在25英里标准范围内,但是我得到的结果很少,偏离了86英里到800英里。 示例:这是我的中心经度:37.2790669,-121.874722

  • 我有一个Postgres表,其中包含一些使用shapefile创建的数据。我需要找到所有在给定纬度和经度半径2英里范围内相交的记录。我使用了一些查询,包括下面这个。 从us_census_zcta中选择 ST_INTERSECTS(几何、圆(点(40.730610, -73.935242), 2)); 但是没有一个管用。我到底做错了什么?如何才能得到我需要的结果? SRID为4269(NAD 83

  • 在我的应用程序中,我将Yii与PostgreSQL和PostGIS一起使用。我有一个具有字段“location”的模型“user”。Location-field的内容对人来说毫无意义,但是Postgres可以使用st_astext-method将Location-field的值转换为经度和纬度。我已经在我的模型中为经度和纬度创建了虚拟字段。当我保存我的模型时,我使用beforeSave和after

  • 问题内容: 我是Hibernate Spatial的新手,正在尝试对给定半径内的对象执行简单查询。我已经使用来自Google Maps和其他来源的数据在数据库中创建了许多条目,这些条目的属性分别对应于经度和纬度。此属性在我的Entity类中定义如下: 我现在正在尝试找出如何搜索所有坐标位于距给定点 x 公里半径内的实体对象的方法。例如,我想找到落在该点半径50公里以内的对象(12.34567,-7

  • 我有一对纬度 然而,我需要一个更灵活的解决方案,例如,我希望能够指定1/3, 1/4, 8/9等对点之间。 注:lat1,long1

  • 我在处理一个单词搜索问题。我正确地实现了dfs搜索,但在其他地方有一些琐碎的错误。