UK Grid reference to screen coordinates (maths not good)
Hi all,
I'm having trouble getting my head round how to translate some grid reference coordinates into screen coordinates including a scale.
The three things i'd pass to the method would be something like this
public int[] translateGridRef(int gridCoords[], int[] screenSize,
int scale)
Through these three factors i would like to be able to work out the position of any road for example on my screen. I'm using a mobile device and the screen size is 144x176, the scale might be 1:25000 say, and the coordinates would be stored in a database for each node of the road.
Each node of the road would have specific grid reference coordinates down to the nearest meter squared like x45679 y45672. These would then be translated into screen coordinates based on the screenSize and the scale required.
Could anyone help me with this i'd really appreciate it.
Thanks
Phill
Heres a full class to do what you want (what can I say...I was bored).
Its based on the assumption that your map and coordinates are cartesian
and that your coordiate system starts at the top left of the map.
The two classes below should be put in separate files (although they dont need to be). Point was written to avoid dependencies on awt (is
very similar to java.awt.Point but uses long rather than int's).
/**
* class that represents a screen as a viewport onto a cartesian
* map. Provides functionallity for changing the viewports location
* and scale.
* @author matfud
* @copyright public domain
*/
public class ScreenCoord
{
// the coordinate (map) of the top left point of this screen
private Point topLeft = new Point();
private long scaleFactor = 1;
// the size of the screen in pixels
private Point screenSize = new Point(144,176);
/** Creates a new instance of ScreenCoord */
public ScreenCoord()
{
}
public ScreenCoord(Point screen)
{
if(screen!=null)
{
if((screen.getX()>0)&&(screen.getY()>0))
this.screenSize = new Point(screen);
}
}
public ScreenCoord(Point screen,Point topLeft)
{
if(screen!=null)
{
if((screen.getX()>0)&&(screen.getY()>0))
this.screenSize = new Point(screen);
}
this.topLeft = new Point(topLeft);
}
/**
* sets the scale that will be used. If you want a scale of
* 4,000 metres on the map to one metre per pixel in the view
* port use a value of 4000. best resolution available is 1 pixel
* per meter.
*
* @param the new scale
* @return the old value of scale or on error the parameter passed
*/
public long setScale(long scale)
{
if(scale<=0) return scale;
long old = this.scaleFactor;
this.scaleFactor = scale;
return old;
}
/**
* return the scale factor used
*/
public long getScale()
{
return this.scaleFactor;
}
/**
* return the size of the screen
*/
public Point getScreenSize()
{
return new Point(this.screenSize);
}
/**
* set the location of the top left of the screen.
*@param the new location
*@returns the old location
*/
public Point setLocation(Point topLeft)
{
Point old = this.topLeft;
this.topLeft = new Point(topLeft);
return old;
}
/**
* return the location of the topleft of this screen
*/
public Point getLocation()
{
return new Point(this.topLeft);
}
/**
* convert a map coordiate into a screen coordinate. Returns null
*if the screen coordiantes do not exist
*/
public Point screenCoordFromMapCoord(Point coord)
{
long x = (long) ((coord.getX()-this.topLeft.getX())/((double)this.scaleFactor));
if((x>=this.screenSize.getX())||(x<0)) return null;
long y = (long) ((coord.getY()-this.topLeft.getY())/((double)this.scaleFactor));
if((y>=this.screenSize.getY())||(y<0)) return null;
return new Point(x,y);
}
/**
* convert a screen coordinate into a map coordiate. This works even if the
* screen coordinate does not exist inside the screen (ie. x=-12,y=42000)
*/
public Point mapCoordFromScreenCoord(Point coord)
{
long x = (coord.getX()*this.scaleFactor)+this.topLeft.getX();
long y = (coord.getY()*this.scaleFactor)+this.topLeft.getY();
return new Point(x,y);
}
public String toString()
{
return "location: "+this.topLeft+"\nscreen: "+this.screenSize+"\nscale: "+this.scaleFactor;
}
/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
ScreenCoord sc = new ScreenCoord(new Point(800, 600),new Point(0, 0));
Point tmpA = new Point(10,14);
Point tmpB = sc.mapCoordFromScreenCoord(tmpA);
System.out.println(sc);
System.out.println("map coord from screen "+tmpA+ " to "+tmpB);
tmpA = sc.screenCoordFromMapCoord(tmpB);
System.out.println("screen coord from map "+tmpB+" to "+tmpA);
sc.setLocation(new Point(5,5));
System.out.println(sc);
tmpA = new Point(10,14);
tmpB = sc.mapCoordFromScreenCoord(tmpA);
System.out.println("map coord from screen "+tmpA+ " to "+tmpB);
tmpA = sc.screenCoordFromMapCoord(tmpB);
System.out.println("screen coord from map "+tmpB+" to "+tmpA);
sc.setScale(32000);
System.out.println(sc);
tmpA = new Point(10,14);
tmpB = sc.mapCoordFromScreenCoord(tmpA);
System.out.println("map coord from screen "+tmpA+ " to "+tmpB);
tmpA = sc.screenCoordFromMapCoord(tmpB);
System.out.println("screen coord from map "+tmpB+" to "+tmpA);
tmpA = new Point(-10,-14);
tmpB = sc.mapCoordFromScreenCoord(tmpA);
System.out.println("map coord from screen "+tmpA+ " to "+tmpB);
tmpA = sc.screenCoordFromMapCoord(tmpB);
System.out.println("screen coord from map "+tmpB+" to "+tmpA);
}
}
/**
* represents a point in 2 dimensions (as longs)
*/
class Point
{
private long x;
private long y;
/**
* default constructor
*/
public Point()
{};
/**
* copy constructor
*/
public Point(Point p)
{
if(p!=null)
{
this.x = p.x;
this.y = p.y;
}
}
/**
* constructor
*/
public Point(long x, long y)
{
this.x = x;
this.y = y;
}
/**
* make this point equal to another
*/
public void makeEqualTo(Point p)
{
if(p!=null)
{
this.x = p.x;
this.y = p.y;
}
}
/**
* set the location this point represents
*/
public void setLocation(long x, long y)
{
this.x = x;
this.y = y;
}
/**
* get the x position of this point
*/
public long getX()
{
return this.x;
}
/**
* get the y position of this point
*/
public long getY()
{
return this.y;
}
public String toString()
{
return "{"+this.x+","+this.y+"}";
}
}
matfud
Mine was initially wrong....sorry, didn't check it but it's been corrected........
The way I thought of it was this:
You wanted the scale to be actual, meaning that the scale you give maps to actual distance on the screen. Matfud's recommendation gives you a representative distance, where 1 pixel represents 1 square metre. The scale is then used to 'multiply up' so that 1 pixel will represent 1 meter * the scale.
If you want actual screen distance to represent actual distance on the map, you need 1 pixel to represent it's actual width and height. So in my example if the width and height of your pixel is 0.001 metres:
public void toPixel(int scale, int topLeftOfScreenOnMapX, int topLeftOfScreenOnMapY, double pixelWidth, double pixelHeight) //pixel width and height must be doubles or floats of course.....ahem
{
x = (x - topLeftOfScreenOnMapX) / scale / pixelWidthInMetres; //slight alteration here..
y = (y - topLeftOfScreenOnMapY) / scale / pixelHeightInMetres; //..and here...
}
top left of the screen is at grid reference 1500,1500
the point you want is at grid reference 1600,1600
the scale is 1:1000
pixel width and height is 0.001 metres
you get a point at x = (1600 - 1500) / 1000 / 0.001 = 100 pixels
and same again for y so your point is at (100,100) on the screen
Matfud's recommendation is basically the same as mine with a value of 1 for pixel width and height. I think copying and pasting his code may well be an option! And if you want to use the pixel width and height, instead of using the default of 1 metre, then put that in.