All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nasa.worldwind.render.ScreenImage Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */

package gov.nasa.worldwind.render;

import com.jogamp.opengl.util.texture.TextureCoords;
import gov.nasa.worldwind.*;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.layers.Layer;
import gov.nasa.worldwind.ogc.kml.KMLConstants;
import gov.nasa.worldwind.ogc.kml.impl.KMLExportUtil;
import gov.nasa.worldwind.pick.PickSupport;
import gov.nasa.worldwind.util.*;

import com.jogamp.opengl.*;
import javax.xml.stream.*;
import java.awt.*;
import java.io.*;
import java.net.URL;

/**
 * Draws an image parallel to the screen at a specified screen location relative to the World Window. If no image is
 * specified, a filled rectangle is drawn in its place.
 *
 * @author tag
 * @version $Id: ScreenImage.java 1171 2013-02-11 21:45:02Z dcollins $
 */
public class ScreenImage extends WWObjectImpl implements Renderable, Exportable
{
    protected Object imageSource;
    protected BasicWWTexture texture;
    protected OrderedImage orderedImage = new OrderedImage();
    protected PickSupport pickSupport = new PickSupport();
    protected double opacity = 1d;
    protected Double rotation;
    protected Color color = Color.WHITE;
    protected Object delegateOwner;

    protected Size size = new Size();
    protected Offset screenOffset;
    protected Offset imageOffset;
    protected Offset rotationOffset;

    // Values computed once per frame and reused during the frame as needed.
    protected long frameNumber = -1;         // Identifies frame used to calculate these values
    protected int width;                     // Width of scaled image
    protected int height;                    // Height of scaled image
    protected int originalImageWidth;        // Width of unscaled image
    protected int originalImageHeight;       // Height of unscaled image
    protected Point rotationPoint;
    /**
     * Indicates the location of this screen image in the viewport (on the screen) in OpenGL coordinates. This property
     * is computed in computeOffsets and used in draw Initially null.
     */
    protected Point screenLocation;
    /**
     * Indicates the location of this screen image in the viewport (on the screen) in AWT coordinates. This property is
     * assigned in setScreenLocation and computeOffsets. In computeOffsets, this
     * is computed by converting the screenLocation from OpenGL coordinates to AWT coordinates. Initially
     * null.
     */
    protected Point awtScreenLocation;
    protected double dx;
    protected double dy;
    protected Layer pickLayer;

    protected class OrderedImage implements OrderedRenderable
    {
        public double getDistanceFromEye()
        {
            return 0;
        }

        public void pick(DrawContext dc, Point pickPoint)
        {
            ScreenImage.this.draw(dc);
        }

        public void render(DrawContext dc)
        {
            ScreenImage.this.draw(dc);
        }
    }

    /**
     * Returns the location of the image on the screen. The position is relative to the upper-left corner of the World
     * Window. The point specified by the image offset will be aligned to this point. If the position was specified as
     * an offset, it may change if the viewport size changes. The value returned by this method is the most recently
     * computed screen location. Call {@link #getScreenLocation(DrawContext)} to ensure an accurate result based on the
     * current viewport.
     *
     * @return the current screen position.
     *
     * @see #getScreenLocation(DrawContext)
     * @see #getImageOffset()
     * @see #getScreenOffset()
     */
    public Point getScreenLocation()
    {
        return this.awtScreenLocation;
    }

    /**
     * Returns the location of the image on the screen. The position is relative to the upper-left corner of the World
     * Window. The image is centered on this position.
     *
     * @param dc The DrawContext in which the image will be drawn.
     *
     * @return the current screen position.
     */
    public Point getScreenLocation(DrawContext dc)
    {
        this.computeOffsets(dc);
        return this.awtScreenLocation;
    }

    /**
     * Convenience method to specify the location of the image on the screen. The specified screenLocation
     * is relative to the upper-left corner of the World Window, and the image is centered on this location.
     *
     * @param screenLocation the screen location on which to center the image. May be null, in which case the image is
     *                       not displayed.
     *
     * @see #setScreenOffset(Offset)
     * @see #setImageOffset(Offset)
     */
    public void setScreenLocation(Point screenLocation)
    {
        // Use units PIXELS for the X screen offset, and and INSET_PIXELS for the Y screen offset. The Offset is in
        // OpenGL coordinates with the origin in the lower-left corner, but the Point is in AWT coordinates with the
        // origin in the upper-left corner. This offset translates the origin from the lower-left to the upper-left
        // corner.
        this.screenOffset = new Offset(screenLocation.getX(), screenLocation.getY(), AVKey.PIXELS, AVKey.INSET_PIXELS);
        this.imageOffset = new Offset(0.5, 0.5, AVKey.FRACTION, AVKey.FRACTION);

        // Set cached screen location to the initial screen location so that it can be retrieved if getScreenLocation()
        // is called before the image is rendered. This maintains backward compatibility with the previous behavior of
        // ScreenImage.
        this.awtScreenLocation = new Point(screenLocation);
    }

    /**
     * Get the offset of the point on the screen to align with the image offset point.
     *
     * @return Offset of the image point that will be aligned to the image offset point.
     *
     * @see #getImageOffset()
     */
    public Offset getScreenOffset()
    {
        return screenOffset;
    }

    /**
     * Set the offset of the image relative to the viewport. The screen point identified by this offset will be aligned
     * to the image point identified by the image offset.
     *
     * @param screenOffset The screen offset.
     *
     * @see #setImageOffset(Offset)
     */
    public void setScreenOffset(Offset screenOffset)
    {
        this.screenOffset = screenOffset;
    }

    /**
     * Get the offset of the point on the image to align with the screen offset point.
     *
     * @return Offset of the image point that will be aligned to the screen offset point.
     *
     * @see #getScreenOffset()
     */
    public Offset getImageOffset()
    {
        return imageOffset;
    }

    /**
     * Set the image offset point. This point will be aligned to the screen point identified by the screen offset.
     *
     * @param imageOffset Offset that identifies a point on the image to align with the screen offset point.
     *
     * @see #setScreenOffset(Offset)
     */
    public void setImageOffset(Offset imageOffset)
    {
        this.imageOffset = imageOffset;
    }

    /**
     * Get the rotation applied to the image.
     *
     * @return Rotation in decimal degrees, or null if there is no rotation.
     *
     * @see #getRotationOffset()
     */
    public Double getRotation()
    {
        return rotation;
    }

    /**
     * Specifies a rotation to be applied to the image.
     *
     * @param rotation Rotation in decimal degrees.
     *
     * @see #setRotationOffset(Offset)
     */
    public void setRotation(Double rotation)
    {
        this.rotation = rotation;
    }

    /**
     * Get the point about which the image is rotated.
     *
     * @return Rotation point in image coordinates, or null if there is no rotation point set. The origin of the
     *         coordinate system is at the lower left corner of the image.
     *
     * @see #getRotation()
     */
    public Offset getRotationOffset()
    {
        return rotationOffset;
    }

    /**
     * Set the point on the image about which rotation is performed.
     *
     * @param rotationOffset Rotation offset.
     *
     * @see #setRotation(Double)
     */
    public void setRotationOffset(Offset rotationOffset)
    {
        this.rotationOffset = rotationOffset;
    }

    /**
     * Get the dimension to apply to the image.
     *
     * @return Image dimension.
     */
    public Size getSize()
    {
        return size;
    }

    /**
     * Set a dynamic dimension to apply to the image. The dimension allows the image to be scaled relative to the
     * viewport size.
     *
     * @param size Image dimension. May not be null.
     */
    public void setSize(Size size)
    {
        if (size == null)
        {
            String msg = Logging.getMessage("nullValue.SizeIsNull");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }

        this.size = size;
    }

    /**
     * Returns the current image source.
     *
     * @return the current image source.
     *
     * @see #getImageSource()
     */
    public Object getImageSource()
    {
        return this.imageSource;
    }

    /**
     * Specifies the image source, which may be either a file path {@link String} or a {@link
     * java.awt.image.BufferedImage}. If the image is not already in memory, it will be loaded in the background.
     *
     * @param imageSource the image source, either a file path {@link String} or a {@link
     *                    java.awt.image.BufferedImage}.
     *
     * @throws IllegalArgumentException if the imageSource is null.
     */
    public void setImageSource(Object imageSource)
    {
        if (imageSource == null)
        {
            String message = Logging.getMessage("nullValue.ImageSource");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.imageSource = imageSource;
        this.texture = null; // New image source, we need to load a new texture
    }

    /**
     * Create and initialize the texture from the image source. If the image is not in memory this method will request
     * that it be loaded and return null.
     *
     * @return The texture, or null if the texture is not yet available.
     */
    protected BasicWWTexture initializeTexture()
    {
        Object imageSource = this.getImageSource();
        if (imageSource instanceof String || imageSource instanceof URL)
        {
            URL imageURL = WorldWind.getDataFileStore().requestFile(imageSource.toString());
            if (imageURL != null)
            {
                this.texture = new BasicWWTexture(imageURL, true);
                this.texture.setUseAnisotropy(false);
            }
        }
        else if (imageSource != null)
        {
            this.texture = new BasicWWTexture(imageSource, true);
            return this.texture;
        }

        return null;
    }

    /**
     * Returns the opacity of the surface. A value of 1 or greater means the surface is fully opaque, a value of 0 means
     * that the surface is fully transparent.
     *
     * @return the surface opacity.
     */
    public double getOpacity()
    {
        return opacity;
    }

    /**
     * If no image is set, or if the image is not yet available, a rectangle will be drawn in this color.
     *
     * @return The color for the default rectangle.
     */
    public Color getColor()
    {
        return this.color;
    }

    /**
     * Set the color of the rectangle drawn when the image cannot be drawn. The image may not be drawn because it has
     * not been loaded, or because no image has been set.
     *
     * @param defaultColor New color for the default rectangle.
     */
    public void setColor(Color defaultColor)
    {
        this.color = defaultColor;
    }

    /**
     * Sets the opacity of the surface. A value of 1 or greater means the surface is fully opaque, a value of 0 means
     * that the surface is fully transparent.
     *
     * @param opacity a positive value indicating the opacity of the surface.
     *
     * @throws IllegalArgumentException if the specified opacity is less than zero.
     */
    public void setOpacity(double opacity)
    {
        if (opacity < 0)
        {
            String message = Logging.getMessage("generic.OpacityOutOfRange", opacity);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.opacity = opacity;
    }

    /**
     * Returns the width of the source image after dynamic scaling has been applied. If no image has been specified, but
     * a dimension has been specified, the width of the dimension is returned.
     *
     * @param dc the current draw context.
     *
     * @return the source image width after scaling.
     *
     * @see #getSize()
     */
    public int getImageWidth(DrawContext dc)
    {
        if (dc == null)
        {
            String msg = Logging.getMessage("nullValue.DrawContextIsNull");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }
        this.computeOffsets(dc);
        return this.width;
    }

    /**
     * Returns the height of the image after dynamic scaling has been applied. If no image has been specified, but a
     * dimension has been specified, the height of the dimension is returned.
     *
     * @param dc the current draw context.
     *
     * @return the source image height after scaling.
     *
     * @see #getSize()
     */
    public int getImageHeight(DrawContext dc)
    {
        if (dc == null)
        {
            String msg = Logging.getMessage("nullValue.DrawContextIsNull");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }
        this.computeOffsets(dc);
        return this.height;
    }

    /**
     * Indicates the object included in {@link gov.nasa.worldwind.event.SelectEvent}s when this object is picked.
     *
     * @return the object identified as the picked object.
     */
    public Object getDelegateOwner()
    {
        return delegateOwner;
    }

    /**
     * Specify the object to identify as the picked object when this shape is picked.
     *
     * @param delegateOwner the object included in {@link gov.nasa.worldwind.event.SelectEvent}s as the picked object.
     */
    public void setDelegateOwner(Object delegateOwner)
    {
        this.delegateOwner = delegateOwner;
    }

    /**
     * Compute the image size, rotation, and position based on the current viewport size. This method updates the
     * calculated values for screen point, rotation point, width, and height. The calculation is not performed if the
     * values have already been calculated for this frame.
     *
     * @param dc DrawContext into which the image will be rendered.
     */
    protected void computeOffsets(DrawContext dc)
    {
        if (dc.getFrameTimeStamp() != this.frameNumber)
        {
            final BasicWWTexture texture = this.getTexture();

            final int viewportWidth = dc.getView().getViewport().width;
            final int viewportHeight = dc.getView().getViewport().height;

            // Compute image size
            if (texture != null)
            {
                this.originalImageWidth = texture.getWidth(dc);
                this.originalImageHeight = texture.getHeight(dc);
            }
            else if (this.getImageSource() == null) // If no image source is set, draw a rectangle
            {
                this.originalImageWidth = 1;
                this.originalImageHeight = 1;
            }
            else // If an image source is set, but the image is not available yet, don't draw anything
            {
                this.frameNumber = dc.getFrameTimeStamp();
                return;
            }

            if (this.size != null)
            {
                Dimension d = this.size.compute(this.originalImageWidth, this.originalImageHeight,
                    viewportWidth, viewportHeight);
                this.width = d.width;
                this.height = d.height;
            }
            else
            {
                this.width = this.originalImageWidth;
                this.height = this.originalImageHeight;
            }

            // Compute rotation
            Offset rotationOffset = this.getRotationOffset();

            // If no rotation offset is set, rotate around the center of the image.
            if (rotationOffset != null)
            {
                // The KML specification according to both OGC and Google states that the rotation point is specified in
                // a coordinate system with the origin at the lower left corner of the screen (0.5, 0.5 is the center
                // of the screen). But Google Earth interprets the point in a coordinate system with origin at the lower
                // left corner of the image (0.5, 0.5 is the center of the image), so we'll do that too.
                Point.Double pointD = rotationOffset.computeOffset(this.width, this.height, null, null);
                rotationPoint = new Point((int) pointD.x, (int) pointD.y);
            }
            else
            {
                this.rotationPoint = new Point(this.width, this.height);
            }

            // Compute position
            if (this.screenOffset != null)
            {
                // Compute the screen location in OpenGL coordinates. There is no need to convert from AWT to OpenGL
                // coordinates because the Offset is already in OpenGL coordinates with its origin in the lower-left
                // corner.
                Point.Double pointD = this.screenOffset.computeOffset(viewportWidth, viewportHeight, null, null);
                this.screenLocation = new Point((int) pointD.x, (int) (pointD.y));
            }
            else
            {
                this.screenLocation = new Point(viewportWidth / 2, viewportHeight / 2);
            }

            // Convert the screen location from OpenGL to AWT coordinates and store the result in awtScreenLocation. The
            // awtScreenLocation property is used in getScreenLocation to indicate the screen location in AWT
            // coordinates.
            this.awtScreenLocation = new Point(this.screenLocation.x, viewportHeight - this.screenLocation.y);

            Point.Double overlayPoint;
            if (this.imageOffset != null)
                overlayPoint = this.imageOffset.computeOffset(this.width, this.height, null, null);
            else
                overlayPoint = new Point.Double(this.originalImageWidth / 2.0, this.originalImageHeight / 2.0);

            this.dx = -overlayPoint.x;
            this.dy = -overlayPoint.y;

            this.frameNumber = dc.getFrameTimeStamp();
        }
    }

    /**
     * Get the texture for this image. The texture is loaded on a background thread. This method will return null until
     * the texture has been loaded.
     *
     * @return The texture or null if the texture is not yet available.
     */
    protected BasicWWTexture getTexture()
    {
        if (this.texture != null)
            return this.texture;
        else
            return this.initializeTexture();
    }

    public void render(DrawContext dc)
    {
        this.computeOffsets(dc);
        this.doRender(dc);
    }

    @SuppressWarnings({"UnusedParameters"})
    public void pick(DrawContext dc, Point pickPoint)
    {
        this.doRender(dc);
    }

    protected void doRender(DrawContext dc)
    {
        if (dc.isPickingMode())
            this.pickLayer = dc.getCurrentLayer();

        dc.addOrderedRenderable(this.orderedImage);
    }

    protected void draw(DrawContext dc)
    {
        if (this.screenLocation == null)
            return;

        GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.

        boolean attribsPushed = false;
        boolean modelviewPushed = false;
        boolean projectionPushed = false;

        try
        {
            gl.glPushAttrib(GL2.GL_DEPTH_BUFFER_BIT
                | GL2.GL_COLOR_BUFFER_BIT
                | GL2.GL_ENABLE_BIT
                | GL2.GL_TRANSFORM_BIT
                | GL2.GL_VIEWPORT_BIT
                | GL2.GL_CURRENT_BIT);
            attribsPushed = true;

            // Don't depth buffer.
            gl.glDisable(GL.GL_DEPTH_TEST);

            // Suppress any fully transparent image pixels
            gl.glEnable(GL2.GL_ALPHA_TEST);
            gl.glAlphaFunc(GL2.GL_GREATER, 0.001f);

            java.awt.Rectangle viewport = dc.getView().getViewport();
            gl.glMatrixMode(GL2.GL_PROJECTION);
            gl.glPushMatrix();
            projectionPushed = true;
            gl.glLoadIdentity();
            gl.glOrtho(0d, viewport.width, 0d, viewport.height, -1, 1);

            gl.glMatrixMode(GL2.GL_MODELVIEW);
            gl.glPushMatrix();
            modelviewPushed = true;
            gl.glLoadIdentity();

            // Apply the screen location transform. The screen location is in OpenGL coordinates with the origin in the
            // lower-left corner, so there is no need to translate from AWT to OpenGL coordinates here.
            gl.glTranslated(this.screenLocation.x + this.dx, this.screenLocation.y + this.dy, 0d);

            Double rotation = this.getRotation();
            if (rotation != null)
            {
                gl.glTranslated(rotationPoint.x, rotationPoint.y, 0);
                gl.glRotated(rotation, 0, 0, 1);
                gl.glTranslated(-rotationPoint.x, -rotationPoint.y, 0);
            }

            double xscale = (double) this.getImageWidth(dc) / originalImageWidth;
            double yscale = (double) this.getImageHeight(dc) / originalImageHeight;

            if (!dc.isPickingMode())
            {
                // Draw either an image or a filled rectangle
                boolean drawImage = this.getTexture() != null;

                gl.glEnable(GL.GL_TEXTURE_2D);
                if (drawImage)
                {
                    if (this.getTexture().bind(dc))
                        gl.glColor4d(1d, 1d, 1d, this.opacity);
                    else
                        drawImage = false; // Can't bind texture, draw rectangle instead
                }

                gl.glEnable(GL.GL_BLEND);
                gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);

                if (drawImage)
                {
                    TextureCoords texCoords = this.getTexture().getTexCoords();
                    gl.glScaled(xscale * this.originalImageWidth, yscale * this.originalImageHeight, 1d);
                    dc.drawUnitQuad(texCoords);
                    gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
                }
                else
                {
                    // Set color of the rectangle that will be drawn instead of an image
                    final Color color = this.getColor();
                    float[] colorRGB = color.getRGBColorComponents(null);
                    gl.glColor4d(colorRGB[0], colorRGB[1], colorRGB[2], (double) color.getAlpha() / 255);

                    // Don't have texture, just draw a rectangle
                    gl.glScaled(xscale, yscale, 1d);
                    dc.drawUnitQuad();
                }
            }
            else
            {
                this.pickSupport.clearPickList();
                this.pickSupport.beginPicking(dc);
                Color color = dc.getUniquePickColor();
                int colorCode = color.getRGB();
                this.pickSupport.addPickableObject(colorCode, this.delegateOwner != null ? this.delegateOwner : this,
                    null, false);
                gl.glColor3ub((byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue());
                gl.glScaled(xscale * this.originalImageWidth, yscale * this.originalImageHeight, 1d);
                dc.drawUnitQuad();
                this.pickSupport.endPicking(dc);
                this.pickSupport.resolvePick(dc, dc.getPickPoint(), this.pickLayer);
            }
        }
        finally
        {
            if (projectionPushed)
            {
                gl.glMatrixMode(GL2.GL_PROJECTION);
                gl.glPopMatrix();
            }
            if (modelviewPushed)
            {
                gl.glMatrixMode(GL2.GL_MODELVIEW);
                gl.glPopMatrix();
            }
            if (attribsPushed)
                gl.glPopAttrib();
        }
    }

    /** {@inheritDoc} */
    public String isExportFormatSupported(String format)
    {
        if (KMLConstants.KML_MIME_TYPE.equalsIgnoreCase(format))
            return Exportable.FORMAT_SUPPORTED;
        else
            return Exportable.FORMAT_NOT_SUPPORTED;
    }

    /**
     * Export the screen image. The {@code output} object will receive the exported data. The type of this object
     * depends on the export format. The formats and object types supported by this class are:
     * 

*

     * Format                                         Supported output object types
     * ================================================================================
     * KML (application/vnd.google-earth.kml+xml)     java.io.Writer
     *                                                java.io.OutputStream
     *                                                javax.xml.stream.XMLStreamWriter
     * 
* * @param mimeType MIME type of desired export format. * @param output An object that will receive the exported data. The type of this object depends on the export * format (see above). * * @throws java.io.IOException If an exception occurs writing to the output object. */ public void export(String mimeType, Object output) throws IOException { if (mimeType == null) { String message = Logging.getMessage("nullValue.Format"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (output == null) { String message = Logging.getMessage("nullValue.OutputBufferIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (KMLConstants.KML_MIME_TYPE.equalsIgnoreCase(mimeType)) { try { exportAsKML(output); } catch (XMLStreamException e) { Logging.logger().throwing(getClass().getName(), "export", e); throw new IOException(e); } } else { String message = Logging.getMessage("Export.UnsupportedFormat", mimeType); Logging.logger().warning(message); throw new UnsupportedOperationException(message); } } /** * Export the screen image to KML as a {@code } element. The {@code output} object will receive the * data. This object must be one of: java.io.Writer java.io.OutputStream javax.xml.stream.XMLStreamWriter. *

* The image path can only be exported if the image source is a path or URL. If the image source is a BufferedImage, * for example, the image will not be exported and no icon reference will be written into the ScreenOverlay tag. * * @param output Object to receive the generated KML. * * @throws XMLStreamException If an exception occurs while writing the KML * @throws IOException if an exception occurs while exporting the data. * @see #export(String, Object) */ protected void exportAsKML(Object output) throws IOException, XMLStreamException { XMLStreamWriter xmlWriter = null; XMLOutputFactory factory = XMLOutputFactory.newInstance(); boolean closeWriterWhenFinished = true; if (output instanceof XMLStreamWriter) { xmlWriter = (XMLStreamWriter) output; closeWriterWhenFinished = false; } else if (output instanceof Writer) { xmlWriter = factory.createXMLStreamWriter((Writer) output); } else if (output instanceof OutputStream) { xmlWriter = factory.createXMLStreamWriter((OutputStream) output); } if (xmlWriter == null) { String message = Logging.getMessage("Export.UnsupportedOutputObject"); Logging.logger().warning(message); throw new IllegalArgumentException(message); } xmlWriter.writeStartElement("ScreenOverlay"); xmlWriter.writeStartElement("visibility"); xmlWriter.writeCharacters("1"); xmlWriter.writeEndElement(); String imgSrcString = null; Object imageSource = this.getImageSource(); if (imageSource instanceof String) imgSrcString = (String) imageSource; else if (imageSource instanceof URL) imgSrcString = imageSource.toString(); // We can only export a link to the image if the image source is a path or URL. if (imgSrcString != null) { xmlWriter.writeStartElement("Icon"); xmlWriter.writeStartElement("href"); xmlWriter.writeCharacters(imgSrcString); xmlWriter.writeEndElement(); // href xmlWriter.writeEndElement(); // Icon } else { // No image string, try to export the color Color color = this.getColor(); if (color != null) { xmlWriter.writeStartElement("color"); xmlWriter.writeCharacters(KMLExportUtil.stripHexPrefix(WWUtil.encodeColorABGR(color))); xmlWriter.writeEndElement(); } } KMLExportUtil.exportOffset(xmlWriter, this.getImageOffset(), "overlayXY"); KMLExportUtil.exportOffset(xmlWriter, this.getScreenOffset(), "screenXY"); Double rotation = this.getRotation(); if (rotation != null) { xmlWriter.writeStartElement("rotation"); xmlWriter.writeCharacters(rotation.toString()); xmlWriter.writeEndElement(); // rotation } KMLExportUtil.exportOffset(xmlWriter, this.getRotationOffset(), "rotationXY"); KMLExportUtil.exportDimension(xmlWriter, this.getSize(), "size"); xmlWriter.writeEndElement(); // ScreenOverlay xmlWriter.flush(); if (closeWriterWhenFinished) xmlWriter.close(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy