src.gov.nasa.worldwind.ogc.kml.KMLAbstractFeature Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of worldwindx Show documentation
Show all versions of worldwindx Show documentation
World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.
/*
* 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.ogc.kml;
import gov.nasa.worldwind.event.Message;
import gov.nasa.worldwind.ogc.kml.impl.*;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.*;
import gov.nasa.worldwind.util.xml.XMLEventParserContext;
import gov.nasa.worldwind.util.xml.atom.*;
import gov.nasa.worldwind.util.xml.xal.XALAddressDetails;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import java.util.*;
/**
* Represents the KML Feature element and provides access to its contents.
*
* KMLAbstractFeature
implements the KMLRenderable
interface, but does not actually render
* anything. Subclasses should override the methods {@link #doPreRender(gov.nasa.worldwind.ogc.kml.impl.KMLTraversalContext,
* gov.nasa.worldwind.render.DrawContext)}
and {@link #doRender(gov.nasa.worldwind.ogc.kml.impl.KMLTraversalContext,
* gov.nasa.worldwind.render.DrawContext)}
to render their contents. If the visibility
property is
* set to false
, this does not call doPreRender
and doRender
during rendering.
*
* @author tag
* @version $Id: KMLAbstractFeature.java 1171 2013-02-11 21:45:02Z dcollins $
*/
public abstract class KMLAbstractFeature extends KMLAbstractObject implements KMLRenderable
{
/** The style selectors specified in the KML Feature element. Is empty if no selectors were specified. */
protected List styleSelectors = new ArrayList();
/**
* The visibility flag for the feature. This field is determined from the visibility element of the KML feature
* initially, but the client may set it directly, in which case it may then differ from the visibility field in the
* fields
table.
*/
protected Boolean visibility; // may be different from the visibility field if application has set it explicitly
/** The region specified in the KML Feature element. Is null if no region was specified. */
protected KMLRegion region;
/** A balloon explicitly associated with this feature by the client. This is not a KML field of the Feature element. */
protected Balloon balloon; // not a KML schema field, merely a convenience field of this class
/**
* Construct an instance.
*
* @param namespaceURI the qualifying namespace URI. May be null to indicate no namespace qualification.
*/
protected KMLAbstractFeature(String namespaceURI)
{
super(namespaceURI);
}
@Override
protected void doAddEventContent(Object o, XMLEventParserContext ctx, XMLEvent event, Object... args)
throws XMLStreamException
{
if (o instanceof KMLAbstractView)
this.setView((KMLAbstractView) o);
else if (o instanceof KMLAbstractTimePrimitive)
this.setTimePrimitive((KMLAbstractTimePrimitive) o);
else if (o instanceof KMLAbstractStyleSelector)
this.addStyleSelector((KMLAbstractStyleSelector) o);
else if (o instanceof KMLRegion)
this.setRegion((KMLRegion) o);
else if (o instanceof Boolean && event.asStartElement().getName().getLocalPart().equalsIgnoreCase("visibility"))
this.setVisibility((Boolean) o);
else
super.doAddEventContent(o, ctx, event, args);
}
public String getName()
{
return (String) this.getField("name");
}
/**
* Indicates whether this KMLAbstractFeature
is enabled for rendering. This returns null
* if no visibility is specified. This indicates the default visibility of true
should be used.
*
* @return true
or null
to draw feature shape, otherwise false
. The default
* value is true
.
*
* @see #setVisibility(Boolean)
*/
public Boolean getVisibility()
{
return this.visibility;
}
/**
* Specifies whether this KMLAbstractFeature
is enabled for rendering.
*
* @param visibility true
or null
to draw this feature, otherwise false
. The
* default value is true
.
*
* @see #getVisibility()
*/
public void setVisibility(Boolean visibility)
{
this.visibility = visibility;
}
public Boolean getOpen()
{
return (Boolean) this.getField("open");
}
public AtomPerson getAuthor()
{
return (AtomPerson) this.getField("author");
}
public AtomLink getLink()
{
return (AtomLink) this.getField("link");
}
public String getAddress()
{
return (String) this.getField("address");
}
public XALAddressDetails getAddressDetails()
{
return (XALAddressDetails) this.getField("AddressDetails");
}
public String getPhoneNumber()
{
return (String) this.getField("phoneNumber");
}
public Object getSnippet()
{
Object o = this.getField("snippet");
if (o != null)
return o;
return this.getField("Snippet");
}
public String getSnippetText()
{
Object o = this.getField("snippet");
if (o != null)
return ((String) o).trim();
KMLSnippet snippet = (KMLSnippet) this.getSnippet();
if (snippet != null && snippet.getCharacters() != null)
return snippet.getCharacters().trim(); // trim because string parser might not have parsed it
return null;
}
public String getDescription()
{
return (String) this.getField("description");
}
protected void setView(KMLAbstractView o)
{
this.setField("AbstractView", o);
}
public KMLAbstractView getView()
{
return (KMLAbstractView) this.getField("AbstractView");
}
protected void setTimePrimitive(KMLAbstractTimePrimitive o)
{
this.setField("AbstractTimePrimitive", o);
}
public KMLAbstractTimePrimitive getTimePrimitive()
{
return (KMLAbstractTimePrimitive) this.getField("AbstractTimePrimitive");
}
public KMLStyleUrl getStyleUrl()
{
return (KMLStyleUrl) this.getField("styleUrl");
}
protected void addStyleSelector(KMLAbstractStyleSelector o)
{
this.styleSelectors.add(o);
}
public List getStyleSelectors()
{
return this.styleSelectors;
}
public boolean hasStyleSelectors()
{
return this.getStyleSelectors() != null && this.getStyleSelectors().size() > 0;
}
public boolean hasStyle()
{
return this.hasStyleSelectors() || this.getStyleUrl() != null;
}
public KMLRegion getRegion()
{
return this.region;
}
protected void setRegion(KMLRegion region)
{
this.region = region;
}
public KMLExtendedData getExtendedData()
{
return (KMLExtendedData) this.getField("ExtendedData");
}
/**
* Set the balloon associated with this feature.
*
* Note: Balloon is not a field in the KML Feature element. It's a direct field of this class and enables the client
* to associate a balloon with the feature.
*
* @param balloon New balloon.
*/
public void setBalloon(Balloon balloon)
{
this.balloon = balloon;
}
/**
* Get the balloon associated with this feature, if any.
*
* @return The balloon associated with the feature. Returns null if there is no associated balloon.
*/
public Balloon getBalloon()
{
return this.balloon;
}
/** {@inheritDoc} */
public void preRender(KMLTraversalContext tc, DrawContext dc)
{
if (tc == null)
{
String message = Logging.getMessage("nullValue.TraversalContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (!this.isFeatureActive(tc, dc))
return;
this.doPreRender(tc, dc);
}
/** {@inheritDoc} */
public void render(KMLTraversalContext tc, DrawContext dc)
{
if (tc == null)
{
String message = Logging.getMessage("nullValue.TraversalContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (!this.isFeatureActive(tc, dc))
return;
this.doRender(tc, dc);
}
/**
* Indicates whether this KMLAbstractFeature
is active and should be rendered on the specified
* DrawContext
. This returns true
if the following conditions are all true
:
*
* - This feature's
visibility
is unspecified (null
) or is set to
* true
. - This feature as no Region and does not inherit a Region from an ancestor, or its
* Region is active for the specified
DrawContext
.
*
* If this feature has no Region, this inherits the Region of its nearest ancestor by using the Region on the top of
* the KML traversal context's region stack (if any). If there is no ancestor Region this feature is assumed to be
* the DrawContext's view and is rendered according to its visibility
flag. A Region is
* considered active if it is visible, and the DrawContext
meets the Region's level of detail
* criteria.
*
* @param tc the current KML traversal context. Specifies an inherited Region (if any) and a detail hint.
* @param dc the current draw context. Used to determine whether this feature's Region is active.
*
* @return true
if this feature should be rendered, otherwise false
.
*/
protected boolean isFeatureActive(KMLTraversalContext tc, DrawContext dc)
{
if (this.getVisibility() != null && !this.getVisibility())
return false;
KMLRegion region = this.getRegion();
if (region == null)
region = tc.peekRegion();
return region == null || region.isActive(tc, dc);
}
/**
* Called from preRender
if this KMLAbstractFeature
's visibility
is not set
* to false
. Subclasses should override this method to pre-render their content.
*
* @param tc the current KML traversal context.
* @param dc the current draw context.
*/
protected void doPreRender(KMLTraversalContext tc, DrawContext dc)
{
// Subclasses override to implement render behavior.
}
/**
* Called from render
if this KMLAbstractFeature
's visibility
is not set to
* false
. Subclasses should override this method to render their content.
*
* @param tc the current KML traversal context.
* @param dc the current draw context.
*/
protected void doRender(KMLTraversalContext tc, DrawContext dc)
{
// Subclasses override to implement render behavior.
}
/**
* Draws the {@link gov.nasa.worldwind.render.Balloon}
associated with this KML feature. This does
* nothing if there is no Balloon
associated with this feature.
*
* @param tc the current KML traversal context.
* @param dc the current draw context.
*/
@SuppressWarnings({"UnusedDeclaration"})
protected void renderBalloon(KMLTraversalContext tc, DrawContext dc)
{
if (this.getBalloon() != null)
this.getBalloon().render(dc);
}
/**
* Obtains the effective values for a specified sub-style (IconStyle, ListStyle, etc.) and state
* (normal or highlight). The returned style is the result of merging values from this feature
* instance's style selectors and its styleUrl, if any, with precedence given to style selectors.
*
* A remote styleUrl that has not yet been resolved is not included in the result. In this case the returned
* sub-style is marked with the value {@link gov.nasa.worldwind.avlist.AVKey#UNRESOLVED}. The same is true when a
* StyleMap style selector contains a reference to an external Style and that reference has not been resolved.
*
* @param styleState the style mode, either \"normal\" or \"highlight\".
* @param subStyle an instance of the sub-style desired, such as {@link gov.nasa.worldwind.ogc.kml.KMLIconStyle}.
* The effective sub-style values are accumulated and merged into this instance. The instance
* should not be one from within the KML document because its values are overridden and augmented;
* it's just an independent variable in which to return the merged attribute values. For
* convenience, the instance specified is returned as the return value of this method.
*
* @return the sub-style values for the specified type and state. The reference returned is the one passed in as the
* subStyle
argument.
*/
public KMLAbstractSubStyle getSubStyle(KMLAbstractSubStyle subStyle, String styleState)
{
return KMLAbstractStyleSelector.mergeSubStyles(this.getStyleUrl(), this.getStyleSelectors(), styleState,
subStyle);
}
@Override
public void applyChange(KMLAbstractObject sourceValues)
{
if (!(sourceValues instanceof KMLAbstractFeature))
{
String message = Logging.getMessage("KML.InvalidElementType", sourceValues.getClass().getName());
Logging.logger().warning(message);
throw new IllegalArgumentException(message);
}
super.applyChange(sourceValues);
KMLAbstractFeature sourceFeature = (KMLAbstractFeature) sourceValues;
if (sourceValues.hasField("visibility"))
this.setVisibility((Boolean) sourceFeature.getField("visibility"));
if (sourceFeature.getRegion() != null)
this.setRegion(sourceFeature.getRegion());
if (sourceFeature.getStyleSelectors() != null && sourceFeature.getStyleSelectors().size() > 0)
{
this.mergeStyleSelectors(sourceFeature);
this.onChange(new Message(KMLAbstractObject.MSG_STYLE_CHANGED, this));
}
}
/**
* Merge a list of incoming style selectors with the current list. If an incoming selector has the same ID as an
* existing one, replace the existing one, otherwise just add the incoming one.
*
* @param sourceFeature the incoming style selectors.
*/
protected void mergeStyleSelectors(KMLAbstractFeature sourceFeature)
{
// Make a copy of the existing list so we can modify it as we traverse the copy.
List styleSelectorsCopy =
new ArrayList(this.getStyleSelectors().size());
styleSelectorsCopy.addAll(this.getStyleSelectors());
for (KMLAbstractStyleSelector sourceSelector : sourceFeature.getStyleSelectors())
{
String id = sourceSelector.getId();
if (!WWUtil.isEmpty(id))
{
for (KMLAbstractStyleSelector existingSelector : styleSelectorsCopy)
{
String currentId = existingSelector.getId();
if (!WWUtil.isEmpty(currentId) && currentId.equals(id))
{
this.getStyleSelectors().remove(existingSelector);
}
}
}
this.getStyleSelectors().add(sourceSelector);
}
}
}