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

gov.nasa.worldwind.ogc.kml.impl.KMLUtil 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.ogc.kml.impl;

import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.ogc.kml.*;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.*;

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

/**
 * @author tag
 * @version $Id: KMLUtil.java 1838 2014-02-05 20:48:12Z dcollins $
 */
public class KMLUtil
{
    public static final String KML_PIXELS = "pixels";
    public static final String KML_FRACTION = "fraction";
    public static final String KML_INSET_PIXELS = "insetPixels";

    public static ShapeAttributes assembleLineAttributes(ShapeAttributes attrs, KMLLineStyle style)
    {
        // Assign the attributes defined in the KML Feature element.

        if (style.getWidth() != null)
            attrs.setOutlineWidth(style.getWidth());

        if (style.getColor() != null)
            attrs.setOutlineMaterial(new Material(WWUtil.decodeColorABGR(style.getColor())));

        if (style.getColorMode() != null && "random".equals(style.getColorMode()))
            attrs.setOutlineMaterial(new Material(WWUtil.makeRandomColor(attrs.getOutlineMaterial().getDiffuse())));

        return attrs;
    }

    public static ShapeAttributes assembleInteriorAttributes(ShapeAttributes attrs, KMLPolyStyle style)
    {
        // Assign the attributes defined in the KML Feature element.

        if (style.getColor() != null)
        {
            Color color = WWUtil.decodeColorABGR(style.getColor());

            attrs.setInteriorMaterial(new Material(color));
            attrs.setInteriorOpacity((double) color.getAlpha() / 255);
        }

        if (style.getColorMode() != null && "random".equals(style.getColorMode()))
            attrs.setInteriorMaterial(new Material(WWUtil.makeRandomColor(attrs.getOutlineMaterial().getDiffuse())));

        return attrs;
    }

    /**
     * Indicate whether a specified sub-style has the "highlight" style-state field.
     *
     * @param subStyle the sub-style to test. May be null, in which case this method returns false.
     *
     * @return true if the sub-style has the "highlight" field, otherwise false.
     */
    public static boolean isHighlightStyleState(KMLAbstractSubStyle subStyle)
    {
        if (subStyle == null)
            return false;

        String styleState = (String) subStyle.getField(KMLConstants.STYLE_STATE);
        return styleState != null && styleState.equals(KMLConstants.HIGHLIGHT);
    }

    public static int convertAltitudeMode(String altMode, int defaultAltMode)
    {
        if ("clampToGround".equals(altMode))
            return WorldWind.CLAMP_TO_GROUND;
        else if ("relativeToGround".equals(altMode))
            return WorldWind.RELATIVE_TO_GROUND;
        else if ("absolute".equals(altMode))
            return WorldWind.ABSOLUTE;
        else
            return defaultAltMode;
    }

    /**
     * Translate a KML units string ("pixels", "insetPixels", or "fraction") into the corresponding WW unit constant
     * ({@link AVKey#PIXELS}, {@link AVKey#INSET_PIXELS}, or {@link AVKey#FRACTION}.
     *
     * @param units KML units to translate.
     *
     * @return WW units, or null if the argument is not a valid KML unit.
     */
    public static String kmlUnitsToWWUnits(String units)
    {
        if (KML_PIXELS.equals(units))
            return AVKey.PIXELS;
        else if (KML_FRACTION.equals(units))
            return AVKey.FRACTION;
        else if (KML_INSET_PIXELS.equals(units))
            return AVKey.INSET_PIXELS;
        else
            return null;
    }

    /**
     * Translate a WorldWind units constant ({@link AVKey#PIXELS}, {@link AVKey#INSET_PIXELS}, or {@link AVKey#FRACTION}
     * to the corresponding KML unit string ("pixels", "insetPixels", or "fraction").
     *
     * @param units World Wind units to translate.
     *
     * @return KML units, or null if the argument is not a valid WW unit.
     */
    public static String wwUnitsToKMLUnits(String units)
    {
        if (AVKey.PIXELS.equals(units))
            return KML_PIXELS;
        else if (AVKey.FRACTION.equals(units))
            return KML_FRACTION;
        else if (AVKey.INSET_PIXELS.equals(units))
            return KML_INSET_PIXELS;
        else
            return null;
    }

    /**
     * Creates a Sector from a KMLAbstractLatLonBoxType's north,
     * south, east, and west coordinates. This returns null if any
     * of these coordinates are unspecified.
     *
     * @param box a box who's coordinates define a Sector.
     *
     * @return the Sector that bounds the specified box's coordinates.
     *
     * @throws IllegalArgumentException if the box is null.
     */
    public static Sector createSectorFromLatLonBox(KMLAbstractLatLonBoxType box)
    {
        if (box == null)
        {
            String message = Logging.getMessage("nullValue.BoxIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        if (box.getNorth() == null || box.getSouth() == null || box.getEast() == null || box.getWest() == null)
            return null;

        double north = box.getNorth();
        double south = box.getSouth();
        double east = box.getEast();
        double west = box.getWest();

        double minLat = Math.min(north, south);
        double maxLat = Math.max(north, south);
        double minLon = Math.min(east, west);
        double maxLon = Math.max(east, west);

        return Sector.fromDegrees(minLat, maxLat, minLon, maxLon);
    }

    /**
     * Get all of the positions that make up a {@link KMLAbstractGeometry}. If the geometry contains other geometries,
     * this method collects all the points from all of the geometries.
     *
     * @param globe     Globe to use to determine altitude above terrain.
     * @param geometry  Geometry to collect positions from.
     * @param positions Placemark positions will be added to this list.
     */
    public static void getPositions(Globe globe, KMLAbstractGeometry geometry, java.util.List positions)
    {
        if (geometry instanceof KMLPoint)
        {
            KMLPoint kmlPoint = (KMLPoint) geometry;
            Position pos = kmlPoint.getCoordinates();

            if (pos != null)
                positions.add(computeAltitude(globe, pos, kmlPoint.getAltitudeMode()));
        }
        else if (geometry instanceof KMLModel)
        {
            KMLModel model = (KMLModel) geometry;
            KMLLocation location = model.getLocation();
            if (location != null)
            {
                Position pos = location.getPosition();
                if (pos != null)
                    positions.add(computeAltitude(globe, pos, model.getAltitudeMode()));
            }
        }
        else if (geometry instanceof KMLLineString) // Also handles KMLLinearRing, since KMLLineString is a subclass of KMLLinearRing
        {
            KMLLineString lineString = (KMLLineString) geometry;
            Position.PositionList positionList = lineString.getCoordinates();
            if (positionList != null)
            {
                positions.addAll(computeAltitude(globe, positionList.list, lineString.getAltitudeMode()));
            }
        }
        else if (geometry instanceof KMLPolygon)
        {
            KMLLinearRing ring = ((KMLPolygon) geometry).getOuterBoundary();
            // Recurse and let the LineString/LinearRing code handle the boundary positions
            getPositions(globe, ring, positions);
        }
        else if (geometry instanceof KMLMultiGeometry)
        {
            java.util.List geoms = ((KMLMultiGeometry) geometry).getGeometries();
            for (KMLAbstractGeometry g : geoms)
            {
                // Recurse, adding positions for the sub-geometry
                getPositions(globe, g, positions);
            }
        }
    }

    /**
     * Compute the altitude of each position in a list, based on altitude mode.
     *
     * @param globe        Globe to use to determine altitude above terrain.
     * @param positions    Positions to compute altitude for.
     * @param altitudeMode A KML altitude mode string.
     *
     * @return A new list of positions with altitudes set based on {@code altitudeMode}.
     */
    public static java.util.List computeAltitude(Globe globe, java.util.List positions,
        String altitudeMode)
    {
        java.util.List outPositions = new ArrayList(positions.size());
        for (Position p : positions)
        {
            outPositions.add(computeAltitude(globe, p, altitudeMode));
        }

        return outPositions;
    }

    /**
     * Create a {@link Position}, taking into account an altitude mode.
     *
     * @param globe        Globe to use to determine altitude above terrain.
     * @param position     Position to evaluate.
     * @param altitudeMode A KML altitude mode string.
     *
     * @return New Position.
     */
    public static Position computeAltitude(Globe globe, Position position, String altitudeMode)
    {
        double height;
        Angle latitude = position.getLatitude();
        Angle longitude = position.getLongitude();

        int altMode = convertAltitudeMode(altitudeMode, WorldWind.CLAMP_TO_GROUND); // KML default
        if (altMode == WorldWind.CLAMP_TO_GROUND)
            height = globe.getElevation(latitude, longitude);
        else if (altMode == WorldWind.RELATIVE_TO_GROUND)
            height = globe.getElevation(latitude, longitude) + position.getAltitude();
        else
            height = position.getAltitude();

        return new Position(latitude, longitude, height);
    }

    /**
     * Rotate the corners of a sector around a normal vector through the sector centroid.
     *
     * @param globe    Globe to use to compute rotated positions.
     * @param sector   Sector to rotate.
     * @param rotation Rotation angle. Positive angles produce counterclockwise rotation.
     *
     * @return List of rotated corners.
     */
    public static java.util.List rotateSector(Globe globe, Sector sector, Angle rotation)
    {
        if (globe == null)
        {
            String message = Logging.getMessage("nullValue.GlobeIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }
        if (sector == null)
        {
            String message = Logging.getMessage("nullValue.SectorIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }
        if (rotation == null)
        {
            String message = Logging.getMessage("nullValue.AngleIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        LatLon[] corners = sector.getCorners();
        java.util.List transformedCorners = new ArrayList(corners.length);

        // Using the four corners of the sector to compute the rotation axis avoids problems with dateline
        // spanning polygons.
        Vec4[] verts = sector.computeCornerPoints(globe, 1);
        Vec4 normalVec = verts[2].subtract3(verts[0]).cross3(verts[3].subtract3(verts[1])).normalize3();
        Matrix rotationMatrix = Matrix.fromAxisAngle(rotation, normalVec);

        Vec4 centerPoint = sector.computeCenterPoint(globe, 1);

        // Rotate each point around the surface normal, and convert back to geographic
        for (Vec4 point : verts)
        {
            point = point.subtract3(centerPoint).transformBy3(rotationMatrix).add3(centerPoint);
            LatLon ll = globe.computePositionFromPoint(point);

            transformedCorners.add(ll);
        }

        return transformedCorners;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy