3

I'm currently trying to develop a functionality where all locations within a particular radius (e.g. within 10km) must be returned. I'm using hibernate's spatial dependency . This is what my entity looks like:

public class LocationEntity {

@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;

@Column(name = "LOCATION", columnDefinition = "geometry")
private Point location;
}

This is the repository for the entity:

public interface LocationRepository extends JpaRepository<LocationEntity, Integer> {

@Query(value = "SELECT l from LocationEntity l WHERE within(l.location, :bounds) = true")
public List<LocationEntity> findWithin(@Param("bounds") Geometry bounds); }

This is how the repository method is being called:

GeometricShapeFactory shapeFactory = new GeometricShapeFactory();
    shapeFactory.setNumPoints(100);
    shapeFactory.setCentre(new Coordinate(27.174835, 78.040753));
    shapeFactory.setSize(2*10);
    List<LocationEntity> locations = repository.findWithin(shapeFactory.createCircle());

The issue I'm facing is that this method returns some locations which don't fall within this radius . Is this the correct approach for the problem statement?

enrg
  • 86
  • 4

1 Answers1

2

Finally got a working solution. Leaving my approach here in case anyone else is faces this issue. Code is sourced from an answer to the question here.

final GeometricShapeFactory shape = new GeometricShapeFactory(new GeometryFactory());// INFO :jts lib
    GeometryFactory factory = new GeometryFactory();
    Point center = factory.createPoint(new Coordinate(latitude, longitude));
    center.setSRID(4326);

    final Coordinate centreInUTM = new Coordinate(projectedToGeographic(center.getX(), center.getY()));
    shape.setCentre(centreInUTM);
    shape.setSize(2 * radius);// distance in meters e.g 10000 for 10 km
    shape.setNumPoints(64);

    String UtmZone = degreeToUtmConverter(latitude, longitude);
    Integer longitudeZone = Integer.parseInt(UtmZone.split(",")[0]);
    char latitudeZone = UtmZone.split(",")[1].charAt(0);
    logger.info("longitude zone:" + longitudeZone);
    logger.info("latitude zone:" + latitudeZone);

    // WGS 1984 is a geographic coordinate system, and UTM is a projected coordinate
    // system
    Polygon polygon = new GeometryFactory().createPolygon(Arrays.stream(shape.createEllipse().getCoordinates())
            .map(c -> geographicToProjected(c.getOrdinate(0), c.getOrdinate(1), longitudeZone, latitudeZone)).toArray(Coordinate[]::new));

    List<LocationEntity> locations = repository.findWithin(polygon);

Added a function for converting lat/long to UTM lat/long zone taken from here

private Coordinate projectedToGeographic(double latitude, double longitude) {

    LatLong latlong = LatLong.valueOf(latitude, longitude, NonSI.DEGREE_ANGLE);

    UTM utm = UTM.latLongToUtm(latlong, ReferenceEllipsoid.WGS84); 

    double cX = utm.getCoordinates()[0];
    double cY = utm.getCoordinates()[1];

    return new Coordinate(cX, cY);
}

private Coordinate geographicToProjected(double easting, double northing, Integer longitudeZone, char latitudeZone) {
    UTM utm = UTM.valueOf(longitudeZone, latitudeZone, easting, northing, SI.METER);// INFO :18T UTM for NYC
    CoordinatesConverter<UTM, LatLong> utmToLatLong = UTM.CRS.getConverterTo(LatLong.CRS);
    LatLong latLong = utmToLatLong.convert(utm);

    double cX = latLong.getCoordinates()[0];
    double cY = latLong.getCoordinates()[1];

    return new Coordinate(cX, cY);
}

private String degreeToUtmConverter(double Lat, double Lon) {
    Integer zone;
    char letter;
    zone = (int) Math.floor(Lon / 6 + 31);
    if (Lat < -72)
        letter = 'C';
    else if (Lat < -64)
        letter = 'D';
    else if (Lat < -56)
        letter = 'E';
    else if (Lat < -48)
        letter = 'F';
    else if (Lat < -40)
        letter = 'G';
    else if (Lat < -32)
        letter = 'H';
    else if (Lat < -24)
        letter = 'J';
    else if (Lat < -16)
        letter = 'K';
    else if (Lat < -8)
        letter = 'L';
    else if (Lat < 0)
        letter = 'M';
    else if (Lat < 8)
        letter = 'N';
    else if (Lat < 16)
        letter = 'P';
    else if (Lat < 24)
        letter = 'Q';
    else if (Lat < 32)
        letter = 'R';
    else if (Lat < 40)
        letter = 'S';
    else if (Lat < 48)
        letter = 'T';
    else if (Lat < 56)
        letter = 'U';
    else if (Lat < 64)
        letter = 'V';
    else if (Lat < 72)
        letter = 'W';
    else
        letter = 'X';
    return zone.toString() + "," + letter;
}
enrg
  • 86
  • 4