Envelope.java

package de.turnertech.ows.gml;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.Optional;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamWriter;

import de.turnertech.ows.Logging;
import de.turnertech.ows.common.OwsContext;
import de.turnertech.ows.srs.SpatialReferenceSystem;
import de.turnertech.ows.srs.SpatialReferenceSystemConverter;
import de.turnertech.ows.srs.SpatialReferenceSystemRepresentation;

public class Envelope implements GmlElement {

    @Deprecated
    public static final String GML_NAME = "boundedBy";

    public static final QName QNAME = new QName(OwsContext.GML_URI, "Envelope");

    protected DirectPosition upperCorner;

    protected DirectPosition lowerCorner;

    // Use with care! This is an inverted and unusable box!
    public Envelope() {
        this(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
    }

    public Envelope(double south, double west, double north, double east) {
        this.lowerCorner = new DirectPosition(SpatialReferenceSystem.EPSG4326, west, south);
        this.upperCorner = new DirectPosition(SpatialReferenceSystem.EPSG4326, east, north);
    }

    public Envelope(final DirectPosition lowerCorner, final DirectPosition upperCorner) {
        this.lowerCorner = lowerCorner;
        this.upperCorner = upperCorner;
    }

    public boolean contains(double latitude, double longitute) {
        return !(latitude > upperCorner.getY() || latitude < lowerCorner.getY() || longitute > upperCorner.getX() || longitute < lowerCorner.getX());
    }

    public boolean intersects(Envelope other) {
        if(this.getNorth() < other.getSouth()) return false;
        if(this.getSouth() > other.getNorth()) return false;
        if(this.getEast() < other.getWest()) return false;
        if(this.getWest() > other.getEast()) return false;
        return true;
    }

    public static Envelope from(Envelope other) {
        return new Envelope(other.getSouth(), other.getWest(), other.getNorth(), other.getEast());
    }

    public static Envelope from(DirectPositionList posList) {
        double maxSouth = Double.POSITIVE_INFINITY;
        double maxWest = Double.POSITIVE_INFINITY;
        double maxNorth = Double.NEGATIVE_INFINITY;
        double maxEast = Double.NEGATIVE_INFINITY;

        for(DirectPosition position : posList) {
            if(position.getY() > maxNorth) maxNorth = position.getY();
            if(position.getY() < maxSouth) maxSouth = position.getY();
            if(position.getX() > maxEast) maxEast = position.getX();
            if(position.getX() < maxWest) maxWest = position.getX();
        }

        // Catch BBOX with a size of 0 (causes errors in many clients, happens with only 1 point)
        if(maxSouth == maxNorth) {
            maxNorth += 0.00001;
            maxSouth -= 0.00001;
        }
        if(maxEast == maxWest) {
            maxEast += 0.00001;
            maxWest -= 0.00001;
        }

        return new Envelope(maxSouth, maxWest, maxNorth, maxEast);
    }

    public void expandToFit(Envelope other) {
        if(other == null) return;
        if(other.getNorth() > getNorth()) upperCorner.setY(other.getNorth());
        if(other.getSouth() < getSouth()) lowerCorner.setY(other.getSouth());
        if(other.getEast() > getEast()) upperCorner.setX(other.getEast());
        if(other.getWest() < getWest()) lowerCorner.setX(other.getWest());
    }

    /**
     * @return the south
     */
    public double getSouth() {
        return lowerCorner.getY();
    }

    /**
     * @return the west
     */
    public double getWest() {
        return lowerCorner.getX();
    }

    /**
     * @return the north
     */
    public double getNorth() {
        return upperCorner.getY();
    }

    /**
     * @return the east
     */
    public double getEast() {
        return upperCorner.getX();
    }

    @Override
    public void writeGml(XMLStreamWriter out, String localName, String namespaceURI, SpatialReferenceSystemRepresentation srsRepresentation) {
        DecimalFormat decimalFormat = new DecimalFormat("0.", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
        decimalFormat.setMaximumFractionDigits(8);

        DirectPosition sw = new DirectPosition(getWest(), getSouth());
        DirectPosition ne = new DirectPosition(getEast(), getNorth());

        Optional<DirectPosition> transformedPos = SpatialReferenceSystemConverter.convertDirectPosition(sw, srsRepresentation.getSrs());
        if (transformedPos.isPresent()) {
            sw = transformedPos.get();
        }

        transformedPos = SpatialReferenceSystemConverter.convertDirectPosition(ne, srsRepresentation.getSrs());
        if (transformedPos.isPresent()) {
            ne = transformedPos.get();
        }

        try {
            writeGmlStartElement(out, localName, namespaceURI);
                out.writeStartElement(GmlElement.NAMESPACE, "Envelope");
                out.writeAttribute(GmlElement.NAMESPACE, "srsName", srsRepresentation.toString());
                    out.writeStartElement(GmlElement.NAMESPACE, "lowerCorner");
                        out.writeCharacters(sw.toString());
                    out.writeEndElement();
                    out.writeStartElement(GmlElement.NAMESPACE, "upperCorner");
                        out.writeCharacters(ne.toString());
                    out.writeEndElement();
                out.writeEndElement();
            out.writeEndElement();
        } catch (Exception e) {
            Logging.LOG.severe("Could not get GML for BoundingBox");
        }
    }

    @Override
    public String getGmlName() {
        return GML_NAME;
    }

}