/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
* ... and it just works.
*
****************************************************/
package de.cismet.cismap.commons.rasterservice.georeferencing;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.PrecisionModel;
import com.vividsolutions.jts.geom.util.AffineTransformation;
import lombok.AccessLevel;
import lombok.Getter;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import de.cismet.cismap.commons.CrsTransformer;
import de.cismet.cismap.commons.gui.piccolo.eventlistener.RasterGeoRefFeature;
import de.cismet.cismap.commons.interaction.CismapBroker;
import de.cismet.cismap.commons.rasterservice.ImageFileMetaData;
import de.cismet.cismap.commons.rasterservice.ImageRasterService;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
* DOCUMENT ME!
*
* @author jruiz
* @version $Revision$, $Date$
*/
public class RasterGeoReferencingHandler {
//~ Instance fields --------------------------------------------------------
private final List<PointCoordinatePair> pairs = new ArrayList<>();
@Getter(AccessLevel.PRIVATE)
private final ListenerHandler listenerHandler = new ListenerHandler();
@Getter(AccessLevel.PRIVATE)
private final Map<Integer, Boolean> positionStates = new HashMap<>();
@Getter private final ImageFileMetaData metaData;
@Getter private final RasterGeoRefFeature feature;
@Getter private final ImageRasterService service;
@Getter(AccessLevel.PRIVATE)
private final AffineTransformation initialTransform;
//~ Constructors -----------------------------------------------------------
/**
* Creates a new RasterGeoReferencingHandler object.
*
* @param service DOCUMENT ME!
* @param metaData DOCUMENT ME!
*/
public RasterGeoReferencingHandler(final ImageRasterService service, final ImageFileMetaData metaData) {
this.service = service;
this.metaData = metaData;
this.feature = new RasterGeoRefFeature(this);
this.initialTransform = metaData.getTransform();
}
//~ Methods ----------------------------------------------------------------
/**
* DOCUMENT ME!
*
* @param position DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws ArrayIndexOutOfBoundsException DOCUMENT ME!
*/
public boolean isPositionEnabled(final int position) throws ArrayIndexOutOfBoundsException {
checḱPosition(position);
return Boolean.TRUE.equals(getPositionStates().get(position));
}
/**
* DOCUMENT ME!
*
* @param position DOCUMENT ME!
* @param enabled DOCUMENT ME!
*
* @throws ArrayIndexOutOfBoundsException DOCUMENT ME!
*/
public void setPositionEnabled(final int position, final boolean enabled) throws ArrayIndexOutOfBoundsException {
checḱPosition(position);
final boolean changed = !new Boolean(enabled).equals(getPositionStates().get(position));
if (changed) {
getPositionStates().put(position, enabled);
updateTransformation();
getListenerHandler().positionChanged(position);
}
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public boolean isComplete() {
return getCompletePairs().length >= 3;
}
/**
* DOCUMENT ME!
*
* @param listener DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public boolean addListener(final RasterGeoReferencingHandlerListener listener) {
return getListenerHandler().add(listener);
}
/**
* DOCUMENT ME!
*
* @param listener DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public boolean removeListener(final RasterGeoReferencingHandlerListener listener) {
return getListenerHandler().remove(listener);
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public int addPair() {
return addPair(null, null);
}
/**
* DOCUMENT ME!
*
* @param point DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public int addPair(final Point point) {
return addPair(point, null);
}
/**
* DOCUMENT ME!
*
* @param coordinate DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public int addPair(final Coordinate coordinate) {
return addPair(null, coordinate);
}
/**
* DOCUMENT ME!
*
* @param point DOCUMENT ME!
* @param coordinate DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public int addPair(final Point point, final Coordinate coordinate) {
final PointCoordinatePair pair = new PointCoordinatePair(point, coordinate);
return addPair(pair);
}
/**
* DOCUMENT ME!
*
* @param pair point DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws IllegalArgumentException DOCUMENT ME!
*/
public int addPair(final PointCoordinatePair pair) throws IllegalArgumentException {
if (pair == null) {
throw new IllegalArgumentException("the given pair is null");
}
final int position;
synchronized (pairs) {
position = pairs.size();
pairs.add(position, (PointCoordinatePair)pair.clone());
getPositionStates().put(position, (pair.getPoint() != null) && (pair.getCoordinate() != null));
}
getListenerHandler().positionAdded(position);
updateTransformation();
return position;
}
/**
* DOCUMENT ME!
*
* @param pair DOCUMENT ME!
* @param position DOCUMENT ME!
*
* @throws IndexOutOfBoundsException DOCUMENT ME!
* @throws IllegalArgumentException DOCUMENT ME!
*/
public void setPair(final PointCoordinatePair pair, final int position) throws IndexOutOfBoundsException,
IllegalArgumentException {
if (pair == null) {
throw new IllegalArgumentException("the given pair is null");
}
synchronized (pairs) {
checḱPosition(position);
pairs.set(position, (PointCoordinatePair)pair.clone());
getListenerHandler().positionChanged(position);
updateTransformation();
}
}
/**
* DOCUMENT ME!
*/
public void removeAllPairs() {
while (getNumOfPairs() > 0) {
removePair(getNumOfPairs() - 1);
}
}
/**
* DOCUMENT ME!
*
* @param position DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws IndexOutOfBoundsException DOCUMENT ME!
*/
public boolean removePair(final int position) throws IndexOutOfBoundsException {
synchronized (pairs) {
checḱPosition(position);
final boolean success = pairs.remove(position) != null;
if (success) {
getPositionStates().remove(position);
getListenerHandler().positionRemoved(position);
updateTransformation();
}
return success;
}
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public int getNumOfPairs() {
return pairs.size();
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public PointCoordinatePair[] getCompletePairs() {
final List<PointCoordinatePair> pairs = Arrays.asList(getPairs(Boolean.TRUE));
final List<PointCoordinatePair> completePairs = new ArrayList<PointCoordinatePair>(pairs.size());
for (int position = 0; position < pairs.size(); position++) {
final PointCoordinatePair pair = pairs.get(position);
if ((pair != null) && (pair.getPoint() != null) && (pair.getCoordinate() != null)) {
completePairs.add(pair);
}
}
return completePairs.toArray(new PointCoordinatePair[0]);
}
/**
* DOCUMENT ME!
*
* @param position DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws IndexOutOfBoundsException DOCUMENT ME!
*/
public PointCoordinatePair getPair(final int position) throws IndexOutOfBoundsException {
synchronized (pairs) {
checḱPosition(position);
return pairs.get(position);
}
}
/**
* DOCUMENT ME!
*
* @param position DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws IndexOutOfBoundsException DOCUMENT ME!
*/
public Point getPoint(final int position) throws IndexOutOfBoundsException {
return getPair(position).getPoint();
}
/**
* DOCUMENT ME!
*
* @param position DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws IndexOutOfBoundsException DOCUMENT ME!
*/
public Coordinate getPointCoordinate(final int position) throws IndexOutOfBoundsException {
final Point point = getPoint(position);
if (point != null) {
final AffineTransformation transform = getMetaData().getTransform();
if (transform != null) {
return transform.transform(new Coordinate(point.getX(), point.getY()), new Coordinate());
} else {
return null;
}
} else {
return null;
}
}
/**
* DOCUMENT ME!
*
* @param position DOCUMENT ME!
* @param point DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws IndexOutOfBoundsException DOCUMENT ME!
*/
public boolean setPoint(final int position, final Point point) throws IndexOutOfBoundsException {
if (position == pairs.size()) {
return addPair(point) >= 0;
} else {
final PointCoordinatePair pair = getPair(position);
if (pair != null) {
pair.setPoint(point);
getListenerHandler().positionChanged(position);
return true;
} else {
return false;
}
}
}
/**
* DOCUMENT ME!
*
* @param position DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws IndexOutOfBoundsException DOCUMENT ME!
*/
public Coordinate getCoordinate(final int position) throws IndexOutOfBoundsException {
return getPair(position).getCoordinate();
}
/**
* DOCUMENT ME!
*
* @param position DOCUMENT ME!
* @param coordinate DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws IndexOutOfBoundsException DOCUMENT ME!
*/
public boolean setCoordinate(final int position, final Coordinate coordinate) throws IndexOutOfBoundsException {
if (position == pairs.size()) {
return addPair(coordinate) >= 0;
} else {
final PointCoordinatePair pair = getPair(position);
if (pair != null) {
pair.setCoordinate(coordinate);
getListenerHandler().positionChanged(position);
updateTransformation();
return true;
} else {
return false;
}
}
}
/**
* DOCUMENT ME!
*
* @param position DOCUMENT ME!
*
* @throws IndexOutOfBoundsException DOCUMENT ME!
*/
public void checḱPosition(final int position) throws IndexOutOfBoundsException {
if ((position < 0) || (position >= pairs.size())) {
throw new IndexOutOfBoundsException();
}
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public PointCoordinatePair[] getPairs() {
return getPairs(null);
}
/**
* DOCUMENT ME!
*
* @param enabled DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public PointCoordinatePair[] getPairs(final Boolean enabled) {
final List<PointCoordinatePair> pairs = new ArrayList<PointCoordinatePair>(this.pairs);
final PointCoordinatePair[] array = new PointCoordinatePair[pairs.size()];
for (int position = 0; position < pairs.size(); position++) {
final boolean include = (enabled == null) || enabled.equals(getPositionStates().get(position));
if (include) {
final PointCoordinatePair pair = pairs.get(position);
final Point point = pair.getPoint();
final Coordinate coordinate = pair.getCoordinate();
array[position] = new PointCoordinatePair(point, coordinate);
}
}
return array;
}
/**
* DOCUMENT ME!
*/
public void updateTransformation() {
final AffineTransformation oldTransformation = getMetaData().getTransform();
final Polygon imageBoundsGeometry = createPolygon(getMetaData().getImageBounds());
final AffineTransformation avgTransform = RasterGeoReferencingBackend.calculateAvgTransformation(
getCompletePairs());
final AffineTransformation transform = (avgTransform != null) ? avgTransform : getInitialTransform();
if (!transform.equals(oldTransformation)) {
final Envelope imageEnvelope = transform.transform(imageBoundsGeometry).getEnvelopeInternal();
getMetaData().setTransform(transform);
getMetaData().setImageEnvelope(imageEnvelope);
getListenerHandler().transformationChanged();
}
}
/**
* DOCUMENT ME!
*
* @param bounds DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
private Polygon createPolygon(final Rectangle bounds) {
final GeometryFactory factory = new GeometryFactory(
new PrecisionModel(),
CrsTransformer.extractSridFromCrs(CismapBroker.getInstance().getSrs().getCode()));
final LinearRing linear = factory.createLinearRing(
new Coordinate[] {
new Coordinate(bounds.getMinX(), bounds.getMinY()),
new Coordinate(bounds.getMaxX(), bounds.getMinY()),
new Coordinate(bounds.getMaxX(), bounds.getMaxY()),
new Coordinate(bounds.getMinX(), bounds.getMaxY()),
new Coordinate(bounds.getMinX(), bounds.getMinY())
});
return factory.createPolygon(linear);
}
/**
* DOCUMENT ME!
*
* @param transforms DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static AffineTransformation createAverageTransformation(final List<AffineTransformation> transforms) {
final double[] avg = new double[6];
for (final AffineTransformation transform : transforms) {
final double[] matrix = transform.getMatrixEntries();
for (int i = 0; i < avg.length; i++) {
avg[i] += matrix[i] / transforms.size();
}
}
return new AffineTransformation(avg);
}
/**
* DOCUMENT ME!
*
* @param position DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public double getError(final int position) {
final PointCoordinatePair pair = pairs.get(position);
if ((getMetaData().getTransform() != null) && (pair != null) && (pair.getPoint() != null)
&& (pair.getCoordinate() != null)) {
final Coordinate point = new Coordinate(pair.getPoint().getX(), pair.getPoint().getY());
final Coordinate transformedPoint = getMetaData().getTransform().transform(point, new Coordinate());
return transformedPoint.distance(pair.getCoordinate());
} else {
return 0;
}
}
/**
* public static List<Object[]> comb(final Object[] input, final int k) { final List<int[]> indices = comb(input,
* k); }.
*
* @param input DOCUMENT ME!
* @param setSize DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static List<Object[]> getCombinations(final Object[] input, final int setSize) {
final List<Object[]> subsets = new ArrayList<>();
final int[] indices = new int[setSize]; // here we'll keep indices
// pointing to elements in input array
if (setSize <= input.length) {
// store first 'setSize' number of indices
for (int index = 0; index < setSize; index++) {
indices[index] = index;
}
subsets.add(getSubset(input, indices));
int index;
do {
// find position of item that can be incremented
index = setSize - 1;
while ((index >= 0) && (indices[index] == (input.length - setSize + index))) {
index--;
}
if (index >= 0) {
indices[index]++; // increment this item
for (++index; index < setSize; index++) { // fill up remaining items
indices[index] = indices[index - 1] + 1;
}
subsets.add(getSubset(input, indices));
}
} while (index >= 0);
}
return subsets;
}
/**
* generate actual subset by index sequence.
*
* @param input DOCUMENT ME!
* @param indices DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static Object[] getSubset(final Object[] input, final int[] indices) {
final Object[] result = new Object[indices.length];
for (int index = 0; index < indices.length; index++) {
result[index] = input[indices[index]];
}
return result;
}
//~ Inner Classes ----------------------------------------------------------
/**
* DOCUMENT ME!
*
* @version $Revision$, $Date$
*/
private class ListenerHandler implements RasterGeoReferencingHandlerListener {
//~ Instance fields ----------------------------------------------------
private final Collection<RasterGeoReferencingHandlerListener> listeners =
new ArrayList<RasterGeoReferencingHandlerListener>();
//~ Methods ------------------------------------------------------------
/**
* DOCUMENT ME!
*
* @param listener DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public boolean add(final RasterGeoReferencingHandlerListener listener) {
return listeners.add(listener);
}
/**
* DOCUMENT ME!
*
* @param listener DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public boolean remove(final RasterGeoReferencingHandlerListener listener) {
return listeners.remove(listener);
}
@Override
public void positionAdded(final int position) {
for (final RasterGeoReferencingHandlerListener listener : listeners) {
listener.positionAdded(position);
}
}
@Override
public void positionRemoved(final int position) {
for (final RasterGeoReferencingHandlerListener listener : listeners) {
listener.positionRemoved(position);
}
}
@Override
public void positionChanged(final int position) {
for (final RasterGeoReferencingHandlerListener listener : listeners) {
listener.positionChanged(position);
}
}
@Override
public void transformationChanged() {
for (final RasterGeoReferencingHandlerListener listener : listeners) {
listener.transformationChanged();
}
}
}
}