001:
002: /*
003: * The JTS Topology Suite is a collection of Java classes that
004: * implement the fundamental operations required to validate a given
005: * geo-spatial data set to a known topological specification.
006: *
007: * Copyright (C) 2001 Vivid Solutions
008: *
009: * This library is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU Lesser General Public
011: * License as published by the Free Software Foundation; either
012: * version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * Lesser General Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022: *
023: * For more information, contact:
024: *
025: * Vivid Solutions
026: * Suite #1A
027: * 2328 Government Street
028: * Victoria BC V8T 5G5
029: * Canada
030: *
031: * (250)385-6040
032: * www.vividsolutions.com
033: */
034: package com.vividsolutions.jts.geom;
035:
036: import java.io.Serializable;
037: import java.util.HashMap;
038: import java.util.Map;
039:
040: /**
041: * Specifies the precision model of the {@link Coordinate}s in a {@link Geometry}.
042: * In other words, specifies the grid of allowable
043: * points for all <code>Geometry</code>s.
044: * <p>
045: * The {@link makePrecise} method allows rounding a coordinate to
046: * a "precise" value; that is, one whose
047: * precision is known exactly.
048: *<p>
049: * Coordinates are assumed to be precise in geometries.
050: * That is, the coordinates are assumed to be rounded to the
051: * precision model given for the geometry.
052: * JTS input routines automatically round coordinates to the precision model
053: * before creating Geometries.
054: * All internal operations
055: * assume that coordinates are rounded to the precision model.
056: * Constructive methods (such as boolean operations) always round computed
057: * coordinates to the appropriate precision model.
058: * <p>
059: * Currently three types of precision model are supported:
060: * <ul>
061: * <li>FLOATING - represents full double precision floating point.
062: * This is the default precision model used in JTS
063: * <li>FLOATING_SINGLE - represents single precision floating point.
064: * <li>FIXED - represents a model with a fixed number of decimal places.
065: * A Fixed Precision Model is specified by a scale factor.
066: * The scale factor specifies the grid which numbers are rounded to.
067: * Input coordinates are mapped to fixed coordinates according to the following
068: * equations:
069: * <UL>
070: * <LI> jtsPt.x = round( (inputPt.x * scale ) / scale
071: * <LI> jtsPt.y = round( (inputPt.y * scale ) / scale
072: * </UL>
073: * </ul>
074: * Coordinates are represented internally as Java double-precision values.
075: * Since Java uses the IEEE-394 floating point standard, this
076: * provides 53 bits of precision. (Thus the maximum precisely representable
077: * integer is 9,007,199,254,740,992).
078: *<p>
079: * JTS methods currently do not handle inputs with different precision models.
080: *
081: *@version 1.7
082: */
083: public class PrecisionModel implements Serializable, Comparable {
084: /**
085: * Determines which of two {@link PrecisionModel}s is the most precise
086: * (allows the greatest number of significant digits).
087: *
088: * @param pm1 a PrecisionModel
089: * @param pm2 a PrecisionModel
090: * @return the PrecisionModel which is most precise
091: */
092: public static PrecisionModel mostPrecise(PrecisionModel pm1,
093: PrecisionModel pm2) {
094: if (pm1.compareTo(pm2) >= 0)
095: return pm1;
096: return pm2;
097: }
098:
099: private static final long serialVersionUID = 7777263578777803835L;
100:
101: /**
102: * The types of Precision Model which JTS supports.
103: * <p>
104: * This class is only for use to support the "enums" for the types of precision model.
105: * <p>
106: * <i>
107: * Note: Type should be declared as private to this class,
108: * but JBuilder 7 throws a compiler exception
109: * when trying to compile with the "private" keyword. Package-private is safe enough.
110: * </i>
111: */
112: static class Type implements Serializable {
113: private static final long serialVersionUID = -5528602631731589822L;
114: private static Map nameToTypeMap = new HashMap();
115:
116: public Type(String name) {
117: this .name = name;
118: nameToTypeMap.put(name, this );
119: }
120:
121: private String name;
122:
123: public String toString() {
124: return name;
125: }
126:
127: /**
128: * @see http://www.javaworld.com/javaworld/javatips/jw-javatip122.html
129: */
130: private Object readResolve() {
131: return nameToTypeMap.get(name);
132: }
133: }
134:
135: /**
136: * Fixed Precision indicates that coordinates have a fixed number of decimal places.
137: * The number of decimal places is determined by the log10 of the scale factor.
138: */
139: public static final Type FIXED = new Type("FIXED");
140: /**
141: * Floating precision corresponds to the standard Java
142: * double-precision floating-point representation, which is
143: * based on the IEEE-754 standard
144: */
145: public static final Type FLOATING = new Type("FLOATING");
146: /**
147: * Floating single precision corresponds to the standard Java
148: * single-precision floating-point representation, which is
149: * based on the IEEE-754 standard
150: */
151: public static final Type FLOATING_SINGLE = new Type(
152: "FLOATING SINGLE");
153:
154: /**
155: * The maximum precise value representable in a double. Since IEE754
156: * double-precision numbers allow 53 bits of mantissa, the value is equal to
157: * 2^53 - 1. This provides <i>almost</i> 16 decimal digits of precision.
158: */
159: public final static double maximumPreciseValue = 9007199254740992.0;
160:
161: /**
162: * The type of PrecisionModel this represents.
163: */
164: private Type modelType;
165: /**
166: * The scale factor which determines the number of decimal places in fixed precision.
167: */
168: private double scale;
169:
170: /**
171: * Creates a <code>PrecisionModel</code> with a default precision
172: * of FLOATING.
173: */
174: public PrecisionModel() {
175: // default is floating precision
176: modelType = FLOATING;
177: }
178:
179: /**
180: * Creates a <code>PrecisionModel</code> that specifies
181: * an explicit precision model type.
182: * If the model type is FIXED the scale factor will default to 1.
183: *
184: * @param modelType the type of the precision model
185: */
186: public PrecisionModel(Type modelType) {
187: this .modelType = modelType;
188: if (modelType == FIXED) {
189: setScale(1.0);
190: }
191: }
192:
193: /**
194: * Creates a <code>PrecisionModel</code> that specifies Fixed precision.
195: * Fixed-precision coordinates are represented as precise internal coordinates,
196: * which are rounded to the grid defined by the scale factor.
197: *
198: *@param scale amount by which to multiply a coordinate after subtracting
199: * the offset, to obtain a precise coordinate
200: *@param offsetX not used.
201: *@param offsetY not used.
202: *
203: * @deprecated offsets are no longer supported, since internal representation is rounded floating point
204: */
205: public PrecisionModel(double scale, double offsetX, double offsetY) {
206: modelType = FIXED;
207: setScale(scale);
208: }
209:
210: /**
211: * Creates a <code>PrecisionModel</code> that specifies Fixed precision.
212: * Fixed-precision coordinates are represented as precise internal coordinates,
213: * which are rounded to the grid defined by the scale factor.
214: *
215: *@param scale amount by which to multiply a coordinate after subtracting
216: * the offset, to obtain a precise coordinate
217: */
218: public PrecisionModel(double scale) {
219: modelType = FIXED;
220: setScale(scale);
221: }
222:
223: /**
224: * Copy constructor to create a new <code>PrecisionModel</code>
225: * from an existing one.
226: */
227: public PrecisionModel(PrecisionModel pm) {
228: modelType = pm.modelType;
229: scale = pm.scale;
230: }
231:
232: /**
233: * Tests whether the precision model supports floating point
234: * @return <code>true</code> if the precision model supports floating point
235: */
236: public boolean isFloating() {
237: return modelType == FLOATING || modelType == FLOATING_SINGLE;
238: }
239:
240: /**
241: * Returns the maximum number of significant digits provided by this
242: * precision model.
243: * Intended for use by routines which need to print out precise values.
244: *
245: * @return the maximum number of decimal places provided by this precision model
246: */
247: public int getMaximumSignificantDigits() {
248: int maxSigDigits = 16;
249: if (modelType == FLOATING) {
250: maxSigDigits = 16;
251: } else if (modelType == FLOATING_SINGLE) {
252: maxSigDigits = 6;
253: } else if (modelType == FIXED) {
254: maxSigDigits = 1 + (int) Math.ceil(Math.log(getScale())
255: / Math.log(10));
256: }
257: return maxSigDigits;
258: }
259:
260: /**
261: * Returns the multiplying factor used to obtain a precise coordinate.
262: * This method is private because PrecisionModel is intended to
263: * be an immutable (value) type.
264: *
265: *@return the amount by which to multiply a coordinate after subtracting
266: * the offset
267: */
268: public double getScale() {
269: return scale;
270: }
271:
272: /**
273: * Gets the type of this PrecisionModel
274: * @return the type of this PrecisionModel
275: */
276: public Type getType() {
277: return modelType;
278: }
279:
280: /**
281: * Sets the multiplying factor used to obtain a precise coordinate.
282: * This method is private because PrecisionModel is intended to
283: * be an immutable (value) type.
284: *
285: */
286: private void setScale(double scale) {
287: this .scale = Math.abs(scale);
288: }
289:
290: /**
291: * Returns the x-offset used to obtain a precise coordinate.
292: *
293: * @return the amount by which to subtract the x-coordinate before
294: * multiplying by the scale
295: * @deprecated Offsets are no longer used
296: */
297: public double getOffsetX() {
298: //We actually don't use offsetX and offsetY anymore ... [Jon Aquino]
299: return 0;
300: }
301:
302: /**
303: * Returns the y-offset used to obtain a precise coordinate.
304: *
305: * @return the amount by which to subtract the y-coordinate before
306: * multiplying by the scale
307: * @deprecated Offsets are no longer used
308: */
309: public double getOffsetY() {
310: return 0;
311: }
312:
313: /**
314: * Sets <code>internal</code> to the precise representation of <code>external</code>.
315: *
316: * @param external the original coordinate
317: * @param internal the coordinate whose values will be changed to the
318: * precise representation of <code>external</code>
319: * @deprecated use makePrecise instead
320: */
321: public void toInternal(Coordinate external, Coordinate internal) {
322: if (isFloating()) {
323: internal.x = external.x;
324: internal.y = external.y;
325: } else {
326: internal.x = makePrecise(external.x);
327: internal.y = makePrecise(external.y);
328: }
329: internal.z = external.z;
330: }
331:
332: /**
333: * Returns the precise representation of <code>external</code>.
334: *
335: *@param external the original coordinate
336: *@return the coordinate whose values will be changed to the precise
337: * representation of <code>external</code>
338: * @deprecated use makePrecise instead
339: */
340: public Coordinate toInternal(Coordinate external) {
341: Coordinate internal = new Coordinate(external);
342: makePrecise(internal);
343: return internal;
344: }
345:
346: /**
347: * Returns the external representation of <code>internal</code>.
348: *
349: *@param internal the original coordinate
350: *@return the coordinate whose values will be changed to the
351: * external representation of <code>internal</code>
352: * @deprecated no longer needed, since internal representation is same as external representation
353: */
354: public Coordinate toExternal(Coordinate internal) {
355: Coordinate external = new Coordinate(internal);
356: return external;
357: }
358:
359: /**
360: * Sets <code>external</code> to the external representation of <code>internal</code>
361: * .
362: *
363: *@param internal the original coordinate
364: *@param external the coordinate whose values will be changed to the
365: * external representation of <code>internal</code>
366: * @deprecated no longer needed, since internal representation is same as external representation
367: */
368: public void toExternal(Coordinate internal, Coordinate external) {
369: external.x = internal.x;
370: external.y = internal.y;
371: }
372:
373: /**
374: * Rounds a numeric value to the PrecisionModel grid.
375: * Asymmetric Arithmetic Rounding is used, to provide
376: * uniform rounding behaviour no matter where the number is
377: * on the number line.
378: * <p>
379: * <b>Note:</b> Java's <code>Math#rint</code> uses the "Banker's Rounding" algorithm,
380: * which is not suitable for precision operations elsewhere in JTS.
381: */
382: public double makePrecise(double val) {
383: if (modelType == FLOATING_SINGLE) {
384: float floatSingleVal = (float) val;
385: return (double) floatSingleVal;
386: }
387: if (modelType == FIXED) {
388: return Math.round(val * scale) / scale;
389: // return Math.rint(val * scale) / scale;
390: }
391: // modelType == FLOATING - no rounding necessary
392: return val;
393: }
394:
395: /**
396: * Rounds a Coordinate to the PrecisionModel grid.
397: */
398: public void makePrecise(Coordinate coord) {
399: // optimization for full precision
400: if (modelType == FLOATING)
401: return;
402:
403: coord.x = makePrecise(coord.x);
404: coord.y = makePrecise(coord.y);
405: //MD says it's OK that we're not makePrecise'ing the z [Jon Aquino]
406: }
407:
408: public String toString() {
409: String description = "UNKNOWN";
410: if (modelType == FLOATING) {
411: description = "Floating";
412: } else if (modelType == FLOATING_SINGLE) {
413: description = "Floating-Single";
414: } else if (modelType == FIXED) {
415: description = "Fixed (Scale=" + getScale() + ")";
416: }
417: return description;
418: }
419:
420: public boolean equals(Object other) {
421: if (!(other instanceof PrecisionModel)) {
422: return false;
423: }
424: PrecisionModel otherPrecisionModel = (PrecisionModel) other;
425: return modelType == otherPrecisionModel.modelType
426: && scale == otherPrecisionModel.scale;
427: }
428:
429: /**
430: * Compares this {@link PrecisionModel} object with the specified object for order.
431: * A PrecisionModel is greater than another if it provides greater precision.
432: * The comparison is based on the value returned by the
433: * {@link getMaximumSignificantDigits) method.
434: * This comparison is not strictly accurate when comparing floating precision models
435: * to fixed models; however, it is correct when both models are either floating or fixed.
436: *
437: *@param o the <code>PrecisionModel</code> with which this <code>PrecisionModel</code>
438: * is being compared
439: *@return a negative integer, zero, or a positive integer as this <code>PrecisionModel</code>
440: * is less than, equal to, or greater than the specified <code>PrecisionModel</code>
441: */
442: public int compareTo(Object o) {
443: PrecisionModel other = (PrecisionModel) o;
444:
445: int sigDigits = getMaximumSignificantDigits();
446: int otherSigDigits = other.getMaximumSignificantDigits();
447: return (new Integer(sigDigits)).compareTo(new Integer(
448: otherSigDigits));
449: // if (sigDigits > otherSigDigits)
450: // return 1;
451: // else if
452: // if (modelType == FLOATING && other.modelType == FLOATING) return 0;
453: // if (modelType == FLOATING && other.modelType != FLOATING) return 1;
454: // if (modelType != FLOATING && other.modelType == FLOATING) return -1;
455: // if (modelType == FIXED && other.modelType == FIXED) {
456: // if (scale > other.scale)
457: // return 1;
458: // else if (scale < other.scale)
459: // return -1;
460: // else
461: // return 0;
462: // }
463: // Assert.shouldNeverReachHere("Unknown Precision Model type encountered");
464: // return 0;
465: }
466: }
|