/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2005-2012, Open Source Geospatial Foundation (OSGeo) * (C) 2007-2012, Geomatys * * 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; * version 2.1 of the License. * * 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. */ package org.geotoolkit.coverage.sql; import java.util.Date; import java.util.Comparator; import java.awt.Dimension; import org.geotoolkit.util.DateRange; import org.apache.sis.measure.NumberRange; import org.geotoolkit.display.shape.XRectangle2D; import static java.lang.Double.NaN; /** * Compare deux entrées {@link CoverageReference} en fonction d'un critère arbitraire. Ce * comparateur sert à classer un tableau d'images en fonction de leur intérêt par rapport * à ce qui avait été demandé. L'implémentation par défaut favorise les images dont la plage * de temps couvre le mieux la plage demandée (les dates de début et de fin), et n'examinera * la couverture spatiale que si deux images ont une couverture temporelle équivalente. Cette * politique est appropriée lorsque les images couvrent à peu près la même région, et que les * dates de ces images est le principal facteur qui varie. Les critères de comparison utilisés * sont: * <p> * <ul> * <li>Pour chaque image, la quantité [<i>temps à l'intérieur de la plage de temps * demandée</i>]-[<i>temps à l'extérieur de la plage de temps demandé</i>] sera * calculée. Si une des image à une quantité plus grande, elle sera choisie.</li> * <li>Sinon, si une image se trouve mieux centrée sur la plage de temps demandée, cette * image sera choisie.</li> * <li>Sinon, pour chaque image, l'intersection entre la région de l'image et la région * demandée sera obtenue, et la superficie de cette intersection calculée. Si une * des images obtient une valeur plus grande, cette image sera choisie.</li> * <li>Sinon, la superficie moyenne des pixels des images seront calculées. Si une image * a des pixels d'une meilleure résolution (couvrant une surface plus petite), cette * image sera choisie.</li> * </ul> * * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.10 * * @since 3.10 (derived from Seagis) * @module */ @SuppressWarnings("serial") final class GridCoverageComparator extends XRectangle2D implements Comparator<GridCoverageReference> { /** * The minimal and maximal values along the <var>z</var> dimension. */ private final double zmin, zmax; /** * The minimal and maximal values along the <var>t</var> dimension. * Those values shall not be modified anymore after construction. */ private long tmin, tmax; /** * An estimation of the resolution. This is approximative (not really calculated from the * {@code gridToCRS} transform). This is left to NaN if not applicable. */ private final double scaleX, scaleY; /** * Creates a new instance for the given region of interest. * * @param regionOfInterest The spatio-temporal coordinates of the requested region * in units of the database CRS. */ GridCoverageComparator(final CoverageEnvelope regionOfInterest) { scaleX = scaleY = NaN; setRect(regionOfInterest.getHorizontalRange()); final NumberRange<?> zRange = regionOfInterest.getVerticalRange(); zmin = zRange.getMinDouble(); zmax = zRange.getMaxDouble(); setTimeRange(regionOfInterest.getTimeRange()); // scaleX and scaleY are not used by this instance. } /** * Creates a new instance for the given entry. * * @param entry The entry for which to create a comparator. */ private GridCoverageComparator(final GridCoverageReference entry) { setRect(entry.getXYRange()); final GridGeometryEntry geometry = ((GridCoverageEntry) entry).getIdentifier().geometry; final Dimension size = geometry.getImageSize(); scaleX = getWidth() / size.width; scaleY = getHeight() / size.height; zmin = geometry.standardMinZ; zmax = geometry.standardMaxZ; setTimeRange(entry.getTimeRange()); } /** * Sets the date range of this entry. This is used by constructors only. */ private void setTimeRange(final DateRange range) { Date t; tmin = ((t = range.getMinValue()) != null) ? t.getTime() : Long.MIN_VALUE; tmax = ((t = range.getMaxValue()) != null) ? t.getTime() : Long.MAX_VALUE; } /** * Compares two {@link GridCoverageReference} entries. * * @return +1 if {@code entry1} should be preferred to {@code entry2}. * -1 if {@code entry2} should be preferred to {@code entry1}. * 0 if both entries seem of equal interest. */ @Override public int compare(final GridCoverageReference entry1, final GridCoverageReference entry2) { final GridCoverageComparator ev1 = new GridCoverageComparator(entry1); final GridCoverageComparator ev2 = new GridCoverageComparator(entry2); long t1, t2; t1 = ev1.uncoveredTime(this); t2 = ev2.uncoveredTime(this); if (t1 > t2) return +1; if (t1 < t2) return -1; t1 = ev1.timeOffset(this); t2 = ev2.timeOffset(this); if (t1 > t2) return +1; if (t1 < t2) return -1; ev1.intersect(this); ev2.intersect(this); double d1 = ev1.area(); double d2 = ev2.area(); if (d1 < d2) return +1; if (d1 > d2) return -1; d1 = ev1.resolution(); d2 = ev2.resolution(); if (d1 > d2) return +1; if (d1 < d2) return -1; return 0; } /** * Retourne une mesure de la correspondance entre la plage de temps couverte par l'image * et la plage de temps qui avait été demandée. Une valeur de 0 indique que la plage de * l'image correspond exactement à la plage demandée. Une valeur supérieure à 0 indique * que l'image ne couvre pas toute la plage demandée, où qu'elle couvre aussi du temps * en dehors de la plage demandée. */ private long uncoveredTime(final GridCoverageComparator regionOfInterest) { final long rmin = regionOfInterest.tmin; final long rmax = regionOfInterest.tmax; final long lower = Math.max(tmin, rmin); final long upper = Math.min(tmax, rmax); final long range = Math.max(0, upper-lower); // Find intersection range. return ((rmax-rmin) - range) + // > 0 if image do not cover all requested range. ((tmax-tmin) - range); // > 0 if image cover some part outside requested range. } /** * Retourne une mesure de l'écart entre la date de l'image et la date demandée. * Une valeur de 0 indique que l'image est exactement centrée sur la plage de * dates demandée. Une valeur supérieure à 0 indique que le centre de l'image * est décalée. */ private long timeOffset(final GridCoverageComparator regionOfInterest) { return Math.abs((tmin - regionOfInterest.tmin) + (tmax - regionOfInterest.tmax)); } /** * Returns an estimation of the area, in units of the database CRS * (may be a product of angles). Greater is better. */ private double area() { return getWidth() * getHeight(); } /** * Returns an estimation of the resolution. * Smaller is better. */ private double resolution() { return Math.hypot(scaleX, scaleY); } }