Axis ---- A quick page to help take apart the ``CoordinateReferenceSystem`` object and figure out what the raw data is measuring. This is most often phrased as the question "What axis is X?" on our mailing list. Using Transform ^^^^^^^^^^^^^^^ In the normal course of events you will set up a series of math transforms to map from your data representation to the screen. World to Screen ''''''''''''''' We want to set up the following: * Create a "screen" ``CoordinateReferenceSystem`` using ``DISPLAY_X`` and ``DISPLAY_Y`` as axis * Construct a ``MathTransform`` transforming from your data's ``CoordinateReferenceSystem`` to the "screen" It is often easier to treat the problem in two steps: 1. Make a "viewport" model recording the location on the world you are displaying, For your first cut capture this "viewport" using ``DefaultGeographicCRS.WGS84``:: MathTransform data2world = CRS.findMathTransform( crs, DefaultGeographicCRS.WGS84 ); 2. Construct a ``MathTransform`` from your "viewport" CRS to the "screen" CRS.:: AffineTransform2D world2screen = new AffineTransform2D(); // start with identity transform world2screen.translate( viewport.minLong, viewport.maxLat ); // relocate to the viewport world2screen.scale( viewport.getWidth() / screen.width, viewport.getHeight() / screen.height ); // scale to fit world2screen.scale( 1.0, -1.0 ); // flip to match screen In the OGC Geographic Objects specification, the "viewport" CRS is named objective CRS and the "screen" CRS is named display CRS). 3. Combine the two ``MathTransforms``:: MathTransform transform = defaultMathTransformFactory.createConcatenatedTransform( data2world, world2screen ); 4. Use the transform to modify your Geometry for the screen:: Geometry screenGeometry = geometry.transform( screenCRS, transform ); 5. Draw with confidence knowing that you are using ``DISPLAY_X`` and ``DISPLAY_Y``:: final int X = 0; final int Y = 1; public void drawPoint( Graphics g, Position point ){ int x = point.getOrdinate(X); int x = point.getOrdinate(Y); g.drawPoint(x, y); } 6. As long as you do this correctly everything will work out, you will know what "X" is (because you defined it) and you will have forced your data into ``DISPLAY_X`` and ``DISPLAY_Y``. Screen to World ''''''''''''''' The above instructions seem to cause some confusion; it may be easier to take the inverse of your ``world2screen`` transform (usually you have one around since you were using it for drawing).:: AffineTransform world2screen = RendererUtilities.worldToScreenTransform(mapContext.getLayerBounds(), new Rectangle(panelMap.getWidth(), panelMap.getHeight())); AffineTransform screen2world = world2screen.createInverse(); Point2D pointScreen = screen2world.transform(pointScreenAbsolute, null); Avoid Assumptions ^^^^^^^^^^^^^^^^^ A common assumption is that each each ``Position`` contains data in (x,y) order (i.e. matching the screen). There exist many methods that to help: * ``getHorizontalCRS`` return the ``GeographicCRS`` or ``ProjectedCRS`` part, or a ``DerivedCRS`` based on the above, that applies to the Earth's surface (i.e. real geophysical meaning - not the first two axis). * You would still need to account for axis direction and polar coordinates on your own time. A really common assumption for Java developers is to treat Geometry in exactly the same manner as Java2D Shape:: public void drawPoint( Graphics g, Position position ){ int x = (position.getOrdinate(X) - dx) * scale; int y = height - ((position.getOrdinate(Y) - dy) * scale); g.drawPoint(x, y); } This code can be improved in several ways: * "x" is assumed to be ordinate(0), "y" is assumed to be ordinate(1) * A complicated transform is being performed by hand, "y" is inverted to match screen orientation, a transform is specified using ``dx`` and ``dy`` offsets and the entire result is scaled This code will fail when presented with: * data in "y"/"x" order * data in which the direction of the axis is not what was expected * data that was collected in polar coordinates Please note that some GeoTools classes, such as ``CRSUtilities.getCRS2D``, often make use of this assumption; blindly returning the first 2 dimensions no matter what they are. Quick Fix ''''''''' If you run into some information that has proceeded with the above assumptions you can make a quick fix:: public void drawPoint( Graphics g, Position position ){ final int X = 0; final int Y = 1; int x = (position.getOrdinate(X) - dx) * scale; int y = height - ((position.getOrdinate(Y) - dy)*scale); g.drawPoint(x, y); } You will also need to provided a set of global hints:: public void static main(String args[] ){ Map config = new HashMap(); config.put( Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, true ); config.put( Hints.FORCE_STANDARD_AXIS_DIRECTIONS, true ); Hints hints = new Hints( config ); GeoTools.init( hints ); // Set FactoryUsingWKT as the default ...application code... } GeoTools will now do its best to create ``CoordinateReferenceSystem`` objects that agree with your assumptions: * data is in (x,y) order * data is collected in the expected direction (i.e. EAST and WEST are the same) Lookup Axis ^^^^^^^^^^^ The following will allow you to math up to a correct axis:: public void drawPoint( Graphics g, Position position ){ final int X = indexOfX( position.getCoordinateReferenceSystem() ); final int Y = indexOfY( position.getCoordinateReferenceSystem() ); int x = (position.getOrdinate(X) - dx) * scale; int y = height - ((position.getOrdinate(Y) - dy)*scale); g.drawPoint(x, y); } Where the following has been defined:: private int indexOfX( CoordinateReferenceSystem crs ){ Set up = new HashSet(); up.add( AxisDirection.DISPLAY_LEFT ); up.add( AxisDirection.EAST ); up.add( AxisDirection.GEOCENTRIC_X ); up.add( AxisDirection.COLUMN_POSITIVE ); return indexOf( cs, up ); } private int indexOfX( CoordinateReferenceSystem crs ){ Set up = new HashSet(); up.add( AxisDirection.DISPLAY_UP ); up.add( AxisDirection.NORTH ); up.add( AxisDirection.GEOCENTRIC_Y ); up.add( AxisDirection.ROW_POSITIVE ); return indexOf( cs, up ); } private int indexOf( CoordinateReferenceSystem crs, Set direction ){ CoordinateSystem cs = coordinateReferenceSystem.getCoordinateSystem(); for( int index=0; index