/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2011-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2011-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.display.shape;
import java.awt.Shape;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.AffineTransform;
import java.awt.geom.IllegalPathStateException;
import org.opengis.util.FactoryException;
import org.opengis.referencing.crs.ProjectedCRS;
import org.opengis.referencing.operation.MathTransform2D;
import org.opengis.referencing.operation.TransformException;
import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.geotoolkit.test.gui.ShapeTestBase;
import org.geotoolkit.test.referencing.WKT;
import org.junit.*;
/**
* Tests the {@link ProjectedShape} class.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.20
*
* @since 3.20
*/
public final strictfp class ProjectedShapeTest extends ShapeTestBase {
/**
* The projection to use for testing purpose.
* This field is computed by {@link #createReferenceShape(Shape, MathTransform2D)}.
*/
private MathTransform2D projection;
/**
* Transforms the given shape using the given projection, then apply a correction for fitting
* the shape in the test viewer. This method also set the {@link #projection} to the resulting
* concatenated transform.
*/
private Shape createReferenceShape(final Shape shape, MathTransform2D tr) throws TransformException {
final Path2D path = transform(shape, tr);
Rectangle2D bounds = path.getBounds2D();
final AffineTransform adjust = AffineTransform.getTranslateInstance(SHAPE_X, SHAPE_Y);
adjust.scale(2*SHAPE_WIDTH / bounds.getWidth(), 2*SHAPE_HEIGHT / bounds.getHeight());
adjust.translate(-bounds.getX(), -bounds.getY());
path.transform(adjust);
tr = MathTransforms.concatenate(tr, (MathTransform2D) org.geotoolkit.referencing.operation.MathTransforms.linear(adjust));
projection = tr;
return path;
}
/**
* Projects a shape and compare with a shape projected by other means.
* Current version is only a visual test.
*
* @throws FactoryException If the transform can not be created.
* @throws TransformException If an error occurred while transforming some points.
*/
@Test
public void testWithLambert() throws FactoryException, TransformException {
final ProjectedCRS crs = (ProjectedCRS) CRS.fromWKT(WKT.PROJCS_LAMBERT_CONIC_NTF);
final GeneralPath path = new GeneralPath();
path.moveTo(10.000000f, 10.0000000f);
path.lineTo(56.666667f, -6.6666667f);
path.lineTo(56.666667f, -40.000000f);
path.lineTo(80.000000f, 10.0000000f);
path.lineTo(56.666667f, 60.0000000f);
path.lineTo(56.666667f, 26.6666667f);
path.closePath();
final Shape reference = createReferenceShape(path, (MathTransform2D) crs.getConversionFromBase().getMathTransform());
final Shape target = ProjectedShape.wrap(path, projection);
show(target, reference, false);
}
/**
* Creates a transformed shape by sampling an arbitrary amount of point on each line segment.
* This shape is used as a reference for comparison with clever shapes, computed from cubic
* approximation or other formulas. This method is not intended to be efficient.
*
* @param shape The shape to transform.
* @param mt The math transform to apply on the shape.
* @return The transformed shape.
*/
public static Path2D transform(final Shape shape, final MathTransform2D mt) {
final double flatness = ShapeUtilities.getFlatness(shape);
final Path2D.Double path = new Path2D.Double();
final PathIterator it = shape.getPathIterator(null, flatness);
final double[] coords = new double[6];
double x0=Double.NaN, y0=Double.NaN;
try {
while (!it.isDone()) {
final int code = it.currentSegment(coords);
switch (code) {
case PathIterator.SEG_CLOSE: {
path.closePath();
break;
}
case PathIterator.SEG_MOVETO: {
x0 = coords[0];
y0 = coords[1];
mt.transform(coords, 0, coords, 0, 1);
path.moveTo(coords[0], coords[1]);
break;
}
case PathIterator.SEG_LINETO: {
final double x = coords[0];
final double y = coords[1];
double dx = x - x0;
double dy = y - y0;
final int n = Math.max(1, (int) Math.round(Math.hypot(dx, dy) / flatness));
dx /= n;
dy /= n;
for (int i=n; --i>=0;) {
coords[0] = x0 = x - dx*i;
coords[1] = y0 = y - dy*i;
mt.transform(coords, 0, coords, 0, 1);
path.lineTo(coords[0], coords[1]);
}
break;
}
default: throw new AssertionError(code);
}
it.next();
}
} catch (TransformException cause) {
IllegalPathStateException e = new IllegalPathStateException(cause.toString());
e.initCause(cause);
throw e;
}
return path;
}
}