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

src.gov.nasa.worldwind.formats.vpf.VPFBasicSymbolFactory Maven / Gradle / Ivy

Go to download

World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.

There is a newer version: 2.0.0-986
Show 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.formats.vpf;

import gov.nasa.worldwind.avlist.AVList;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.*;

import java.awt.*;
import java.util.*;

/**
 * @author dcollins
 * @version $Id: VPFBasicSymbolFactory.java 1171 2013-02-11 21:45:02Z dcollins $
 */
public class VPFBasicSymbolFactory implements VPFSymbolFactory
{
    private static final double DEFAULT_ICON_MAX_SIZE = 10000; // Max 10km

    protected VPFPrimitiveData primitiveData;
    protected VPFFeatureFactory featureFactory;
    protected VPFSymbolSupport symbolSupport;

    public VPFBasicSymbolFactory(VPFTile tile, VPFPrimitiveData primitiveData)
    {
        this.primitiveData = primitiveData;
        this.featureFactory = new VPFBasicFeatureFactory(tile, primitiveData);
    }

    public VPFSymbolSupport getStyleSupport()
    {
        return this.symbolSupport;
    }

    public void setStyleSupport(VPFSymbolSupport symbolSupport)
    {
        this.symbolSupport = symbolSupport;
    }

    /**
     * @param featureClass
     *
     * @return
     */
    public Collection createPointSymbols(VPFFeatureClass featureClass)
    {
        if (featureClass == null)
        {
            String message = Logging.getMessage("nullValue.FeatureClassIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        FeatureMap map = this.createFeatureMap(featureClass);
        if (map == null)
            return null;

        ArrayList symbols = new ArrayList();
        this.doCreatePointSymbols(map, symbols);
        return symbols;
    }

    /**
     * @param featureClass
     *
     * @return
     */
    public Collection createLineSymbols(VPFFeatureClass featureClass)
    {
        if (featureClass == null)
        {
            String message = Logging.getMessage("nullValue.FeatureClassIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        FeatureMap map = this.createFeatureMap(featureClass);
        if (map == null)
            return null;

        ArrayList symbols = new ArrayList();
        this.doCreateLineSymbols(map, symbols);
        return symbols;
    }

    /**
     * @param featureClass
     *
     * @return
     */
    public Collection createAreaSymbols(VPFFeatureClass featureClass)
    {
        if (featureClass == null)
        {
            String message = Logging.getMessage("nullValue.FeatureClassIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        FeatureMap map = this.createFeatureMap(featureClass);
        if (map == null)
            return null;

        ArrayList symbols = new ArrayList();
        this.doCreateAreaSymbols(map, symbols);
        return symbols;
    }

    /**
     * @param featureClass
     *
     * @return
     */
    public Collection createTextSymbols(VPFFeatureClass featureClass)
    {
        if (featureClass == null)
        {
            String message = Logging.getMessage("nullValue.FeatureClassIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        FeatureMap map = this.createFeatureMap(featureClass);
        if (map == null)
            return null;

        ArrayList symbols = new ArrayList();
        this.doCreateTextSymbols(map, symbols);
        return symbols;
    }

    /**
     * @param featureClass
     *
     * @return
     */
    public Collection createComplexSymbols(VPFFeatureClass featureClass)
    {
        if (featureClass == null)
        {
            String message = Logging.getMessage("nullValue.FeatureClassIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        throw new UnsupportedOperationException();
    }

    protected FeatureMap createFeatureMap(VPFFeatureClass featureClass)
    {
        // Get the features associated with the feature class.
        Collection features = featureClass.createFeatures(this.featureFactory);
        if (features == null)
            return null;

        FeatureMap map = new FeatureMap();

        for (VPFFeature feature : features)
        {
            // Get the symbol keys associated with the current feature.
            Iterable symbolKeys = this.getSymbolKeys(feature);
            if (symbolKeys == null)
                continue;

            // Map the feature according to its associated symbol key.
            for (VPFSymbolKey key : symbolKeys)
            {
                map.addFeature(key, feature);
            }
        }

        return map;
    }

    //**************************************************************//
    //********************  Symbol Assembly  ***********************//
    //**************************************************************//

    /**
     * From MIL-DTL-89045A, section 3.5.3.1.1: A point feature may be symbolized as either a point symbol or as a text
     * label or both.
     * 

* From MIL-HDBK-857A, section 6.5.3.1: For point features (e.g., buoys, beacons, lights) that are composed of * several symbol components, displaying the components according to the row ids in the *sym.txt file will result in * the properly constructed composite symbol. */ protected void doCreatePointSymbols(FeatureMap featureMap, Collection outCollection) { for (Map.Entry entry : featureMap.entrySet()) { CombinedFeature feature = entry.getValue(); for (VPFSymbolAttributes attr : this.getSymbolAttributes(feature, entry.getKey())) { switch (attr.getFeatureType()) { // Construct a renderable object for each point symbol. case POINT: this.addPointSymbol(feature, attr, outCollection); break; // Construct a renderable object for each label symbol. case LABEL: this.addTextLabel(feature, attr, outCollection); break; } } } } /** * From MIL-DTL-89045A, section 3.5.3.1.1: A linear feature will be symbolized exclusively by a line symbol that may * or may not be labeled. */ protected void doCreateLineSymbols(FeatureMap featureMap, Collection outCollection) { for (Map.Entry entry : featureMap.entrySet()) { CombinedFeature feature = entry.getValue(); for (VPFSymbolAttributes attr : this.getSymbolAttributes(feature, entry.getKey())) { switch (attr.getFeatureType()) { // Construct a renderable object for each line symbol. case LINE: this.addLineSymbol(feature, attr, outCollection); break; // Construct a renderable object for each label symbol. case LABEL: this.addTextLabel(feature, attr, outCollection); break; } } } } /** * From MIL-DTL-89045A, section 3.5.3.1.1: An area feature may be symbolized by any combination of point, line, or * area symbol as well as with a text label. *

* From MIL-HDBK-857A, section 6.4.1.4: It is also possible for there to exist multiple symbology components for * area features. The most common situation is the need for the addition of the low accuracy symbol (see * 6.4.1.3.4). This situation is implemented in the same way as for points, with an additional row in the *sym.txt * table to control the placement of the low accuracy symbol. *

* It is also possible for the symbolization of an area feature to require multiple rows to specify the components * of the full area symbol. This situation will exist for those area features requiring both a solid fill and a * pattern fill. In this case, the two area symbols will be specified using two rows in the *sym.txt file with the * row specifying the solid fill always preceding the row specifying the pattern fill. If the same area feature * also requires a boundary and/or a centered point symbol, those symbols will be specified in the second row for * the area feature (along with the area pattern). Section 6.5 explains the ramifications of this approach in more * detail. *

* For these reasons, as well as for the placement of text labels (see section 6.4.1.5), it is crucial that * application software access all rows from the sym.txt file for a given product/delineation/feature code in order * to ensure full symbolization for any feature. *

* From MIL-HDBK-857A, section 6.5.3.2: There are some area features (e.g., Maritime Areas) that require both a * solid fill and one or more pattern fills. Since the areasym column can only contain a single CGM reference, * there is a separate row in the *sym.txt file for each of the area symbols, as well as for the line symbol and/or * point symbol that apply to the specific area feature. These multiple rows will have sequential row ids in the * *sym.txt file according to the order in which the symbols are to be displayed on the screen: solid fill, pattern * fill (may be more than one), linear boundary, centered point symbol (may be more than one). */ protected void doCreateAreaSymbols(FeatureMap featureMap, Collection outCollection) { for (Map.Entry entry : featureMap.entrySet()) { CombinedFeature feature = entry.getValue(); for (VPFSymbolAttributes attr : this.getSymbolAttributes(feature, entry.getKey())) { switch (attr.getFeatureType()) { // Construct a renderable object for each area symbol. case AREA: this.addAreaSymbol(feature, attr, outCollection); break; // Construct a renderable object for each line symbol. // This renderable area has been configured to draw its outline, and not its fill. Because the display // order is data driven; we want to control the display order of the fill and the outline independently. case LINE: this.addAreaSymbol(feature, attr, outCollection); break; // Construct a renderable object for each point symbol. case POINT: this.addPointLabel(feature, attr, outCollection); break; // Construct a renderable object for each label symbol. case LABEL: this.addTextLabel(feature, attr, outCollection); break; } } } } /** * From MIL-DTL-89045A, section 3.5.3.1.1: VPF products can contain a fourth type of feature known as a text * feature. GeoSym does not include rules to display text features. The application software should refer to the * MIL-STD-2407 for information on how to display VPF text features. */ protected void doCreateTextSymbols(FeatureMap featureMap, Collection outCollection) { for (Map.Entry entry : featureMap.entrySet()) { CombinedFeature feature = entry.getValue(); for (VPFSymbolAttributes attr : this.getSymbolAttributes(feature, entry.getKey())) { switch (attr.getFeatureType()) { // Construct a renderable object for each area symbol. case TEXT: this.addTextSymbol(feature, attr, outCollection); break; } } } } protected void addPointSymbol(CombinedFeature feature, VPFSymbolAttributes attr, Collection outCollection) { // Build the list of locations and headings associated with each point symbol. int numSymbols = 0; boolean haveUniqueHeadings = false; ArrayList locations = new ArrayList(); ArrayList headings = new ArrayList(); for (VPFFeature subFeature : feature) { String primitiveName = feature.getFeatureClass().getPrimitiveTableName(); Angle heading = this.getPointSymbolHeading(attr, subFeature); for (int id : subFeature.getPrimitiveIds()) { CompoundVecBuffer combinedCoords = this.primitiveData.getPrimitiveCoords(primitiveName); if (combinedCoords != null) { VecBuffer coords = combinedCoords.subBuffer(id); if (coords != null) { if (!haveUniqueHeadings) haveUniqueHeadings = headings.size() > 0 && !headings.contains(heading); locations.add(coords.getPosition(0)); headings.add(heading); numSymbols++; } } } } if (haveUniqueHeadings) { for (int i = 0; i < numSymbols; i++) { SurfaceIcon o = new SurfaceIcon("", locations.get(i)); o.setHeading(headings.get(i)); this.applyPointSymbolAttributes(attr, o); outCollection.add(new VPFSymbol(feature, attr, o)); } } else { SurfaceIcons o = new SurfaceIcons("", locations); if (headings.get(0) != null) o.setHeading(headings.get(0)); this.applyPointSymbolAttributes(attr, o); outCollection.add(new VPFSymbol(feature, attr, o)); } } protected void addLineSymbol(CombinedFeature feature, VPFSymbolAttributes attr, Collection outCollection) { SurfaceShape o = new VPFSurfaceLine(feature, this.primitiveData); this.applySymbolAttributes(attr, o); outCollection.add(new VPFSymbol(feature, attr, o)); } protected void addAreaSymbol(CombinedFeature feature, VPFSymbolAttributes attr, Collection outCollection) { SurfaceShape o = new VPFSurfaceArea(feature, this.primitiveData); this.applySymbolAttributes(attr, o); outCollection.add(new VPFSymbol(feature, attr, o)); } protected void addTextSymbol(CombinedFeature feature, VPFSymbolAttributes attr, Collection outCollection) { String primitiveName = feature.getFeatureClass().getPrimitiveTableName(); CompoundVecBuffer combinedCoords = this.primitiveData.getPrimitiveCoords(primitiveName); CompoundStringBuilder combinedStrings = this.primitiveData.getPrimitiveStrings(primitiveName); // Construct a renderable object for the first text symbol. for (int id : feature.getPrimitiveIds()) { VecBuffer coords = combinedCoords.subBuffer(id); CharSequence text = combinedStrings.subSequence(id); if (text != null) text = WWUtil.trimCharSequence(text); GeographicText o = new UserFacingText(text, coords.getPosition(0)); this.applyTextAttributes(attr, o); outCollection.add(new VPFSymbol(feature, attr, o)); } } protected void addTextLabel(CombinedFeature feature, VPFSymbolAttributes attr, Collection outCollection) { for (VPFFeature subFeature : feature) { this.addTextLabel(subFeature, attr, outCollection); } } protected void addTextLabel(VPFFeature feature, VPFSymbolAttributes attr, Collection outCollection) { VPFSymbolAttributes.LabelAttributes[] labelAttr = attr.getLabelAttributes(); if (labelAttr == null || labelAttr.length == 0) return; for (VPFSymbolAttributes.LabelAttributes la : labelAttr) { GeographicText o = new UserFacingText("", null); this.applyLabelAttributes(la, feature, o); // Do not specify any symbol attributes for the label. outCollection.add(new VPFSymbol(feature, null, o)); } } protected void addPointLabel(CombinedFeature feature, VPFSymbolAttributes attr, Collection outCollection) { // Build the list of point symbol locations associated with each sub-feature. ArrayList locations = new ArrayList(); for (VPFFeature subFeature : feature) { if (subFeature.getBounds() != null) { locations.add(subFeature.getBounds().toSector().getCentroid()); } } SurfaceIcons o = new SurfaceIcons("", locations); this.applyPointSymbolAttributes(attr, o); outCollection.add(new VPFSymbol(feature, attr, o)); } //**************************************************************// //******************** Symbol Attribute Assembly *************// //**************************************************************// protected Iterable getSymbolKeys(VPFFeature feature) { String fcode = feature.getStringValue("f_code"); return this.symbolSupport.getSymbolKeys(feature.getFeatureClass(), fcode, feature); } protected Iterable getSymbolAttributes(VPFFeature feature, VPFSymbolKey symbolKey) { return this.symbolSupport.getSymbolAttributes(feature.getFeatureClass(), symbolKey); } protected void applyPointSymbolAttributes(VPFSymbolAttributes attr, SurfaceIcon icon) { if (attr.getIconImageSource() != null) icon.setImageSource(attr.getIconImageSource()); icon.setUseMipMaps(attr.isMipMapIconImage()); icon.setScale(attr.getIconImageScale()); icon.setMaxSize(DEFAULT_ICON_MAX_SIZE); } protected Angle getPointSymbolHeading(VPFSymbolAttributes attr, AVList featureAttributes) { if (attr.getOrientationAttributeName() == null) { return null; } Object o = featureAttributes.getValue(attr.getOrientationAttributeName()); if (o instanceof Number) { Double d = ((Number) o).doubleValue(); return Angle.fromDegrees(d); } else if (o instanceof String) { Double d = WWUtil.convertStringToDouble((String) o); if (d != null) return Angle.fromDegrees(d); } return null; } protected void applySymbolAttributes(VPFSymbolAttributes attr, SurfaceShape surfaceShape) { surfaceShape.setAttributes(attr); } public void applyTextAttributes(VPFSymbolAttributes attr, GeographicText text) { VPFSymbolAttributes.LabelAttributes[] labelAttr = attr.getLabelAttributes(); if (labelAttr != null && labelAttr.length > 0) { text.setFont(labelAttr[0].getFont()); text.setColor(labelAttr[0].getColor()); text.setBackgroundColor(labelAttr[0].getBackgroundColor()); } else { text.setFont(Font.decode("Arial-PLAIN-12")); text.setColor(attr.getInteriorMaterial().getDiffuse()); text.setBackgroundColor(WWUtil.computeContrastingColor(attr.getInteriorMaterial().getDiffuse())); } } protected void applyLabelAttributes(VPFSymbolAttributes.LabelAttributes attr, VPFFeature feature, GeographicText text) { text.setFont(attr.getFont()); text.setColor(attr.getColor()); text.setBackgroundColor(attr.getBackgroundColor()); LatLon location = this.computeLabelLocation(attr, feature); if (location != null) text.setPosition(new Position(location, 0)); String labelText = this.symbolSupport.getSymbolLabelText(attr, feature); if (labelText != null) text.setText(labelText); } protected LatLon computeLabelLocation(VPFSymbolAttributes.LabelAttributes attr, VPFFeature feature) { LatLon location = feature.getBounds().toSector().getCentroid(); // If we are given label offset parameters, compute the label location using the offset azimuth and offset arc // length. if (attr.getOffset() != 0) { VPFLibrary library = feature.getFeatureClass().getCoverage().getLibrary(); Angle offsetAzimuth = attr.getOffsetAngle(); Angle offsetLength = library.computeArcLengthFromMapDistance(attr.getOffset()); if (offsetAzimuth != null && offsetLength != null) { location = LatLon.greatCircleEndPosition(location, offsetAzimuth, offsetLength); } } return location; } //**************************************************************// //******************** Feature Support ***********************// //**************************************************************// protected static class FeatureMap extends HashMap { public void addFeature(VPFSymbolKey key, VPFFeature feature) { CombinedFeature combined = this.get(key); if (combined == null) { combined = new CombinedFeature(feature.getFeatureClass()); this.put(key, combined); } combined.add(feature); } } protected static class CombinedFeature extends VPFFeature implements Iterable { private ArrayList featureList; public CombinedFeature(VPFFeatureClass featureClass) { super(featureClass, -1, new VPFBoundingBox(0, 0, 0, 0), null); this.featureList = new ArrayList(); } public VPFBoundingBox getBounds() { return combineBounds(this.featureList); } public int[] getPrimitiveIds() { return combinePrimitiveIds(this.featureList); } public void add(VPFFeature feature) { this.featureList.add(feature); } public Iterator iterator() { return this.featureList.iterator(); } } protected static VPFBoundingBox combineBounds(Iterable features) { VPFBoundingBox bounds = null; for (VPFFeature f : features) { bounds = (bounds != null) ? f.getBounds().union(bounds) : f.getBounds(); } return bounds; } protected static int[] combinePrimitiveIds(Iterable features) { int length = 0; for (VPFFeature f : features) { if (f.getPrimitiveIds() != null) { length += f.getPrimitiveIds().length; } } int[] array = new int[length]; int position = 0; for (VPFFeature f : features) { if (f.getPrimitiveIds() != null) { int[] src = f.getPrimitiveIds(); System.arraycopy(src, 0, array, position, src.length); position += src.length; } } return array; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy