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