/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2013, 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.display.canvas;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import javax.measure.UnitConverter;
import javax.measure.Unit;
import org.apache.sis.measure.Units;
import org.apache.sis.geometry.GeneralDirectPosition;
import org.geotoolkit.referencing.GeodeticCalculator;
import org.geotoolkit.referencing.operation.matrix.XAffineTransform;
import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
import org.apache.sis.referencing.operation.matrix.AffineTransforms2D;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.cs.CoordinateSystem;
import org.opengis.referencing.cs.CoordinateSystemAxis;
import org.opengis.referencing.operation.TransformException;
/**
*
* @author Johann Sorel (Geomatys)
*/
public final class CanvasUtilities {
/**
* Used in SLD/SE to calculate scale for degree CRSs.
*/
private static final double SE_DEGREE_TO_METERS = 6378137.0 * 2.0 * Math.PI / 360;
private static final double DEFAULT_DPI = 90; // ~ 0.28 * 0.28mm
private static final double PIXEL_SIZE = 0.0254;
private CanvasUtilities(){}
/**
* Returns the geographic scale, in a ground unit manner, relation between map display size
* and real ground unit meters.
*
* @param center
* @param objToDisp
* @param crs
* @return
* @throws org.opengis.referencing.operation.TransformException
* @throws IllegalStateException If the affine transform used for conversion is in illegal state.
*/
public static double getGeographicScale(Point2D center, AffineTransform2D objToDisp, CoordinateReferenceSystem crs) throws TransformException {
final double[] P1 = new double[]{center.getX(), center.getY()};
final double[] P2 = new double[]{P1[0], P1[1] + 1};
final AffineTransform trs;
try {
trs = objToDisp.createInverse();
} catch (NoninvertibleTransformException ex) {
throw new TransformException(ex.getLocalizedMessage(), ex);
}
trs.transform(P1, 0, P1, 0, 1);
trs.transform(P2, 0, P2, 0, 1);
final Unit unit = crs.getCoordinateSystem().getAxis(0).getUnit();
final double distance;
if (unit.isCompatible(Units.METRE)) {
final Point2D p1 = new Point2D.Double(P1[0], P1[1]);
final Point2D p2 = new Point2D.Double(P2[0], P2[1]);
final UnitConverter conv = unit.getConverterTo(Units.METRE);
distance = conv.convert(p1.distance(p2));
} else {
/*
* If the latitude ordinates (for example) are outside the +/-90°
* range, translate the points in order to bring them back in the
* domain of validity.
*/
final CoordinateSystem cs = crs.getCoordinateSystem();
for (int i = cs.getDimension(); --i >= 0;) {
final CoordinateSystemAxis axis = cs.getAxis(i);
double delta = P1[i] - axis.getMaximumValue();
if (delta > 0) {
P1[i] -= delta;
P2[i] -= delta;
}
delta = P2[i] - axis.getMaximumValue();
if (delta > 0) {
P1[i] -= delta;
P2[i] -= delta;
}
delta = axis.getMinimumValue() - P1[i];
if (delta > 0) {
P1[i] += delta;
P2[i] += delta;
}
delta = axis.getMinimumValue() - P2[i];
if (delta > 0) {
P1[i] += delta;
P2[i] += delta;
}
}
final GeodeticCalculator gc = new GeodeticCalculator(crs);
final GeneralDirectPosition pos1 = new GeneralDirectPosition(crs);
pos1.setOrdinate(0, P1[0]);
pos1.setOrdinate(1, P1[1]);
final GeneralDirectPosition pos2 = new GeneralDirectPosition(crs);
pos2.setOrdinate(0, P2[0]);
pos2.setOrdinate(1, P2[1]);
try {
gc.setStartingPosition(pos1);
gc.setDestinationPosition(pos2);
} catch (TransformException ex) {
throw new TransformException(ex.getLocalizedMessage(), ex);
} catch (IllegalArgumentException ex) {
//might happen when changing projection and moving the area.
//the coordinate can be out of the crs area, which causes this exception
throw new TransformException(ex.getLocalizedMessage(), ex);
}
distance = Math.abs(gc.getOrthodromicDistance());
}
final double displayToDevice = 1f / DEFAULT_DPI * 0.0254f;
return distance / displayToDevice;
}
/**
* @param envelope : canvas objective bounds 2D
* @param objToDisp
* @param dispBounds
* @return
*/
public static double computeSEScale(final Envelope envelope, AffineTransform objToDisp, Rectangle dispBounds) {
final CoordinateReferenceSystem objCRS = envelope.getCoordinateReferenceSystem();
final int width = dispBounds.width;
if (AffineTransforms2D.getRotation(objToDisp) != 0.0) {
final double scale = XAffineTransform.getScale(objToDisp);
if(objCRS instanceof GeographicCRS) {
return (SE_DEGREE_TO_METERS*DEFAULT_DPI) / (scale*PIXEL_SIZE);
} else {
return DEFAULT_DPI / (scale *PIXEL_SIZE);
}
}else{
if(objCRS instanceof GeographicCRS) {
return (envelope.getSpan(0) * SE_DEGREE_TO_METERS) / (width / DEFAULT_DPI*PIXEL_SIZE);
} else {
return envelope.getSpan(0) / (width / DEFAULT_DPI*PIXEL_SIZE);
}
}
}
}