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...
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.
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);
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;
}
}
Related
Im writing a program, that takes 3d coordinates and places them on a 3d globe, projected onto a 2d screen. To turn the coordinates around an axis, i wanted to use the normal of the two points (start- and end- point (of the flight)), to turn it onto the x3 axis and then turn the rest of the points in the same manner, to eventually calculate the points, that i need to animate a point, that goes from point A to point B. the matrix I use to put the points onto the globe is:
public double[] genXYZ(double phi, double theta) {
double[] coords3D = new double[3];
phi = Math.toRadians(phi);
theta = Math.toRadians(theta);
coords3D[0] = Math.cos(theta) * Math.cos(phi);
coords3D[1] = Math.cos(theta) * Math.sin(phi);
coords3D[2] = Math.sin(theta);
return coords3D;
}
everything works just fine. Until I try to calculate the phi and theta angles of the normal (after I calculate the normal correctly).
public double[] getNAngles(double[] ncoords) {
double[] NAngles = new double[2];
NAngles[1] = Math.asin(Math.toRadians(ncoords[2]));
NAngles[0] = Math.acos(ncoords[0] / Math.cos(NAngles[1]));
NAngles[0] = Math.toDegrees(NAngles[0]);
NAngles[1] = Math.toDegrees(NAngles[1]);
System.out.println("N phi: " + NAngles[0]);
System.out.println("N theta: " + NAngles[1]);
return NAngles;
}
Globe
Cyan: start (Greenwich)
Magenta: end (Istanbul)
Black: normal
Pink: what the code calculated, what angles black has.
Thanks in advance!
Remove Math.toRadians call because coords are not angles, they are dimensionless values in range -1..1
NAngles[1] = Math.asin(ncoords[2]);
Also consider using atan2 function for NAngles[0] calculation to exclude sign effects and arccosine angle limit (0..Pi).
NAngles[0] = Math.atan2(ncoords[1], ncoords[0]);
Please take a look at my other question which this was done incorrectly here.
What I need to do
I have an image of a map of my country, I took the piece of the map from Google Maps, so that means I know all of the corner's coordinates in longitude and latitude. My program needs to show up the map, and paint targets on it where each target has its only longitude and latitude, similar to how radar displays targets.
The problem
The problem with my solution is that it's not really using real mathematical formulas to get the X, Y position. It uses simple division and multiple by ratio with minimum and maximum as you can see in my other question.
this is the method:
protected Location getCoordinatesByGlobe(float latitude, float longitude) {
/**
* Work out minimum and maximums, clamp inside map bounds
*/
latitude = Math.max(mapLatitudeMin, Math.min(mapLatitudeMax, latitude));
longitude = Math.max(mapLongitudeMin, Math.min(mapLongitudeMax, longitude));
/**
* We need the distance from 0 or minimum long/lat
*/
float adjLon = longitude - mapLongitudeMin;
float adjLat = latitude - mapLatitudeMin;
float mapLongWidth = mapLongitudeMax - mapLongitudeMin;
float mapLatHeight = mapLatitudeMax - mapLatitudeMin;
float mapWidth = mapImage.getWidth();
float mapHeight = mapImage.getHeight();
float longPixelRatio = mapWidth / mapLongWidth;
float latPixelRatio = mapHeight / mapLatHeight;
int x = Math.round(adjLon * longPixelRatio) - 3;// these are offsets for the target icon that shows.. eedit laterrr #oz
int y = Math.round(adjLat * latPixelRatio) + 3; //
// turn it up
y = (int) (mapHeight - y);
return new Location(x, y);
}
What I have tried
So I was a bit with myself tried to think of something logical, on how can I do this. And I came up with something that doesn't really work exactly:
If we have the corner top-left for example coordinates (Longitude and latitude), and we have the coordinates of the target that we want to display, that means we can do distanceToPoint to know how many kilometers far from the start it is.
After that, we need to know the heading to that point, so we do calculateHeading which gives us the angle to the target point.
So lets call A the starting point (top-left corner)
float aLat = 33.49f;
float aLong = 33.69f;
And our target point we call it b:
float bLat = 32f;
float bLong = 35f;
And then we can calculate the distance from A to B in kilometers:
double km = distanceTopPoint(aLat, aLong, bLat, bLong);
And then we calculate the angle to the point:
double angle = calculateHeading(aLat, aLong, bLat, bLong);
And if we have the km distance and angle, we can know the distance in km for longitude and latitude:
int latDistance = (int) Math.round(km * Math.cos(angle));
int lonDistance = (int) Math.round(km * Math.sin(angle));
So now I probably have the distance from the longitude to the target's longitude and same for latitude. But what can I do with this information?
I tried thinking again, and I figured out that I can know the distance from the left top corner to the right top corner distance in km and same for top left corner to top left bottom corner. And then I can do width / km to get the km per pixel.
But I am really unsure, im sure that I am doing something wrong.
Any ideas?
The Mercator projection is a cylindrical projection, i.e. the generalized coordinates can be calculated as:
a = longitude
b = tan(latitude)
These are unscaled coordinates, i.e. they do not correspond to pixel positions.
Let's say you have an image of w x h pixels that represents the area between (min_long, min_lat) - (max_long, max_lat). These coordinates can be converted to generalized projected coordinates with the above formulas, which yields (min_a, min_b) - (max_a, max_b).
Now you need a linear mapping of the generalized coordinates to pixel positions. This linear mapping can be expressed with four parameters (two scaling parameters and two offset parameters):
x = s_a * a + o_a
y = s_b * b = o_a
Therefore, you need to find the four parameters. You know that the top left corner has pixel coordinates (0, 0) and generalized coordinates (min_a, max_b). Similarly for the bottom right corner. This gives you four constraints and a linear system of equations:
0 = s_a * min_a + o_a
0 = s_b * max_b + o_b
w = s_a * max_a + o_a
h = s_b * min_b + o_b
The solution of this system is:
s_a = w / (max_a - min_a)
o_a = -w * min_a / (max_a - min_a)
s_b = -h / (max_b - min_b)
o_b = h * max_b / (max_b - min_b)
And this is it. If you want the pixel coordinates for some arbitrary point `(long, lat), then do the following:
Calculate the generalized coordinates a and b (be careful to use radians when calculating the tangens).
Use the linear map to convert a and b to pixel coordinates x and y with the pre-calculated parameters.
Inversion
To get latitude and longitude from pixel coordinates, do the following:
Calculate the generalized coordinates:
a = (x - o_a) / s_a
b = (x - o_b) / s_b
Calculate the geo-coordinates:
longitude = a
latitude = arc tan (b)
Again, be careful about radians/degrees.
I am using a JGraphModelAdapter to convert a Jgraph to a JGrapht. Its working fine and my vertices are displayed on the gui as they should.
I have been given some base code to work with when editing the positions of the vertices on the GUI. (Seen Below)
private void positionVertices() {
for (MapLocation aVertex : this.graphContents.vertexSet()) {
double xPostion = (?);
double yPostion = (?);
this.positionAVertex(aVertex, xPostion, yPostion);
}
}
private void positionAVertex(Object vertex, double x, double y) {
DefaultGraphCell vertexDisplayCell = this.theModelAdapter
.getVertexCell(vertex);
AttributeMap cellAttributes = vertexDisplayCell.getAttributes();
Rectangle2D bounds = GraphConstants.getBounds(cellAttributes);
Rectangle2D newBounds = new Rectangle2D.Double(x, y, bounds.getWidth(),
bounds.getHeight());
GraphConstants.setBounds(cellAttributes, newBounds);
HashMap<DefaultGraphCell, AttributeMap> cellAttributeMap = new HashMap<>();
cellAttributeMap.put(vertexDisplayCell, cellAttributes);
this.theModelAdapter.edit(cellAttributeMap, null, null, null);
}
Each MapLocation(Vertex) object represents a Zip code of a place in America, it has access to an accurate latitude and longitude getter.
Using them I should be able to position the vertices at proportionally realistic places on my gui. However I am having difficulty determining what formula (Denoted by (?)) would work well in this situation.
The frame is set to MAXIMIZED_BOTH but must work on any monitor size.
Thanks for any assistance in advance.
In light of your comments, I'll make the following assumptions.
You're only going to display the 48 contiguous states, that is, no Alaska or Hawaii.
You don't need the accuracy that would come with spherical trigonometry - treating North America as a flat rectangle is adequate.
Your latitudes are given as a positive number of degrees north.
Your longitudes are given as a positive number of degrees west (you may want to check this - I would expect this for data that was limited to USA, although common international usage is to have positive degrees for east and negative for west).
The borders of the region that you want to display are 125 degrees and 65 degrees west, and 50 degrees and 25 degrees north.
The area on your screen where the map is drawn goes from (left, top) to (left + width, top + height)
Given those assumptions, here's how to convert longitude and latitude to (x,y).
x = left + width * ( 125 - longitude ) / 60;
y = top + height * ( 50 - latitude ) / 25;
Note that if your longitudes are in fact negative for degrees west, you can just replace - with + in the expression for x.
This is the code that work
double xPostion = (124 - Math.abs(aVertex.getLongitude()));
xPostion = xPostion / 57;
xPostion = xPostion * this.myWidth;
double yPostion = 49 - aVertex.getLatitude();
yPostion = yPostion / 25;
yPostion = yPostion * this.myHeight ;
57 is difference in longitude from eastern most continental US and western most.
25 is the difference in latitude from the norther most continental US and the southern most
124 is the longitude of the western most continental US.
49 is the latitude of the norther most continental US.
I have the following setup of items in real life:
The radar is static, which means it always has the same position. The A-item can move and its position can be whatever. From the radar I can read the x and y coordinates of A in relation to the radar. I have written the following classes to describe the position of each item:
public class Position {
public enum Direction {
EAST, WEST, NORTH, SOUTH
};
public final Direction latitudeDirection, longitudeDirection;
public final float latitude, longitude, altitude;
public Position(Direction latitudeDirection, Direction longitudeDirection,
float latitude, float longitude, float altitude) {
this.latitudeDirection = latitudeDirection;
this.longitudeDirection = longitudeDirection;
this.latitude = latitude;
this.longitude = longitude;
this.altitude = altitude;
}
public Position(float radarX, float radarY) {
// TODO: Implement the question here
this.altitude = Config.RADAR_POSITION.altitude;
}
}
class Config {
// Position of the radar
public static final Position RADAR_POSITION = new Position(
Position.Direction.NORTH, // Latitude direction
Position.Direction.EAST, // Longitude direction
55.0f, // Latitude
13.0f, // Longitude
60.0f); // Altitude
// Facing direction of the radar in degrees. 0° is north, advancing
// clockwise.
public static final float RADAR_FACING_DIRECTION = 10.0f;
}
Now given the geographic coordinates of the radar, the x and y coordinates of A relative to the radar and the facing direction of the radar relative to the North, how can I calculate the absolute geographic coordinates of A?
The curvature of the earth is not an issue since the maximum value of x and/or y cannot be more than a couple hundred meters.
As an example, you can use trigonometric functions to create triangles to find coordinates of A:
In this case, Ax = (y)(cos 10) - (x)(cos 80), and you could work out Ay similarly.
This way, you are never stuck in degrees, you are simply working in meters.
The robust solution is Vishal's comment in the OP, which was posted whilst I was drawing and scanning:
xnew = x * cos(theta) - y * sin(theta);
ynew = x * sin(theta) + y * cos(theta);
In general, you can use the following steps:
transform your radar position (lat, lon, height) into metric earth centered earth fixed xyz-system (ECEF)
You can then use/combine any rotation and translation arguments/matrices, which describe radar rotation and object position, in this metric system
back transform newly acquired xzy coordinates to lat/lon/h
There are many ressources for such transformations, check this, for instance: http://www.gmat.unsw.edu.au/snap/gps/clynch_pdfs/coordcvt.pdf
You can also introduce a scene coordinate system, if needed (ENU). Here is a fairly good overview describing the relation of UTM, ECEF, ENU and geodotic coordinates(Lat/lon/h):
http://www.dirsig.org/docs/new/coordinates.html
If you need sample code for ECEF to/from Geodetic conversion, have a look at the matlab code, http://www.mathworks.de/de/help/map/ref/ecef2geodetic.html,
or use a library like GDAL (http://www.gdal.org/)
I think it should be possible to do it this way: Convert the x and y coordinates to polar coordinates r and theta, (with the radar as the origin). Subtract the radar's rotation, and convert back to cartesian coordinates. Then you just have to convert to latitude and longitude and add the coordinates of the radar.
double r = Math.hypot(radarX, radarY);
double theta = Math.atan2(radarY, radarX);
theta -= Math.toRadians(Config.RADAR_FACING_DIRECTION);
float x = (float) r * Math.cos(theta);
float y = (float) r * Math.sin(theta);
longitude = metersToLongitude(Config.RADAR_POSITION, y) + Config.RADAR_POSITION.longitude;
latitude = metersToLatitude(Config.RADAR_POSITION, x) + Config.RADAR_POSITION.latitude;
I found formulae for the length of a degree of latitude and longitude on Wikipedia. A degree of latitude is the same everywhere but longitude gets smaller near the poles.
static float metersToLatitude(Position near, float meters) {
return meters / 110.6*1000;
}
static float metersToLongitude(Position near, float meters) {
float lat = Math.toRadians(near.latitude);
return meters /
(111132.954 - 559.822 * Math.cos(2*lat) + 1.175 * Math.cos(4*lat));
}
Unfortunately this doesn't seem to work and I can't figure out why.
If you want to express your coordinates in positive degrees east/west/north/south you'll also have to check if they're negative and invert them and the direction in that case.
Does this help for conversion?
http://en.wikipedia.org/wiki/Geodetic_system#From_geodetic_to_ECEF ?
Finally I solved it. The code is attached below. Since Samuel Edwin Ward's answer is the one that inspired me, I will accept his answer.
public Position(float radarX, float radarY) {
// Convert A's position to distance and bearing in relation to the North
double objDistance = (Math.hypot(radarX, radarY) / 6367500 /* Mean earth radius */);
double objBearing = (Math.atan2(radarY, radarX) + Math.toRadians(Config.RADAR_BEARING));
// Convert the Radar's geographic coordinates to radians
double latitudeRadar = Math.toRadians(Config.RADAR_POSITION.latitude);
double longitudeRadar = Math.toRadians(Config.RADAR_POSITION.longitude);
// Calculate A's geographic coordinates in radians
double latitudeObject = Math.asin(Math.sin(latitudeRadar)*Math.cos(objDistance) +
Math.cos(latitudeRadar)*Math.sin(objDistance)*Math.cos(objBearing));
double longitudeObject = longitudeRadar + Math.atan2(Math.sin(objBearing)*Math.sin(objDistance)*Math.cos(latitudeRadar),
Math.cos(objDistance)-Math.sin(latitudeRadar)*Math.sin(latitudeObject));
// Normalize to -180 ... +180 degrees
longitudeObject = (longitudeObject+3*Math.PI) % (2*Math.PI) - Math.PI;
// Set the A's coordinates in degrees
this.latitude = (float) Math.toDegrees(latitudeObject);
this.longitude = (float) Math.toDegrees(longitudeObject);
// Set the rest of the arguments
this.latitudeDirection = Config.RADAR_POSITION.latitudeDirection;
this.longitudeDirection = Config.RADAR_POSITION.longitudeDirection;
this.altitude = Config.RADAR_POSITION.altitude;
}
You rotate all coordinates in the local system of the radar -10° and are then able to just add the radar's x/y coordinates to the A object coordinates.
I'm new to GIS applications and I have a little problem. I'm trying to plot a polygon using geographic coordinates but I need to convert these coordinates to points before I can use them in plotting the polygon.
I don't know how to convert these coordinates to points to use for the polygon. I spent all my yesterday finding out how to do this but I really still don't get it.
I tried using the geography datatype in ms sql server 2008 but I couldn't find a java api in retrieving the polygon or the converted coordinates.
Please help me with a sample code on how to do this.
Thanks a lot people!!!
This is my code. 'Code'
double earth=6371;
double focal=500;
double lat= 47.653 ;
double lon = -122.358 ;
double latitude = lat*Math.PI/180;
double longitude = lon*Math.PI/180;
double x = earth * Math.sin(latitude)*Math.cos(longitude);
double y = earth * Math.sin(latitude)*Math.sin(longitude);
double z = earth * Math.cos(latitude);
double projectedX = x*focal /(focal+z);
double projectedY = y * focal / (focal+z);
int magx = (int) Math.round(projectedX * 5);
int magy = (int) Math.round(projectedY *5);
System.out.println ("MAG X : "+magx);
System.out.println ("MAG Y : "+magy);
I just plug in d mag x and y into my polygon but nothing comes up.
This is the syntax for creating and retrieving a geography Polygon in SQL Server from a set of n coordinates:
DECLARE #Polygon geography;
SET #Polygon = geography::STPolyFromText('POLYGON((Lon1 Lat1, Lon2 Lat2, ... Lonn Latn, Lon1 Lat1))', 4326);
SELECT #Polygon;
(This assumes you're using geographic coordinates relative to the WGS84 datum. If you don't know what that means, they probably are.)