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. (">=" instead of ">") 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 195 /** 196 * Computes the scale of this bounds with respect to the given width/height. 197 * @param width the width 198 * @param height the height 199 * @return the computed scale 200 */ 201 public double getScale(final int width, final int height) { 202 // -20 to leave some border 203 int w = width - 20; 204 if (w < 20) { 205 w = 20; 206 } 207 int h = height - 20; 208 if (h < 20) { 209 h = 20; 210 } 211 212 double scaleX = getDeltaEast() / w; 213 double scaleY = getDeltaNorth() / h; 214 return Math.max(scaleX, scaleY); 215 } 216 217 private double getDeltaNorth() { 218 return maxNorth - minNorth; 219 } 220 221 private double getDeltaEast() { 222 return maxEast - minEast; 223 } 224}