arcgissamples\globe\DigitizeTool.java—ArcObjects 10.4 Help for Java | ArcGIS for Desktop
Digitize points using OpenGL
arcgissamples\globe\DigitizeTool.java
/* Copyright 2015 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 at <your ArcGIS install location>/DeveloperKit10.4/userestrictions.txt.
* 
*/
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;
}