001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, Geotools Project Management Committee (PMC)
005: * (C) 2003, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.coverage;
018:
019: // J2SE dependencies
020: import java.awt.geom.Point2D;
021: import java.awt.geom.Dimension2D;
022: import java.awt.geom.Rectangle2D;
023: import java.awt.geom.AffineTransform;
024: import java.awt.image.RenderedImage;
025: import java.awt.image.renderable.RenderableImage;
026: import java.util.Date;
027:
028: // JAI dependencies
029: import javax.media.jai.util.Range;
030:
031: // OpenGIS dependencies
032: import org.opengis.coverage.Coverage;
033: import org.opengis.coverage.SampleDimension;
034: import org.opengis.coverage.CannotEvaluateException;
035: import org.opengis.referencing.FactoryException;
036: import org.opengis.referencing.cs.AxisDirection;
037: import org.opengis.referencing.cs.CoordinateSystem;
038: import org.opengis.referencing.crs.CoordinateReferenceSystem;
039: import org.opengis.referencing.operation.CoordinateOperation;
040: import org.opengis.referencing.operation.CoordinateOperationFactory;
041: import org.opengis.referencing.operation.MathTransform;
042: import org.opengis.referencing.operation.MathTransform2D;
043: import org.opengis.referencing.operation.TransformException;
044: import org.opengis.geometry.Envelope;
045: import org.opengis.geometry.DirectPosition;
046: import org.opengis.geometry.MismatchedDimensionException;
047: import org.opengis.metadata.extent.GeographicBoundingBox;
048: import org.opengis.util.InternationalString;
049:
050: // Geotools dependencies
051: import org.geotools.factory.Hints;
052: import org.geotools.coverage.grid.GridCoverage2D;
053: import org.geotools.coverage.grid.GridCoverageFactory;
054: import org.geotools.geometry.GeneralDirectPosition;
055: import org.geotools.referencing.CRS;
056: import org.geotools.referencing.ReferencingFactoryFinder;
057: import org.geotools.referencing.crs.DefaultGeographicCRS;
058: import org.geotools.referencing.crs.DefaultTemporalCRS;
059: import org.geotools.referencing.operation.transform.ProjectiveTransform;
060: import org.geotools.referencing.operation.TransformPathNotFoundException;
061: import org.geotools.metadata.iso.extent.GeographicBoundingBoxImpl;
062: import org.geotools.resources.geometry.XRectangle2D;
063: import org.geotools.resources.CRSUtilities;
064: import org.geotools.resources.i18n.Errors;
065: import org.geotools.resources.i18n.ErrorKeys;
066:
067: /**
068: * Convenience view of an other coverage with <var>x</var>, <var>y</var> and <var>time</var> axis.
069: * This class provides {@code evaluate} methods in two versions: the usual one expecting
070: * a complete {@linkplain DirectPosition direct position}, and an other one expecting the
071: * {@linkplain Point2D spatial position} and the {@linkplain Date date} as separated arguments.
072: * This class will detects by itself which dimension is the time axis. It will also tries to uses
073: * the {@code Point2D}'s {@linkplain java.awt.geom.Point2D.Double#x x} value for
074: * {@linkplain AxisDirection#EAST east} or west direction, and the
075: * {@linkplain java.awt.geom.Point2D.Double#y y} value for {@linkplain AxisDirection#NORTH north}
076: * or south direction. The dimension mapping can be examined with the {@link #toSourceDimension}
077: * method.
078: * <br><br>
079: * <strong>Note:</strong> This class is not thread safe for performance reasons. If desired,
080: * users should create one instance of {@code SpatioTemporalCoverage3D} for each thread.
081: *
082: * @since 2.1
083: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/main/java/org/geotools/coverage/SpatioTemporalCoverage3D.java $
084: * @version $Id: SpatioTemporalCoverage3D.java 25787 2007-06-10 16:04:55Z desruisseaux $
085: * @author Martin Desruisseaux
086: */
087: public class SpatioTemporalCoverage3D extends AbstractCoverage {
088: /**
089: * The hints for the creation of coordinate operation.
090: * The default coordinate operation factory should be suffisient.
091: */
092: private static final Hints HINTS = null;
093:
094: /**
095: * A set of usual axis directions for <var>x</var> and <var>y</var> values (opposite directions
096: * not required). If an ordinate value is orientated toward one of those directions, it will be
097: * interpreted as the {@link java.awt.geom.Point2D.Double#x} value if the direction was found at
098: * an even index, or as the {@link java.awt.geom.Point2D.Double#y} value if the direction was
099: * found at an odd index.
100: */
101: private static final AxisDirection[] DIRECTIONS = {
102: AxisDirection.EAST, AxisDirection.NORTH,
103: AxisDirection.DISPLAY_RIGHT, AxisDirection.DISPLAY_UP,
104: AxisDirection.COLUMN_POSITIVE, AxisDirection.ROW_POSITIVE };
105:
106: /**
107: * The wrapped coverage.
108: */
109: private final Coverage coverage;
110:
111: /**
112: * The temporal coordinate system, as a Geotools implementation in order to gets the
113: * {@link DefaultTemporalCRS#toDate} and {@link DefaultTemporalCRS#toValue} methods.
114: */
115: private final DefaultTemporalCRS temporalCRS;
116:
117: /**
118: * The dimension of the temporal coordinate system.
119: * All other dimensions are expected to be the temporal ones.
120: */
121: private final int temporalDimension;
122:
123: /**
124: * The dimension for <var>x</var> and <var>y</var> coordinates.
125: */
126: private final int xDimension, yDimension;
127:
128: /**
129: * The geographic bounding box. Will be computed only when first needed.
130: */
131: private transient GeographicBoundingBox boundingBox;
132:
133: /**
134: * The direct position to uses for {@code evaluate(...)} methods.
135: * This object is cached and reused for performance purpose. However,
136: * this caching sacrifies {@code SpatioTemporalCoverage3D} thread safety.
137: */
138: private final GeneralDirectPosition coordinate;
139:
140: /**
141: * The grid coverage factory for {@link #getCoverage2D} method.
142: * Will be created only when first needed.
143: */
144: private transient GridCoverageFactory factory;
145:
146: /**
147: * Constructs a new coverage. The coordinate reference system will be the same than the
148: * wrapped coverage, which must be three dimensional. This CRS must have a
149: * {@linkplain DefaultTemporalCRS temporal} component.
150: *
151: * @param name The name for this coverage, or {@code null} for the same than {@code coverage}.
152: * @param coverage The source coverage.
153: * @throws IllegalArgumentException if the coverage CRS doesn't have a temporal component.
154: */
155: public SpatioTemporalCoverage3D(final CharSequence name,
156: final Coverage coverage) throws IllegalArgumentException {
157: super (name, coverage);
158: final CoordinateSystem cs = crs.getCoordinateSystem();
159: final int dimension = cs.getDimension();
160: if (dimension != 3) {
161: throw new MismatchedDimensionException(Errors.format(
162: ErrorKeys.MISMATCHED_DIMENSION_$2, new Integer(3),
163: new Integer(dimension)));
164: }
165: if (coverage instanceof SpatioTemporalCoverage3D) {
166: final SpatioTemporalCoverage3D source = (SpatioTemporalCoverage3D) coverage;
167: this .coverage = source.coverage;
168: this .temporalCRS = source.temporalCRS;
169: this .temporalDimension = source.temporalDimension;
170: this .xDimension = source.xDimension;
171: this .yDimension = source.yDimension;
172: this .boundingBox = source.boundingBox;
173: } else {
174: this .coverage = coverage;
175: temporalCRS = DefaultTemporalCRS.wrap(CRS
176: .getTemporalCRS(crs));
177: if (temporalCRS == null) {
178: throw new IllegalArgumentException(
179: Errors
180: .format(ErrorKeys.ILLEGAL_COORDINATE_REFERENCE_SYSTEM));
181: }
182: temporalDimension = CRSUtilities.getDimensionOf(crs,
183: temporalCRS.getClass());
184: final int xDimension = (temporalDimension != 0) ? 0 : 1;
185: final int yDimension = (temporalDimension != 2) ? 2 : 1;
186: Boolean swap = null; // 'null' if unknown, otherwise TRUE or FALSE.
187: control: for (int p = 0; p <= 1; p++) {
188: final AxisDirection direction;
189: direction = cs
190: .getAxis(p == 0 ? xDimension : yDimension)
191: .getDirection().absolute();
192: for (int i = 0; i < DIRECTIONS.length; i++) {
193: if (direction.equals(DIRECTIONS[i])) {
194: final boolean needSwap = (i & 1) != p;
195: if (swap == null) {
196: swap = Boolean.valueOf(needSwap);
197: } else if (swap.booleanValue() != needSwap) {
198: swap = null; // Found an ambiguity; stop the search.
199: break control;
200: }
201: }
202: }
203: }
204: if (swap != null && swap.booleanValue()) {
205: this .xDimension = yDimension;
206: this .yDimension = xDimension;
207: } else {
208: this .xDimension = xDimension;
209: this .yDimension = yDimension;
210: }
211: }
212: assert temporalDimension >= 0 && temporalDimension < dimension : temporalDimension;
213: coordinate = new GeneralDirectPosition(dimension); // Each instance must have its own.
214: }
215:
216: /**
217: * Returns the coverage specified at construction time.
218: *
219: * @since 2.2
220: */
221: public final Coverage getWrappedCoverage() {
222: return coverage;
223: }
224:
225: /**
226: * The number of sample dimensions in the coverage.
227: * For grid coverages, a sample dimension is a band.
228: *
229: * @return The number of sample dimensions in the coverage.
230: */
231: public int getNumSampleDimensions() {
232: return coverage.getNumSampleDimensions();
233: }
234:
235: /**
236: * Retrieve sample dimension information for the coverage.
237: *
238: * @param index Index for sample dimension to retrieve. Indices are numbered 0 to
239: * (<var>{@linkplain #getNumSampleDimensions n}</var>-1).
240: * @return Sample dimension information for the coverage.
241: * @throws IndexOutOfBoundsException if {@code index} is out of bounds.
242: */
243: public SampleDimension getSampleDimension(final int index)
244: throws IndexOutOfBoundsException {
245: return coverage.getSampleDimension(index);
246: }
247:
248: /**
249: * Returns the {@linkplain #getEnvelope envelope} geographic bounding box.
250: * The bounding box coordinates uses the {@linkplain DefaultGeographicCRS#WGS84 WGS84} CRS.
251: *
252: * @return The geographic bounding box.
253: * @throws TransformException if the envelope can't be transformed.
254: */
255: public GeographicBoundingBox getGeographicBoundingBox()
256: throws TransformException {
257: if (boundingBox == null) {
258: final Envelope envelope = getEnvelope();
259: Rectangle2D geographicArea = XRectangle2D
260: .createFromExtremums(envelope
261: .getMinimum(xDimension), envelope
262: .getMinimum(yDimension), envelope
263: .getMaximum(xDimension), envelope
264: .getMaximum(yDimension));
265: final CoordinateReferenceSystem sourceCRS = CRS
266: .getHorizontalCRS(crs);
267: final CoordinateReferenceSystem targetCRS = DefaultGeographicCRS.WGS84;
268: if (!CRS.equalsIgnoreMetadata(targetCRS, sourceCRS)) {
269: final CoordinateOperation transform;
270: final CoordinateOperationFactory factory;
271: factory = ReferencingFactoryFinder
272: .getCoordinateOperationFactory(HINTS);
273: try {
274: transform = factory.createOperation(sourceCRS,
275: targetCRS);
276: } catch (FactoryException exception) {
277: throw new TransformPathNotFoundException(exception);
278: }
279: geographicArea = CRS.transform(transform,
280: geographicArea, geographicArea);
281: }
282: boundingBox = (GeographicBoundingBox) new GeographicBoundingBoxImpl(
283: geographicArea).unmodifiable();
284: }
285: return boundingBox;
286: }
287:
288: /**
289: * Returns the {@linkplain #getEnvelope envelope} time range.
290: * The returned range contains {@link Date} objects.
291: */
292: public Range getTimeRange() {
293: final Envelope envelope = getEnvelope();
294: return new Range(Date.class, temporalCRS.toDate(envelope
295: .getMinimum(temporalDimension)), temporalCRS
296: .toDate(envelope.getMaximum(temporalDimension)));
297: }
298:
299: /**
300: * Returns the dimension in the wrapped coverage for the specified dimension in this coverage.
301: * The {@code evaluate(Point2D, Date)} methods expect ordinates in the
302: * (<var>x</var>, <var>y</var>, <var>t</var>) order.
303: * The {@code evaluate(DirectPosition)} methods and the wrapped coverage way uses a different
304: * order.
305: *
306: * @param dimension A dimension in this coverage:
307: * 0 for <var>x</var>,
308: * 1 for <var>y</var> or
309: * 2 for <var>t</var>.
310: * @return The corresponding dimension in the wrapped coverage.
311: *
312: * @see #toDate
313: * @see #toPoint2D
314: * @see #toDirectPosition
315: */
316: public final int toSourceDimension(final int dimension) {
317: switch (dimension) {
318: case 0:
319: return xDimension;
320: case 1:
321: return yDimension;
322: case 2:
323: return temporalDimension;
324: default:
325: throw new IllegalArgumentException();
326: }
327: }
328:
329: /**
330: * Returns a coordinate point for the given spatial position and date.
331: *
332: * @param point The spatial position.
333: * @param date The date.
334: * @return The coordinate point.
335: *
336: * @see #toDate
337: * @see #toPoint2D
338: *
339: * @since 2.2
340: */
341: public final DirectPosition toDirectPosition(final Point2D point,
342: final Date date) {
343: coordinate.ordinates[xDimension] = point.getX();
344: coordinate.ordinates[yDimension] = point.getY();
345: coordinate.ordinates[temporalDimension] = temporalCRS
346: .toValue(date);
347: return coordinate;
348: }
349:
350: /**
351: * Returns the date for the specified direct position. This method (together with
352: * {@link #toPoint2D toPoint2D}) is the converse of {@link #toDirectPosition toDirectPosition}.
353: *
354: * @param position The direct position, as computed by
355: * {@link #toDirectPosition toDirectPosition}.
356: * @return The date.
357: *
358: * @see #toPoint2D
359: * @see #toDirectPosition
360: *
361: * @since 2.2
362: */
363: public final Date toDate(final DirectPosition position) {
364: return temporalCRS.toDate(position
365: .getOrdinate(temporalDimension));
366: }
367:
368: /**
369: * Returns the spatial coordinate for the specified direct position. This method (together with
370: * {@link #toDate toDate}) is the converse of {@link #toDirectPosition toDirectPosition}.
371: *
372: * @param position The direct position, as computed by
373: * {@link #toDirectPosition toDirectPosition}.
374: * @return The spatial coordinate.
375: *
376: * @see #toDate
377: * @see #toDirectPosition
378: *
379: * @since 2.2
380: */
381: public final Point2D toPoint2D(final DirectPosition position) {
382: return new Point2D.Double(position.getOrdinate(xDimension),
383: position.getOrdinate(yDimension));
384: }
385:
386: /**
387: * Returns a sequence of boolean values for a given point in the coverage.
388: *
389: * @param point The coordinate point where to evaluate.
390: * @param time The date where to evaluate.
391: * @param dest An array in which to store values, or {@code null} to create a new array.
392: * @return The {@code dest} array, or a newly created array if {@code dest} was null.
393: * @throws PointOutsideCoverageException if {@code point} or {@code time} is outside coverage.
394: * @throws CannotEvaluateException if the computation failed for some other reason.
395: */
396: public final boolean[] evaluate(final Point2D point,
397: final Date time, boolean[] dest)
398: throws CannotEvaluateException {
399: try {
400: return evaluate(toDirectPosition(point, time), dest);
401: } catch (OrdinateOutsideCoverageException exception) {
402: if (exception.getOutOfBoundsDimension() == temporalDimension) {
403: exception = new OrdinateOutsideCoverageException(
404: exception, time);
405: }
406: throw exception;
407: }
408: }
409:
410: /**
411: * Returns a sequence of byte values for a given point in the coverage.
412: *
413: * @param point The coordinate point where to evaluate.
414: * @param time The date where to evaluate.
415: * @param dest An array in which to store values, or {@code null} to create a new array.
416: * @return The {@code dest} array, or a newly created array if {@code dest} was null.
417: * @throws PointOutsideCoverageException if {@code point} or {@code time} is outside coverage.
418: * @throws CannotEvaluateException if the computation failed for some other reason.
419: */
420: public final byte[] evaluate(final Point2D point, final Date time,
421: byte[] dest) throws CannotEvaluateException {
422: try {
423: return evaluate(toDirectPosition(point, time), dest);
424: } catch (OrdinateOutsideCoverageException exception) {
425: if (exception.getOutOfBoundsDimension() == temporalDimension) {
426: exception = new OrdinateOutsideCoverageException(
427: exception, time);
428: }
429: throw exception;
430: }
431: }
432:
433: /**
434: * Returns a sequence of integer values for a given point in the coverage.
435: *
436: * @param point The coordinate point where to evaluate.
437: * @param time The date where to evaluate.
438: * @param dest An array in which to store values, or {@code null} to create a new array.
439: * @return The {@code dest} array, or a newly created array if {@code dest} was null.
440: * @throws PointOutsideCoverageException if {@code point} or {@code time} is outside coverage.
441: * @throws CannotEvaluateException if the computation failed for some other reason.
442: */
443: public final int[] evaluate(final Point2D point, final Date time,
444: int[] dest) throws CannotEvaluateException {
445: try {
446: return evaluate(toDirectPosition(point, time), dest);
447: } catch (OrdinateOutsideCoverageException exception) {
448: if (exception.getOutOfBoundsDimension() == temporalDimension) {
449: exception = new OrdinateOutsideCoverageException(
450: exception, time);
451: }
452: throw exception;
453: }
454: }
455:
456: /**
457: * Returns a sequence of float values for a given point in the coverage.
458: *
459: * @param point The coordinate point where to evaluate.
460: * @param time The date where to evaluate.
461: * @param dest An array in which to store values, or {@code null} to create a new array.
462: * @return The {@code dest} array, or a newly created array if {@code dest} was null.
463: * @throws PointOutsideCoverageException if {@code point} or {@code time} is outside coverage.
464: * @throws CannotEvaluateException if the computation failed for some other reason.
465: */
466: public final float[] evaluate(final Point2D point, final Date time,
467: float[] dest) throws CannotEvaluateException {
468: try {
469: return evaluate(toDirectPosition(point, time), dest);
470: } catch (OrdinateOutsideCoverageException exception) {
471: if (exception.getOutOfBoundsDimension() == temporalDimension) {
472: exception = new OrdinateOutsideCoverageException(
473: exception, time);
474: }
475: throw exception;
476: }
477: }
478:
479: /**
480: * Returns a sequence of double values for a given point in the coverage.
481: *
482: * @param point The coordinate point where to evaluate.
483: * @param time The date where to evaluate.
484: * @param dest An array in which to store values, or {@code null} to create a new array.
485: * @return The {@code dest} array, or a newly created array if {@code dest} was null.
486: * @throws PointOutsideCoverageException if {@code point} or {@code time} is outside coverage.
487: * @throws CannotEvaluateException if the computation failed for some other reason.
488: */
489: public final double[] evaluate(final Point2D point,
490: final Date time, double[] dest)
491: throws CannotEvaluateException {
492: try {
493: return evaluate(toDirectPosition(point, time), dest);
494: } catch (OrdinateOutsideCoverageException exception) {
495: if (exception.getOutOfBoundsDimension() == temporalDimension) {
496: exception = new OrdinateOutsideCoverageException(
497: exception, time);
498: }
499: throw exception;
500: }
501: }
502:
503: /**
504: * Returns the value vector for a given point in the coverage.
505: *
506: * @param coord The coordinate point where to evaluate.
507: * @throws PointOutsideCoverageException if {@code coord} is outside coverage.
508: * @throws CannotEvaluateException if the computation failed for some other reason.
509: */
510: public final Object evaluate(final DirectPosition coord)
511: throws CannotEvaluateException {
512: return coverage.evaluate(coord);
513: }
514:
515: /**
516: * Returns a sequence of boolean values for a given point in the coverage.
517: */
518: public final boolean[] evaluate(final DirectPosition coord,
519: boolean[] dest) throws CannotEvaluateException {
520: return coverage.evaluate(coord, dest);
521: }
522:
523: /**
524: * Returns a sequence of byte values for a given point in the coverage.
525: */
526: public final byte[] evaluate(final DirectPosition coord, byte[] dest)
527: throws CannotEvaluateException {
528: return coverage.evaluate(coord, dest);
529: }
530:
531: /**
532: * Returns a sequence of integer values for a given point in the coverage.
533: */
534: public final int[] evaluate(final DirectPosition coord, int[] dest)
535: throws CannotEvaluateException {
536: return coverage.evaluate(coord, dest);
537: }
538:
539: /**
540: * Returns a sequence of float values for a given point in the coverage.
541: */
542: public final float[] evaluate(final DirectPosition coord,
543: float[] dest) throws CannotEvaluateException {
544: return coverage.evaluate(coord, dest);
545: }
546:
547: /**
548: * Returns a sequence of double values for a given point in the coverage.
549: */
550: public final double[] evaluate(final DirectPosition coord,
551: final double[] dest) throws CannotEvaluateException {
552: return coverage.evaluate(coord, dest);
553: }
554:
555: /**
556: * Returns a 2 dimensional grid coverage for the given date. The grid geometry will be computed
557: * in order to produces image with the {@linkplain #getDefaultPixelSize() default pixel size},
558: * if any.
559: *
560: * @param time The date where to evaluate.
561: * @return The grid coverage at the specified time, or {@code null}
562: * if the requested date fall in a hole in the data.
563: * @throws PointOutsideCoverageException if {@code time} is outside coverage.
564: * @throws CannotEvaluateException if the computation failed for some other reason.
565: *
566: * @see #getRenderableImage(Date)
567: * @see RenderableImage#createDefaultRendering()
568: *
569: * @todo Find some way to avoid the cast in the {@code return} statement.
570: */
571: public GridCoverage2D getGridCoverage2D(final Date time)
572: throws CannotEvaluateException {
573: final InternationalString name = getName();
574: final CoordinateReferenceSystem crs = CRS
575: .getHorizontalCRS(this .crs);
576: final RenderedImage image = getRenderableImage(time)
577: .createDefaultRendering();
578: final GridSampleDimension[] bands = new GridSampleDimension[getNumSampleDimensions()];
579: for (int i = 0; i < getNumSampleDimensions(); i++) {
580: bands[i] = GridSampleDimension.wrap(getSampleDimension(i));
581: }
582: final MathTransform gridToCRS;
583: gridToCRS = ProjectiveTransform.create((AffineTransform) image
584: .getProperty("gridToCRS"));
585: if (factory == null) {
586: factory = org.geotools.coverage.FactoryFinder
587: .getGridCoverageFactory(HINTS);
588: }
589: return (GridCoverage2D) factory.create(name, image, crs,
590: gridToCRS, bands, null, null);
591: }
592:
593: /**
594: * Returns 2D view of this grid coverage at the given date. For images produced by the
595: * {@linkplain RenderableImage#createDefaultRendering() default rendering}, the size
596: * will be computed from the {@linkplain #getDefaultPixelSize() default pixel size},
597: * if any.
598: *
599: * @param date The date where to evaluate the images.
600: * @return The renderable image.
601: */
602: public RenderableImage getRenderableImage(final Date date) {
603: return new Renderable(date);
604: }
605:
606: /**
607: * Constructs rendered images on demand.
608: *
609: * @version $Id: SpatioTemporalCoverage3D.java 25787 2007-06-10 16:04:55Z desruisseaux $
610: * @author Martin Desruisseaux
611: */
612: private final class Renderable extends AbstractCoverage.Renderable {
613: /**
614: * Construct a {@code Renderable} object for the supplied date.
615: */
616: public Renderable(final Date date) {
617: super (xDimension, yDimension);
618: coordinate.ordinates[temporalDimension] = temporalCRS
619: .toValue(date);
620: }
621:
622: /**
623: * Returns a rendered image with width and height computed from
624: * {@link Coverage3D#getDefaultPixelSize()}.
625: */
626: public RenderedImage createDefaultRendering() {
627: final Dimension2D pixelSize = getDefaultPixelSize();
628: if (pixelSize == null) {
629: return super .createDefaultRendering();
630: }
631: return createScaledRendering((int) Math.round(getWidth()
632: / pixelSize.getWidth()), (int) Math
633: .round(getHeight() / pixelSize.getHeight()), null);
634: }
635: }
636:
637: /**
638: * Returns the default pixel size for images to be produced by {@link #getRenderableImage(Date)}.
639: * This method is invoked by {@link RenderableImage#createDefaultRendering()} for computing a
640: * default image size. The default implementation for this method always returns {@code null}.
641: * Subclasses should overrides this method in order to provides a pixel size better suited to
642: * their data.
643: *
644: * @return The default pixel size, or {@code null} if no default is provided.
645: */
646: protected Dimension2D getDefaultPixelSize() {
647: return null;
648: }
649: }
|