com.revolsys.geometry.model.impl.BoundingBoxDoubleGf.java Source code

Java tutorial

Introduction

Here is the source code for com.revolsys.geometry.model.impl.BoundingBoxDoubleGf.java

Source

/*
 * The JTS Topology Suite is a collection of Java classes that
 * implement the fundamental operations required to validate a given
 * geo-spatial data set to a known topological specification.
 *
 * Copyright (C) 2001 Vivid Solutions
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * For more information, contact:
 *
 *     Vivid Solutions
 *     Suite #1A
 *     2328 Government Street
 *     Victoria BC  V8T 5G5
 *     Canada
 *
 *     (250)385-6040
 *     www.vividsolutions.com
 */
package com.revolsys.geometry.model.impl;

import java.io.Serializable;

import javax.measure.Measurable;
import javax.measure.Measure;
import javax.measure.quantity.Length;
import javax.measure.quantity.Quantity;
import javax.measure.unit.SI;
import javax.measure.unit.Unit;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;

import com.revolsys.collection.list.Lists;
import com.revolsys.geometry.cs.CoordinateSystem;
import com.revolsys.geometry.cs.ProjectedCoordinateSystem;
import com.revolsys.geometry.cs.projection.CoordinatesOperation;
import com.revolsys.geometry.cs.projection.ProjectionFactory;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.Geometry;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.model.LinearRing;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.model.Polygon;
import com.revolsys.geometry.model.coordinates.list.CoordinatesListUtil;
import com.revolsys.geometry.model.vertex.Vertex;
import com.revolsys.geometry.util.BoundingBoxUtil;
import com.revolsys.logging.Logs;
import com.revolsys.record.Record;
import com.revolsys.util.MathUtil;
import com.revolsys.util.number.Doubles;

/**
 *  Defines a rectangular region of the 2D coordinate plane.
 *  It is often used to represent the bounding box of a {@link Geometry},
 *  e.g. the minimum and maximum x and y values of the {@link Coordinates}s.
 *  <p>
 *  Note that Envelopes support infinite or half-infinite regions, by using the values of
 *  <code>Double.POSITIVE_INFINITY</code> and <code>Double.NEGATIVE_INFINITY</code>.
 *  <p>
 *  When BoundingBoxDoubleGf objects are created or initialized,
 *  the supplies extent values are automatically sorted into the correct order.
 *
 *@version 1.7
 */
public class BoundingBoxDoubleGf implements Serializable, BoundingBox {

    /**
     * The bitmask that indicates that a point lies below
     * this <code>Rectangle2D</code>.
     * @since 1.2
     */
    public static final int OUT_BOTTOM = 8;

    /**
     * The bitmask that indicates that a point lies to the left of
     * this <code>Rectangle2D</code>.
     * @since 1.2
     */
    public static final int OUT_LEFT = 1;

    /**
     * The bitmask that indicates that a point lies to the right of
     * this <code>Rectangle2D</code>.
     * @since 1.2
     */
    public static final int OUT_RIGHT = 4;

    /**
     * The bitmask that indicates that a point lies above
     * this <code>Rectangle2D</code>.
     * @since 1.2
     */
    public static final int OUT_TOP = 2;

    /** The serialization version. */
    private static final long serialVersionUID = -810356856421113732L;

    static {
        ConvertUtils.register(new Converter() {

            @Override
            public Object convert(@SuppressWarnings("rawtypes") final Class paramClass, final Object paramObject) {
                if (paramObject == null) {
                    return null;
                } else if (BoundingBox.class.isAssignableFrom(paramClass)) {
                    if (paramObject instanceof BoundingBox) {
                        return paramObject;
                    } else {
                        return BoundingBox.newBoundingBox(paramObject.toString());
                    }
                }
                return null;
            }
        }, BoundingBox.class);
    }

    public static boolean isEmpty(final double minX, final double maxX) {
        if (Double.isNaN(minX)) {
            return true;
        } else if (Double.isNaN(maxX)) {
            return true;
        } else {
            return maxX < minX;
        }
    }

    private final double[] bounds;

    private GeometryFactory geometryFactory;

    public BoundingBoxDoubleGf() {
        this.bounds = null;
    }

    /**
     * Construct a new Bounding Box.
     *
     * @param geometryFactory The geometry factory.
     */
    public BoundingBoxDoubleGf(final GeometryFactory geometryFactory) {
        this.geometryFactory = geometryFactory;
        this.bounds = null;
    }

    public BoundingBoxDoubleGf(final GeometryFactory geometryFactory, final int axisCount, final double... bounds) {
        this.geometryFactory = geometryFactory;
        if (bounds == null || bounds.length == 0 || axisCount < 1) {
            this.bounds = null;
        } else if (bounds.length % axisCount == 0) {
            this.bounds = BoundingBoxUtil.newBounds(axisCount);
            BoundingBoxUtil.expand(geometryFactory, this.bounds, bounds);
        } else {
            throw new IllegalArgumentException("Expecting a multiple of " + axisCount + " not " + bounds.length);
        }
    }

    public BoundingBoxDoubleGf(final GeometryFactory geometryFactory, final Iterable<? extends Point> points) {
        this.geometryFactory = geometryFactory;
        double[] bounds = null;
        if (points != null) {
            for (final Point point : points) {
                if (point != null) {
                    if (bounds == null) {
                        bounds = BoundingBoxUtil.newBounds(geometryFactory, point);
                    } else {
                        BoundingBoxUtil.expand(geometryFactory, bounds, point);
                    }
                }
            }
        }
        this.bounds = bounds;
    }

    public BoundingBoxDoubleGf(final GeometryFactory geometryFactory, final LineString points) {
        this.geometryFactory = geometryFactory;
        double[] bounds = null;
        if (points != null) {
            for (int i = 0; i < points.getVertexCount(); i++) {
                final Point point = points.getPoint(0);
                if (bounds == null) {
                    bounds = BoundingBoxUtil.newBounds(geometryFactory, point);
                } else {
                    BoundingBoxUtil.expand(geometryFactory, bounds, point);
                }
            }
        }
        this.bounds = bounds;
    }

    public BoundingBoxDoubleGf(final GeometryFactory geometryFactory, final Point point) {
        this.geometryFactory = geometryFactory;
        double[] bounds = null;
        if (point != null) {
            bounds = BoundingBoxUtil.newBounds(geometryFactory, point);
        }
        this.bounds = bounds;
    }

    public BoundingBoxDoubleGf(final GeometryFactory geometryFactory, final Point... points) {
        this(geometryFactory, Lists.newArray(points));
    }

    public BoundingBoxDoubleGf(final GeometryFactory geometryFactory, final Vertex vertex) {
        this((Point) vertex);
    }

    public BoundingBoxDoubleGf(final int axisCount, final double... bounds) {
        this(null, axisCount, bounds);
    }

    public BoundingBoxDoubleGf(final Iterable<? extends Point> points) {
        this(null, points);
    }

    public BoundingBoxDoubleGf(final LineString points) {
        this(null, points);
    }

    public BoundingBoxDoubleGf(final Point... points) {
        this(null, points);
    }

    @Override
    public BoundingBox clipToCoordinateSystem() {
        final GeometryFactory geometryFactory = getGeometryFactory();
        final CoordinateSystem coordinateSystem = geometryFactory.getCoordinateSystem();
        if (coordinateSystem == null) {
            return this;
        } else {
            final BoundingBox areaBoundingBox = coordinateSystem.getAreaBoundingBox();
            return intersection(areaBoundingBox);
        }
    }

    /**
     * <p>Bounding boxes are immutable so clone returns this.</p>
     *
     * @return this
     */
    @Override
    public BoundingBox clone() {
        return this;
    }

    @Override
    public BoundingBox convert(final GeometryFactory geometryFactory) {
        final GeometryFactory factory = getGeometryFactory();
        if (geometryFactory == null) {
            return this;
        } else if (factory == geometryFactory) {
            return this;
        } else if (factory == null || factory.getCoordinateSystem() == null) {
            return new BoundingBoxDoubleGf(geometryFactory, getAxisCount(), getBounds());
        } else if (isEmpty()) {
            return new BoundingBoxDoubleGf(geometryFactory);
        } else {
            final CoordinatesOperation operation = ProjectionFactory.getCoordinatesOperation(factory,
                    geometryFactory);
            if (operation != null) {

                double xStep = getWidth() / 10;
                double yStep = getHeight() / 10;
                final double scaleXY = geometryFactory.getScaleXY();
                if (scaleXY > 0) {
                    if (xStep < 1 / scaleXY) {
                        xStep = 1 / scaleXY;
                    }
                    if (yStep < 1 / scaleXY) {
                        yStep = 1 / scaleXY;
                    }
                }

                final int axisCount = getAxisCount();

                final double minX = getMinX();
                final double maxX = getMaxX();
                final double minY = getMinY();
                final double maxY = getMaxY();

                final double[] bounds = getBounds();
                bounds[0] = Double.NaN;
                bounds[1] = Double.NaN;
                bounds[axisCount] = Double.NaN;
                bounds[axisCount + 1] = Double.NaN;

                final double[] to = new double[2];
                expand(geometryFactory, bounds, operation, to, minX, minY);
                expand(geometryFactory, bounds, operation, to, minX, maxY);
                expand(geometryFactory, bounds, operation, to, minX, minY);
                expand(geometryFactory, bounds, operation, to, maxX, minY);

                if (xStep != 0) {
                    for (double x = minX + xStep; x < maxX; x += xStep) {
                        expand(geometryFactory, bounds, operation, to, x, minY);
                        expand(geometryFactory, bounds, operation, to, x, maxY);
                    }
                }
                if (yStep != 0) {
                    for (double y = minY + yStep; y < maxY; y += yStep) {
                        expand(geometryFactory, bounds, operation, to, minX, y);
                        expand(geometryFactory, bounds, operation, to, maxX, y);
                    }
                }
                return new BoundingBoxDoubleGf(geometryFactory, axisCount, bounds);
            } else {
                return this;
            }
        }
    }

    @Override
    public BoundingBox convert(GeometryFactory geometryFactory, final int axisCount) {
        final GeometryFactory sourceGeometryFactory = getGeometryFactory();
        if (geometryFactory == null || sourceGeometryFactory == null) {
            return this;
        } else {
            geometryFactory = geometryFactory.convertAxisCount(axisCount);
            boolean copy = false;
            if (geometryFactory != null && sourceGeometryFactory != geometryFactory) {
                final int srid = getCoordinateSystemId();
                final int srid2 = geometryFactory.getCoordinateSystemId();
                if (srid <= 0) {
                    if (srid2 > 0) {
                        copy = true;
                    }
                } else if (srid != srid2) {
                    copy = true;
                }
                if (!copy) {
                    for (int axisIndex = 0; axisIndex < axisCount; axisIndex++) {
                        final double scale = sourceGeometryFactory.getScale(axisIndex);
                        final double scale1 = geometryFactory.getScale(axisIndex);
                        if (!Doubles.equal(scale, scale1)) {
                            copy = true;
                        }
                    }
                }
            }
            if (copy) {
                return convert(geometryFactory);
            } else {
                return this;
            }
        }
    }

    @Override
    public boolean coveredBy(final double... bounds) {
        final double minX1 = bounds[0];
        final double minY1 = bounds[1];
        final double maxX1 = bounds[2];
        final double maxY1 = bounds[3];
        final double minX2 = getMinX();
        final double minY2 = getMinY();
        final double maxX2 = getMaxX();
        final double maxY2 = getMaxY();
        return BoundingBoxUtil.covers(minX1, minY1, maxX1, maxY1, minX2, minY2, maxX2, maxY2);
    }

    /**
     * Tests if the <code>BoundingBoxDoubleGf other</code>
     * lies wholely inside this <code>BoundingBoxDoubleGf</code> (inclusive of the boundary).
     *
     *@param  other the <code>BoundingBoxDoubleGf</code> to check
     *@return true if this <code>BoundingBoxDoubleGf</code> covers the <code>other</code>
     */
    @Override
    public boolean covers(BoundingBox other) {
        if (other == null || isEmpty() || other.isEmpty()) {
            return false;
        } else {
            other = other.convert(getGeometryFactory());
            final double minX = getMinX();
            final double minY = getMinY();
            final double maxX = getMaxX();
            final double maxY = getMaxY();

            return other.coveredBy(minX, minY, maxX, maxY);
        }
    }

    /**
     * Tests if the given point lies in or on the envelope.
     *
     *@param  x  the x-coordinate of the point which this <code>BoundingBoxDoubleGf</code> is
     *      being checked for containing
     *@param  y  the y-coordinate of the point which this <code>BoundingBoxDoubleGf</code> is
     *      being checked for containing
     *@return    <code>true</code> if <code>(x, y)</code> lies in the interior or
     *      on the boundary of this <code>BoundingBoxDoubleGf</code>.
     */
    @Override
    public boolean covers(final double x, final double y) {
        if (isEmpty()) {
            return false;
        } else {
            final double minX = getMinX();
            final double minY = getMinY();
            final double maxX = getMaxX();
            final double maxY = getMaxY();

            return BoundingBoxUtil.covers(minX, minY, maxX, maxY, x, y, x, y);
        }
    }

    @Override
    public boolean covers(final Geometry geometry) {
        if (geometry == null) {
            return false;
        } else {
            final BoundingBox boundingBox = geometry.getBoundingBox();
            return covers(boundingBox);
        }
    }

    /**
     * Tests if the given point lies in or on the envelope.
     *
     *@param  p  the point which this <code>BoundingBoxDoubleGf</code> is
     *      being checked for containing
     *@return    <code>true</code> if the point lies in the interior or
     *      on the boundary of this <code>BoundingBoxDoubleGf</code>.
     */
    @Override
    public boolean covers(final Point point) {
        if (point == null || point.isEmpty()) {
            return false;
        } else {
            final GeometryFactory geometryFactory = getGeometryFactory();
            final Point projectedPoint = point.convertGeometry(geometryFactory);
            final double x = projectedPoint.getX();
            final double y = projectedPoint.getY();
            return covers(x, y);
        }
    }

    /**
     * Computes the distance between this and another
     * <code>BoundingBoxDoubleGf</code>.
     * The distance between overlapping Envelopes is 0.  Otherwise, the
     * distance is the Euclidean distance between the closest points.
     */
    @Override
    public double distance(BoundingBox boundingBox) {
        final GeometryFactory geometryFactory = getGeometryFactory();
        boundingBox = boundingBox.convert(geometryFactory);
        final double minX = getMinX();
        final double minY = getMinY();
        final double maxX = getMaxX();
        final double maxY = getMaxY();

        final double minX2 = boundingBox.getMinX();
        final double minY2 = boundingBox.getMinY();
        final double maxX2 = boundingBox.getMaxX();
        final double maxY2 = boundingBox.getMaxY();

        if (isEmpty(minX, maxX) || isEmpty(minX2, maxX2)) {
            // Empty
            return Double.MAX_VALUE;
        } else if (!(minX2 > maxX || maxX2 < minX || minY2 > maxY || maxY2 < minY)) {
            // Intersects
            return 0;
        } else {
            double dx;
            if (maxX < minX2) {
                dx = minX2 - maxX;
            } else {
                if (minX > maxX2) {
                    dx = minX - maxX2;
                } else {
                    dx = 0;
                }
            }

            double dy;
            if (maxY < minY2) {
                dy = minY2 - maxY;
            } else if (minY > maxY2) {
                dy = minY - maxY2;
            } else {
                dy = 0;
            }

            if (dx == 0.0) {
                return dy;
            } else if (dy == 0.0) {
                return dx;
            } else {
                return Math.sqrt(dx * dx + dy * dy);
            }
        }
    }

    @Override
    public double distance(final Geometry geometry) {
        final BoundingBox boundingBox = geometry.getBoundingBox();
        return distance(boundingBox);
    }

    /**
     * Computes the distance between this and another
     * <code>BoundingBoxDoubleGf</code>.
     * The distance between overlapping Envelopes is 0.  Otherwise, the
     * distance is the Euclidean distance between the closest points.
     */
    @Override
    public double distance(Point point) {
        point = point.convertGeometry(getGeometryFactory());
        if (intersects(point)) {
            return 0;
        } else {
            final double x = point.getX();
            final double y = point.getY();

            final double minX = getMinX();
            final double minY = getMinY();
            final double maxX = getMaxX();
            final double maxY = getMaxY();

            double dx = 0.0;
            if (maxX < x) {
                dx = x - maxX;
            } else if (minX > x) {
                dx = minX - x;
            }

            double dy = 0.0;
            if (maxY < y) {
                dy = y - maxY;
            } else if (minY > y) {
                dy = minY - y;
            }

            // if either is zero, the envelopes overlap either vertically or
            // horizontally
            if (dx == 0.0) {
                return dy;
            }
            if (dy == 0.0) {
                return dx;
            }
            return Math.sqrt(dx * dx + dy * dy);
        }
    }

    @Override
    public boolean equals(final Object other) {
        if (other instanceof BoundingBox) {
            final BoundingBox boundingBox = (BoundingBox) other;
            if (isEmpty()) {
                return boundingBox.isEmpty();
            } else if (boundingBox.isEmpty()) {
                return false;
            } else if (getCoordinateSystemId() == boundingBox.getCoordinateSystemId()) {
                if (getMaxX() == boundingBox.getMaxX()) {
                    if (getMaxY() == boundingBox.getMaxY()) {
                        if (getMinX() == boundingBox.getMinX()) {
                            if (getMinY() == boundingBox.getMinY()) {
                                return true;
                            }
                        }
                    }
                }

            }
        }
        return false;
    }

    /**
     * Return a new bounding box expanded by delta.
     *
     * @param delta
     * @return
     */
    @Override
    public BoundingBox expand(final double delta) {
        return expand(delta, delta);
    }

    /**
     * Return a new bounding box expanded by deltaX, deltaY.
     *
     * @param delta
     * @return
     */
    @Override
    public BoundingBox expand(final double deltaX, final double deltaY) {
        if (isEmpty() || deltaX == 0 && deltaY == 0) {
            return this;
        } else {
            final GeometryFactory geometryFactory = getGeometryFactory();
            final double x1 = getMinX() - deltaX;
            final double x2 = getMaxX() + deltaX;
            final double y1 = getMinY() - deltaY;
            final double y2 = getMaxY() + deltaY;

            if (x1 > x2 || y1 > y2) {
                return new BoundingBoxDoubleGf(geometryFactory);
            } else {
                return new BoundingBoxDoubleGf(geometryFactory, 2, x1, y1, x2, y2);
            }
        }
    }

    private void expand(final GeometryFactory geometryFactory, final double[] bounds,
            final CoordinatesOperation operation, final double[] to, final double... from) {

        operation.perform(2, from, 2, to);
        BoundingBoxUtil.expand(geometryFactory, bounds, to);
    }

    @Override
    public BoundingBox expand(final Point point) {
        final GeometryFactory geometryFactory = getGeometryFactory();
        if (isEmpty()) {
            return new BoundingBoxDoubleGf(geometryFactory, point);
        } else {
            final double x = point.getX();
            final double y = point.getY();

            double minX = getMinX();
            double maxX = getMaxX();
            double minY = getMinY();
            double maxY = getMaxY();

            if (x < minX) {
                minX = x;
            }
            if (x > maxX) {
                maxX = x;
            }
            if (y < minY) {
                minY = y;
            }
            if (y > maxY) {
                maxY = y;
            }
            return new BoundingBoxDoubleGf(geometryFactory, 2, minX, minY, maxX, maxY);
        }
    }

    @Override
    public BoundingBox expandPercent(final double factor) {
        return expandPercent(factor, factor);
    }

    @Override
    public BoundingBox expandPercent(final double factorX, final double factorY) {
        if (isEmpty()) {
            return this;
        } else {
            final double deltaX = getWidth() * factorX / 2;
            final double deltaY = getHeight() * factorY / 2;
            return expand(deltaX, deltaY);
        }
    }

    @Override
    public BoundingBox expandToInclude(final BoundingBox other) {
        if (other == null || other.isEmpty()) {
            return this;
        } else {
            final GeometryFactory geometryFactory = getGeometryFactory();
            final BoundingBox convertedOther = other.convert(geometryFactory);
            if (isEmpty()) {
                return convertedOther;
            } else if (covers(convertedOther)) {
                return this;
            } else {
                final double minX = Math.min(getMinX(), convertedOther.getMinX());
                final double maxX = Math.max(getMaxX(), convertedOther.getMaxX());
                final double minY = Math.min(getMinY(), convertedOther.getMinY());
                final double maxY = Math.max(getMaxY(), convertedOther.getMaxY());
                return new BoundingBoxDoubleGf(geometryFactory, 2, minX, minY, maxX, maxY);
            }
        }
    }

    @Override
    public BoundingBox expandToInclude(final double... coordinates) {
        if (coordinates == null || coordinates.length < 2) {
            return this;
        } else {
            final GeometryFactory geometryFactory = getGeometryFactory();
            if (isEmpty()) {
                return new BoundingBoxDoubleGf(geometryFactory, coordinates.length, coordinates);
            } else {
                final double[] bounds = getBounds();
                final int axisCount = getAxisCount();
                BoundingBoxUtil.expand(bounds, axisCount, coordinates);
                return new BoundingBoxDoubleGf(geometryFactory, axisCount, bounds);
            }
        }
    }

    @Override
    public BoundingBox expandToInclude(final Geometry geometry) {
        if (geometry == null || geometry.isEmpty()) {
            return this;
        } else {
            final GeometryFactory geometryFactory = getGeometryFactory();
            final Geometry convertedGeometry = geometry.convertGeometry(geometryFactory);
            final BoundingBox box = convertedGeometry.getBoundingBox();
            return expandToInclude(box);
        }
    }

    public BoundingBox expandToInclude(final Point point) {
        return expandToInclude((Geometry) point);
    }

    @Override
    public BoundingBox expandToInclude(final Record object) {
        if (object != null) {
            final Geometry geometry = object.getGeometry();
            return expandToInclude(geometry);
        }
        return this;
    }

    /**
     * Gets the area of this envelope.
     *
     * @return the area of the envelope
     * @return 0.0 if the envelope is null
     */
    @Override
    public double getArea() {
        if (getAxisCount() < 2) {
            return 0;
        } else {
            final double width = getWidth();
            final double height = getHeight();
            return width * height;
        }
    }

    /**
     * Get the aspect ratio x:y.
     *
     * @return The aspect ratio.
     */
    @Override
    public double getAspectRatio() {
        final double width = getWidth();
        final double height = getHeight();
        final double aspectRatio = width / height;
        return aspectRatio;
    }

    @Override
    public int getAxisCount() {
        if (this.bounds == null) {
            return 0;
        } else {
            return this.bounds.length / 2;
        }
    }

    public Point getBottomLeftPoint() {
        return getGeometryFactory().point(getMinX(), getMinY());
    }

    public Point getBottomRightPoint() {
        return getGeometryFactory().point(getMaxX(), getMinY());
    }

    @Override
    public double[] getBounds() {
        if (this.bounds == null) {
            return this.bounds;
        } else {
            return this.bounds.clone();
        }
    }

    @Override
    public double[] getBounds(final int axisCount) {
        if (this.bounds == null) {
            return this.bounds;
        } else {
            final double[] bounds = new double[2 * axisCount];
            for (int i = 0; i < axisCount; i++) {
                bounds[i] = getMin(i);
                bounds[i + axisCount] = getMax(i);
            }
            return bounds;
        }
    }

    @Override
    public Point getCentre() {
        if (isEmpty()) {
            return this.geometryFactory.point();
        } else {
            final double centreX = getCentreX();
            final double centreY = getCentreY();
            return this.geometryFactory.point(centreX, centreY);
        }
    }

    @Override
    public double getCentreX() {
        return getMinX() + getWidth() / 2;
    }

    @Override
    public double getCentreY() {
        return getMinY() + getHeight() / 2;
    }

    /**
     * Get the geometry factory.
     *
     * @return The geometry factory.
     */
    @Override
    public CoordinateSystem getCoordinateSystem() {
        final GeometryFactory geometryFactory = getGeometryFactory();
        if (geometryFactory == null) {
            return null;
        } else {
            return geometryFactory.getCoordinateSystem();
        }
    }

    /**
     * maxX,minY
     * minX,minY
     * minX,maxY
     * maxX,maxY
     */
    @Override
    public Point getCornerPoint(int index) {
        if (isEmpty()) {
            return null;
        } else {
            final double minX = getMinX();
            final double maxX = getMaxX();
            final double minY = getMinY();
            final double maxY = getMaxY();
            index = index % 4;
            switch (index) {
            case 0:
                return new PointDoubleGf(getGeometryFactory(), maxX, minY);
            case 1:
                return new PointDoubleGf(getGeometryFactory(), minX, minY);
            case 2:
                return new PointDoubleGf(getGeometryFactory(), minX, maxY);
            default:
                return new PointDoubleGf(getGeometryFactory(), maxX, maxY);
            }
        }
    }

    @Override
    public LineString getCornerPoints() {
        final double minX = getMinX();
        final double maxX = getMaxX();
        final double minY = getMinY();
        final double maxY = getMaxY();
        return new LineStringDouble(2, maxX, minY, minX, minY, minX, maxY, maxX, maxY);
    }

    @Override
    public GeometryFactory getGeometryFactory() {
        return this.geometryFactory;
    }

    /**
     *  Returns the difference between the maximum and minimum y values.
     *
     *@return    max y - min y, or 0 if this is a null <code>BoundingBoxDoubleGf</code>
     */
    @Override
    public double getHeight() {
        if (getAxisCount() < 2) {
            return 0;
        } else {
            return getMaxY() - getMinY();
        }
    }

    @Override
    public Measure<Length> getHeightLength() {
        final double height = getHeight();
        final CoordinateSystem coordinateSystem = getCoordinateSystem();
        if (coordinateSystem == null) {
            return Measure.valueOf(height, SI.METRE);
        } else {
            return Measure.valueOf(height, coordinateSystem.getLengthUnit());
        }
    }

    @Override
    public double getMax(final int axisIndex) {
        if (this.bounds == null || axisIndex >= getAxisCount()) {
            return Double.NaN;
        } else {
            return BoundingBoxUtil.getMax(this.bounds, axisIndex);
        }
    }

    @Override
    public <Q extends Quantity> Measurable<Q> getMaximum(final int axisIndex) {
        final Unit<Q> unit = getUnit();
        final double max = this.getMax(axisIndex);
        return Measure.valueOf(max, unit);
    }

    @Override
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public <Q extends Quantity> double getMaximum(final int axisIndex, final Unit convertUnit) {
        final Measurable<Quantity> max = getMaximum(axisIndex);
        return max.doubleValue(convertUnit);
    }

    /**
     *  Returns the <code>BoundingBoxDoubleGf</code>s maximum x-value. min x > max x
     *  indicates that this is a null <code>BoundingBoxDoubleGf</code>.
     *
     *@return    the maximum x-coordinate
     */
    @Override
    public double getMaxX() {
        return getMax(0);
    }

    /**
     *  Returns the <code>BoundingBoxDoubleGf</code>s maximum y-value. min y > max y
     *  indicates that this is a null <code>BoundingBoxDoubleGf</code>.
     *
     *@return    the maximum y-coordinate
     */
    @Override
    public double getMaxY() {
        return getMax(1);
    }

    @Override
    public double getMin(final int axisIndex) {
        if (this.bounds == null) {
            return Double.NaN;
        } else {
            return BoundingBoxUtil.getMin(this.bounds, axisIndex);
        }
    }

    @Override
    public <Q extends Quantity> Measurable<Q> getMinimum(final int axisIndex) {
        final Unit<Q> unit = getUnit();
        final double min = this.getMin(axisIndex);
        return Measure.valueOf(min, unit);
    }

    @Override
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public <Q extends Quantity> double getMinimum(final int axisIndex, final Unit convertUnit) {
        final Measurable<Quantity> min = getMinimum(axisIndex);
        return min.doubleValue(convertUnit);
    }

    /**
     *  Returns the <code>BoundingBoxDoubleGf</code>s minimum x-value. min x > max x
     *  indicates that this is a null <code>BoundingBoxDoubleGf</code>.
     *
     *@return    the minimum x-coordinate
     */
    @Override
    public double getMinX() {
        return getMin(0);
    }

    /**
     *  Returns the <code>BoundingBoxDoubleGf</code>s minimum y-value. min y > max y
     *  indicates that this is a null <code>BoundingBoxDoubleGf</code>.
     *
     *@return    the minimum y-coordinate
     */
    @Override
    public double getMinY() {
        return getMin(1);
    }

    @Override
    public Point getRandomPointWithin() {
        final double x = getMinX() + getWidth() * Math.random();
        final double y = getMinY() + getHeight() * Math.random();
        return this.geometryFactory.point(x, y);
    }

    @Override
    public Point getTopLeftPoint() {
        return getGeometryFactory().point(getMinX(), getMaxY());
    }

    public Point getTopRightPoint() {
        return getGeometryFactory().point(getMaxX(), getMaxY());
    }

    @SuppressWarnings("unchecked")
    private <Q extends Quantity> Unit<Q> getUnit() {
        final CoordinateSystem coordinateSystem = getCoordinateSystem();
        if (coordinateSystem == null) {
            return (Unit<Q>) SI.METRE;
        } else {
            return coordinateSystem.<Q>getUnit();
        }
    }

    /**
     *  Returns the difference between the maximum and minimum x values.
     *
     *@return    max x - min x, or 0 if this is a null <code>BoundingBoxDoubleGf</code>
     */
    @Override
    public double getWidth() {
        if (getAxisCount() < 2) {
            return 0;
        } else {
            final double minX = getMinX();
            final double maxX = getMaxX();

            return maxX - minX;
        }
    }

    @Override
    public Measure<Length> getWidthLength() {
        final double width = getWidth();
        final CoordinateSystem coordinateSystem = getCoordinateSystem();
        if (coordinateSystem == null) {
            return Measure.valueOf(width, SI.METRE);
        } else {
            return Measure.valueOf(width, coordinateSystem.getLengthUnit());
        }
    }

    @Override
    public int hashCode() {
        final double minX = getMinX();
        final double minY = getMinY();
        final double maxX = getMaxX();
        final double maxY = getMaxY();
        int result = 17;
        result = 37 * result + MathUtil.hashCode(minX);
        result = 37 * result + MathUtil.hashCode(maxX);
        result = 37 * result + MathUtil.hashCode(minY);
        result = 37 * result + MathUtil.hashCode(maxY);
        return result;
    }

    /**
     * Computes the intersection of two {@link BoundingBoxDoubleGf}s.
     *
     * @param env the envelope to intersect with
     * @return a new BoundingBox representing the intersection of the envelopes (this will be
     * the null envelope if either argument is null, or they do not intersect
     */
    @Override
    public BoundingBox intersection(final BoundingBox boundingBox) {
        final GeometryFactory geometryFactory = getGeometryFactory();
        final BoundingBox convertedBoundingBox = boundingBox.convert(geometryFactory);
        if (isEmpty() || convertedBoundingBox.isEmpty() || !intersects(convertedBoundingBox)) {
            return new BoundingBoxDoubleGf(geometryFactory);
        } else {
            final double intMinX = Math.max(getMinX(), convertedBoundingBox.getMinX());
            final double intMinY = Math.max(getMinY(), convertedBoundingBox.getMinY());
            final double intMaxX = Math.min(getMaxX(), convertedBoundingBox.getMaxX());
            final double intMaxY = Math.min(getMaxY(), convertedBoundingBox.getMaxY());
            return new BoundingBoxDoubleGf(geometryFactory, 2, intMinX, intMinY, intMaxX, intMaxY);
        }
    }

    /**
     *  Check if the region defined by <code>other</code>
     *  overlaps (intersects) the region of this <code>BoundingBoxDoubleGf</code>.
     *
     *@param  other  the <code>BoundingBoxDoubleGf</code> which this <code>BoundingBoxDoubleGf</code> is
     *          being checked for overlapping
     *@return        <code>true</code> if the <code>BoundingBoxDoubleGf</code>s overlap
     */
    @Override
    public boolean intersects(final BoundingBox other) {
        if (isEmpty() || other.isEmpty()) {
            return false;
        } else {
            final GeometryFactory geometryFactory = getGeometryFactory();
            final BoundingBox convertedBoundingBox = other.convert(geometryFactory, 2);
            final double minX = getMinX();
            final double minY = getMinY();
            final double maxX = getMaxX();
            final double maxY = getMaxY();

            final double minX2 = convertedBoundingBox.getMinX();
            final double minY2 = convertedBoundingBox.getMinY();
            final double maxX2 = convertedBoundingBox.getMaxX();
            final double maxY2 = convertedBoundingBox.getMaxY();
            return !(minX2 > maxX || maxX2 < minX || minY2 > maxY || maxY2 < minY);
        }
    }

    /**
     *  Check if the point <code>(x, y)</code>
     *  overlaps (lies inside) the region of this <code>BoundingBoxDoubleGf</code>.
     *
     *@param  x  the x-ordinate of the point
     *@param  y  the y-ordinate of the point
     *@return        <code>true</code> if the point overlaps this <code>BoundingBoxDoubleGf</code>
     */
    @Override
    public boolean intersects(final double x, final double y) {
        if (isEmpty()) {
            return false;
        } else {
            final double minX = getMinX();
            final double minY = getMinY();
            final double maxX = getMaxX();
            final double maxY = getMaxY();
            return !(x > maxX || x < minX || y > maxY || y < minY);
        }
    }

    @Override
    public boolean intersects(double x0, double y0, double x1, double y1) {
        int out1 = outcode(x0, y0);
        int out2 = outcode(x1, y1);
        final double xmin = getMinX();
        final double ymin = getMinY();
        final double xmax = getMaxX();
        final double ymax = getMaxY();
        while (true) {
            if ((out1 | out2) == 0) {
                return true;
            } else if ((out1 & out2) != 0) {
                return false;
            } else {

                int out;
                if (out1 != 0) {
                    out = out1;
                } else {
                    out = out2;
                }

                double x = 0;
                double y = 0;
                if ((out & OUT_TOP) != 0) {
                    x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0);
                    y = ymax;
                } else if ((out & OUT_BOTTOM) != 0) {
                    x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0);
                    y = ymin;
                } else if ((out & OUT_RIGHT) != 0) {
                    y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0);
                    x = xmax;
                } else if ((out & OUT_LEFT) != 0) {
                    y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0);
                    x = xmin;
                }

                if (out == out1) {
                    x0 = x;
                    y0 = y;
                    out1 = outcode(x0, y0);
                } else {
                    x1 = x;
                    y1 = y;
                    out2 = outcode(x1, y1);
                }
            }
        }
    }

    @Override
    public boolean intersects(final Geometry geometry) {
        return geometry.intersects(this);
    }

    /**
     *  Check if the point <code>p</code>
     *  overlaps (lies inside) the region of this <code>BoundingBoxDoubleGf</code>.
     *
     *@param  p  the <code>Coordinate</code> to be tested
     *@return        <code>true</code> if the point overlaps this <code>BoundingBoxDoubleGf</code>
     */
    @Override
    public boolean intersects(final Point point) {
        return point.intersects(this);
    }

    @Override
    public boolean isEmpty() {
        final double minX = getMinX();
        final double maxX = getMaxX();
        if (Double.isNaN(minX)) {
            return true;
        } else if (Double.isNaN(maxX)) {
            return true;
        } else {
            return maxX < minX;
        }
    }

    @Override
    public boolean isWithinDistance(final BoundingBox boundingBox, final double maxDistance) {
        final double distance = boundingBox.distance(boundingBox);
        if (distance < maxDistance) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean isWithinDistance(final Geometry geometry, final double maxDistance) {
        final BoundingBox boundingBox = geometry.getBoundingBox();
        return isWithinDistance(boundingBox, maxDistance);
    }

    /**
     * <p>Construct a new new BoundingBox by moving the min/max x coordinates by xDisplacement and
     * the min/max y coordinates by yDisplacement. If the bounding box is null or the xDisplacement
     * and yDisplacement are 0 then this bounding box will be returned.</p>
     *
     * @param xDisplacement The distance to move the min/max x coordinates.
     * @param yDisplacement The distance to move the min/max y coordinates.
     * @return The moved bounding box.
     */
    @Override
    public BoundingBox move(final double xDisplacement, final double yDisplacement) {
        if (isEmpty() || xDisplacement == 0 && yDisplacement == 0) {
            return this;
        } else {
            final GeometryFactory geometryFactory = getGeometryFactory();
            final double x1 = getMinX() + xDisplacement;
            final double x2 = getMaxX() + xDisplacement;
            final double y1 = getMinY() + yDisplacement;
            final double y2 = getMaxY() + yDisplacement;
            return new BoundingBoxDoubleGf(geometryFactory, 2, x1, y1, x2, y2);
        }
    }

    private int outcode(final double x, final double y) {
        int out = 0;
        if (x < getMinX()) {
            out |= OUT_LEFT;
        } else if (x > getMaxX()) {
            out |= OUT_RIGHT;
        }
        if (y < getMinY()) {
            out |= OUT_BOTTOM;
        } else if (y > getMaxY()) {
            out |= OUT_TOP;
        }
        return out;
    }

    @Override
    public Geometry toGeometry() {
        GeometryFactory geometryFactory = getGeometryFactory();
        if (geometryFactory == null) {
            geometryFactory = GeometryFactory.floating(0, 2);
        }
        if (isEmpty()) {
            return geometryFactory.point();
        } else {
            final double minX = getMinX();
            final double minY = getMinY();
            final double maxX = getMaxX();
            final double maxY = getMaxY();
            final double width = getWidth();
            final double height = getHeight();
            if (width == 0 && height == 0) {
                return geometryFactory.point(minX, minY);
            } else if (width == 0 || height == 0) {
                return geometryFactory.lineString(2, minX, minY, maxX, maxY);
            } else {
                return geometryFactory.polygon(
                        geometryFactory.linearRing(2, minX, minY, minX, maxY, maxX, maxY, maxX, minY, minX, minY));
            }
        }
    }

    @Override
    public Polygon toPolygon() {
        return toPolygon(100, 100);

    }

    @Override
    public Polygon toPolygon(final GeometryFactory factory) {
        return toPolygon(factory, 100, 100);
    }

    @Override
    public Polygon toPolygon(final GeometryFactory factory, final int numSegments) {
        return toPolygon(factory, numSegments, numSegments);
    }

    @Override
    public Polygon toPolygon(GeometryFactory geometryFactory, int numX, int numY) {
        if (isEmpty()) {
            return geometryFactory.polygon();
        } else {
            final GeometryFactory factory = getGeometryFactory();
            if (geometryFactory == null) {
                if (factory == null) {
                    geometryFactory = GeometryFactory.floating(0, 2);
                } else {
                    geometryFactory = factory;
                }
            }
            try {
                double minStep = 0.00001;
                final CoordinateSystem coordinateSystem = factory.getCoordinateSystem();
                if (coordinateSystem instanceof ProjectedCoordinateSystem) {
                    minStep = 1;
                } else {
                    minStep = 0.00001;
                }

                double xStep;
                final double width = getWidth();
                if (numX <= 1) {
                    numX = 1;
                    xStep = width;
                } else {
                    xStep = width / numX;
                    if (xStep < minStep) {
                        xStep = minStep;
                    }
                    numX = Math.max(1, (int) Math.ceil(width / xStep));
                }

                double yStep;
                if (numY <= 1) {
                    numY = 1;
                    yStep = getHeight();
                } else {
                    yStep = getHeight() / numY;
                    if (yStep < minStep) {
                        yStep = minStep;
                    }
                    numY = Math.max(1, (int) Math.ceil(getHeight() / yStep));
                }

                final double minX = getMinX();
                final double maxX = getMaxX();
                final double minY = getMinY();
                final double maxY = getMaxY();
                final int numCoordinates = 1 + 2 * (numX + numY);
                final double[] coordinates = new double[numCoordinates * 2];
                int i = 0;

                CoordinatesListUtil.setCoordinates(coordinates, 2, i, maxX, minY);
                i++;
                for (int j = 0; j < numX - 1; j++) {
                    CoordinatesListUtil.setCoordinates(coordinates, 2, i, maxX - j * xStep, minY);
                    i++;
                }
                CoordinatesListUtil.setCoordinates(coordinates, 2, i, minX, minY);
                i++;

                for (int j = 0; j < numY - 1; j++) {
                    CoordinatesListUtil.setCoordinates(coordinates, 2, i, minX, minY + j * yStep);
                    i++;
                }
                CoordinatesListUtil.setCoordinates(coordinates, 2, i, minX, maxY);
                i++;

                for (int j = 0; j < numX - 1; j++) {
                    CoordinatesListUtil.setCoordinates(coordinates, 2, i, minX + j * xStep, maxY);
                    i++;
                }

                CoordinatesListUtil.setCoordinates(coordinates, 2, i, maxX, maxY);
                i++;

                for (int j = 0; j < numY - 1; j++) {
                    CoordinatesListUtil.setCoordinates(coordinates, 2, i, maxX, minY + (numY - j) * yStep);
                    i++;
                }
                CoordinatesListUtil.setCoordinates(coordinates, 2, i, maxX, minY);

                final LinearRing ring = factory.linearRing(2, coordinates);
                final Polygon polygon = factory.polygon(ring);
                if (geometryFactory == null) {
                    return polygon;
                } else {
                    return (Polygon) polygon.convertGeometry(geometryFactory);
                }
            } catch (final IllegalArgumentException e) {
                Logs.error(this, "Unable to convert to polygon: " + this, e);
                return geometryFactory.polygon();
            }
        }
    }

    @Override
    public Polygon toPolygon(final int numSegments) {
        return toPolygon(numSegments, numSegments);
    }

    @Override
    public Polygon toPolygon(final int numX, final int numY) {
        final GeometryFactory geometryFactory = getGeometryFactory();
        return toPolygon(geometryFactory, numX, numY);
    }

    @Override
    public String toString() {
        final StringBuilder s = new StringBuilder();
        final int srid = getCoordinateSystemId();
        if (srid > 0) {
            s.append("SRID=");
            s.append(srid);
            s.append(";");
        }
        if (isEmpty()) {
            s.append("BBOX EMPTY");
        } else {
            s.append("BBOX");
            final int axisCount = getAxisCount();
            if (axisCount == 3) {
                s.append(" Z");
            } else if (axisCount == 4) {
                s.append(" ZM");
            } else if (axisCount != 2) {
                s.append(" ");
                s.append(axisCount);
            }
            s.append("(");
            for (int axisIndex = 0; axisIndex < axisCount; axisIndex++) {
                if (axisIndex > 0) {
                    s.append(',');
                }
                s.append(Doubles.toString(getMin(axisIndex)));
            }
            s.append(' ');
            for (int axisIndex = 0; axisIndex < axisCount; axisIndex++) {
                if (axisIndex > 0) {
                    s.append(',');
                }
                s.append(Doubles.toString(getMax(axisIndex)));
            }
            s.append(')');
        }
        return s.toString();
    }

}