/* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ import java.awt.Component; import java.awt.event.*; import java.net.URL; import java.rmi.RemoteException; import javax.swing.JFrame; import javax.swing.JLabel; import visad.*; import visad.data.mcidas.AreaAdapter; import visad.data.mcidas.BaseMapAdapter; import visad.georef.EarthLocation; import visad.georef.EarthLocationTuple; import visad.georef.MapProjection; import visad.java3d.*; import visad.jmet.GRIBCoordinateSystem; /** * Example class to show how to create a Display side CoordinateSystem * for supporting various map projections. Also shows how to create * a component for displaying the EarthLocation of the cursor. */ public class MapProjectionDisplay extends DisplayImplJ3D { CoordinateSystem coordinateSystem = null; ScalarMap latitudeMap = null; ScalarMap longitudeMap = null; ScalarMap altitudeMap = null; JLabel locationLabel = new JLabel("Lat: Lon: Alt:"); DisplayRendererJ3D displayRenderer; /** * Construct a new MapProjectionDisplay using an image for * the projection. Will be displayed on a 2D surface * @param imageSource image to use */ public MapProjectionDisplay(String imageSource) throws Exception { this(imageSource, false); } /** * Construct a new MapProjectionDisplay using an image for * the projection. * @param imageSource image to use for MapProjection * @param do3D construct as a 3D display if true * @see visad.georef.MapProjection */ public MapProjectionDisplay(String imageSource, boolean do3D) throws Exception { super("MapProjectionDisplay", (do3D == true) ? (DisplayRendererJ3D) new DefaultDisplayRendererJ3D() : (DisplayRendererJ3D) new TwoDDisplayRendererJ3D()); displayRenderer = (DisplayRendererJ3D) getDisplayRenderer(); if (imageSource != null) { // get an image and use the domain CS as the map projection AreaAdapter aa = new AreaAdapter(imageSource); coordinateSystem = new MapProjectionAdapter( (MapProjection) aa.getCoordinateSystem()); } else { // use another known MapProjection class coordinateSystem = // Lambert grid new MapProjectionAdapter(new GRIBCoordinateSystem(211)); //LatLon Grid //new MapProjectionAdapter( // new GRIBCoordinateSystem(0, 73, 73, -90, -30, 90, 240, 5, 2.5)); } // create new DisplayTupleType // These are the DisplayTypes that Latitude/Longitude/Altitude // are mapped to. DisplayRealType displayLatitudeType = new DisplayRealType( "ProjectionLat", true, -90.0, 90.0, 0.0, CommonUnit.degree); DisplayRealType displayLongitudeType = new DisplayRealType( "ProjectionLon", true, -180.0, 180.0, 0.0, CommonUnit.degree); DisplayRealType displayAltitudeType = new DisplayRealType( "ProjectionAlt", true, -1.0, 1.0, -1.0, null); // default of -1 sets at bottom DisplayTupleType displayTupleType = new DisplayTupleType( new DisplayRealType[] { displayLatitudeType, displayLongitudeType, displayAltitudeType}, coordinateSystem); /* * Add in the ScalarMaps * RealType.Latitude -> displayLatitudeType * RealType.Longitude -> displayLongitudeType * RealType.Altitude -> displayAltitudeType * Thus any data that we add to the display that has lat/lon/alt * will get displayed automagically. * * We also set up ScalarMaps of: * RealType.XAxis -> Display.XAxis * RealType.YAxis -> Display.YAxis * RealType.ZAxis -> Display.ZAxis * to set the bounds for these axes. */ latitudeMap = new ScalarMap(RealType.Latitude, displayLatitudeType); addMap(latitudeMap); latitudeMap.setRangeByUnits(); longitudeMap = new ScalarMap(RealType.Longitude, displayLongitudeType); addMap(longitudeMap); longitudeMap.setRangeByUnits(); ScalarMap map = new ScalarMap(RealType.XAxis, Display.XAxis); map.setRange(-1.0, 1.0); addMap(map); map = new ScalarMap(RealType.YAxis, Display.YAxis); map.setRange(-1.0, 1.0); addMap(map); if (do3D) { altitudeMap = new ScalarMap( RealType.Altitude, displayAltitudeType); altitudeMap.setRange(0, 16000); addMap(altitudeMap); map = new ScalarMap(RealType.ZAxis, Display.ZAxis); map.setRange(-1.0, 1.0); addMap(map); } /* uncomment if you want to see the image as well. if (imageSource != null) { FlatField imgData = aa.getData(); FunctionType ftype = (FunctionType) imgData.getType(); RealTupleType rtype = (RealTupleType)ftype.getRange(); RealType imageType = (RealType) rtype.getComponent(0); DataReference imageRef = new DataReferenceImpl("ref"); imageRef.setData(imgData); ConstantMap[] zMap = new ConstantMap[] { new ConstantMap(0.0, Display.ZAxis) }; addReference(imageRef, zMap); } */ DataReference mapRef = new DataReferenceImpl("maplines"); BaseMapAdapter bma = new BaseMapAdapter( new URL("ftp://ftp.ssec.wisc.edu/pub/visad-2.0/OUTLSUPW")); mapRef.setData(bma.getData()); addReference(mapRef); // Enable the MOUSE_MOVED event so we can display the cursor's // EarthLocation enableEvent(DisplayEvent.MOUSE_MOVED); addDisplayListener(new DisplayListener() { public void displayChanged(DisplayEvent event) { int id = event.getId(); try { if (id == event.MOUSE_MOVED) { pointerMoved(event.getX(), event.getY()); } } catch (Exception e) { System.err.println(e); } } }); // Add a KeyboardBehavior so we can use Ctrl-R to reset the // display and use other keyboard controls for zoom/pan displayRenderer.addKeyboardBehavior( new KeyboardBehaviorJ3D(displayRenderer)); } /** * Test with 'java -Xmx64m MapProjectionDisplay <do3D> <image>'. * @param do3D "true" if you want a 3D display, X for 2D * @param image image for MapProjection (file or ADDE URL) */ public static void main(String[] args) throws Exception { boolean do3D = (args.length > 0) ? args[0].equalsIgnoreCase("true") : false; String imageSource = (args.length > 1) ? args[1] : null; MapProjectionDisplay display = new MapProjectionDisplay(imageSource, do3D); JFrame frame = new JFrame("Map Projection test"); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); frame.getContentPane().add(display.getComponent()); frame.getContentPane().add("South", display.getLocationIndicator()); frame.pack(); frame.show(); } /** * Get the center lat/lon/alt of the projection. * @return center location */ public EarthLocation getCenterPoint() { return getEarthLocation(new double[] {0, 0, 0}); } /** * Get the EarthLocation of a point in XYZ space * @param xyz RealTuple with MathType * RealTupleType.SpatialCartesian3DTuple) * @return point in lat/lon/alt space. */ public EarthLocation getEarthLocation(RealTuple xyz) { EarthLocation el = null; try { el = getEarthLocation( new double[] { ((Real) xyz.getComponent(0)).getValue(), ((Real) xyz.getComponent(1)).getValue(), ((Real) xyz.getComponent(2)).getValue()}); } catch (VisADException e) { e.printStackTrace();} // can't happen catch (RemoteException e) { e.printStackTrace();} // can't happen return el; } /** * Get the EarthLocation of a point in XYZ space * @param xyz double[3] of x,y,z coords. * @return point in lat/lon/alt space. */ public EarthLocation getEarthLocation(double[] xyz) { EarthLocationTuple value = null; try { float[][] numbers = visad.Set.doubleToFloat( coordinateSystem.fromReference( new double[][] { new double[] {xyz[0]}, new double[] {xyz[1]}, new double[] {xyz[2]}})); Real lat = new Real( RealType.Latitude, getScaledValue( latitudeMap, numbers[0][0]), coordinateSystem.getCoordinateSystemUnits()[0]); Real lon = new Real( RealType.Longitude, getScaledValue( longitudeMap, numbers[1][0]), coordinateSystem.getCoordinateSystemUnits()[1]); Real alt = null; if (isDisplay3D()) { Real fakeAltitude = new Real( RealType.ZAxis, getScaledValue( altitudeMap, numbers[2][0]), coordinateSystem.getCoordinateSystemUnits()[2]); alt = new Real(RealType.Altitude, fakeAltitude.getValue()); } else { alt = new Real(RealType.Altitude, 0); } value = new EarthLocationTuple(lat, lon, alt); } catch (VisADException e) { e.printStackTrace();} // can't happen catch (RemoteException e) { e.printStackTrace();} // can't happen return value; } /** * Returns the spatial (XYZ) coordinates of the particular EarthLocation * @return RealTuple of display coordinates. */ public RealTuple getSpatialCoordinates(EarthLocation el) { if (el == null) throw new NullPointerException( "MapProjectionDisplay.getSpatialCoorindate(): " + "null input EarthLocation"); RealTuple spatialLoc = null; try { float[][] temp = coordinateSystem.toReference( new float[][] { latitudeMap.scaleValues( new double[] { el.getLatitude().getValue(CommonUnit.degree) }), longitudeMap.scaleValues( new double[] { el.getLongitude().getValue(CommonUnit.degree) }), (isDisplay3D() == true) ? altitudeMap.scaleValues( new double[] { el.getAltitude().getValue(CommonUnit.meter) }) : new float[] {0} }); double[] xyz = new double[3]; xyz[0] = temp[0][0]; xyz[1] = temp[1][0]; xyz[2] = temp[2][0]; spatialLoc = new RealTuple(RealTupleType.SpatialCartesian3DTuple, xyz); } catch (VisADException e) { e.printStackTrace();} // can't happen catch (RemoteException e) { e.printStackTrace();} // can't happen return spatialLoc; } /** return a scaled value from the ScalarMap */ private float getScaledValue(ScalarMap map, float value) { return (map != null) ? map.inverseScaleValues(new float[] {value})[0] : 0.f; } /** * See if this is a 2D or 3D display. * @return true if 3D */ public boolean isDisplay3D() { return !displayRenderer.getMode2D(); } /** * Handles a change in the position of the mouse-pointer. */ private void pointerMoved(int x, int y) throws UnitException, VisADException, RemoteException { /* * Convert from (pixel, line) Java Component coordinates to (latitude, * longitude) */ VisADRay ray = displayRenderer.getMouseBehavior().findRay(x, y); EarthLocation el = getEarthLocation( new double[] {ray.position[0], ray.position[1], ray.position[2] }); locationLabel.setText(el.toString()); } /** * Get the component that will show the location. * @return a component to show the cursor location. */ public Component getLocationIndicator() { return locationLabel; } /** * An adapter for MapProjection coordinate systems (ie: ones with * a reference of Lat/Lon). Allows for the conversion from lat/lon * to Display.DisplaySpatialCartesianTuple (XYZ). Altitude (z) values * are held constant. */ protected class MapProjectionAdapter extends CoordinateSystem { private final MapProjection mapProjection; private final int latIndex; private final int lonIndex; private final double scaleX; private final double scaleY; private final double offsetX; private final double offsetY; /** * Construct a new CoordinateSystem which uses a MapProjection for * the transformations between x,y and lat/lon. * * @param mapProjection CoordinateSystem that transforms from xy * in the data space to lat/lon. * @exception VisADException can't create the necessary VisAD object */ public MapProjectionAdapter(MapProjection mapProjection) throws VisADException { super( Display.DisplaySpatialCartesianTuple, new Unit[] {CommonUnit.degree, CommonUnit.degree, null}); this.mapProjection = mapProjection; latIndex = mapProjection.getLatitudeIndex(); lonIndex = mapProjection.getLongitudeIndex(); java.awt.geom.Rectangle2D bounds = mapProjection.getDefaultMapArea(); /* System.out.println("X = " + bounds.getX() + " Y = "+ bounds.getY() + " width = "+ bounds.getWidth() + " height = "+ bounds.getHeight()); */ scaleX = bounds.getWidth()/2.0; scaleY = bounds.getHeight()/2.0; offsetX = bounds.getX() + scaleX; offsetY = bounds.getY() + scaleY; } /** * Transform latitude/longitude/altitude value to XYZ * * @param latlonalt array of latitude, longitude, altitude values * @return array of display xyz values. * * @exception VisADException can't create the necessary VisAD object */ public double[][] toReference(double[][] latlonalt) throws VisADException { if (latlonalt == null || latlonalt[0].length < 1) throw new VisADException( "MapProjection.toReference: null input array"); int numpoints = latlonalt[0].length; double[][] t2 = new double[2][numpoints]; for (int i = 0; i < numpoints; i++) { t2[latIndex][i] = latlonalt[0][i]; t2[lonIndex][i] = latlonalt[1][i]; } t2 = mapProjection.fromReference(t2); if (t2 == null) throw new VisADException( "MapProjection.toReference: " + "Can't do (lat,lon) to (x,y) transformation"); for (int i = 0; i < numpoints; i++) { latlonalt[0][i] = (t2[0][i]-offsetX)/scaleX; latlonalt[1][i] = (t2[1][i]-offsetY)/scaleY; } return latlonalt; } /** * Transform display XYZ values to latitude/longitude/altitude * * @param xyz array of Display.DisplaySpatialCartesianTuple XYZ values * @return array of display lat/lon/alt values. * * @exception VisADException can't create the necessary VisAD object */ public double[][] fromReference(double[][] xyz) throws VisADException { if (xyz == null || xyz[0].length < 1) throw new VisADException( "MapProjection.fromReference: null input array"); int numpoints = xyz[0].length; double[][] t2 = new double[2][numpoints]; for (int i = 0; i < numpoints; i++) { t2[0][i] = xyz[0][i]*scaleX + offsetX; t2[1][i] = xyz[1][i]*scaleY + offsetY; } t2 = mapProjection.toReference(t2); if (t2 == null) throw new VisADException( "MapProjection.toReference: " + "Can't do (x,y) to (lat,lon) transformation"); for (int i = 0; i < numpoints; i++) { xyz[0][i] = t2[latIndex][i]; xyz[1][i] = t2[lonIndex][i]; } return xyz; } public boolean equals(Object obj) { if (!(obj instanceof MapProjectionAdapter)) return false; MapProjectionAdapter that = (MapProjectionAdapter) obj; return (that.mapProjection).equals(mapProjection); } public String toString() { return "Using " + mapProjection.toString(); } } }