/src/worldwind/kml/KMLLayer.java

http://wwj-kml.googlecode.com/ · Java · 931 lines · 668 code · 147 blank · 116 comment · 111 complexity · ba1752470568e7f2039be8f5a3879833 MD5 · raw file

  1. package worldwind.kml;
  2. import gov.nasa.worldwind.geom.Angle;
  3. import gov.nasa.worldwind.geom.LatLon;
  4. import gov.nasa.worldwind.geom.Position;
  5. import gov.nasa.worldwind.geom.Sector;
  6. import gov.nasa.worldwind.geom.Vec4;
  7. import gov.nasa.worldwind.layers.AbstractLayer;
  8. import gov.nasa.worldwind.pick.PickedObject;
  9. import gov.nasa.worldwind.render.DrawContext;
  10. import gov.nasa.worldwind.render.IconRenderer;
  11. import gov.nasa.worldwind.render.Renderable;
  12. import gov.nasa.worldwind.render.UserFacingIcon;
  13. import gov.nasa.worldwind.render.WWIcon;
  14. import java.awt.Color;
  15. import java.awt.Point;
  16. import java.util.ArrayList;
  17. import java.util.Iterator;
  18. import java.util.List;
  19. import javax.media.opengl.GL;
  20. import javax.media.opengl.glu.GLU;
  21. import javax.media.opengl.glu.GLUtessellator;
  22. import javax.media.opengl.glu.GLUtessellatorCallbackAdapter;
  23. import worldwind.kml.model.AltitudeMode;
  24. import worldwind.kml.model.KML3DModel;
  25. import worldwind.kml.model.KMLColor;
  26. import worldwind.kml.model.KMLCoord;
  27. import worldwind.kml.model.KMLFile;
  28. import worldwind.kml.model.KMLFolder;
  29. import worldwind.kml.model.KMLGraphic;
  30. import worldwind.kml.model.KMLLineString;
  31. import worldwind.kml.model.KMLMultiGeometry;
  32. import worldwind.kml.model.KMLObject;
  33. import worldwind.kml.model.KMLPlacemark;
  34. import worldwind.kml.model.KMLPoint;
  35. import worldwind.kml.model.KMLPolygon;
  36. import worldwind.kml.model.KMLStyle;
  37. /**
  38. * Created by IntelliJ IDEA. User: tgleason Date: Sep 7, 2008 Time: 1:08:59 PM
  39. * To change this template use File | Settings | File Templates.
  40. */
  41. public class KMLLayer extends AbstractLayer {
  42. protected KMLFile kmlFile;
  43. private double tessDistance = 10;
  44. private double fudgeFactor = 1;
  45. private int displayList = -1;
  46. private double simplificationFactor = 0.1;
  47. private List<KMLPlacemark> lineStringsToDraw = new ArrayList<KMLPlacemark>();
  48. private List<KMLPlacemark> pointsToDraw = new ArrayList<KMLPlacemark>();
  49. private List<WWIcon> activeIcons = new ArrayList<WWIcon>();
  50. private IconRenderer iconRenderer = new IconRenderer();
  51. private boolean invalid = false;
  52. private KMLPlacemark selectedPoint = null;
  53. /** Current vertical exaggeration factor. */
  54. private double verticalExaggeration;
  55. public KMLLayer(KMLFile kmlFile) {
  56. this.kmlFile = kmlFile;
  57. this.verticalExaggeration = 1.0;
  58. }
  59. public boolean isInvalid() {
  60. return invalid;
  61. }
  62. public void setInvalid(boolean invalid) {
  63. this.invalid = invalid;
  64. }
  65. /**
  66. * Render the layer. Some notes: - The first time this is called, it will
  67. * set up a OpenGL display list for most things. The others will be stored
  68. * in other data structures - If the sector containing this layer is not on
  69. * the screen, we'll drop out without drawing - If the size of the sector
  70. * containing this layer is less than one pixel, we'll drop out - To draw:
  71. * call the display list, then draw lines that are tesselated, then add
  72. * icons to the draw context
  73. *
  74. * @param dc
  75. */
  76. protected void doRender(DrawContext dc) {
  77. setVerticalExaggeration(dc.getVerticalExaggeration());
  78. if (invalid) {
  79. invalid = false;
  80. dc.getGL().glDeleteLists(displayList, 1);
  81. displayList = -1;
  82. lineStringsToDraw.clear();
  83. pointsToDraw.clear();
  84. }
  85. if (displayList == -1) {
  86. System.out.println("Building display list...");
  87. displayList = dc.getGL().glGenLists(1);
  88. dc.getGL().glNewList(displayList, GL.GL_COMPILE);
  89. drawFolder(dc, kmlFile.getRootFolder());
  90. dc.getGL().glEndList();
  91. System.out.println("Done.");
  92. }
  93. if (!dc.getVisibleSector().intersects(kmlFile.getSector())) {
  94. return;
  95. }
  96. if (sizeInPixels(dc, kmlFile.getSector()) < 1) {
  97. return;
  98. }
  99. GL gl = dc.getGL();
  100. setupGL(dc, gl);
  101. // -- Draw most stuff
  102. gl.glCallList(displayList);
  103. // -- Draw tesselated lines
  104. for (KMLPlacemark kmlPlacemark : lineStringsToDraw) {
  105. KMLLineString lineString = (KMLLineString) kmlPlacemark
  106. .getGraphic();
  107. if (dc.getVisibleSector().intersects(lineString.getSector())) {
  108. drawLineString(dc, lineString, kmlPlacemark.getStyle());
  109. }
  110. }
  111. unsetupGL(gl);
  112. // -- Add point placemarks to draw list, if we're zoomed in far enough
  113. // -- Right now just supports "pushpin" icons. A lot to do here. Should
  114. // put the icon
  115. // -- list in a quadtree in case there are millions of them
  116. // -- NOTE: Drawing these icons seems to take a lot longer than it
  117. // should. I think the IconRendered needs some work
  118. activeIcons = new ArrayList<WWIcon>();
  119. for (KMLPlacemark kmlPlacemark : pointsToDraw) {
  120. KMLPoint kmlPoint = (KMLPoint) kmlPlacemark.getGraphic();
  121. // System.out.println("Intersects: " +
  122. // dc.getVisibleSector().intersects(kmlPoint.getSector()));
  123. if (dc.getVisibleSector().intersects(kmlPoint.getSector())
  124. && sizeInPixels(dc, kmlPoint.getSector()) > 2) {
  125. UserFacingIcon icon = new UserFacingIcon("placemark.png",
  126. pointPosition(dc, kmlPoint));
  127. if (selectedPoint == kmlPlacemark) {
  128. icon.setHighlighted(true);
  129. }
  130. activeIcons.add(icon);
  131. }
  132. }
  133. iconRenderer.render(dc, activeIcons);
  134. }
  135. public void pick(DrawContext dc, Point point) {
  136. selectedPoint = null;
  137. Point flipped = new Point(point.x,
  138. (int) (dc.getView().getViewport().getHeight()) - point.y);
  139. for (KMLPlacemark kmlPlacemark : pointsToDraw) {
  140. KMLPoint kmlPoint = (KMLPoint) kmlPlacemark.getGraphic();
  141. if (dc.getVisibleSector().intersects(kmlPoint.getSector())
  142. && sizeInPixels(dc, kmlPoint.getSector()) > 2) {
  143. if (distanceInPixels(dc, pointPosition(dc, kmlPoint), flipped) < 10) {
  144. selectedPoint = kmlPlacemark;
  145. int[] viewport = new int[4];
  146. java.nio.ByteBuffer pixel = com.sun.opengl.util.BufferUtil
  147. .newByteBuffer(3);
  148. GL gl = dc.getGL();
  149. gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
  150. gl.glReadPixels(dc.getPickPoint().x,
  151. viewport[3] - dc.getPickPoint().y, 1, 1, GL.GL_RGB,
  152. GL.GL_UNSIGNED_BYTE, pixel);
  153. Color topColor = new Color(pixel.get(0) & 0xff,
  154. pixel.get(1) & 0xff, pixel.get(2) & 0xff, 0);
  155. dc.getPickedObjects().clear();
  156. dc.addPickedObject(new PickedObject(topColor.getRGB(),
  157. kmlPlacemark, pointPosition(dc, kmlPoint), false));
  158. break;
  159. }
  160. }
  161. }
  162. }
  163. private Position pointPosition(DrawContext dc, KMLPoint kmlPoint) {
  164. Angle lat, lon;
  165. double height;
  166. lat = Angle.fromDegrees(kmlPoint.getCoord().getLat());
  167. lon = Angle.fromDegrees(kmlPoint.getCoord().getLon());
  168. switch (kmlPoint.getAltitudeMode()) {
  169. case KMLPoint.ABSOLUTE:
  170. height = kmlPoint.getCoord().getHeight();
  171. break;
  172. case KMLPoint.RELATIVE_TO_GROUND:
  173. height = kmlPoint.getCoord().getHeight()
  174. + dc.getGlobe().getElevation(lat, lon);
  175. break;
  176. default:
  177. height = dc.getGlobe().getElevation(lat, lon);
  178. }
  179. return new Position(lat, lon, height);
  180. }
  181. private void setupGL(DrawContext dc, GL gl) {
  182. Vec4 cameraPosition = dc.getView().getEyePoint();
  183. gl.glPushAttrib(GL.GL_TEXTURE_BIT | GL.GL_ENABLE_BIT
  184. | GL.GL_CURRENT_BIT | GL.GL_LIGHTING_BIT | GL.GL_TRANSFORM_BIT);
  185. gl.glDisable(GL.GL_TEXTURE_2D);
  186. float[] lightPosition = { (float) (cameraPosition.x * 2),
  187. (float) (cameraPosition.y / 2), (float) (cameraPosition.z),
  188. 0.0f };
  189. float[] lightDiffuse = { 0.4f, 0.4f, 0.4f, 1.0f };
  190. float[] lightAmbient = { 0.3f, 0.3f, 0.3f, 1.0f };
  191. float[] lightSpecular = { 0.5f, 0.5f, 0.5f, 1.0f };
  192. gl.glDisable(GL.GL_COLOR_MATERIAL);
  193. gl.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, lightPosition, 0);
  194. gl.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE, lightDiffuse, 0);
  195. gl.glLightfv(GL.GL_LIGHT1, GL.GL_AMBIENT, lightAmbient, 0);
  196. gl.glLightfv(GL.GL_LIGHT1, GL.GL_SPECULAR, lightSpecular, 0);
  197. gl.glDisable(GL.GL_LIGHT0);
  198. gl.glEnable(GL.GL_LIGHT1);
  199. gl.glEnable(GL.GL_LIGHTING);
  200. gl.glEnable(GL.GL_NORMALIZE);
  201. gl.glMatrixMode(GL.GL_MODELVIEW);
  202. gl.glPushMatrix();
  203. }
  204. private void unsetupGL(GL gl) {
  205. gl.glMatrixMode(GL.GL_MODELVIEW);
  206. gl.glPopMatrix();
  207. gl.glDisable(GL.GL_LIGHT1);
  208. gl.glEnable(GL.GL_LIGHT0);
  209. gl.glDisable(GL.GL_LIGHTING);
  210. gl.glDisable(GL.GL_NORMALIZE);
  211. gl.glPopAttrib();
  212. }
  213. /**
  214. * Draw a folder recursively. This is called once, the first time the file
  215. * is displayed. It issues OpenGL commands to draw most of the objects.
  216. * Those commands are stored in a Display List than can be replayed each
  217. * other time the layer is displyed.
  218. *
  219. * Some things aren't drawn now, since they need to be drawn each time
  220. * differently. That includes lines that are tesselated. They need to be
  221. * re-tesselated each frame depending on how much elevation detail is
  222. * available. Also, icons are drawn post-render. All of these things are
  223. * stored in other datastructures so they can be renedered separately every
  224. * frame.
  225. *
  226. * @param dc
  227. * @param folder
  228. */
  229. private void drawFolder(DrawContext dc, KMLFolder folder) {
  230. if (!folder.isVisible())
  231. return;
  232. Iterator<KMLObject> objIter = folder.getObjects().iterator();
  233. while (objIter.hasNext()) {
  234. KMLObject kmlObject = objIter.next();
  235. if (kmlObject instanceof KMLPlacemark) {
  236. KMLPlacemark placemark = (KMLPlacemark) kmlObject;
  237. if (!placemark.isVisible())
  238. continue;
  239. KMLGraphic graphic = placemark.getGraphic();
  240. if (graphic != null) {
  241. if (graphic instanceof KMLLineString) {
  242. KMLLineString lineString = (KMLLineString) graphic;
  243. if (lineString.isExtrude()) {
  244. drawExtrudedLineString(dc, (KMLLineString) graphic);
  245. } else {
  246. lineStringsToDraw.add(placemark);
  247. }
  248. } else if (graphic instanceof KMLPoint) {
  249. // paintPoint(context, (KMLPoint) graphic);
  250. pointsToDraw.add(placemark);
  251. } else if (graphic instanceof KMLPolygon) {
  252. drawPoly(dc, placemark.getStyle(), (KMLPolygon) graphic);
  253. } else if (graphic instanceof KMLMultiGeometry) {
  254. drawMultiGeometry(dc, placemark.getStyle(),
  255. (KMLMultiGeometry) graphic);
  256. } else if (graphic instanceof KML3DModel) {
  257. draw3DModel(dc, placemark.getStyle(),
  258. (KML3DModel) graphic);
  259. }
  260. }
  261. }
  262. }
  263. Iterator<KMLFolder> folders = folder.getChildFolders().iterator();
  264. while (folders.hasNext()) {
  265. KMLFolder kmlFolder = folders.next();
  266. drawFolder(dc, kmlFolder);
  267. }
  268. }
  269. private void draw3DModel(DrawContext dc, KMLStyle style, KML3DModel graphic) {
  270. Renderable mesh = graphic.getMesh();
  271. mesh.render(dc);
  272. }
  273. private void drawMultiGeometry(DrawContext dc, KMLStyle style,
  274. KMLMultiGeometry kmlMultiGeom) {
  275. for (KMLGraphic graphic : kmlMultiGeom.getGraphics()) {
  276. if (graphic != null) {
  277. if (graphic instanceof KMLLineString) {
  278. KMLLineString lineString = (KMLLineString) graphic;
  279. if (lineString.isExtrude()) {
  280. drawExtrudedLineString(dc, (KMLLineString) graphic);
  281. } else {
  282. // lineStringsToDraw.add(placemark);
  283. }
  284. } else if (graphic instanceof KMLPoint) {
  285. // paintPoint(context, (KMLPoint) graphic);
  286. // pointsToDraw.add(placemark);
  287. } else if (graphic instanceof KMLPolygon) {
  288. drawPoly(dc, style, (KMLPolygon) graphic);
  289. }
  290. }
  291. }
  292. }
  293. /**
  294. * Right now, just draws a line. An extruded linestring should display a
  295. * "fence-like" polygon.
  296. *
  297. * @param dc
  298. * @param lineString
  299. */
  300. private void drawExtrudedLineString(DrawContext dc, KMLLineString lineString) {
  301. List<KMLCoord> coords = lineString.getCoords();
  302. if (lineString.isTessellate()) {
  303. coords = interpolateCoords(coords);
  304. }
  305. List<Vec4> points = getExact3dPointsForCoords(dc, coords,
  306. lineString.isAbsolute());
  307. dc.getGL().glLineWidth(3);
  308. dc.getGL().glDisable(GL.GL_LIGHTING);
  309. dc.getGL().glColor3f(1, 0, 0);
  310. dc.getGL().glBegin(GL.GL_LINE_STRIP);
  311. for (int i = 0; i < points.size(); i++) {
  312. Vec4 p = points.get(i);
  313. dc.getGL().glVertex3d(p.x, p.y, p.z);
  314. }
  315. dc.getGL().glEnd();
  316. dc.getGL().glEnable(GL.GL_LIGHTING);
  317. }
  318. /**
  319. * Draw a regular linestring. It will simplify the line depending on the
  320. * detail. This is done via a constant "simplificationFactor". It's a bit of
  321. * a kludge, but seems to work reasonably well.
  322. *
  323. * If it is a tesselated line, it will add detail to make sure it follows
  324. * the terrain at the current view level.
  325. *
  326. * @param dc
  327. * @param lineString
  328. */
  329. private void drawLineString(DrawContext dc, KMLLineString lineString,
  330. KMLStyle style) {
  331. double ppd = dc.getDrawableWidth()
  332. / dc.getVisibleSector().getDeltaLonDegrees();
  333. double stepSize = simplificationFactor / ppd;
  334. List<KMLCoord> coords = lineString.getCoords();
  335. List<KMLCoord> interpCoords = new ArrayList<KMLCoord>();
  336. KMLCoord last = null;
  337. for (int i = 0; i < coords.size(); i++) {
  338. double dist = -1;
  339. if (last != null) {
  340. dist = last.dist(coords.get(i));
  341. if (dist < stepSize && i < (coords.size() - 1)) {
  342. continue;
  343. }
  344. }
  345. if (lineString.isTessellate() && dist > (stepSize * 2)) {
  346. double count = dist / (stepSize * 2);
  347. double dx = (last.getLon() - coords.get(i).getLon()) / count;
  348. double dy = (last.getLat() - coords.get(i).getLat()) / count;
  349. double dh = (last.getHeight() - coords.get(i).getHeight())
  350. / count;
  351. for (double j = 1; j < count; j++) {
  352. KMLCoord coord = new KMLCoord(last.getLon() - dx * j,
  353. last.getLat() - dy * j, last.getHeight() - dh * j);
  354. interpCoords.add(coord);
  355. }
  356. }
  357. interpCoords.add(coords.get(i));
  358. last = coords.get(i);
  359. }
  360. List<Vec4> points = get3dPointsForCoords(dc, interpCoords,
  361. lineString.isAbsolute());
  362. // -- Style line
  363. KMLColor color = new KMLColor(); // TODO: use static
  364. float width = 3;
  365. if (style != null) {
  366. if (style.getLineStyle("color") != null) {
  367. color = (KMLColor) style.getLineStyle("color");
  368. }
  369. Float lineWidth = (Float) style.getLineStyle("width");
  370. if (lineWidth != null) {
  371. width = lineWidth.floatValue();
  372. }
  373. }
  374. dc.getGL().glLineWidth(width);
  375. dc.getGL().glDisable(GL.GL_LIGHTING);
  376. dc.getGL().glColor3f(color.red, color.green, color.blue);
  377. dc.getGL().glBegin(GL.GL_LINE_STRIP);
  378. for (int i = 0; i < points.size(); i++) {
  379. Vec4 p = points.get(i);
  380. dc.getGL().glVertex3d(p.x, p.y, p.z);
  381. }
  382. dc.getGL().glEnd();
  383. dc.getGL().glEnable(GL.GL_LIGHTING);
  384. }
  385. /**
  386. * Tesselate a line segment.
  387. *
  388. * @param coords
  389. * @return
  390. */
  391. private List<KMLCoord> interpolateCoords(List<KMLCoord> coords) {
  392. if (coords.size() < 2)
  393. return coords;
  394. List<KMLCoord> newCoords = new ArrayList<KMLCoord>(coords.size());
  395. newCoords.add(coords.get(0));
  396. KMLCoord last = coords.get(0);
  397. for (int i = 1; i < coords.size(); i++) {
  398. KMLCoord cur = coords.get(i);
  399. double dist = distance(last, cur);
  400. // System.out.println("Dist: " + dist);
  401. if (dist > (tessDistance * 2)) {
  402. double newPoints = dist / tessDistance;
  403. double dx = (last.getLon() - cur.getLon()) / newPoints;
  404. double dy = (last.getLat() - cur.getLat()) / newPoints;
  405. double dh = (last.getHeight() - coords.get(i).getHeight())
  406. / newPoints;
  407. for (int j = 1; j < newPoints; j = j + 1) {
  408. KMLCoord coord = new KMLCoord(last.getLon() - dx * j,
  409. last.getLat() - dy * j, last.getHeight() - dh * j);
  410. newCoords.add(coord);
  411. }
  412. }
  413. newCoords.add(cur);
  414. last = cur;
  415. }
  416. return newCoords;
  417. }
  418. /**
  419. * Draw a polygon. It may be extruded. It may have holes. Its altitude
  420. * coordinates could be absolute or clamped to ground.
  421. *
  422. * @param dc
  423. * @param style
  424. * @param kmlPolygon
  425. */
  426. private void drawPoly(DrawContext dc, KMLStyle style, KMLPolygon kmlPolygon) {
  427. List<KMLCoord> coords = kmlPolygon.getOuter();
  428. boolean absoluteAltitude = AltitudeMode.Absolute.equals(kmlPolygon
  429. .getAltitudeMode());
  430. List<Vec4> outerTop = getExact3dPointsForCoords(dc, coords,
  431. absoluteAltitude);
  432. List<Vec4> outerBase = getExactGroundPointsForCoords(dc, coords, -1);
  433. List<Vec4> innerTop = null;
  434. List<Vec4> innerBase = null;
  435. if (kmlPolygon.getInner() != null) {
  436. List<KMLCoord> innerCoords = kmlPolygon.getInner();
  437. innerTop = getExact3dPointsForCoords(dc, innerCoords, false);
  438. innerBase = getExactGroundPointsForCoords(dc, innerCoords, -1);
  439. }
  440. KMLColor color = null;
  441. if (style != null) {
  442. color = (KMLColor) style.getPolyStyle("color");
  443. }
  444. if (color == null) {
  445. color = new KMLColor();
  446. }
  447. boolean drawOutline = true;
  448. if (style != null && style.getPolyStyle("outline") != null) {
  449. drawOutline = (Boolean) style.getPolyStyle("outline");
  450. }
  451. float colorComps[] = new float[] { color.red, color.green, color.blue,
  452. color.alpha };
  453. float white[] = new float[] { 1, 1, 1, 1 };
  454. float lightPosition[] = new float[] { 0, 0, 1, 0 };
  455. dc.getGL().glEnable(GL.GL_LIGHTING);
  456. dc.getGL().glEnable(GL.GL_LIGHT0);
  457. dc.getGL().glPushMatrix();
  458. dc.getGL().glLoadIdentity();
  459. dc.getGL().glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, lightPosition, 0);
  460. dc.getGL().glPopMatrix();
  461. dc.getGL().glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE,
  462. new float[] { 1, 1, 1, 1 }, 0);
  463. dc.getGL().glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, white, 0);
  464. dc.getGL().glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, colorComps, 0);
  465. dc.getGL().glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT,
  466. new float[] { 0, 0, 0, 1 }, 0);
  467. dc.getGL().glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, 50);
  468. dc.getGL().glMaterialfv(GL.GL_FRONT, GL.GL_EMISSION,
  469. new float[] { 0, 0, 0, 1 }, 0);
  470. GLUtessellator tobj = dc.getGLU().gluNewTess();
  471. PloygonTessCallback callback = new PloygonTessCallback(dc);
  472. dc.getGLU().gluTessCallback(tobj, GLU.GLU_TESS_VERTEX, callback);
  473. dc.getGLU().gluTessCallback(tobj, GLU.GLU_TESS_BEGIN, callback);
  474. dc.getGLU().gluTessCallback(tobj, GLU.GLU_TESS_END, callback);
  475. dc.getGLU().gluTessCallback(tobj, GLU.GLU_TESS_ERROR, callback);
  476. // dc.getGL().glCullFace(GL.GL_BACK);
  477. // dc.getGL().glFrontFace(dc.getGL().GL_CCW);
  478. // dc.getGL().glCullFace(GL.GL_FRONT_AND_BACK);
  479. // dc.getGL().glEnable(dc.getGL().GL_CULL_FACE);
  480. dc.getGL().glEnable(GL.GL_BLEND);
  481. dc.getGL().glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
  482. dc.getGL().glEnable(GL.GL_POLYGON_SMOOTH);
  483. // dc.getGL().glDisable(GL.GL_DEPTH_TEST);
  484. if (outerTop.size() > 2) {
  485. Vec4 p1 = outerTop.get(1).subtract3(outerTop.get(0)).normalize3();
  486. Vec4 p2 = outerTop.get(2).subtract3(outerTop.get(0)).normalize3();
  487. Vec4 normal = p1.cross3(p2).normalize3();
  488. dc.getGL().glNormal3d(normal.x, normal.y, normal.z);
  489. }
  490. dc.getGLU().gluTessBeginPolygon(tobj, null);
  491. // -- Outer contour
  492. dc.getGLU().gluTessBeginContour(tobj);
  493. for (int i = 0; i < outerTop.size(); i++) {
  494. Vec4 point = outerTop.get(i);
  495. double vals[] = point.toArray3(new double[3], 0);
  496. dc.getGLU().gluTessVertex(tobj, vals, 0, vals);
  497. }
  498. dc.getGLU().gluTessEndContour(tobj);
  499. // -- Inner contour
  500. if (innerTop != null) {
  501. dc.getGLU().gluTessBeginContour(tobj);
  502. for (int i = 0; i < innerTop.size(); i++) {
  503. Vec4 point = innerTop.get(i);
  504. double vals[] = point.toArray3(new double[3], 0);
  505. dc.getGLU().gluTessVertex(tobj, vals, 0, vals);
  506. }
  507. dc.getGLU().gluTessEndContour(tobj);
  508. }
  509. dc.getGLU().gluTessEndPolygon(tobj);
  510. if (kmlPolygon.isExtrude()) {
  511. for (int i = 0; i < outerTop.size(); i++) {
  512. dc.getGL().glBegin(GL.GL_POLYGON);
  513. Vec4 p1 = outerTop.get(i);
  514. Vec4 p2 = outerTop.get((i + 1) % outerTop.size());
  515. Vec4 p3 = outerBase.get((i + 1) % outerBase.size());
  516. Vec4 p4 = outerBase.get(i);
  517. Vec4 pp1 = p2.subtract3(p1).normalize3();
  518. Vec4 pp2 = p3.subtract3(p1).normalize3();
  519. Vec4 normal = pp2.cross3(pp1).normalize3();
  520. dc.getGL().glNormal3d(normal.x, normal.y, normal.z);
  521. dc.getGL().glVertex3d(p1.x, p1.y, p1.z);
  522. dc.getGL().glVertex3d(p2.x, p2.y, p2.z);
  523. dc.getGL().glVertex3d(p3.x, p3.y, p3.z);
  524. dc.getGL().glVertex3d(p4.x, p4.y, p4.z);
  525. dc.getGL().glEnd();
  526. }
  527. if (innerTop != null) {
  528. for (int i = 0; i < innerTop.size(); i++) {
  529. dc.getGL().glBegin(GL.GL_POLYGON);
  530. Vec4 p1 = innerTop.get(i);
  531. Vec4 p2 = innerTop.get((i + 1) % innerTop.size());
  532. Vec4 p3 = innerBase.get((i + 1) % innerBase.size());
  533. Vec4 p4 = innerBase.get(i);
  534. dc.getGL().glVertex3d(p1.x, p1.y, p1.z);
  535. dc.getGL().glVertex3d(p2.x, p2.y, p2.z);
  536. dc.getGL().glVertex3d(p3.x, p3.y, p3.z);
  537. dc.getGL().glVertex3d(p4.x, p4.y, p4.z);
  538. dc.getGL().glEnd();
  539. }
  540. }
  541. }
  542. dc.getGL().glDisable(GL.GL_BLEND);
  543. dc.getGL().glDisable(GL.GL_POLYGON_SMOOTH);
  544. if (drawOutline) {
  545. dc.getGL().glColor3f(1, 1, 1);
  546. dc.getGL().glLineWidth(1.5f);
  547. dc.getGL().glBegin(GL.GL_LINES);
  548. dc.getGL().glEnable(GL.GL_LINE_SMOOTH);
  549. for (int i = 0; i < outerTop.size(); i++) {
  550. Vec4 vec = outerTop.get(i);
  551. Vec4 vec2 = outerTop.get((i + 1) % outerTop.size());
  552. Vec4 vec3 = outerBase.get(i);
  553. dc.getGL().glVertex3d(vec.x, vec.y, vec.z);
  554. dc.getGL().glVertex3d(vec2.x, vec2.y, vec2.z);
  555. if (kmlPolygon.isExtrude()) {
  556. dc.getGL().glVertex3d(vec.x, vec.y, vec.z);
  557. dc.getGL().glVertex3d(vec3.x, vec3.y, vec3.z);
  558. }
  559. }
  560. if (innerTop != null) {
  561. for (int i = 0; i < innerTop.size(); i++) {
  562. Vec4 vec = innerTop.get(i);
  563. Vec4 vec2 = innerTop.get((i + 1) % innerTop.size());
  564. Vec4 vec3 = innerBase.get(i);
  565. dc.getGL().glVertex3d(vec.x, vec.y, vec.z);
  566. dc.getGL().glVertex3d(vec2.x, vec2.y, vec2.z);
  567. if (kmlPolygon.isExtrude()) {
  568. dc.getGL().glVertex3d(vec.x, vec.y, vec.z);
  569. dc.getGL().glVertex3d(vec3.x, vec3.y, vec3.z);
  570. }
  571. }
  572. }
  573. dc.getGL().glEnd();
  574. dc.getGL().glDisable(GL.GL_LINE_SMOOTH);
  575. }
  576. dc.getGL().glEnable(GL.GL_LIGHTING);
  577. }
  578. /**
  579. * Force the underlying code to load the most accurate value for the height
  580. * of a point. This is called on setup for some points. If there are many,
  581. * it may take some time to load them all.
  582. *
  583. * @param dc
  584. * @param coords
  585. * @param fudge
  586. * @return
  587. */
  588. private List<Vec4> getExactGroundPointsForCoords(DrawContext dc,
  589. List<KMLCoord> coords, double fudge) {
  590. List<Vec4> points = new ArrayList<Vec4>(coords.size());
  591. for (KMLCoord coord : coords) {
  592. Vec4 point = getExact3dPointForLocation(dc, coord.getLat(),
  593. coord.getLon(), fudge);
  594. points.add(point);
  595. }
  596. return points;
  597. }
  598. private List<Vec4> get3dPointsForCoords(DrawContext dc,
  599. List<KMLCoord> coords, boolean absolute) {
  600. List<Vec4> points = new ArrayList<Vec4>(coords.size());
  601. if (absolute) {
  602. for (KMLCoord coord : coords) {
  603. Angle latAngle = Angle.fromDegrees(coord.getLat());
  604. Angle lonAngle = Angle.fromDegrees(coord.getLon());
  605. Vec4 point = dc.getGlobe().computePointFromPosition(latAngle,
  606. lonAngle, coord.getHeight());
  607. points.add(point);
  608. }
  609. } else {
  610. for (KMLCoord coord : coords) {
  611. Vec4 point = computeSurfacePoint(dc, coord, 1);
  612. points.add(point);
  613. }
  614. }
  615. return points;
  616. }
  617. private Vec4 computeSurfacePoint(DrawContext context, KMLCoord kmlCoord,
  618. double offset) {
  619. Angle lat = Angle.fromDegrees(kmlCoord.getLat());
  620. Angle lon = Angle.fromDegrees(kmlCoord.getLon());
  621. Vec4 point = context.getSurfaceGeometry().getSurfacePoint(lat, lon,
  622. offset);
  623. if (point != null)
  624. return point;
  625. // Point is outside the current sector geometry, so compute it from the
  626. // globe.
  627. return context.getGlobe().computePointFromPosition(lat, lon, offset);
  628. }
  629. private List<Vec4> getExact3dPointsForCoords(DrawContext dc,
  630. List<KMLCoord> coords, boolean absolute) {
  631. List<Vec4> points = new ArrayList<Vec4>(coords.size());
  632. if (absolute) {
  633. for (KMLCoord coord : coords) {
  634. Angle latAngle = Angle.fromDegrees(coord.getLat());
  635. Angle lonAngle = Angle.fromDegrees(coord.getLon());
  636. Vec4 point = dc.getGlobe().computePointFromPosition(latAngle,
  637. lonAngle, coord.getHeight() * verticalExaggeration);
  638. points.add(point);
  639. }
  640. } else {
  641. for (KMLCoord coord : coords) {
  642. Vec4 point = getExact3dPointForLocation(dc, coord.getLat(),
  643. coord.getLon(),
  644. Math.max(coord.getHeight(), fudgeFactor));
  645. points.add(point);
  646. }
  647. }
  648. return points;
  649. }
  650. private Vec4 getExact3dPointForLocation(DrawContext dc, double lat,
  651. double lon, double offset) {
  652. Double height = null;
  653. Angle latAngle = Angle.fromDegrees(lat);
  654. Angle lonAngle = Angle.fromDegrees(lon);
  655. while (height == null) {
  656. // FIXME getBestElevation() NWW 0.5 changed to getElevation() NWW
  657. // 0.6
  658. // is this behavior right ?
  659. height = dc.getGlobe().getElevation(latAngle, lonAngle);
  660. if (height == null) {
  661. System.out.print(".");
  662. try {
  663. Thread.sleep(500);
  664. } catch (InterruptedException e) {
  665. Thread.interrupted();
  666. }
  667. }
  668. }
  669. return dc.getGlobe().computePointFromPosition(latAngle, lonAngle,
  670. height + offset);
  671. }
  672. /**
  673. * Guesstimate the size in pixels that a given sector would be on teh
  674. * screen. This is useful to see whether we should draw something that would
  675. * be too small to see.
  676. *
  677. * @param dc
  678. * @param sec
  679. * @return
  680. */
  681. private double sizeInPixels(DrawContext dc, Sector sec) {
  682. return Math.max(
  683. distanceInPixels(dc, sec.getMinLatitude(),
  684. sec.getMinLongitude(), sec.getMaxLatitude(),
  685. sec.getMaxLongitude()),
  686. distanceInPixels(dc, sec.getMinLatitude(),
  687. sec.getMaxLongitude(), sec.getMaxLatitude(),
  688. sec.getMinLongitude()));
  689. }
  690. private double distanceInPixels(DrawContext dc, Angle lat1, Angle lon1,
  691. Angle lat2, Angle lon2) {
  692. double model[] = new double[16];
  693. double proj[] = new double[16];
  694. int view[] = new int[4];
  695. dc.getGL().glGetDoublev(GL.GL_MODELVIEW_MATRIX, model, 0);
  696. dc.getGL().glGetDoublev(GL.GL_PROJECTION_MATRIX, proj, 0);
  697. dc.getGL().glGetIntegerv(GL.GL_VIEWPORT, view, 0);
  698. Vec4 p1 = dc.getGlobe().computePointFromPosition(
  699. new Position(lat1, lon1, 0));
  700. Vec4 p2 = dc.getGlobe().computePointFromPosition(
  701. new Position(lat2, lon2, 0));
  702. double p1S[] = new double[4];
  703. double p2S[] = new double[4];
  704. dc.getGLU().gluProject(p1.x, p1.y, p1.z, model, 0, proj, 0, view, 0,
  705. p1S, 0);
  706. dc.getGLU().gluProject(p2.x, p2.y, p2.z, model, 0, proj, 0, view, 0,
  707. p2S, 0);
  708. double p1x = p1S[0];
  709. double p1y = p1S[1];
  710. double p2x = p2S[0];
  711. double p2y = p2S[1];
  712. return Math.sqrt((p1x - p2x) * (p1x - p2x) + (p1y - p2y) * (p1y - p2y));
  713. }
  714. private double distanceInPixels(DrawContext dc, Position pos, Point p) {
  715. double model[] = new double[16];
  716. double proj[] = new double[16];
  717. int view[] = new int[4];
  718. dc.getGL().glGetDoublev(GL.GL_MODELVIEW_MATRIX, model, 0);
  719. dc.getGL().glGetDoublev(GL.GL_PROJECTION_MATRIX, proj, 0);
  720. dc.getGL().glGetIntegerv(GL.GL_VIEWPORT, view, 0);
  721. Vec4 p1 = dc.getGlobe().computePointFromPosition(pos);
  722. double p1S[] = new double[4];
  723. dc.getGLU().gluProject(p1.x, p1.y, p1.z, model, 0, proj, 0, view, 0,
  724. p1S, 0);
  725. double p1x = p1S[0];
  726. double p1y = p1S[1];
  727. double p2x = p.x;
  728. double p2y = p.y;
  729. // if (p1x>0 && p1x<1000 && p1y>0 && p1y<800)
  730. // System.out.println("Loc: " + p1x + ", " + p1y);
  731. return Math.sqrt((p1x - p2x) * (p1x - p2x) + (p1y - p2y) * (p1y - p2y));
  732. }
  733. private double distance(KMLCoord coord1, KMLCoord coord2) {
  734. LatLon ll1 = LatLon.fromDegrees(coord1.getLat(), coord1.getLon());
  735. LatLon ll2 = LatLon.fromDegrees(coord2.getLat(), coord2.getLon());
  736. return LatLon.ellipsoidalDistance(ll1, ll2, 6378137, 6356752.3); // consts
  737. // from
  738. // wikipedia
  739. }
  740. private void setVerticalExaggeration(double ve) {
  741. // if vertical exaggeration has changed, need to rebuild display lists
  742. if (ve != this.verticalExaggeration) {
  743. verticalExaggeration = ve;
  744. setInvalid(true);
  745. }
  746. }
  747. static class PloygonTessCallback extends GLUtessellatorCallbackAdapter {
  748. DrawContext dc;
  749. PloygonTessCallback(DrawContext dc) {
  750. this.dc = dc;
  751. }
  752. public void begin(int i) {
  753. dc.getGL().glBegin(i);
  754. }
  755. public void end() {
  756. dc.getGL().glEnd();
  757. }
  758. public void combine(double[] coords, Object[] data, float[] weight,
  759. Object[] outData) {
  760. double[] vertex = new double[6];
  761. int i;
  762. vertex[0] = coords[0];
  763. vertex[1] = coords[1];
  764. vertex[2] = coords[2];
  765. for (i = 3; i < 6/* 7OutOfBounds from C! */; i++)
  766. vertex[i] = weight[0] //
  767. * ((double[]) data[0])[i]
  768. + weight[1]
  769. * ((double[]) data[1])[i]
  770. + weight[2]
  771. * ((double[]) data[2])[i]
  772. + weight[3]
  773. * ((double[]) data[3])[i];
  774. outData[0] = vertex;
  775. }
  776. public void vertex(Object o) {
  777. double vals[] = (double[]) o;
  778. dc.getGL().glVertex3dv(vals, 0);
  779. }
  780. public void error(int i) {
  781. System.out.println("Error: " + i);
  782. }
  783. }
  784. }