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

gov.nasa.worldwind.formats.vpf.VPFLibrary 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.formats.vpf;

import gov.nasa.worldwind.avlist.*;
import gov.nasa.worldwind.exception.WWRuntimeException;
import gov.nasa.worldwind.geom.Angle;
import gov.nasa.worldwind.util.*;

import java.io.File;
import java.util.*;
import java.util.regex.*;

/**
 * DIGEST Part 2, Annex C.2.2.2.4 and C.2.3.5
 *
 * @author dcollins
 * @version $Id: VPFLibrary.java 1171 2013-02-11 21:45:02Z dcollins $
 */
public class VPFLibrary extends AVListImpl
{
    private VPFDatabase database;
    private VPFBoundingBox bounds;
    private Map coverageMap = new HashMap();
    private Map tileMap = new HashMap();
    private VPFTile[] tiles;
    private VPFBufferedRecordData libraryHeaderTable;
    private VPFBufferedRecordData coverageAttributeTable;
    private VPFBufferedRecordData geographicReferenceTable;

    protected VPFLibrary(VPFDatabase database)
    {
        if (database == null)
        {
            String message = Logging.getMessage("nullValue.DatabaseIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.database = database;
    }

    /**
     * Constructs a VPF Library from the specified VPF Database and library name. This initializes the Library Header
     * Table, the Coverage Attribute Table, and the Geographic Reference Table.
     *
     * @param database the Database which the Library resides in.
     * @param name     the Library's name.
     *
     * @return a new Library from the specified Database with the specified name.
     *
     * @throws IllegalArgumentException if the database is null, or if the name is null or empty.
     */
    public static VPFLibrary fromFile(VPFDatabase database, String name)
    {
        if (database == null)
        {
            String message = Logging.getMessage("nullValue.DatabaseIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        if (WWUtil.isEmpty(name))
        {
            String message = Logging.getMessage("nullValue.NameIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        File file = new File(database.getFilePath(), name);
        if (!file.exists())
        {
            String message = Logging.getMessage("generic.FileNotFound", file.getPath());
            Logging.logger().severe(message);
            throw new WWRuntimeException(message);
        }

        // Library tables.
        VPFBufferedRecordData lht = VPFUtils.readTable(new File(file, VPFConstants.LIBRARY_HEADER_TABLE));
        if (lht == null)
        {
            String message = Logging.getMessage("VPF.LibraryHeaderTableMissing");
            throw new WWRuntimeException(message);
        }

        VPFBufferedRecordData cat = VPFUtils.readTable(new File(file, VPFConstants.COVERAGE_ATTRIBUTE_TABLE));
        if (cat == null)
        {
            String message = Logging.getMessage("VPF.CoverageAttributeTableMissing");
            throw new WWRuntimeException(message);
        }

        VPFBufferedRecordData grt = VPFUtils.readTable(new File(file, VPFConstants.GEOGRAPHIC_REFERENCE_TABLE));
        if (grt == null)
        {
            String message = Logging.getMessage("VPF.GeographicReferenceTableMissing");
            throw new WWRuntimeException(message);
        }

        VPFLibrary library = new VPFLibrary(database);
        library.setLibraryHeaderTable(lht);
        library.setCoverageAttributeTable(cat);
        library.setGeographicReferenceTable(grt);

        // Library metadata attributes.
        VPFRecord record = database.getLibraryAttributeTable().getRecord("library_name", name);
        if (record != null)
            library.bounds = VPFUtils.getExtent(record);

        record = lht.getRecord(1);
        if (record != null)
        {
            VPFUtils.checkAndSetValue(record, "library_name", AVKey.DISPLAY_NAME, library);
            VPFUtils.checkAndSetValue(record, "description", AVKey.DESCRIPTION, library);
        }

        // Library Coverages.
        Collection col = createCoverages(library, cat);
        if (col != null)
            library.setCoverages(col);

        // Library tiles.
        VPFCoverage cov = library.getCoverage(VPFConstants.TILE_REFERENCE_COVERAGE);
        if (cov != null)
        {
            VPFTile[] tiles = createTiles(cov);
            if (tiles != null)
            {
                library.setTiles(tiles);
            }
            else
            {
                String message = Logging.getMessage("VPF.NoTilesInTileReferenceCoverage");
                Logging.logger().warning(message);
            }
        }

        // Coverage tiled attributes.
        for (VPFCoverage coverage : library.getCoverages())
        {
            boolean tiled = isCoverageTiled(library, coverage);
            coverage.setTiled(tiled);
        }

        return library;
    }

    public VPFDatabase getDatabase()
    {
        return this.database;
    }

    /**
     * Returns the text name of this Library.
     *
     * @return name of this Library.
     */
    public String getName()
    {
        return this.getStringValue(AVKey.DISPLAY_NAME);
    }

    /**
     * Returns a text description of this Library.
     *
     * @return description of this Library.
     */
    public String getDescription()
    {
        return this.getStringValue(AVKey.DESCRIPTION);
    }

    public String getFilePath()
    {
        StringBuilder sb = new StringBuilder(this.database.getFilePath());
        sb.append(File.separator);
        sb.append(this.getName());
        return sb.toString();
    }

    public VPFBoundingBox getBounds()
    {
        return this.bounds;
    }

    /**
     * Returns the number of Coverages associated with this Library. If this Library is not associated with any
     * Coverage, this returns 0.
     *
     * @return number of Coverages associated with this Library.
     */
    public int getNumCoverages()
    {
        return this.coverageMap.size();
    }

    public boolean containsCoverage(String name)
    {
        if (name == null)
        {
            String message = Logging.getMessage("nullValue.NameIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        return this.coverageMap.containsKey(name);
    }

    public VPFCoverage getCoverage(String name)
    {
        if (name == null)
        {
            String message = Logging.getMessage("nullValue.NameIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        return this.coverageMap.get(name);
    }

    public Set getCoverageNames()
    {
        return Collections.unmodifiableSet(this.coverageMap.keySet());
    }

    public Collection getCoverages()
    {
        return Collections.unmodifiableCollection(this.coverageMap.values());
    }

    public void setCoverages(Collection collection)
    {
        this.removeAllCoverages();

        if (collection != null)
            this.addAllCoverages(collection);
    }

    public void addCoverage(VPFCoverage coverage)
    {
        if (coverage == null)
        {
            String message = Logging.getMessage("nullValue.CoverageIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.coverageMap.put(coverage.getName(), coverage);
    }

    public void addAllCoverages(Collection collection)
    {
        if (collection == null)
        {
            String message = Logging.getMessage("nullValue.CollectionIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        for (VPFCoverage cov : collection)
        {
            this.addCoverage(cov);
        }
    }

    public void removeCoverage(VPFCoverage coverage)
    {
        if (coverage == null)
        {
            String message = Logging.getMessage("nullValue.CoverageIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.coverageMap.remove(coverage.getName());
    }

    public void removeAllCoverages()
    {
        this.coverageMap.clear();
    }

    public boolean hasTiledCoverages()
    {
        return this.getCoverage(VPFConstants.TILE_REFERENCE_COVERAGE) != null;
    }

    public int getNumTiles()
    {
        return (this.tiles != null) ? this.tiles.length : 0;
    }

    public VPFTile[] getTiles()
    {
        if (this.tiles == null)
            return null;

        VPFTile[] copy = new VPFTile[this.tiles.length];
        System.arraycopy(this.tiles, 0, copy, 0, this.tiles.length);
        return copy;
    }

    public void setTiles(VPFTile[] array)
    {
        if (array == null)
        {
            this.tiles = null;
            this.tileMap.clear();
            return;
        }

        this.tiles = new VPFTile[array.length];
        System.arraycopy(array, 0, this.tiles, 0, array.length);

        this.tileMap.clear();
        for (VPFTile tile : array)
        {
            this.tileMap.put(tile.getId(), tile);
        }
    }

    public VPFTile getTile(int tileId)
    {
        return this.tileMap.get(tileId);
    }

    public String getProductType()
    {
        if (this.libraryHeaderTable == null || this.libraryHeaderTable.getNumRecords() == 0)
            return null;

        VPFRecord record = this.libraryHeaderTable.getRecord(1);
        Object o = (record != null) ? record.getValue("product_type") : null;
        return (o != null) ? o.toString() : null;
    }

    public double getMapScale()
    {
        if (this.libraryHeaderTable == null || this.libraryHeaderTable.getNumRecords() == 0)
            return 0;

        VPFRecord record = this.libraryHeaderTable.getRecord(1);
        Object o = (record != null) ? record.getValue("scale") : null;
        return (o != null && o instanceof Number) ? ((Number) o).doubleValue() : 0;
    }

    public Angle computeArcLengthFromMapDistance(double millimeters)
    {
        if (this.geographicReferenceTable == null || this.geographicReferenceTable.getNumRecords() == 0)
            return null;

        VPFRecord record = this.geographicReferenceTable.getRecord(1);
        if (record == null)
            return null;

        String s = (String) record.getValue("units");
        Double unitsCoefficient = parseUnitsCoefficient(s);
        if (unitsCoefficient == null)
        {
            String message = Logging.getMessage("VPF.UnrecognizedUnits", s);
            Logging.logger().severe(message);
            return null;
        }

        s = (String) record.getValue("ellipsoid_detail");
        double[] ellipsoidParams = parseEllipsoidDetail(s);
        if (ellipsoidParams == null || ellipsoidParams.length != 2)
        {
            String message = Logging.getMessage("VPF.UnrecognizedEllipsoidDetail", s);
            Logging.logger().severe(message);
            return null;
        }

        // Multiply the distance in map units (millimeters) by the map scale to determine a real world offset in meters,
        // then divide by the Globe's radius to get offset length in arc radians.
        double meters = this.getMapScale() * millimeters / 1000d;
        double radius = unitsCoefficient * Math.max(ellipsoidParams[0], ellipsoidParams[1]);
        return Angle.fromRadians(meters / radius);
    }

    public VPFBufferedRecordData getLibraryHeaderTable()
    {
        return this.libraryHeaderTable;
    }

    public void setLibraryHeaderTable(VPFBufferedRecordData table)
    {
        if (table == null)
        {
            String message = Logging.getMessage("nullValue.TableIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.libraryHeaderTable = table;
    }

    public VPFBufferedRecordData getGeographicReferenceTable()
    {
        return this.geographicReferenceTable;
    }

    public void setGeographicReferenceTable(VPFBufferedRecordData table)
    {
        if (table == null)
        {
            String message = Logging.getMessage("nullValue.TableIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.geographicReferenceTable = table;
    }

    public VPFBufferedRecordData getCoverageAttributeTable()
    {
        return this.coverageAttributeTable;
    }

    public void setCoverageAttributeTable(VPFBufferedRecordData table)
    {
        if (table == null)
        {
            String message = Logging.getMessage("nullValue.TableIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.coverageAttributeTable = table;
    }

    protected static Double parseUnitsCoefficient(String value)
    {
        if (WWUtil.isEmpty(value))
            return null;

        if (value.toLowerCase().startsWith("f"))
        {
            return 1d / WWMath.METERS_TO_FEET;
        }
        else if (value.toLowerCase().startsWith("m"))
        {
            return 1d;
        }
        else
        {
            return null;
        }
    }

    protected static double[] parseEllipsoidDetail(String value)
    {
        if (WWUtil.isEmpty(value))
            return null;

        Pattern pattern = Pattern.compile("[A][=](.+)\\s+[B][=](.+)\\s+?(.+)?");
        Matcher matcher = pattern.matcher(value);
        if (!matcher.matches())
            return null;

        String aString = matcher.group(1);
        String bString = matcher.group(2);
        if (WWUtil.isEmpty(aString) || WWUtil.isEmpty(bString))
            return null;

        Double a = WWUtil.convertStringToDouble(aString);
        Double b = WWUtil.convertStringToDouble(bString);
        if (a == null || b == null)
            return null;

        return new double[] {a, b};
    }

    //**************************************************************//
    //********************  Coverage Assembly  *********************//
    //**************************************************************//

    protected static Collection createCoverages(VPFLibrary library, VPFBufferedRecordData table)
    {
        ArrayList list = new ArrayList();

        for (VPFRecord row : table)
        {
            String name = (String) row.getValue("coverage_name");
            if (name != null)
            {
                VPFCoverage coverage = VPFUtils.readCoverage(library, name);
                if (coverage != null)
                    list.add(coverage);
            }
        }

        return list;
    }

    protected static boolean isCoverageTiled(VPFLibrary lib, VPFCoverage cov)
    {
        if (cov.getName().equals(VPFConstants.TILE_REFERENCE_COVERAGE))
            return false;

        if (lib == null || lib.getCoverage(VPFConstants.TILE_REFERENCE_COVERAGE) == null)
            return false;

        VPFTile[] tiles = lib.getTiles();
        if (tiles == null)
            return false;

        for (VPFTile tile : tiles)
        {
            File tmp = new File(cov.getFilePath(), tile.getName());
            if (tmp.exists())
                return true;
        }

        return false;
    }

    //**************************************************************//
    //********************  Tile Assembly  *************************//
    //**************************************************************//

    protected static VPFTile[] createTiles(VPFCoverage coverage)
    {
        VPFFeatureClassSchema[] schemas = coverage.getFeatureClasses(new VPFFeatureTableFilter());
        if (schemas == null || schemas.length == 0)
            return null;

        VPFFeatureClassSchema tileRefSchema = null;
        for (VPFFeatureClassSchema s : schemas)
        {
            if (s.getClassName().equalsIgnoreCase(VPFConstants.TILE_REFERENCE_COVERAGE))
            {
                tileRefSchema = s;
                break;
            }
        }

        if (tileRefSchema == null)
            return null;

        VPFFeatureClassFactory factory = new VPFBasicFeatureClassFactory();
        VPFFeatureClass areaClass = factory.createFromSchema(coverage, tileRefSchema);
        return createTiles(areaClass);
    }

    protected static VPFTile[] createTiles(VPFFeatureClass featureClass)
    {
        VPFPrimitiveDataFactory primitiveFactory = new VPFBasicPrimitiveDataFactory(null);
        VPFPrimitiveData primitiveData = primitiveFactory.createPrimitiveData(featureClass.getCoverage());
        if (primitiveData == null)
            return null;

        VPFFeatureFactory featureFactory = new VPFBasicFeatureFactory(null, primitiveData);
        Collection features = featureClass.createFeatures(featureFactory);
        if (features == null || features.size() == 0)
            return null;

        VPFTile[] tiles = new VPFTile[features.size()];

        int index = 0;
        for (VPFFeature feature : features)
        {
            String tileName = feature.getStringValue("tile_name");
            if (tileName != null)
                tileName = fixTileName(tileName);

            tiles[index++] = new VPFTile(feature.getId(), tileName, feature.getBounds());
        }

        return tiles;
    }

    protected static String fixTileName(String s)
    {
        s = s.toLowerCase();
        s = s.replace("\\", File.separator);
        return s;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy