daphshez
daphshez

Reputation: 9648

Geotools: bounding box for a buffer in wgs84

I am need a Java function that will generate a bounding box (rectangle) around a buffer. The buffer is defined by the center point (WGS84 coordinate) and the radius (in meters).

Getting a bounding box for a buffer in JTS seems to be quite simple:

Point center = ....
Geometry boundingBox = center.buffer(...).getEnvelope();

This however is pure planar geometry. Is there a way to do this using a coordinate reference system with the distance given in meters?

Optimally with Geotools but other Java solutions will also work...

Upvotes: 6

Views: 3728

Answers (4)

Jedi-Philosopher
Jedi-Philosopher

Reputation: 146

This is an old question but somebody may still find it in google.

We created a jts-geometry-utils library for using JTS together with WGS84 coordinates. It uses geotools package to detect most suitable local CRS, project your geometry to it, perform geometry operations that need distance in meters (areas, lengths, buffers, distances etc) and then if needed reprojects operation results back to WGS84

E.g. buffering works like

Geometry wgsGeometry = ...
Geometry bufferedWgs = ProjectionUtils.bufferProjected(wgsGeometry, 100);

Check it at https://github.com/Urban-Research-Lab/jts-geometry-utils

Upvotes: 0

Greg Kendall
Greg Kendall

Reputation: 73

Here is a simple solution that I used to generate bounding box coordinates that I use with GeoNames citieJSON API to get nearby big cities from a gps decimal coordinate.

This is a Java method from my GitHub repository: FusionTableModifyJava

I had a decimal GPS location and I needed to find the biggest city/state "near" that location. I needed a relatively accurate bounding box to pass to the citiesJSON GeoNames webservice to get back the biggest city in that bounding box. I pass the location and the "radius" I am interested in (in km) and it gives back the north, south, east, west decimal coordinates needed to pass to citiesJSON.

(I found these resources useful in doing my research:

Calculate distance, bearing and more between Latitude/Longitude points.

Longitude - Wikipedia)

It is not super accurate but accurate enough for what I was using it for:

    // Compute bounding Box coordinates for use with Geonames API.
    class BoundingBox
    {
        public double north, south, east, west;
        public BoundingBox(String location, float km)
        {
             //System.out.println(location + " : "+ km);
            String[] parts = location.replaceAll("\\s","").split(","); //remove spaces and split on ,

            double lat = Double.parseDouble(parts[0]);
            double lng = Double.parseDouble(parts[1]);

            double adjust = .008983112; // 1km in degrees at equator.
            //adjust = 0.008983152770714983; // 1km in degrees at equator.

            //System.out.println("deg: "+(1.0/40075.017)*360.0);


            north = lat + ( km * adjust);
            south = lat - ( km * adjust);

            double lngRatio = 1/Math.cos(Math.toRadians(lat)); //ratio for lng size
            //System.out.println("lngRatio: "+lngRatio);

            east = lng + (km * adjust) * lngRatio;
            west = lng - (km * adjust) * lngRatio;
        }

    }

Upvotes: 0

Eduardo
Eduardo

Reputation: 4382

Although you have approached it in another way, I have another solution for that. The results will be way more precise than with your proposed solution.

GeometryFactory GEOMETRY_FACTORY = JTSFactoryFinder.getGeometryFactory();

// Remember, order is (longitude, latitude)
Coordinate center = Coordinate(2.29443, 48.85816);
Point point = GEOMETRY_FACTORY.createPoint(center);

// Buffer 50KM around the point, then get the envelope
Envelope envelopeInternal = buffer(point, 50000).getEnvelopeInternal();

// Then you can play with the envelope, e.g.,
double minX = envelopeInternal.getMinX();
double maxX = envelopeInternal.getMaxX();

// The buffer using distanceInMeters
private Geometry buffer(Geometry geometry, double distanceInMeters) throws FactoryException, TransformException {
     String code = "AUTO:42001," + geometry.getCentroid().getCoordinate().x + "," + geometry.getCentroid().getCoordinate().y;
     CoordinateReferenceSystem auto = CRS.decode(code);

     MathTransform toTransform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, auto);
     MathTransform fromTransform = CRS.findMathTransform(auto, DefaultGeographicCRS.WGS84);

     Geometry pGeom = JTS.transform(geometry, toTransform);
     Geometry pBufferedGeom = pGeom.buffer(distanceInMeters);
     return JTS.transform(pBufferedGeom, fromTransform);
}

And here is the map with the result, buffer in red, envelope in black.

Buffer and envelope

Upvotes: 5

daphshez
daphshez

Reputation: 9648

I ended up using a GeodeticCalculator to manually find the corners of the box. Frankly the results aren't very precise, but that's the best solution I found till now:

 GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
 CoordinateReferenceSystem wgs84 = DefaultGeographicCRS.WGS84;
 GeodeticCalculator geodeticCalculator = new GeodeticCalculator(wgs84);
 geodeticCalculator.setStartingGeographicPoint(center.getX(), center.getY());
 Coordinate[] coordinates = new Coordinate[5];
 for (int i = 0; i < 4; i++) {
    geodeticCalculator.setDirection(-180 + i * 90 + 45, bufferRadiusMeters * Math.sqrt(2));
    Point2D point2D = geodeticCalculator.getDestinationGeographicPoint();
    coordinates[i] = new Coordinate(point2D.getX(), point2D.getY());
 }
 coordinates[4] = coordinates[0];
 Polygon box = geometryFactory.createPolygon(coordinates);

Upvotes: 1

Related Questions