Finding Locations Inside of Radius in Java
In some project, we may need location transactions.
Such as, we have a system that allows users to find bikes nearby. In most of projects, the system use some cloud services for this requirement but if we want to create own location service, we can create it easly.
Let’s examine in detail:
Firstly we need a java project. We can generate a Spring Boot project easily with Spring Initializer.
Now, we need to PostgreSQL server and we can install it with homebrew in macOS.
(You can follow it from https://www.postgresql.org/download/)
In terminal:
brew install postgresql
For geographical transactions we have to add Postgis extension to PostgreSQL server. We can install it with homebrew.
(You can follow it from https://postgis.net/install/)
In terminal:
brew install postgis
After installation, we have to add the extension.
We should download Postgres.app here.
We can access the command line for all databases here.
In Terminal:
CREATE EXTENSION postgis;
And we should see the extension in pgAdmin.
Installation processes finished. Let’s start the coding.
We have to add this dependencies in pom.xml
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-spatial</artifactId>
</dependency>
Now we can use JTS classes in project.
1. Point
Point class has X and Y coordinates like latitude and longitude. We need users location like (20, 20) and we should generate point class with location information.
2. Polygon
Polygon should be a circle for this problem. We will see how it should be generated.
Point and polygon scheme example:
We need a entity class:
@Entity
@Data
public class bikeEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(columnDefinition = "geometry(Point,4326)")
private Point location;
@Column(nullable = false)
private String bike;
}
This is the most important part. Normally we can do this without postgis but for this we need to get all the data from the db and then filter the data in the radius. This method will be costly and slow as it will run in RAM.
So we should do this process with sql.
Repository:
@Repository
public interface bikeRepository extends JpaRepository<bikeEntity, Long> {
@Query(value = "SELECT u from bikeEntity u WHERE geometry_within(u.location, :polygon) = true")
List<bikeEntity> findWithin(@Param("polygon") Polygon polygon);
}
And we should pass Polygon parameter to this query.
Service:
@Autowired
bikeRepository BikeRepository;
public List<bikeEntity> getBikes(Long radius, Long x, Long y, Pageable pageable) {
GeometricShapeFactory shapeFactory = new GeometricShapeFactory();
shapeFactory.setNumPoints(32);
shapeFactory.setCentre(new Coordinate(x, y));
shapeFactory.setSize(radius * 2);
Polygon polygon = shapeFactory.createCircle();
List<bikeEntity> bikeListInsideOfRadius = BikeRepository(polygon);
return bikeListInsideOfRadius() > 0 ? bikeListInsideOfRadius : new ArrayList<>();
}
public Point parseLocation(double x, double y) {
Geometry geometry = wktToGeometry(String.format("POINT (%s %s)", x, y));
Point p = (Point) geometry;
p.setSRID(4326);
return p;
}
That’s all. With this solution, you can filter datas by location.