/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2004-2008, Open Source Geospatial Foundation (OSGeo)
*
* 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.geometry.jts.transform;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateSequence;
import com.vividsolutions.jts.geom.CoordinateSequenceFactory;
import com.vividsolutions.jts.geom.impl.CoordinateArraySequenceFactory;
import java.util.Arrays;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
/**
* A default implementation of {@linkplain CoordinateSequenceTransformer coordinate sequence
* transformer}. This transformer applies the coordinate transformations immediately (which
* means that caller are immediately notified if a transformation fails).
* <p>
* This transformer support {@linkplain MathTransform math transform} with up to 3 source
* or target dimensions. This transformer is not thread-safe.
*
* @module
* @since 2.1
* @version $Id$
* @author Andrea Aime
* @author Martin Desruisseaux
*/
public class CoordinateSequenceMathTransformer implements CoordinateSequenceTransformer {
/**
* The coordinate sequence factory to use.
*/
static final CoordinateSequenceFactory DEFAULT_CS_FACTORY = CoordinateArraySequenceFactory.instance();
/**
* A buffer for coordinate transformations. We choose a length which is divisible by
* both 2 and 3, since JTS coordinates may be up to three-dimensional. If the number
* of coordinates point to transform is greater than the buffer capacity, then the
* buffer will be flushed to the destination array before to continue. We avoid to
* create a buffer as large than the number of point to transforms, because it would
* consume a large amount of memory for big geometries.
*/
private final transient double[] buffer = new double[96];
private final CoordinateSequenceFactory csf;
private MathTransform transform = null;
/**
* Constructs a default coordinate sequence transformer.
*/
public CoordinateSequenceMathTransformer(final MathTransform transform) {
this(null,transform);
}
/**
* Constructs a coordinate sequence transformer with the given CoordinateSequenceFactory.
*/
public CoordinateSequenceMathTransformer(final CoordinateSequenceFactory csf, final MathTransform transform) {
if(csf == null){
this.csf = DEFAULT_CS_FACTORY;
}else{
this.csf = csf;
}
this.transform =transform;
}
public synchronized void setTransform(final MathTransform transform) {
this.transform = transform;
}
public synchronized MathTransform getTransform() {
return transform;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized CoordinateSequence transform(final CoordinateSequence sequence, final int minpoints)
throws TransformException {
final int sourceDim = transform.getSourceDimensions();
final int targetDim = transform.getTargetDimensions();
final int size = sequence.size();
final Coordinate[] tcs = new Coordinate[size];
final int bufferCapacity = buffer.length / Math.max(sourceDim, targetDim);
int remainingBeforeFlush = Math.min(bufferCapacity, size);
int ib = 0; // Index in the buffer array.
int it = 0; // Index in the target array.
for (int i = 0; i < size; i++) {
switch (sourceDim) { // Fall through in every cases.
default: Arrays.fill(buffer, ib + 3, ib + sourceDim, Double.NaN);
case 3: buffer[ib + 2] = sequence.getOrdinate(i, 2);
case 2: buffer[ib + 1] = sequence.getY(i);
case 1: buffer[ib] = sequence.getX(i);
case 0: break;
}
ib += sourceDim;
if (--remainingBeforeFlush == 0) {
/*
* The buffer is full, or we just copied the last coordinates.
* Transform the coordinates and flush to the destination array.
*/
assert (ib % sourceDim) == 0;
final int n = ib / sourceDim;
transform.transform(buffer, 0, buffer, 0, n);
ib = 0;
for (int j = 0; j < n; j++) {
switch (targetDim) {
default: throw new MismatchedDimensionException();
case 3:
tcs[it++] = new Coordinate(buffer[ib++], buffer[ib++], buffer[ib++]);
break;
case 2:
tcs[it++] = new Coordinate(buffer[ib++], buffer[ib++]);
break;
case 1:
tcs[it++] = new Coordinate(buffer[ib++], Double.NaN);
break;
case 0:
tcs[it++] = new Coordinate(Double.NaN, Double.NaN);
break;
}
}
assert ib == (n * targetDim);
ib = 0;
remainingBeforeFlush = Math.min(bufferCapacity, size - (i + 1));
}
}
assert it == tcs.length : tcs.length - it;
return csf.create(tcs);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("CoordinateSequenceTransformer : Mathtransform : ");
sb.append(transform);
return sb.toString();
}
}