【问题标题】:Predicates in Hibernate-Spatial + PostGIS to search within miles radius with Latitude / LongitudeHibernate-Spatial + PostGIS 中的谓词使用纬度/经度在英里半径内搜索
【发布时间】:2017-01-19 19:40:23
【问题描述】:

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

虽然之前已经提出过此类问题,但之前仅使用 Predicates 提出过一次,并且可能不是最新的,因为它不起作用。而且我必须能够使用谓词而不是 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 的设置器中调用更新位置。

谓词,我从Using JPA Criteria Api and hibernate spatial 4 together得到的

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 中的休眠空间版本:

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

我怀疑问题可能是由于模型/架构设置为“地理(点,4326)”类型,但 shapeFactory 创建了“几何(点,4326)”类型的半径。

非常感谢您对此提供的任何帮助。谢谢。

【问题讨论】:

    标签: hibernate postgis predicate hibernate-spatial


    【解决方案1】:

    我只阅读了你的一部分问题,但有几件事引发了几个危险信号:

    • 您需要将轴顺序翻转为 (X Y) 或 (经纬度)
    • ST_Within 仅适用于 geometry 类型,不适用于 geography
    • 要在区域内查找点,请勿创建不完美的缓冲点
    • 您会发现ST_DWithin 更快,并且geography 类型得到了很好的支持;对于半径,只需将您的英里转换为米

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-15
      • 1970-01-01
      • 1970-01-01
      • 2015-07-04
      • 2012-02-18
      • 1970-01-01
      • 2015-06-15
      相关资源
      最近更新 更多