PageRenderTime 99ms CodeModel.GetById 36ms app.highlight 55ms RepoModel.GetById 1ms app.codeStats 0ms

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