arcgissamples\globe\DigitizeTool.java
/* Copyright 2010 ESRI * * All rights reserved under the copyright laws of the United States * and applicable international laws, treaties, and conventions. * * You may freely redistribute and use this sample code, with or * without modification, provided you include the original copyright * notice and use restrictions. * * See the use restrictions. * */ package arcgissamples.globe; import java.io.IOException; import java.util.ArrayList; import javax.media.opengl.GL; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawableFactory; import com.esri.arcgis.analyst3d.ICamera; import com.esri.arcgis.analyst3d.ISceneViewer; import com.esri.arcgis.controls.BaseTool; import com.esri.arcgis.controls.GlobeHookHelper; import com.esri.arcgis.controls.IGlobeHookHelper; import com.esri.arcgis.geometry.IVector3D; import com.esri.arcgis.geometry.Vector3D; import com.esri.arcgis.globecore.GlobeDisplay; import com.esri.arcgis.globecore.IGlobeDisplay3; import com.esri.arcgis.globecore.IGlobeDisplayEventsAdapter; import com.esri.arcgis.globecore.IGlobeDisplayEventsAfterDrawEvent; import com.esri.arcgis.globecore.IGlobeViewUtil; import com.esri.arcgis.interop.AutomationException; class DigitizeTool extends BaseTool { private static final long serialVersionUID = 1L; private IGlobeHookHelper hookHelper = null; private IGlobeViewUtil globeViewUtil = null; private IGlobeDisplay3 globeDisplay = null; private ISceneViewer viewer = null; private IVector3D vector3D = null; private int x; private int y; private boolean drawPoint = false; private ArrayList<GLPoint> pointsArray = new ArrayList<GLPoint>(); private double[] modelViewMatrix = null; private double[] billboardMatrix = null; private int billboardRectList = 0; private boolean checked = false; public void onCreate(Object hook) { try { hookHelper = new GlobeHookHelper(); hookHelper.setHookByRef(hook); this.enabled = true; this.caption = "Digitize Points"; this.category = "Digitize Points"; this.cursorPath = this.getClass().getClassLoader().getResource( "GlobeDigitizeTool.cur").getPath(); this.bitmapPath = this.getClass().getClassLoader().getResource( "GlobeDigitizeTool.bmp").getPath(); if(System.getProperty("os.name").toLowerCase().indexOf("win")>-1){ this.cursorPath =this.cursorPath.substring(1).replaceAll("%20"," "); this.bitmapPath =this.bitmapPath.substring(1).replaceAll("%20"," "); } vector3D = new Vector3D(); modelViewMatrix = new double[16]; billboardMatrix = new double[16]; } catch (Exception e) { e.printStackTrace(); } } public void onClick() { this.checked = !this.checked; try { globeViewUtil = (IGlobeViewUtil) hookHelper.getCamera(); globeDisplay = (IGlobeDisplay3) hookHelper.getGlobeDisplay(); viewer = hookHelper.getActiveViewer(); // start listening to globe display events ((GlobeDisplay) globeDisplay).addIGlobeDisplayEventsListener(new IGlobeDisplayEventsAdapter() { private static final long serialVersionUID = 1L; private GL gl = null; private GLContext glContext = null; @Override public void afterDraw( IGlobeDisplayEventsAfterDrawEvent theEvent) throws IOException, AutomationException { // do only the first time if (glContext == null) { // Tell JOGL that we are going to use // our own OpenGL Context glContext = GLDrawableFactory.getFactory() .createExternalGLContext(); glContext.makeCurrent(); gl = glContext.getGL(); CreateDisplayLists(); } // get OpenGL model matrix which is required in order to // billboard the icons gl.glGetDoublev(GL.GL_MODELVIEW_MATRIX, modelViewMatrix, 0); // populate the billboard matrix billboardMatrix[0] = modelViewMatrix[0]; billboardMatrix[1] = modelViewMatrix[4]; billboardMatrix[2] = modelViewMatrix[8]; billboardMatrix[3] = 0; billboardMatrix[4] = modelViewMatrix[1]; billboardMatrix[5] = modelViewMatrix[5]; billboardMatrix[6] = modelViewMatrix[9]; billboardMatrix[7] = 0; billboardMatrix[8] = modelViewMatrix[2]; billboardMatrix[9] = modelViewMatrix[6]; billboardMatrix[10] = modelViewMatrix[10]; billboardMatrix[11] = 0; billboardMatrix[12] = 0; billboardMatrix[13] = 0; billboardMatrix[14] = 0; billboardMatrix[15] = 1; // draw the mouse feedback if (true == drawPoint) { // convert the mouse coordinate into geocentric // (OpenGL) // coordinate system double[] glX = { -1 }; double[] glY = { -1 }; double[] glZ = { -1 }; globeViewUtil.windowToGeocentric(hookHelper .getGlobeDisplay(), hookHelper .getActiveViewer(), x, y, true, glX, glY, glZ); // draw the converted point on the surface of // the globe gl.glPointSize(30.0f); gl.glColor3ub((byte) 255, (byte) 0, (byte) 0); gl.glBegin(GL.GL_POINTS); gl.glVertex3f((float) glX[0], (float) glY[0], (float) glZ[0]); gl.glEnd(); } // draw the point in the points array if (pointsArray.size() > 0) { // for each item, we need to get the distance // from the // camera (in geocentric units) in order to // scale it double[] dblObsX = { -1 }; double[] dblObsY = { -1 }; double dblObsZ, dMagnitude, scale; ICamera camera = viewer.getCamera(); camera.getObserver().queryCoords(dblObsX, dblObsY); dblObsZ = camera.getObserver().getZ(); // draw the static points for (GLPoint p : pointsArray) { // get the distance from the camera to the drawn // item. // This distance will determine whteher to draw // the // item as a dot or as // a full icon. vector3D.setComponents(dblObsX[0] - p.x, dblObsY[0] - p.y, dblObsZ - p.z); dMagnitude = vector3D.getMagnitude(); scale = 0.04 * dMagnitude; gl.glPushMatrix(); // translate to the items location gl.glTranslatef((float) p.x, (float) p.y, (float) p.z); // orient the icon so that it'll face the camera OrientBillboard(); // scale the item (original size is 1 ubit) gl.glScaled(scale, scale, 1.0); gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // draw the item gl.glCallList(billboardRectList); gl.glPopMatrix(); } } } // / Create the display lists used to draw the icons // / the call to this method must be made from // BeforeDraw, // AfterDraw or DrawImmidiate. // / calling this method from anywhere else might end up // in // unexpected results since OpenGL state is // / not guaranteed. // / Please note that in this sample, a texture is being // mapped // to the item as part of the // / display list. However, it is possible to map a // texture to // the geometry at the time of drawing and thus // / use the same geometry for different textures private void CreateDisplayLists() { try { // create the texture for the bitmap int texId = CreateTexture("pin.png"); if (0 == texId) throw new Exception("Error generating texture"); // the quad size is set to 1 unit. Therefor you // will // have to scale it // each time before drawing. billboardRectList = gl.glGenLists(1); gl.glNewList(billboardRectList, GL.GL_COMPILE); gl.glPushMatrix(); // shift the item 1/2 unit to the left so that // it'll get // drawn around the // middle of its base gl.glTranslatef(-0.5f, 0.0f, 0.0f); // Set the OpenGL flags gl.glDisable(GL.GL_LIGHTING); gl.glEnable(GL.GL_BLEND); gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); gl.glDepthFunc(GL.GL_LEQUAL); // bind the texture gl.glEnable(GL.GL_TEXTURE_2D); gl.glBindTexture(GL.GL_TEXTURE_2D, texId); // create the geometry (quad) and specify the // texture // coordinates gl.glBegin(GL.GL_QUADS); gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(0.0f, 0.0f, 0.0f); gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(0.0f, 1.0f, 0.0f); gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(1.0f, 1.0f, 0.0f); gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(1.0f, 0.0f, 0.0f); gl.glEnd(); gl.glPopMatrix(); gl.glPolygonMode(GL.GL_FRONT, GL.GL_FILL); // Set off the OpenGL flags gl.glEnable(GL.GL_POINT_SMOOTH); gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); gl.glDisable(GL.GL_TEXTURE_2D); gl.glDisable(GL.GL_BLEND); gl.glEndList(); } catch (Exception ex) { ex.printStackTrace(); } } // / <summary> // / Given a bitmap (GDI+), create for it an OpenGL // texture and // return its ID // / </summary> // / <param name="bitmap"></param> // / <returns>the OGL texture id</returns> // / <remarks>in order to allow hardware acceleration, // texture // size must be power of two.</remarks> private int CreateTexture(String image) { int[] textureID = new int[1]; gl.glEnable(GL.GL_TEXTURE_2D); gl.glGenTextures(1, textureID, 0); gl.glBindTexture(GL.GL_TEXTURE_2D, textureID[0]); // set the texture parameters gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, (int) GL.GL_LINEAR); gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, (int) GL.GL_LINEAR); gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, (int) GL.GL_MODULATE); ImageTexturizer.Texture texture = null; try { texture = ImageTexturizer.readTexture(image, true); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA8, texture.getWidth(), texture.getHeight(), 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, texture .getPixels()); // // return the newly created texture id return textureID[0]; } // / <summary> // / orient the icons so that it'll face the camera // / </summary> private void OrientBillboard() { gl.glMultMatrixd(billboardMatrix, 0); } }); } catch (Exception e) { e.printStackTrace(); } } @Override public boolean isChecked() { return this.checked; } @Override public boolean deactivate() { this.checked = false; return true; } @Override public void onMouseDown(int button, int shift, int x, int y) { this.x = x; this.y = y; drawPoint = true; // Refresh the display so that the AfterDraw will get called try { viewer.redraw(false); } catch (Exception e) { e.printStackTrace(); } } @Override public void onMouseMove(int button, int shift, int x, int y) { if (false == drawPoint) return; // cache the coordinate since we need it for the AfterDraw this.x = x; this.y = y; // Refresh the display so that the AfterDraw will get called try { viewer.redraw(false); } catch (Exception e) { e.printStackTrace(); } } @Override public void onMouseUp(int button, int shift, int x, int y) { drawPoint = false; try { double[] geocentricX = new double[1]; double[] geocentricY = new double[1]; double[] geocentricZ = new double[1]; // convert the window coordinate into geocentric (OGL)coordinate globeViewUtil.windowToGeocentric(globeDisplay, viewer, x, y, true, geocentricX, geocentricY, geocentricZ); // set a new point GLPoint glPnt = new GLPoint(geocentricX[0], geocentricY[0], geocentricZ[0]); // add the point to the point array pointsArray.add(glPnt); // Refresh the display so that the AfterDraw will get called viewer.redraw(false); } catch (Exception e) { e.printStackTrace(); } } } class GLPoint { public GLPoint(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } public double x; public double y; public double z; }