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

[936 byte] By [J2ME_Philla] at [2007-9-29 7:56:44]
# 1

I think you need to know the width and height of a pixel? Or at least what you want a pixel to represent. Then you can translate from metres to pixels. Can you get that information? You'll also need to know where the top left of your screen is in relation to the map.

After that you'd do something like:

public class GridCoord

{

public int x;

public int y;

public void scale(int scale)

{

x = x / scale;

y = y / scale;

}

public void toPixel(int topLeftOfScreenOnMapX, int topLeftOfScreenOnMapY, int pixelWidth, int pixelHeight)

{

x = (x - topLeftOfScreenOnMapX) * pixelWidth;

y = (y - topLeftOfScreenOnMapY) * pixelHeight;

}

The 'design' went a little bit astray here (it started off well-intentioned) but you get the idea......

Someone else may well have a more elegant solution though.

woollybarra at 2007-7-14 21:44:45 > top of Java-index,Other Topics,Algorithms...
# 2

Sorry, that looks like shite I'll try again:

public class GridCoord

{

public int x;

public int y;

public void toPixel(int scale, int topLeftOfScreenOnMapX, int topLeftOfScreenOnMapY, int pixelWidth, int pixelHeight)

{

x = (x - topLeftOfScreenOnMapX) / scale * pixelWidth;

y = (y - topLeftOfScreenOnMapY) / scale * pixelHeight;

}

woollybarra at 2007-7-14 21:44:45 > top of Java-index,Other Topics,Algorithms...
# 3
I'm not sure i understand how that works below. I think i have worked out a way to do it but that way looks like its a little less complex. Though i'm not sure i understand whats what from it. Could you plug in some example figures. Thanks Phill
J2ME_Philla at 2007-7-14 21:44:45 > top of Java-index,Other Topics,Algorithms...
# 4

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

matfuda at 2007-7-14 21:44:45 > top of Java-index,Other Topics,Algorithms...
# 5

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.

woollybarra at 2007-7-14 21:44:45 > top of Java-index,Other Topics,Algorithms...
# 6

Yep. Mine doesn't allow you to zoom in.

Anyway using either method you will then have to query your database

for all entries that are:

greater then or equal to

cs.mapCoordFromScreenCoord(new Point(0,0));

the top left of view port

and less then

cs.mapCoordFromScreenCoord(cs.getScreenSize());

the bottom right

Then you run through all matches from these queries and draw then to

screen by getting the screen coords using the screenCoordFromMapCoord method (which returns the pixel they map too).

matfud

matfuda at 2007-7-14 21:44:45 > top of Java-index,Other Topics,Algorithms...
# 7

Well i've been going at it a pretty good number of hours now and i have a method which works, thought both those methods look much less complicated so i'll try out both this afternoon.

Cheers fellas appreciate it alot.

Phill

P.s I think the UK grid reference system uses coordinates which work

from the bottom right upwards. http://www.adit.co.uk/html/osgrid.html and there coordinates are always based on 10km grids squares.

J2ME_Philla at 2007-7-14 21:44:45 > top of Java-index,Other Topics,Algorithms...
# 8

The methods will still work using a origin at the bottom left.

The reason I chose top left as the origin is that most routines for

drawing to screen are based on a top left origin. Your map will just

appear to be flipped vertically when drawn to screen.

To correct just subtract the y-screen coord obtained from the

mapCoordToScreenCoord() method from the y-value returned by

getScreenSize(); (or do this inside the ScreenCoord class. And you

should probably rename the topLeft variable to bottomLeft throughout

the class)

matfud

matfuda at 2007-7-14 21:44:45 > top of Java-index,Other Topics,Algorithms...
# 9
Thanks matfud. Phill
J2ME_Philla at 2007-7-14 21:44:45 > top of Java-index,Other Topics,Algorithms...
# 10
See java.awt.geom.AffineTransform.
pmuurray@bigpond.coma at 2007-7-14 21:44:45 > top of Java-index,Other Topics,Algorithms...