001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data;
003
004import org.openstreetmap.josm.data.coor.EastNorth;
005import org.openstreetmap.josm.tools.Utils;
006
007/**
008 * This is a simple data class for "rectangular" areas of the world, given in
009 * east/north min/max values.
010 *
011 * @author imi
012 */
013public class ProjectionBounds {
014    /**
015     * The minimum east coordinate.
016     */
017    public double minEast;
018    /**
019     * The minimum north coordinate.
020     */
021    public double minNorth;
022    /**
023     * The maximum east coordinate.
024     */
025    public double maxEast;
026    /**
027     * The minimum north coordinate.
028     */
029    public double maxNorth;
030
031    /**
032     * Construct bounds out of two points.
033     * @param min min east/north
034     * @param max max east/north
035     */
036    public ProjectionBounds(EastNorth min, EastNorth max) {
037        this.minEast = min.east();
038        this.minNorth = min.north();
039        this.maxEast = max.east();
040        this.maxNorth = max.north();
041    }
042
043    /**
044     * Construct bounds out of a single point.
045     * @param p east/north
046     */
047    public ProjectionBounds(EastNorth p) {
048        this.minEast = this.maxEast = p.east();
049        this.minNorth = this.maxNorth = p.north();
050    }
051
052    /**
053     * Construct bounds out of a center point and east/north dimensions.
054     * @param center center east/north
055     * @param east east dimension
056     * @param north north dimension
057     */
058    public ProjectionBounds(EastNorth center, double east, double north) {
059        this.minEast = center.east()-east/2.0;
060        this.minNorth = center.north()-north/2.0;
061        this.maxEast = center.east()+east/2.0;
062        this.maxNorth = center.north()+north/2.0;
063    }
064
065    /**
066     * Construct bounds out of two points.
067     * @param minEast min east
068     * @param minNorth min north
069     * @param maxEast max east
070     * @param maxNorth max north
071     */
072    public ProjectionBounds(double minEast, double minNorth, double maxEast, double maxNorth) {
073        this.minEast = minEast;
074        this.minNorth = minNorth;
075        this.maxEast = maxEast;
076        this.maxNorth = maxNorth;
077    }
078
079    /**
080     * Construct uninitialized bounds.
081     * <p>
082     * At least one call to {@link #extend(EastNorth)} or {@link #extend(ProjectionBounds)}
083     * is required immediately after construction to initialize the {@code ProjectionBounds}
084     * instance and make it valid.
085     * <p>
086     * Uninitialized {@code ProjectionBounds} must not be passed to other methods
087     * or used in any way other than initializing it.
088     */
089    public ProjectionBounds() {
090        this.minEast = Double.POSITIVE_INFINITY;
091        this.minNorth = Double.POSITIVE_INFINITY;
092        this.maxEast = Double.NEGATIVE_INFINITY;
093        this.maxNorth = Double.NEGATIVE_INFINITY;
094    }
095
096    /**
097     * Extends bounds to include point {@code e}.
098     * @param e east/north to include
099     */
100    public void extend(EastNorth e) {
101        if (e.east() < minEast) {
102            minEast = e.east();
103        }
104        if (e.east() > maxEast) {
105            maxEast = e.east();
106        }
107        if (e.north() < minNorth) {
108            minNorth = e.north();
109        }
110        if (e.north() > maxNorth) {
111            maxNorth = e.north();
112        }
113    }
114
115    /**
116     * Extends bounds to include bounds {@code b}.
117     * @param b bounds to include
118     * @since 11774
119     */
120    public void extend(ProjectionBounds b) {
121        if (b.minEast < minEast) {
122            minEast = b.minEast;
123        }
124        if (b.maxEast > maxEast) {
125            maxEast = b.maxEast;
126        }
127        if (b.minNorth < minNorth) {
128            minNorth = b.minNorth;
129        }
130        if (b.maxNorth > maxNorth) {
131            maxNorth = b.maxNorth;
132        }
133    }
134
135    /**
136     * Returns the center east/north.
137     * @return the center east/north
138     */
139    public EastNorth getCenter() {
140        return new EastNorth((minEast + maxEast) / 2.0, (minNorth + maxNorth) / 2.0);
141    }
142
143    @Override
144    public String toString() {
145        return "ProjectionBounds["+minEast+','+minNorth+','+maxEast+','+maxNorth+']';
146    }
147
148    /**
149     * The two bounds intersect? Compared to java Shape.intersects, if does not use
150     * the interior but the closure. ("&gt;=" instead of "&gt;")
151     * @param b projection bounds
152     * @return {@code true} if the two bounds intersect
153     */
154    public boolean intersects(ProjectionBounds b) {
155        return b.maxEast >= minEast &&
156        b.maxNorth >= minNorth &&
157        b.minEast <= maxEast &&
158        b.minNorth <= maxNorth;
159    }
160
161    /**
162     * Check, if a point is within the bounds.
163     * @param en the point
164     * @return true, if <code>en</code> is within the bounds
165     */
166    public boolean contains(EastNorth en) {
167        return minEast <= en.east() && en.east() <= maxEast &&
168                minNorth <= en.north() && en.north() <= maxNorth;
169    }
170
171    /**
172     * Returns the min east/north.
173     * @return the min east/north
174     */
175    public EastNorth getMin() {
176        return new EastNorth(minEast, minNorth);
177    }
178
179    /**
180     * Returns the max east/north.
181     * @return the max east/north
182     */
183    public EastNorth getMax() {
184        return new EastNorth(maxEast, maxNorth);
185    }
186
187    /**
188     * Determines if the bounds area is not null
189     * @return {@code true} if the area is not null
190     */
191    public boolean hasExtend() {
192        return !Utils.equalsEpsilon(minEast, maxEast) || !Utils.equalsEpsilon(minNorth, maxNorth);
193    }
194}