There are two solutions to this:
Convert the radius in meters to degrees and treat the problem as a planar problem
Convert the lat/lon point to meters, calculate the circle in a locally planar projection and reproject back to lat/lon.
For 1 you could do something like which will be fine for small radii near the equator:
GeodeticCalculator calc = new GeodeticCalculator(DefaultGeographicCRS.WGS84);
calc.setStartingGeographicPoint(point.getX(), point.getY());
calc.setDirection(0.0, 10000);
Point2D p2 = calc.getDestinationGeographicPoint();
calc.setDirection(90.0, 10000);
Point2D p3 = calc.getDestinationGeographicPoint();
double dy = p2.getY() - point.getY();
double dx = p3.getX() - point.getX();
double distance = (dy + dx) / 2.0;
Polygon p1 = (Polygon) point.buffer(distance);
I'll show some code for the second as it is more general (i.e. it works better and for a better range of radii).
First you need to find a local projection, GeoTools provides a "pseudo" projection AUTO42001,x,y
which is a UTM projection centred at X,Y:
public SimpleFeature bufferFeature(SimpleFeature feature, Measure<Double, Length> distance) {
// extract the geometry
GeometryAttribute gProp = feature.getDefaultGeometryProperty();
CoordinateReferenceSystem origCRS = gProp.getDescriptor().getCoordinateReferenceSystem();
Geometry geom = (Geometry) feature.getDefaultGeometry();
Geometry pGeom = geom;
MathTransform toTransform, fromTransform = null;
// reproject the geometry to a local projection
if (!(origCRS instanceof ProjectedCRS)) {
double x = geom.getCoordinate().x;
double y = geom.getCoordinate().y;
String code = "AUTO:42001," + x + "," + y;
// System.out.println(code);
CoordinateReferenceSystem auto;
try {
auto = CRS.decode(code);
toTransform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, auto);
fromTransform = CRS.findMathTransform(auto, DefaultGeographicCRS.WGS84);
pGeom = JTS.transform(geom, toTransform);
} catch (MismatchedDimensionException | TransformException | FactoryException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
So now pGeom
is our point in metres. Buffering it is now easy
Geometry out = bufferFeature(pGeom, distance.doubleValue(SI.METER));
then we project back to WGS84 (lat/lon) using the reverse transform we looked up earlier:
retGeom = JTS.transform(out, fromTransform);
There is then a little messing around to change the feature type to reflect the fact we are returning a polygon instead of a Point. The full code is in this gist.
When I run it I get the following output:
POINT (10.840378413128576 3.4152050343701745)
POLYGON ((10.84937634426605 3.4151876838951822, 10.849200076653755 3.413423962919184, 10.84868480171117 3.4117286878605766, 10.847850322146979 3.4101670058279794, 10.846728706726902 3.4087989300555464, 10.845363057862208 3.407677033830687, 10.843805855306746 3.406844430298736, 10.84211693959797 3.406333115754347, 10.840361212705258 3.4061627400701946, 10.838606144204721 3.4063398515107184, 10.836919178768184 3.4068576449605277, 10.835365144548726 3.4076962232621035, 10.834003762019957 3.408823361646906, 10.832887348980522 3.410195745914279, 10.832058809914859 3.411760636805914, 10.831549986992338 3.4134578966399034, 10.831380436105858 3.4152223003379722, 10.831556675029052 3.416986042039048, 10.832071932633442 3.4186813409639054, 10.832906408849936 3.4202430463705085, 10.834028035422469 3.4216111414662183, 10.835393708241908 3.422733050021835, 10.836950943907517 3.4235656570147763, 10.838639896841123 3.424076965623486, 10.840395659406198 3.4242473268789406, 10.842150756595839 3.4240701947133396, 10.843837739370569 3.4235523773972796, 10.845391776937724 3.4227137757216988, 10.846753148314034 3.4215866180136185, 10.847869537398722 3.4202142214154887, 10.848698043354238 3.4186493270628633, 10.849206829051935 3.4169520731645546, 10.84937634426605 3.4151876838951822))