Handling spatial reference systems
Describes various conversions.
Describes how to convert between spatial reference systems by means of mathematical transformations.
Describes how to convert between one coordinate system and another.
Describes the predefined math transformations available.
Helps you get started with coordinate transformations and coordinate systems.
Shows how to import an
.ivl map file whose coordinate system is known into a
JViews Maps manager, and how to lay graphic objects over that map.
Describes the classes for managing units.
Converting between two spatial reference systems
The conversion between two Spatial Reference Systems (SRS) is performed by a coordinate transformation, that is, a mathematical transformation or a chain of mathematical transformations.
For example, when superimposing Global Positioning System (GPS) points (expressed in longitude and latitude) on a state map (where coordinates are expressed in meters, in
UTM projection), the GPS coordinates are expressed in a geographic coordinate system, while the state map coordinates are in a projected coordinate system. Once the SRS for each data source is known, the
JViews Maps package is able to construct a mathematical transformation to convert from one coordinate system to another.
SRS and coordinate transformations are mainly used for two purposes.
The first one is to render the pipeline of the reader framework, and the main steps are:
1. Defining the source SRS
The SRS of source data is often stored in the reader or in the feature of the iterator that is used. If not, the reader provides the setSourceCoordinateSystem() method. The SRS of source data is stored in map features returned by the iterator.
2. Defining the target SRS
The SRS of target data is optional. In fact, the rendering mechanism only needs a coordinate transformation. Before rendering your graphic objects, you have to create the coordinate transformation once.
3. Creating the coordinate transformation
If no coordinate transformation is needed, the rendering can be performed using an identity transformation.
4. Iterating on map features provided by the iterator, and rendering them.
The second one is to position free data, (without the renderers, you create the coordinate of the graphic object yourself), and the main steps are nearly the same:
1. Defining the source SRS.
2. Defining the target SRS.
3. Creating the coordinate transformation.
4. Transforming each coordinate manually.
The following packages define some useful SRS to be used with maps:
Contains coordinate systems and related classes.
Defines the transformations between coordinate systems.
Defines factories and utility classes to convert coordinate systems to and from Open GIS Well-Known Text (WKT) specifications.
Defines projections used in projected coordinate systems.
NOTE The projection package also contains the definition of ellipsoids and geodetic datum (horizontal datum), even when these are not specific to projections and are used by the coordsys package. This is to keep the compatibility with earlier JViews Maps versions prior to 5.0, where only projected coordinate systems were handled through the use of projections.
Converting coordinates between coordinate systems
Once coordinate systems are defined, there must be some way to convert from one coordinate system to another. This is done using coordinate transformations, defined in the package
ilog.views.maps.srs.coordtrans.
Overview of the coordinate transformations
As soon as you work with different coordinate systems and datums, you need to be able to convert from one to another. When working with coordinate systems, this can be performed using functions.
The coordinate transformations, as defined by the
ilog.views.maps.srs.coordtrans.
IlvCoordinateTransformation class, contain the information that allows you to manipulate them:
The
source coordinate system
The
target coordinate system
The
Math Transform to convert coordinates from one to another
In addition to that, all the coordinate transformations implement the following methods:
This method is the basic one. It transforms the source coordinate, storing the result in result, or in a newly allocated coordinate if result is null. The method returns the transformed point.
This is the vectorized version of the previous method. This allows the transformations to convert a whole batch of coordinates, possibly allowing some optimizations to be performed.
Optionally, transformations can implement the
getInverse method that returns an inverse transformation.
The following sections describe the predefined built in transformations of JViews Maps, and provide some examples of the transformation package.
Transformation paths
To transform the coordinates from one coordinate system to another, mathematical functions are needed. These mathematical functions can be either simple straight forward functions, or more complicated transformations. The
JViews Maps package includes the most elementary transformations (or transformation steps) used for coordinate conversions, and these are available in the
ilog.views.maps.srs.coordtrans package.
The chaining of elementary transformations from one coordinate system to another is called a transformation path.
The static method
CreateTransformation from the class
IlvCoordinateTransformation can automatically create transformation paths from coordinate systems of the
ilog.views.maps.srs.coordsys package. To find the transformation paths created, refer to the following figure:
Transformation paths
Example
Convert from the following projected coordinate system:
Projection: Lambert Azimuthal Conformal Conic
Datum: NTF (Nouvelle Triangulation de la France)
Associated geographic coordinate system based on Clark 1880 ellipsoid, IGN modified.
to the following one:
Projection: Mercator
Datum: European 1960
Associated geographic coordinate system: International.
The transformation path selected by CreateTransformation() will be:
1. Projection transformation from Lambert to geographic NTF/Clark1880
2. Molodensky conversion from NTF/Clark1880 to European 1960/International systems
3. Projection transformation from geographic European 1960/International to Mercator.
At each step, the relevant unit conversion is added by using the
affine transforms.
Note that this path is not the only existing path. It is also possible to convert from geographic coordinates to geocentric coordinates, in which case the transformation path would be:
1. Projection transformation from Lambert to geographic NTF/Clark1880
2. Geocentric transform to geocentric NTF coordinates
3. Affine transform to convert from NTF to European 1960 coordinates
4. Geocentric transform from European 1960 to geographic coordinates
5. Projection transformation from geographic European 1960/International to Mercator.
Predefined math transformations
Abridged Molodensky Transform
The standard way to convert coordinates from one datum to another is to convert first the coordinates to geocentric coordinates, apply the datum shift and rotation parameters, and then convert them back to geographic coordinates.
As an alternative to this transform, the IlvAbridgedMoldenskyTransform class implements directly a transform derived directly from the Molodensky formulas. The Abridged form of these formulas are quite satisfactory for three-parameter transformations.
This transform can work on either 2-D (only latitude and longitude are modified), or 3-D (the ellipsoidal height of coordinates is also modified).
Affine Transform
Affine transforms are commonly used in coordinate transformation. An affine transform is simply defined by a 4x4 double values matrix, and are applied to coordinates by multiplying them as if they were one 1x4 matrix.
Affine transforms are mainly used in JViews Maps for unit conversions in a transformation path.
Another use of affine transforms is to use them to implement Bursa Wolf transformations. The Bursa Wolf transformation is applied to geocentric coordinates to model a seven-parameter datum change. A seven-parameter datum is defined by the dX, dY, dZ axis shifts, the eX, eY, eZ rotations around the axis, and a scale factor expressed in parts per million. The matrix to use for Bursa Wolf transformation is:
Concatenated Transform
There are some cases where a straightforward mathematical function cannot be found to convert from one coordinate system to another. In those cases, some elementary transformations can be chained to build the full transformation. This is typically the case when converting from a projected coordinate system P1 to another one P2, using different datums: first you need to convert coordinates from P1 to the geographic coordinate system, then apply a
datum conversion on these coordinate systems, and then convert them to the final coordinate system P2.
The
IlvConcatenatedTransform class allows multiple transformation steps to be chained and used as a unique transformation.
Geocentric Transform
The function used to convert from geocentric to ellipsoid and the function used to convert from ellipsoid to geocentric are grouped together in the
IlvGeocentricTransform class. Actually, the transformation is performed by specialized versions of this class:
converts geocentric coordinates (x, y, z) to ellipsoidal (or geographic) coordinates (lon, lat, H)
converts ellipsoidal coordinates (lon, lat, H) to geocentric coordinates (x, y, z)
Projection Transform
The
IlvProjectionTransform class implements a transformation in which an
IlvProjection is used to convert coordinates from geographic coordinates to a projected coordinate system, and vice versa.
This transform encapsulates a projection, and uses the
forward method if the transformation is a forward transformation, or the
inverse method if the transformation is an inverse transformation.
Transforming data
This section is based on a simple example that illustrates the basic operations required to define the coordinate systems, and shows how to use the coordinate transformations back and forth.
The complete source code of the example on which this section is based can be found in the following file:
Example overview
The Sample1.java file contains a very simple program that shows how to convert coordinates from geographic coordinates to a projected coordinate system, using a Mercator projection.
This class has only a static main() method, in which the coordinate systems are instantiated and the coordinate is transformed.
Choosing a source and a destination coordinate system
As our example uses coordinate systems and transformation, the relevant packages must be imported, as well as the projection package for the Mercator projection definition:
import ilog.views.maps.*;
import ilog.views.maps.srs.coordsys.*;
import ilog.views.maps.srs.coordtrans.*;
import ilog.views.maps.projection.*;
The first important step is to define the source and the target coordinate systems.
For the source coordinate system, some latitude and longitude coordinates expressed in degrees are needed. This is the kind of coordinate defined in a geographic coordinate system. In this example, the WGS84 coordinate is used. The WGS84 geographic coordinate system defines ellipsoidal coordinates over the standard WGS84 ellipsoid.
IlvCoordinateSystem sourceCS = IlvGeographicCoordinateSystem.WGS84;
Now these coordinates must be changed to Mercator coordinates. Then you can create a new projected coordinate system using the Mercator projection to express coordinates. Note that the Mercator projection should use exactly the same geodetic parameters as the WGS84 geographic coordinate system. The latter is passed as the geographic coordinate system of the projected coordinate system.
IlvCoordinateSystem targetCS =
new IlvProjectedCoordinateSystem("Mercator",
IlvGeographicCoordinateSystem.WGS84,
new IlvMercatorProjection(),
IlvLinearUnit.METER,
"X", // The X axis name
"Y"); // The Y axis name
Transforming coordinates
In order to transform the coordinates, you need an
IlvCoordinateTransformation. This is performed by calling the automatic transformation creation method:
IlvCoordinateTransformation CT =
IlvCoordinateTransformation.CreateTransformation(sourceCS, targetCS);
To convert coordinates, you just have to store the coordinates in an
IlvCoordinate, and then call the
transform() method of the coordinate transformation.
// The coordinate to convert : 45W, 30N
IlvCoordinate coord = new IlvCoordinate(-45D, 30D);
try {
coord = CT.transform(coord,
coord); // put the result in coord
}
catch (IlvCoordinateTransformationException e) {
System.out.println("Transformation exception for this data");
}
The transform() method takes two parameters: the first one is the source coordinate to transform, the second one is an IlvCoordinate to hold the result of the transformation. When this second parameter is null, a new IlvCoordinate is allocated and used. The method returns the result coordinate.
Of course, as in this example, it is possible to use the same IlvCoordinate as source and destination.
Note that the IlvCoordinateTransformation.transform() method may throw different kinds of exception if the transformation process leads to mathematical errors or overflows. Most of the time, when those methods are thrown, the transformation is not defined at the specified point.
Displaying the result
The result of the transformation is now stored in the coord variable. In the following code example, the result is expressed in meters, which is the default measurement unit.
System.out.println("The Mercator coordinates of 45W 30N is ");
System.out.println("x = " + (int) coord.x + " m");
System.out.println("y = " + (int) coord.y + " m");
NOTE It does not make much sense to interpret these values as distances since the center of the projection is far from the projected point, and projections do not maintain distances on a large scale
Getting the inverse transformation
At this point, the methodology to convert from geographic coordinates to Mercator coordinates is available.
To convert coordinates from Mercator back to geographic, use the
getInverse method, which returns the inverse transformation, if any.
// Get the inverse transformation.
IlvCoordinateTransformation invCT = CT.getInverse();
// Transform the point.
try {
coord = invCT.transform(coord,
coord); // put the result in coord
}
catch (IlvCoordinateTransformationException e) {
System.out.println("TransformationException exception for this data");
}
Printing geographic coordinates
To print geographic coordinates, you can use the
toDMS conversion method from the
IlvAngularUnit class. This method converts an angle specified by a double value in a unit to DMS encoding.
For example, IlvAngularUnit.DEGREE.toDMS() converts an angle specified in degrees to DMS.
System.out.println("The inverse projection is "
+ IlvAngularUnit.DEGREE.toDMS(coord.x,false)
+ " "
+ IlvAngularUnit.DEGREE.toDMS(coord.y,true));
Adding Graphic Objects on Top of an Imported Map
This section is based on an example that loads a map of the USA projected with a Lambert Azimuthal Equal Area projection into a manager, and adds cities on top of the map. The geographic coordinates indicated by the mouse pointer as well as the name of the cities pointed to are displayed in text fields at the bottom of the window. This section also gives information about the instantiating and the parameterization of a projected coordinate system, describes how to convert data from geographic coordinates to this projected coordinate system, and how to use a view interactor.
The complete source code for this example can be found in the following file:
NOTE The purpose of this example is to give a tutorial on how to use coordinate transformations. It does not use
JViews Maps beans such as
IlvJMouseCoordinateViewer, to integrate with a data source or for map layer management.
Initializing coordinate systems
Since you need to convert from geographic coordinates to projected coordinates and vice versa, you need to keep an instance of these transformations. This is performed by means of the createTransformations() method.
First, initialize an instance of the projection used in the coordinate system of the imported map. In this example, the usa.ivl file is in Lambert Azimuthal Equal Area projection.
The projection parameters are:
Name of the Projection | Lambert Azimuthal Equal Area |
Central Meridian | 100DW |
Central parallel | 40DN |
Measurement unit | Meters (the default value) |
Offset | 0 (the default value) |
For more information on projection parameters see the section
Projection parameters.
private void createTransformations()
{
// Create the projection.
IlvProjection projection = new IlvLambertAzimuthalEqualAreaProjection();
projection.setEllipsoid(IlvEllipsoid.SPHERE);
try {
double centralMeridian = IlvAngularUnit.RADIAN.fromDMS("100DW");
double centralParallel = IlvAngularUnit.RADIAN.fromDMS("40DN");
projection.setCentralMeridian(centralMeridian);
projection.setCentralParallel(centralParallel);
} catch (IllegalArgumentException e) {
System.out.println("wrong string passed to "
+ "IlvAngularUnit.RADIAN.fromDMS");
System.out.println("unable to create the projection for the file"
+ "usa.ivl");
System.exit(0);
}
Note that projection parameters are always specified using kernel units. In this case, the central meridian and parallel have to be specified in radians. Use the method IlvAngularUnit.RADIAN.fromDMS() to achieve this goal.
Once the Lambert Azimuthal projection has been initialized, you only have to create the corresponding projected coordinate system:
// Create the projected coordinate system.
IlvProjectedCoordinateSystem projectedCS =
new IlvProjectedCoordinateSystem("Lambert Azimutal Equal Area",
projection);
You also create a geographic coordinate system whose ellipsoid is a simple sphere:
// Create the geographic coordinate system.
IlvGeographicCoordinateSystem geoCS =
new IlvGeographicCoordinateSystem(IlvHorizontalShiftDatum.SPHERE_WGS84,
IlvMeridian.GREENWICH);
Finally, create the coordinate transformation and store also the inverse transformation for future quick reference:
// A coordinate transform.
geo2projCT =
IlvCoordinateTransformation.CreateTransformation(geoCS,projectedCS);
// The inverse transform.
proj2geoCT = geo2projCT.getInverse();
Adding cities
The addCitites() method adds a number of cities on top of the imported map of the United States:
private void addCities()
{
addCity("Washington", "39D11'N", "76D51W");
addCity("New York", "40D59'N", "73D39'W");
addCity("Miami", "25D58'N", "80D02'W");
addCity("San Francisco", "37D44'N", "122D20'W");
addCity("Seattle", "47D51'N", "122D01'W");
addCity("Denvers", "39D50'N", "104D53'W");
}
The coordinate conversion is performed in the addCity() method.
First, this method converts the DMS coordinates specified as string to degrees, in order to have the longitude and latitude coordinates of each point:
private void addCity(String cityName, String lat, String lon)
{
try {
double latitude = IlvAngularUnit.DEGREE.fromDMS(lat);
double longitude = IlvAngularUnit.DEGREE.fromDMS(lon);
IlvCoordinate coordinate = new IlvCoordinate(longitude,
latitude);
Then, it computes the projected coordinates by applying the forward coordinate transformation. If the coordinates cannot be transformed (for example, because the tolerance conditions have been exceeded), the transformation may throw an exception. This is why the code is contained inside a try/catch block.
The IlvProjectionUtil.invertY() method is then called to invert the y-coordinate: in the JViews Maps manager coordinate system, the y-axis is oriented downwards, whereas, in the projection coordinate system, it is oriented upwards.
geo2projCT.transform(coordinate,coordinate);
IlvPoint p = new IlvPoint((float)coordinate.x,
(float)coordinate.y);
IlvProjectionUtil.invertY(p);
Note also that a new IlvPoint is allocated here. It will be used to create graphic objects (an IlvMarker ) to represent the city. This graphic object is added to Layer #1 of the manager (Layer #0 contains the boundaries of the USA).
IlvMarker marker = new IlvMarker(p, IlvMarker.IlvMarkerFilledDiamond);
marker.setSize(4);
marker.setForeground(Color.red);
manager.addObject(marker, 1, false);
marker.setName(cityName);
} catch (IlvCoordinateTransformationException e) {
e.printStackTrace();
}
Setting the view interactor
In order to track mouse position, a view interactor is used. This is performed by means of the setViewInteractor() method.
First, create a selection interactor that will be used to select cities on the map. This interactor is configured so that multiple selections are not allowed, and the map cannot be modified, which means that the user will not be able to move cities around the map. The interactor is associated with the view. Note also that the elements making up Layer #0 (that is, borders) cannot be selected either.
private void setViewInteractor()
{
IlvSelectInteractor interactor = new IlvSelectInteractor();
interactor.setDragAllowed(false);
interactor.setEditionAllowed(false);
interactor.setMoveAllowed(false);
interactor.setMultipleSelectionMode(false);
manager.setSelectable(0, false);
mgrview.pushInteractor(interactor);
Then, add a listener to the interactor that will display the longitude and the latitude indicated by the mouse in the appropriate text field. To compute the latitude and the longitude, apply the inverse transformation computed previously. The result returned by this method is formatted with the IlvAngularUnit.DEGREE.toDMS() method.
// Display the position of the mouse.
interactor.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
// Get the point in manager coordinates.
IlvTransformer t = mgrview.getTransformer();
IlvPoint p = new IlvPoint(e.getX(), e.getY());
t.inverse(p);
IlvProjectionUtil.invertY(p);
// Display the mouse position.
try {
IlvCoordinate c = new IlvCoordinate(p.x,p.y);
proj2geoCT.transform(c,c);
llField.setText(IlvAngularUnit.DEGREE.toDMS(c.x,false)
+ " " +
IlvAngularUnit.DEGREE.toDMS(c.x,true));
} catch (IlvCoordinateTransformationException ex) {
System.out.println("Unable to inverse this point " +
ex.getMessage());
}
}
});
Finally, add a listener to the manager. This listener will display the name of the selected city in the appropriate text field.
manager.addManagerSelectionListener(new ManagerSelectionListener(){
public void selectionChanged(ManagerSelectionChangedEvent e) {
IlvGraphic g = e.getGraphic();
if (g == null)
return;
String name = g.getName();
if (name != null)
cityField.setText(name);
}
});
After you have compiled your sample, the map shown in
Setting a view interactor should appear on the screen:
Setting a view interactor
Managing units
The management of units is performed by using three classes included in the
ilog.views.maps package:
These classes deprecate the former ilog.views.maps.IlvUnitConverter, which did not make a clear distinction between units to measure angles and distances.
Units are simply reference systems to measure physical quantity. By convention, a kernel unit is defined for each type of unit. For angles, the kernel units are radians, and for lengths, the kernel units are meters. This allows an easy conversion between any derived units.
Units are attached to each axis of a coordinate system. This means that inside a coordinate system, you can have each ordinate expressed in different coordinates. For example, with geographic coordinate, you can have the x- and y-ordinates expressed in degrees, while the z-ordinate (corresponding for example to the ellipsoid height) is expressed in meters. Usually, in a coordinate system, the same unit is used to measure all physical quantities that are alike (for example, degrees for all angles).
The complete source code of the example can be found in the following file:
Predefined units
The package contains a list of predefined units, to measure both lengths and angles. A predefined unit is referenced by its abbreviation. To access a predefined unit converter, you can use either the static instance of the relevant class, or the static method
GetRegisteredUnit.
See the following tables for a list of the predefined angular and linear units supplied with JViews Maps.
Predefined angular units
Abbreviation | Full Name | ToKernel |
rad | Radian | 1.0 |
deg | Degree | PI / 180 |
grad | Grad | PI / 200 |
Predefined linear units
Abbreviation | Full Name | ToKernel |
km | Kilometer | 1000.0 |
m | Meter | 1.0 |
dm | Decimeter | 0.1 |
cm | Centimeter | 0.01 |
mm | Millimeter | 0.0010 |
kmi | International Nautical Mile | 1852.0 |
in | International Inch | 0.0254 |
ft | International Foot | 0.3048 |
yd | International Yard | 0.9144 |
mi | International Statute Mile | 1609.344 |
fath | International Fathom | 1.8288 |
ch | International Chain | 20.1168 |
link | International Link | 0.201168 |
us-in | U.S. Surveyor’s Inch | 0.025400050800101603 |
us-ft | U.S. Surveyor’s Foot | 0.304800609601219 |
us-yd | U.S. Surveyor’s Yard | 0.914401828803658 |
us-ch | U.S. Surveyor’s Chain | 20.11684023368047 |
us-mi | U.S. Surveyor’s Statute Mile | 1609.347218694437 |
ind-yd | Indian Yard | 0.91439523 |
ind-ft | Indian Foot | 0.30479841 |
ind-ch | Indian Chain | 20.11669506 |
Defining units
The IlvUnit class is the superclass of all the units.The JViews Maps package provides two specialized versions of this class:
For units measuring length.
For units measuring angles.
To define a new unit, you can create a new instance of these two classes specifying three parameters:
The factor of conversion to kernel units (the number of kernel units necessary to define one unit).
The abbreviation for your unit.
The full name of the unit.
For example, the following code creates a new instance of
IlvLinearUnit to convert meters to feet (assuming than 1 ft = 0.3048 meters):
IlvLinearUnit unit = new IlvLinearUnit(0.3048,
"ft",
"International foot");
Using units
Units can be used for simple conversion between values, or in a coordinate system. Various standard units are defined in the built-in unit classes of the
JViews Maps package. These units can be retrieved by using either predefined static members of the classes
IlvLinearUnit and
IlvAngularUnit (these are the most used units), or the
GetRegisteredUnit method.
Simple unit conversion
For example, to convert meters to feet you can use either the static definition of feet as follows:
IlvLinearUnit unit = IlvLinearUnit.FT;
double meters = 100D;
double feet = unit.fromMeters(meters);
System.out.println("100 m = " + feet + " ft");
// The following is also valid, as the kernel unit for lengths
// is the meter.
double other_feet = unit.fromKernel(meters);
System.out.println("100 m = " + feet + " ft");
or the registered version:
IlvUnit unit = IlvUnit.GetRegisteredUnit("ft");
double meters = 100D;
double feet = unit.fromKernel(meters);
System.out.println("100 m = " + feet + " ft");
For standard conversion of units, the IlvUnit class defines the following methods:
The
toKernel method converts from the unit to the kernel unit.
The
fromKernel method converts from kernel unit to the specific unit.
In addition, the
IlvLinearUnit class (respectively
IlvAngularUnit ) defines the
toMeters and
fromMeters methods (respectively
toRadians and
fromRadians methods) for applications that need explicit method calls.
Units in coordinate systems
Units are part of the definition of coordinate systems, which means that all the coordinate systems have to be constructed with units for axis. Once the unit is defined for a coordinate system, the transformation factory is able to automatically add subsequent affine transforms to convert coordinates in the transformation path.
Copyright © 2018, Rogue Wave Software, Inc. All Rights Reserved.