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

gov.nasa.worldwind.symbology.AbstractTacticalSymbol 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.symbology;

import com.jogamp.opengl.util.texture.*;
import com.jogamp.opengl.util.texture.awt.AWTTextureIO;
import gov.nasa.worldwind.*;
import gov.nasa.worldwind.avlist.*;
import gov.nasa.worldwind.drag.*;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.layers.Layer;
import gov.nasa.worldwind.pick.*;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.*;

import com.jogamp.opengl.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;
import java.util.List;

/**
 * @author dcollins
 * @version $Id: AbstractTacticalSymbol.java 2366 2014-10-02 23:16:31Z tgaskins $
 */
public abstract class AbstractTacticalSymbol extends WWObjectImpl implements TacticalSymbol, Movable, Draggable
{
    protected static class IconSource
    {
        protected IconRetriever retriever;
        protected String symbolId;
        protected AVList retrieverParams;

        public IconSource(IconRetriever retriever, String symbolId, AVList retrieverParams)
        {
            this.retriever = retriever;
            this.symbolId = symbolId;

            if (retrieverParams != null)
            {
                // If the specified parameters are non-null, then store a copy of the parameters in this key's params
                // property to insulate it from changes made by the caller. This params list must not change after
                // construction this key's properties must be immutable.
                this.retrieverParams = new AVListImpl();
                this.retrieverParams.setValues(retrieverParams);
            }
        }

        public IconRetriever getRetriever()
        {
            return this.retriever;
        }

        public String getSymbolId()
        {
            return this.symbolId;
        }

        public AVList getRetrieverParams()
        {
            return this.retrieverParams;
        }

        @Override
        public boolean equals(Object o)
        {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;

            IconSource that = (IconSource) o;

            if (this.retriever != null ? !this.retriever.equals(that.retriever)
                : that.retriever != null)
                return false;
            if (this.symbolId != null ? !this.symbolId.equals(that.symbolId) : that.symbolId != null)
                return false;

            if (this.retrieverParams != null && that.retrieverParams != null)
            {
                Set> theseEntries = this.retrieverParams.getEntries();
                Set> thoseEntries = that.retrieverParams.getEntries();

                return theseEntries.equals(thoseEntries);
            }
            return (this.retrieverParams == null && that.retrieverParams == null);
        }

        @Override
        public int hashCode()
        {
            int result = this.retriever != null ? this.retriever.hashCode() : 0;
            result = 31 * result + (this.symbolId != null ? this.symbolId.hashCode() : 0);
            result = 31 * result + (this.retrieverParams != null ? this.retrieverParams.getEntries().hashCode() : 0);
            return result;
        }

        @Override
        public String toString()
        {
            return this.symbolId;
        }
    }

    // Use an IconKey as the texture's image source. The image source is what defines the contents of this texture,
    // and is used as an address for the texture's contents in the cache.
    protected static class IconTexture extends LazilyLoadedTexture
    {
        public IconTexture(IconSource imageSource)
        {
            super(imageSource);
        }

        public IconTexture(IconSource imageSource, boolean useMipMaps)
        {
            super(imageSource, useMipMaps);
        }

        protected boolean loadTextureData()
        {
            TextureData td = this.createIconTextureData();

            if (td != null)
                this.setTextureData(td);

            return td != null;
        }

        protected TextureData createIconTextureData()
        {
            try
            {
                IconSource source = (IconSource) this.getImageSource();
                BufferedImage image = source.getRetriever().createIcon(source.getSymbolId(),
                    source.getRetrieverParams());

                if (image == null)
                {
                    // IconRetriever returns null if the symbol identifier is not recognized, or if the parameter list
                    // specified an empty icon. In either case, we mark the texture initialization as having failed to
                    // suppress  any further requests.
                    this.textureInitializationFailed = true;
                    return null;
                }

                return AWTTextureIO.newTextureData(Configuration.getMaxCompatibleGLProfile(), image,
                    this.isUseMipMaps());
            }
            catch (Exception e)
            {
                String msg = Logging.getMessage("Symbology.ExceptionRetrievingTacticalIcon", this.getImageSource());
                Logging.logger().log(java.util.logging.Level.SEVERE, msg, e);
                this.textureInitializationFailed = true; // Suppress subsequent requests for this tactical icon.
                return null;
            }
        }

        @Override
        protected Runnable createRequestTask()
        {
            return new IconRequestTask(this);
        }

        protected static class IconRequestTask implements Runnable
        {
            protected final IconTexture texture;

            protected IconRequestTask(IconTexture texture)
            {
                if (texture == null)
                {
                    String message = Logging.getMessage("nullValue.TextureIsNull");
                    Logging.logger().severe(message);
                    throw new IllegalArgumentException(message);
                }

                this.texture = texture;
            }

            public void run()
            {
                if (Thread.currentThread().isInterrupted())
                    return; // the task was cancelled because it's a duplicate or for some other reason

                if (this.texture.loadTextureData())
                    this.texture.notifyTextureLoaded();
            }

            public boolean equals(Object o)
            {
                if (this == o)
                    return true;
                if (o == null || getClass() != o.getClass())
                    return false;

                final IconRequestTask that = (IconRequestTask) o;
                return this.texture != null ? this.texture.equals(that.texture) : that.texture == null;
            }

            public int hashCode()
            {
                return (this.texture != null ? this.texture.hashCode() : 0);
            }

            public String toString()
            {
                return this.texture.getImageSource().toString();
            }
        }
    }

    protected static class IconAtlasElement extends TextureAtlasElement
    {
        protected Point point;
        /** Indicates the last time, in milliseconds, the element was requested or added. */
        protected long lastUsed = System.currentTimeMillis();

        public IconAtlasElement(TextureAtlas atlas, IconSource source)
        {
            super(atlas, source);
        }

        public Point getPoint()
        {
            return this.point;
        }

        public void setPoint(Point point)
        {
            this.point = point;
        }

        @Override
        protected boolean loadImage()
        {
            BufferedImage image = this.createModifierImage();

            if (image != null)
                this.setImage(image);

            return image != null;
        }

        protected BufferedImage createModifierImage()
        {
            try
            {
                IconSource source = (IconSource) this.getImageSource();
                BufferedImage image = source.getRetriever().createIcon(source.getSymbolId(),
                    source.getRetrieverParams());

                if (image == null)
                {
                    // ModifierRetriever returns null if the modifier or its value is not recognized. In either case, we
                    // mark the image initialization as having failed to suppress any further requests.
                    this.imageInitializationFailed = true;
                    return null;
                }

                return image;
            }
            catch (Exception e)
            {
                String msg = Logging.getMessage("Symbology.ExceptionRetrievingGraphicModifier",
                    this.getImageSource());
                Logging.logger().log(java.util.logging.Level.SEVERE, msg, e);
                this.imageInitializationFailed = true; // Suppress subsequent requests for this modifier.
                return null;
            }
        }
    }

    protected static class Label
    {
        protected String text;
        protected Point point;
        protected Font font;
        protected Color color;

        public Label(String text, Point point, Font font, Color color)
        {
            if (text == null)
            {
                String msg = Logging.getMessage("nullValue.StringIsNull");
                Logging.logger().severe(msg);
                throw new IllegalArgumentException(msg);
            }

            if (point == null)
            {
                String msg = Logging.getMessage("nullValue.PointIsNull");
                Logging.logger().severe(msg);
                throw new IllegalArgumentException(msg);
            }

            if (font == null)
            {
                String msg = Logging.getMessage("nullValue.FontIsNull");
                Logging.logger().severe(msg);
                throw new IllegalArgumentException(msg);
            }

            if (color == null)
            {
                String msg = Logging.getMessage("nullValue.ColorIsNull");
                Logging.logger().severe(msg);
                throw new IllegalArgumentException(msg);
            }

            this.text = text;
            this.point = point;
            this.font = font;
            this.color = color;
        }

        public String getText()
        {
            return this.text;
        }

        public Point getPoint()
        {
            return this.point;
        }

        public Font getFont()
        {
            return this.font;
        }

        public Color getColor()
        {
            return this.color;
        }
    }

    protected static class Line
    {
        protected Iterable points;

        public Line()
        {
        }

        public Line(Iterable points)
        {
            if (points == null)
            {
                String msg = Logging.getMessage("nullValue.IterableIsNull");
                Logging.logger().severe(msg);
                throw new IllegalArgumentException(msg);
            }

            this.points = points;
        }

        public Iterable getPoints()
        {
            return points;
        }

        public void setPoints(Iterable points)
        {
            this.points = points;
        }
    }

    protected class OrderedSymbol implements OrderedRenderable
    {
        /**
         * Per-frame Cartesian point corresponding to this symbol's position. Calculated each frame in {@link
         * gov.nasa.worldwind.symbology.AbstractTacticalSymbol#computeSymbolPoints(gov.nasa.worldwind.render.DrawContext,
         * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}. Initially null.
         */
        public Vec4 placePoint;
        /**
         * Per-frame screen point corresponding to the projection of the placePoint in the viewport (on the screen).
         * Calculated each frame in {@link gov.nasa.worldwind.symbology.AbstractTacticalSymbol#computeSymbolPoints(gov.nasa.worldwind.render.DrawContext,
         * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}. Initially null.
         */
        public Vec4 screenPoint;
        /**
         * Per-frame distance corresponding to the distance between the placePoint and the View's eye point. Used to
         * order the symbol as an ordered renderable, and is returned by getDistanceFromEye. Calculated each frame in
         * {@link gov.nasa.worldwind.symbology.AbstractTacticalSymbol#computeSymbolPoints(gov.nasa.worldwind.render.DrawContext,
         * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}. Initially 0.
         */
        public double eyeDistance;
        /**
         * Per-frame screen scale indicating this symbol's x-scale relative to the screen offset. Calculated each frame
         * in {@link #computeTransform(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}.
         * Initially 0.
         */
        public double sx;
        /**
         * Per-frame screen scale indicating this symbol's y-scale relative to the screen offset. Calculated each frame
         * in {@link #computeTransform(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}.
         * Initially 0.
         */
        public double sy;
        /**
         * Per-frame screen offset indicating this symbol's x-offset relative to the screenPoint. Calculated each frame
         * in {@link #computeTransform(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}.
         * Initially 0.
         */
        public double dx;
        /**
         * Per-frame screen offset indicating this symbol's y-offset relative to the screenPoint. Calculated each frame
         * in {@link #computeTransform(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}.
         * Initially 0.
         */
        public double dy;

        public Rectangle layoutRect;
        public Rectangle screenRect;

        /** iconRect with scaling applied, used to lay out text. */
        public Rectangle iconRectScaled;
        /** layoutRect with scaling applied, used to lay out text. */
        public Rectangle layoutRectScaled;

        @Override
        public double getDistanceFromEye()
        {
            return this.eyeDistance;
        }

        @Override
        public void pick(DrawContext dc, Point pickPoint)
        {
            AbstractTacticalSymbol.this.pick(dc, pickPoint, this);
        }

        @Override
        public void render(DrawContext dc)
        {
            AbstractTacticalSymbol.this.drawOrderedRenderable(dc, this);
        }

        public boolean isEnableBatchRendering()
        {
            return AbstractTacticalSymbol.this.isEnableBatchRendering();
        }

        protected void doDrawOrderedRenderable(DrawContext dc, PickSupport pickCandidates)
        {
            AbstractTacticalSymbol.this.doDrawOrderedRenderable(dc, pickCandidates, this);
        }

        public boolean isEnableBatchPicking()
        {
            return AbstractTacticalSymbol.this.isEnableBatchPicking();
        }

        public Layer getPickLayer()
        {
            return AbstractTacticalSymbol.this.pickLayer;
        }
    }

    /** Default unit format. */
    public static final UnitsFormat DEFAULT_UNITS_FORMAT = new UnitsFormat();

    /** The image file displayed while the icon is loading. */
    public static final String LOADING_IMAGE_PATH =
        Configuration.getStringValue("gov.nasa.worldwind.avkey.MilStd2525LoadingIconPath",
            "images/doc-loading-128x128.png");

    protected static final String LAYOUT_ABSOLUTE = "gov.nasa.worldwind.symbology.TacticalSymbol.LayoutAbsolute";
    protected static final String LAYOUT_RELATIVE = "gov.nasa.worldwind.symbology.TacticalSymbol.LayoutRelative";
    protected static final String LAYOUT_NONE = "gov.nasa.worldwind.symbology.TacticalSymbol.LayoutNone";
    /**
     * The default depth offset in device independent depth units: -8200. This value is configured to match the depth
     * offset produced by existing screen elements such as PointPlacemark. This value was determined empirically.
     */
    protected static final double DEFAULT_DEPTH_OFFSET = -8200;
    protected static final long DEFAULT_MAX_TIME_SINCE_LAST_USED = 10000;
    /**
     * The default glyph texture atlas. This texture atlas holds all glyph images loaded by calls to
     * layoutGlyphModifier. Initialized with initial dimensions of 1024x128 and maximum dimensions of
     * 2048x2048. Configured to remove the least recently used texture elements when more space is needed.
     */
    protected static final TextureAtlas DEFAULT_GLYPH_ATLAS = new TextureAtlas(1024, 128, 2048, 2048);
    /**
     * Maximum expected size of a symbol, used to estimate screen bounds for view frustum culling. This value is
     * configured a bit higher than a symbol is likely to be drawn in practice to err on the side of not culling a
     * symbol that is not visible, rather culling one that is visible.
     */
    protected static final int MAX_SYMBOL_DIMENSION = 256;
    /** The default number of label lines to expect when computing the minimum size of the text layout rectangle. */
    protected static final int DEFAULT_LABEL_LINES = 5;

    /** The attributes used if attributes are not specified. */
    protected static TacticalSymbolAttributes defaultAttrs;

    static
    {
        // Create and populate the default attributes.
        defaultAttrs = new BasicTacticalSymbolAttributes();
        defaultAttrs.setOpacity(BasicTacticalSymbolAttributes.DEFAULT_OPACITY);
        defaultAttrs.setScale(BasicTacticalSymbolAttributes.DEFAULT_SCALE);
        defaultAttrs.setTextModifierMaterial(BasicTacticalSymbolAttributes.DEFAULT_TEXT_MODIFIER_MATERIAL);

        // Configure the atlas to remove old texture elements that are likely no longer used to make room for new
        // modifiers when the atlas is full.
        DEFAULT_GLYPH_ATLAS.setEvictOldElements(true);
    }

    /**
     * Indicates whether this symbol is drawn when in view. true if this symbol is drawn when in view,
     * otherwise false. Initially true.
     */
    protected boolean visible = true;
    /**
     * Indicates whether this symbol is highlighted. true if this symbol is highlighted, otherwise
     * false. Initially false.
     */
    protected boolean highlighted;
    /**
     * Indicates this symbol's geographic position. See {@link #setPosition(gov.nasa.worldwind.geom.Position)} for a
     * description of how tactical symbols interpret their position. Must be non-null, and is initialized during
     * construction.
     */
    protected Position position;
    /**
     * Indicates this symbol's altitude mode. See {@link #setAltitudeMode(int)} for a description of the valid altitude
     * modes. Initially Worldwind.ABSOLUTE.
     */
    protected int altitudeMode = WorldWind.ABSOLUTE;
    /**
     * Indicates whether this symbol draws its supplemental graphic modifiers. true if this symbol draws
     * its graphic modifiers, otherwise false. Initially true.
     */
    protected boolean showGraphicModifiers = true;
    /**
     * Indicates whether this symbol draws its supplemental text modifiers. true if this symbol draws its
     * text modifiers, otherwise false. Initially true.
     */
    protected boolean showTextModifiers = true;

    /** Indicates an object to attach to the picked object list instead of this symbol. */
    protected Object delegateOwner;
    protected boolean enableBatchRendering = true;
    protected boolean enableBatchPicking = true;
    /** Indicates whether or not to display the implicit location modifier. */
    protected boolean showLocation = true;
    /** Indicates whether or not to display the implicit hostile indicator modifier. */
    protected boolean showHostileIndicator;
    /**
     * Indicates the current text and graphic modifiers assigned to this symbol. This list of key-value pairs contains
     * both the modifiers specified by the string identifier during construction, and those specified by calling {@link
     * #setModifier(String, Object)}. Initialized to a new AVListImpl, and populated during construction from values in
     * the string identifier and the modifiers list.
     */
    protected AVList modifiers = new AVListImpl();
    /**
     * Modifiers active this frame. This list is determined by copying {@link #modifiers}, and applying changings in
     * {@link #applyImplicitModifiers(gov.nasa.worldwind.avlist.AVList)}.
     */
    protected AVList activeModifiers = new AVListImpl();
    /**
     * Indicates this symbol's normal (as opposed to highlight) attributes. May be null, indicating that
     * the default attributes are used. Initially null.
     */
    protected TacticalSymbolAttributes normalAttrs;
    /**
     * Indicates this symbol's highlight attributes. May be null, indicating that the default attributes
     * are used. Initially null.
     */
    protected TacticalSymbolAttributes highlightAttrs;
    /**
     * Indicates this symbol's currently active attributes. Updated in {@link #determineActiveAttributes}. Initialized
     * to a new BasicTacticalSymbolAttributes.
     */
    protected TacticalSymbolAttributes activeAttrs = new BasicTacticalSymbolAttributes();
    protected Offset offset;
    protected Offset iconOffset;
    protected Size iconSize;
    protected Double depthOffset;
    protected IconRetriever iconRetriever;
    protected IconRetriever modifierRetriever;

    /**
     * The frame used to calculate this symbol's per-frame values. Set to the draw context's frame number each frame.
     * Initially -1.
     */
    protected long frameNumber = -1;
    protected OrderedSymbol thisFramesOrderedSymbol;

    protected Rectangle iconRect;

    /**
     * Screen rect computed from the icon and static modifiers. This rectangle is cached and only recomputed when the
     * icon or modifiers change.
     */
    protected Rectangle staticScreenRect;
    /**
     * Layout rect computed from the icon and static modifiers. This rectangle is cached and only recomputed when the
     * icon or modifiers change.
     */
    protected Rectangle staticLayoutRect;

    /** Indicates that one or more glyphs have not been resolved. */
    protected boolean unresolvedGlyph;

    protected List currentGlyphs = new ArrayList();
    protected List




© 2015 - 2024 Weber Informatics LLC | Privacy Policy