Home > database >  Get nearest position with Haversine Formula in Spring
Get nearest position with Haversine Formula in Spring

Time:02-10

I have an entitiy which has two columns in the database like this:

@Column(name = "latitude", nullable = false)
private float currentPositionLatitude;

@Column(name = "longitude", nullable = false)
private float currentPositionLongitude;

And I have multiple entrys in my database(PostgresQl) for the entity with different latitudes and longitudes for all of them. Now I want to give a point (lat,lon) and a radius and show all entitys which are near the point. I first tried it with the Harversine formula like this in Spring Jpa :

   String HAVERSINE_PART = "(6371 * acos(cos(radians(:latitude)) * cos(radians(s.latitude)) *"  
        " cos(radians(s.longitude) - radians(:longitude))   sin(radians(:latitude)) * sin(radians(s.latitude))))";

    @Query("SELECT currentPositionLatitude,currentPositionLongitude FROM Entity WHERE  '%:HAVERSINE_PART%' < :distance ORDER BY '%:HAVERSINE_PART%' DESC")
      public List<Entity> findEntiryWithLocation(
      @Param("currentPositionLatitude") final float latitude,
      @Param("currentPositionLongitude") final float longitude, @Param("distance") final double distance )

this didnt work so I tried:

    @Query("SELECT s FROM Entity s WHERE "   HAVERSINE_PART   " < :distance ORDER BY "  HAVERSINE_PART   " DESC")
List<Entity> findEntityWithInDistance(
@Param("latitude") double latitude,
 @Param("longitude") double longitude,
 @Param("distance") double distanceWithInKM );

this also didnt work so then I tried:

@Query(value = "SELECT *, earth_distance(ll_to_earth(:latitude, :longitude), ll_to_earth(latitude, longitude)) as distance FROM Car WHERE  (earth_box(ll_to_earth(:latitude, :longitude), :radiusInMeters) @> ll_to_earth(latitude, longitude))ORDER BY distance ASC",
nativeQuery = true)
List<Entity> getPlacesNear ( @Param("latitude") float latitude,
                             @Param("longitude")float longitude,
                             @Param("radiusInMeters") float radiusInMeters);

This compiled but when I tried to put in values in my controller I got the error: jdbc error :2022 no dialect mapping

Here my methods for the service and the controller(the datatypes have changed maybe):

public List<Entity>showNearestEntityByDistances(float latitude, float longitude, float distance){
        return EntityRepository.getPlacesNear(latitude,longitude,distance);
    }

and the endpoint:

@GetMapping("/location")
public List<Entity>showEntityNearby(@RequestParam float latitude,@RequestParam float longitude,@RequestParam float distance){
  

    return carService.showNearestEntitysByDistances(latitude,longitude,distance);
}

I already looked at : https://www.postgresql.org/docs/current/earthdistance.html and postgresql jpa earth_distance query

but it didnt worked. Has anyone an idea what Im doing wrong ? Thanks in regard.

CodePudding user response:

The following worked for me:

first, run:

 create extension cube;

 create extension earthdistance; 

This installs extensions that enable, among other things, location-based queries without installing PostGIS

Then, Instead of storing your lat and long in separate columns, store them together in a 'point' type column, keeping in mind that the longitude comes first, not the latitude as you would expect, e.g.

  create table Entity (city varchar(100), location point);

and store your data, e.g.

insert into places(name, location) values ('lambertville', point((74.9429,40.3659)));
    
insert into places(name, location) values ('ny', point(74.0060, 40.7128));
    
insert into places(name, location) values ('phila', point( 75.1652, 39.9526));

(note that Lambertville, NJ is somewhere around half-way between Philly and New York).

Then, if, for example, you want to select Entities that are less than a certain distance from another entity, you can do something like:

select * from places 
 where 
location <@> (select location from places where name = 'ny') < 70  
  and name != 'ny';

or, if you have the coordinates already, and want to find entities withing a certain distance,

select * from places 
 where 
location <@> point(74.0060, 40.7128) < 70    
  and name != 'ny';

I have not checked whether these queries can be adapted to work with JPA @Query annotations, YMMV.

CodePudding user response:

I got it working changing a few things:

 @Query(value = "SELECT * FROM Entity s WHERE "   HAVERSINE_PART   " < :distance ORDER BY "  HAVERSINE_PART   " DESC",nativeQuery = true)
List<Entity> findEntityWithInDistance(
@Param("latitude") double latitude,
 @Param("longitude") double longitude,
 @Param("distance") double distanceWithInKM );
  • Related