[GIS] I nead to find the nearest places based on latitude/longitude points

distancegeotoolshibernate-spatialjava

I have a java aplication backed by a mysql database which contains events with locations (latitude/longitude), the location data is saved as POINT data type.

I nead to find the nearest events in some radius. I am already able to do this but the results are innacurate.

I'm also using hibernate spatial to make the query.

I'm using the folowing libraries :
1.) com.vividsolutions.jts … for creating geometry types
2.) Geotools org.geotools … for calculating distance between points

First I make a circle Geometry with a center placed in a current location this circle is given some radius. For example 500m if I want to find the events in the distance of 500m. The problem is that the Geometry construct takes neads the radius in degrees. If i'm not mistaken the distance in degrees changes based on a point on earth.

then I query the database with the within hibernate spatial function, but the results are unnacurate.

So I dont know how to calculate the distance in degrees based on location on earth. I think there should already be some library that is doing this, but for now I havent find an example. Can you advise me some code?

My Code:

//calculate the shape where the events should be 
Geometry circle = createCircle(currentLongitude,currentLatitude, radiusInKm);
//retrieve the events from database that are within the calculated shape
List<si.arctur.model.Event> seznam = find(circle.toString());


    //the methods
private static Geometry createCircle(double x, double y, final double RADIUS) {

          double latitude = 110.54;
          double longitude = 111.320;

          //TODO beter accuracy
          double radiusInDegreesLatitude = RADIUS / latitude;
          double radiusInDegreesLongitude = RADIUS / Math.abs((longitude * Math.cos(latitude)));

          GeometricShapeFactory shapeFactory = new GeometricShapeFactory();
          shapeFactory.setNumPoints(1000);
          shapeFactory.setHeight(radiusInDegreesLatitude * 2);
          shapeFactory.setWidth(radiusInDegreesLongitude * 2);
          shapeFactory.setCentre(new Coordinate(x, y));//there are your coordinates
          Polygon shape = shapeFactory.createCircle();
          return shape; 
    }

    private List find(String wktFilter) {
        Geometry filter = wktToGeometry(wktFilter);
        Query query = em.createQuery("select e from Event e where within(e.location, :filter) = true", si.arctur.model.Event.class);
        query.setParameter("filter", filter);
        return query.getResultList();
    }

    private Geometry wktToGeometry(String wktPoint) {
        WKTReader fromText = new WKTReader();
        Geometry geom = null;
        try {
            geom = fromText.read(wktPoint);
        } catch (ParseException e) {
            throw new RuntimeException("Not a WKT string:" + wktPoint);
        }
        return geom;
    }

Best Answer

Yes, dealing with distance (or area) in unprojected latitude/longitude coordinates is fraught with peril. The length of a degree of latitude is always the same (60 nautical miles), but the length of a degree of longitude varies with the cosine of the latitude.

To do it properly, you want to project your coordinates into some plane coordinate system and do your comparisons in that. (Which plane coordinate system should you use? Not a simple answer, depends where on earth your stuff is.) Geotools must have a library for that, or since you're working in java, check out proj4j.

Alternatively, it's kind of a hack, but if all you are doing is calculating straight-line distances, you could create sort of pseudo-coordinates in meters where y=latitude*60*cf (where cf is the conversion factor to get from nautical miles to meters) and x=longitude*60*cf/cos(latitude). You're assuming a spherical earth, which it isn't. And it will severely break down if your distances are very big. But for 500m it ought to work. If you do it this way it might just be easier to calculate distance yourself, using d=sqr((x2-x1)^2 + (y2-y1)^2)).

Better would be to use Vincenty's formula, which doesn't assume a sphere. Poke around for that, you may find a library that will do it.