Ok, so I took a very different approach, and I got some very interesting results.
First off, I decided to not calculate the location from the entire map, but instead calculate the location from the individual coordinate's tile, and then add on the x and y offset for the tile. I figured this would reduce the amount of mathematical error, since the formulas would be calculated with much smaller values.
This has positioned SJU in the correct place, as well as a few others, but most of them are still a bit off.
First off, my "OSMap" class (which contains a bunch of info about the retrieved map) now looks like this (I have omitted all the code which is not needed for this problem):
public class OSMap {
private int zoom;
private List<List<Tile>> tileList;
/**
* @param latitude the latitude to calculate the distance with
* @return the distance (west to east) per pixel for the map
*/
public double getDistancePerPixel(double latitude) {
// LENGTH_OF_EQUATOR = 40075.016686
return (GeneralConstants.LENGTH_OF_EQUATOR*Math.cos(Math.toRadians(latitude))/Math.pow(2, this.zoom+8));
}
/**
* @return the latitude distance (north to south) per pixel for the map
*/
public double getLatitudeDistancePerPixel() {
// LENGTH_OF_POLES = 40008
return (GeneralConstants.LENGTH_OF_POLES)/(Math.pow(2, this.zoom+8)); //*1.4
}
/**
* Finds the pixel coordinates for the given geographical coordines.
*
* @param toFind the geographical coordinates to find the pixel coordinates for
* @return the pixel coordinates for the geographical coordinates
*/
public Coordinate getPixelLocationForCoordinate(Coordinate toFind) {
// Get the tile for the coordinate
Tile tile = getTileFor(toFind);
if(tile==null)
return null;
// Set the x and y pixel coordinate variables
int xPixels = 0;
int yPixels = 0;
// Offset the x and y pixel coordinates for the given tile
OUTER:
for(List<Tile> row : this.tileList) {
for(Tile rowTile : row) {
if(tile==rowTile) {
break OUTER;
}
xPixels+=GeneralConstants.OS_MAP_IMAGE_WIDTH;
}
xPixels = 0;
yPixels+=GeneralConstants.OS_MAP_IMAGE_HEIGHT;
}
// Origin Geographical Coordinate for the tile
Coordinate tileOrigin = getOriginForCoordinate(tile);
// The horizontal distance per pixel (west to east)
double horizontalDistancePerPixel = getDistancePerPixel(toFind.getY());
// The vertical distance per pixel (north to south)
double verticalDistancePerPixel = getLatitudeDistancePerPixel();
// The horizontal origin to calculate the distance against
Coordinate horizontalOrigin = new Coordinate(tileOrigin.getX(), toFind.getY());
// The horizontal geographical distance
double xDistance = Coordinate.getDistanceBetween(toFind, horizontalOrigin);
// The pixel x coordinate for the tile and the coordinate
int xCoordinate = (int)(xDistance/horizontalDistancePerPixel);
xPixels += xCoordinate;
// The vertical origin to calculate the distance against
Coordinate verticalOrigin = new Coordinate(toFind.getX(), tileOrigin.getY());
// The vertical geographical distance
double yDistance = Coordinate.getDistanceBetween(verticalOrigin, toFind);
// The pixel y coordinate for the tile and the coordinate
int yCoordinate = (int)(yDistance/verticalDistancePerPixel);
yPixels += yCoordinate;
return new Coordinate(xPixels,yPixels);
}
private Coordinate getOriginForCoordinate(Tile originFor) {
return OSMapHelper.getMapOrigin(originFor.getX(),originFor.getY(),this.zoom);
}
private Tile getTileFor(Coordinate tileFor) {
int xTile = OSMapHelper.getXTile(tileFor.getX(),this.zoom);
int yTile = OSMapHelper.getYTile(tileFor.getY(), this.zoom);
for(List<Tile> row : this.tileList) {
for(Tile tile : row) {
if(tile.getX()==xTile && tile.getY()==yTile) {
return tile;
}
}
}
return null;
}
}
The "Tile" object is just a container for the X and Y tile numbers used in the web query to OSM's url. When I load a new map tile, I create a new "Tile" object with the queried X and Y tile numbers and add it to the tile list. I do this so I can calculate the tile offset.
Now, in the class where I draw the map and paint over it, I have this code:
g.setColor(Color.GRAY);
int x = 0;
int y = 0;
while(x<ApplicationConstants.SUB_PANEL_WIDTH) {
g.drawLine(x, 0, x, ApplicationConstants.SUB_PANEL_HEIGHT);
x+=256;
}
while(y<ApplicationConstants.SUB_PANEL_HEIGHT) {
g.drawLine(0, y, ApplicationConstants.SUB_PANEL_WIDTH, y);
y+=256;
}
g.setColor(Color.RED);
double startLon = Database.getAirport("SEA").getLongitude();
double startLat = Database.getAirport("SEA").getLatitude();
double offset = 1;
for(int i=0;i<30;i++) {
Coordinate start = map.getPixelLocationForCoordinate(new Coordinate(startLon,startLat));
if(start==null) {
logger.info("Start Null: "+startLon+", "+startLat);
break;
}
Coordinate end = map.getPixelLocationForCoordinate(new Coordinate(startLon+offset,startLat-offset));
if(end==null) {
logger.info("End Null: "+(startLon+offset)+", "+(startLat-offset));
break;
}
g.drawOval((int)end.getX(), (int)end.getY(), 5,5);
g.drawLine((int)start.getX(), (int)start.getY(), (int)end.getX(), (int)end.getY());
startLon+=offset;
startLat-=offset;
}
g.setColor(Color.BLACK);
for(Airport airport : Database.getUserCompany().getAirports()) {
Coordinate mapLocation = map.getPixelLocationForCoordinate(airport.getCoordinate());
AirportPOI poi = new AirportPOI(airport,mapLocation,POI_RADIUS);
airportMapping.put(airport, poi);
int xCoordinate = (int)mapLocation.getX();
int yCoordinate = (int)mapLocation.getY();
logger.info("Airport ("+airport.getCode()+") Location: ("+mapLocation.getX()+","+mapLocation.getY()+") Coord: ("+airport.getLatitude()+","+airport.getLongitude()+")");
g.fillOval(xCoordinate, yCoordinate, POI_RADIUS, POI_RADIUS);
g.drawString(airport.getCode(), xCoordinate+POI_RADIUS, yCoordinate);
}
This code is doing a few things. First it is painting gray borders around the individual tiles, second it is painting a red line (which should be straight) for debugging, and third it is painting the airport locations and their names.
Now, if the calculations were correct, that red line
should be straight. But, it is not. What appears to happen is the red line is straight until it reaches some certain inconsistent location in a tile, and then suddenly leaps down. I have attached a picture of the generated map to show what I mean.
Any thoughts on what is happening?
mapImage.jpg