package com.revolsys.raster;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.beans.PropertyChangeListener;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import com.revolsys.geometry.cs.CoordinateSystem;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.GeometryFactoryProxy;
import com.revolsys.geometry.model.Point;
import com.revolsys.io.IoFactory;
import com.revolsys.io.map.MapSerializer;
import com.revolsys.logging.Logs;
import com.revolsys.math.matrix.Matrix;
import com.revolsys.spring.resource.Resource;
public interface GeoreferencedImage
extends GeometryFactoryProxy, MapSerializer, PropertyChangeListener {
static double[] calculateLSM(final BoundingBox boundingBox, final int imageWidth,
final int imageHeight, final List<MappedLocation> mappings) {
final Matrix A = getAMatrix(mappings, imageHeight);
final Matrix X = getXMatrix(boundingBox, imageWidth, imageHeight, mappings);
final Matrix P = getWeights(mappings.size());
final Matrix AT = A.transpose();
final Matrix ATP = new Matrix(AT.getRowCount(), P.getColumnCount());
final Matrix ATPA = new Matrix(AT.getRowCount(), A.getColumnCount());
final Matrix ATPX = new Matrix(AT.getRowCount(), 1);
final Matrix x = new Matrix(A.getColumnCount(), 1);
ATP.times(AT, P);
ATPA.times(ATP, A);
ATPX.times(ATP, X);
ATPA.invert();
x.times(ATPA, ATPX);
ATPA.invert();
return x.transpose().getRow(0);
}
static Matrix getAMatrix(final List<MappedLocation> mappings, final int imageHeight) {
final int mappingCount = mappings.size();
final int rowCount = mappingCount * 2;
final Matrix aMatrix = new Matrix(rowCount, 6);
for (int j = 0; j < mappingCount; ++j) {
final MappedLocation mappedLocation = mappings.get(j);
final Point sourcePoint = mappedLocation.getSourcePixel();
final double x = sourcePoint.getX();
final double y = imageHeight - sourcePoint.getY();
aMatrix.setRow(j, x, y, 1.0D, 0.0D, 0.0D, 0.0D);
}
for (int j = mappingCount; j < rowCount; ++j) {
final MappedLocation mappedLocation = mappings.get(j - mappingCount);
final Point sourcePoint = mappedLocation.getSourcePixel();
final double x = sourcePoint.getX();
final double y = imageHeight - sourcePoint.getY();
aMatrix.setRow(j, 0.0D, 0.0D, 0.0D, x, y, 1.0D);
}
return aMatrix;
}
public static Matrix getWeights(final int size) {
final int matrixSize = size * 2;
final Matrix P = new Matrix(matrixSize, matrixSize);
for (int j = 0; j < matrixSize; ++j) {
P.set(j, j, 1.0D);
}
return P;
}
static Matrix getXMatrix(final BoundingBox boundingBox, final int imageWidth,
final int imageHeight, final List<MappedLocation> mappings) {
final int mappingCount = mappings.size();
final int rowCount = mappingCount * 2;
final Matrix xMatrix = new Matrix(rowCount, 1);
for (int j = 0; j < mappingCount; ++j) {
final MappedLocation mappedLocation = mappings.get(j);
final Point targetPixel = mappedLocation.getTargetPixel(boundingBox, imageWidth, imageHeight);
final double x = targetPixel.getX();
xMatrix.set(j, 0, x);
}
for (int j = mappingCount; j < rowCount; ++j) {
final MappedLocation mappedLocation = mappings.get(j - mappingCount);
final Point targetPixel = mappedLocation.getTargetPixel(boundingBox, imageWidth, imageHeight);
final double y = imageHeight - targetPixel.getY();
xMatrix.set(j, 0, y);
}
return xMatrix;
}
static boolean isReadable(final Path path) {
return IoFactory.isAvailable(GeoreferencedImageReadFactory.class, path);
}
default void cancelChanges() {
}
void deleteTiePoint(MappedLocation tiePoint);
default void drawImage(final Graphics2D graphics, final BoundingBox viewBoundingBox,
final int viewWidth, final int viewHeight, final boolean useTransform) {
final BoundingBox imageBoundingBox = getBoundingBox();
if (viewBoundingBox.intersects(imageBoundingBox) && viewWidth > 0 && viewHeight > 0) {
final RenderedImage renderedImage = getRenderedImage();
drawRenderedImage(renderedImage, graphics, viewBoundingBox, viewWidth, viewHeight,
useTransform);
}
}
default void drawRenderedImage(final RenderedImage renderedImage, BoundingBox imageBoundingBox,
final Graphics2D graphics, final BoundingBox viewBoundingBox, final int viewWidth,
final boolean useTransform) {
if (renderedImage != null) {
final int imageWidth = renderedImage.getWidth();
final int imageHeight = renderedImage.getHeight();
if (imageWidth > 0 && imageHeight > 0) {
imageBoundingBox = imageBoundingBox.convert(viewBoundingBox.getGeometryFactory());
final AffineTransform transform = graphics.getTransform();
try {
final double scaleFactor = viewWidth / viewBoundingBox.getWidth();
final double imageMinX = imageBoundingBox.getMinX();
final double viewMinX = viewBoundingBox.getMinX();
final double screenX = (imageMinX - viewMinX) * scaleFactor;
final double imageMaxY = imageBoundingBox.getMaxY();
final double viewMaxY = viewBoundingBox.getMaxY();
final double screenY = -(imageMaxY - viewMaxY) * scaleFactor;
final double imageModelWidth = imageBoundingBox.getWidth();
final int imageScreenWidth = (int)Math.ceil(imageModelWidth * scaleFactor);
final double imageModelHeight = imageBoundingBox.getHeight();
final int imageScreenHeight = (int)Math.ceil(imageModelHeight * scaleFactor);
if (imageScreenWidth > 0 && imageScreenHeight > 0) {
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
if (imageScreenWidth > 0 && imageScreenHeight > 0) {
graphics.translate(screenX, screenY);
if (renderedImage instanceof BufferedImage && !useTransform) {
final BufferedImage bufferedImage = (BufferedImage)renderedImage;
try {
graphics.drawImage(bufferedImage, 0, 0, imageScreenWidth, imageScreenHeight,
null);
} catch (final Throwable e) {
Logs.error(this, imageScreenWidth + "x" + imageScreenHeight, e);
}
} else {
final double scaleX = (double)imageScreenWidth / imageWidth;
final double scaleY = (double)imageScreenHeight / imageHeight;
final AffineTransform imageTransform = new AffineTransform(scaleX, 0, 0, scaleY, 0,
0);
if (useTransform) {
final AffineTransform geoTransform = getAffineTransformation(imageBoundingBox);
imageTransform.concatenate(geoTransform);
}
graphics.drawRenderedImage(renderedImage, imageTransform);
}
}
}
} catch (final Throwable e) {
} finally {
graphics.setTransform(transform);
}
}
}
}
default void drawRenderedImage(final RenderedImage renderedImage, final Graphics2D graphics,
final BoundingBox viewBoundingBox, final int viewWidth, final int viewHeight,
final boolean useTransform) {
final BoundingBox imageBoundingBox = getBoundingBox();
drawRenderedImage(renderedImage, imageBoundingBox, graphics, viewBoundingBox, viewWidth,
useTransform);
}
default AffineTransform getAffineTransformation(final BoundingBox boundingBox) {
final List<MappedLocation> mappings = new ArrayList<>(getTiePoints());
final int count = mappings.size();
final int imageWidth = getImageWidth();
final int imageHeight = getImageHeight();
if (count == 1) {
final MappedLocation tiePoint = mappings.get(0);
final Point sourcePixel = tiePoint.getSourcePixel();
final Point targetPixel = tiePoint.getTargetPixel(boundingBox, imageWidth, imageHeight);
final double translateX = targetPixel.getX() - sourcePixel.getX();
final double translateY = sourcePixel.getY() - targetPixel.getY();
return new AffineTransform(1, 0, 0, 1, translateX, translateY);
} else if (count < 3) {
return new AffineTransform();
}
final double[] affineTransformMatrix = calculateLSM(boundingBox, imageWidth, imageHeight,
mappings);
final double translateX = affineTransformMatrix[2];
final double translateY = affineTransformMatrix[5];
final double scaleX = affineTransformMatrix[0];
final double scaleY = affineTransformMatrix[4];
final double shearX = affineTransformMatrix[1];
final double shearY = affineTransformMatrix[3];
return new AffineTransform(scaleX, shearY, shearX, scaleY, translateX, translateY);
}
BoundingBox getBoundingBox();
default BufferedImage getBufferedImage() {
final RenderedImage renderedImage = getRenderedImage();
if (renderedImage == null) {
return null;
} else if (renderedImage instanceof BufferedImage) {
return (BufferedImage)renderedImage;
} else {
final int width = getImageWidth();
final int height = getImageHeight();
final BufferedImage bufferedImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2 = bufferedImage.createGraphics();
g2.drawRenderedImage(renderedImage, null);
g2.dispose();
return bufferedImage;
}
}
int[] getDpi();
GeoreferencedImage getImage(final CoordinateSystem coordinateSystem);
GeoreferencedImage getImage(final CoordinateSystem coordinateSystem, final double resolution);
GeoreferencedImage getImage(final GeometryFactory geometryFactory);
default double getImageAspectRatio() {
final int imageWidth = getImageWidth();
final int imageHeight = getImageHeight();
if (imageWidth > 0 && imageHeight > 0) {
return (double)imageWidth / imageHeight;
} else {
return 0;
}
}
int getImageHeight();
Resource getImageResource();
int getImageWidth();
List<Dimension> getOverviewSizes();
RenderedImage getRenderedImage();
double getResolution();
List<MappedLocation> getTiePoints();
String getWorldFileExtension();
default boolean hasBoundingBox() {
return !getBoundingBox().isEmpty();
}
default boolean hasGeometryFactory() {
return getGeometryFactory().getCoordinateSystemId() > 0;
}
boolean isHasChanages();
default boolean isHasTransform() {
final int count = getTiePoints().size();
if (count > 2 || count == 1) {
return true;
} else {
return false;
}
}
boolean saveChanges();
void setBoundingBox(final BoundingBox boundingBox);
default void setBoundingBox(final double minX, final double maxY, final double pixelWidth,
final double pixelHeight) {
final GeometryFactory geometryFactory = getGeometryFactory();
final int imageWidth = getImageWidth();
final double maxX = minX + pixelWidth * imageWidth;
final int imageHeight = getImageHeight();
final double minY = maxY + pixelHeight * imageHeight;
final BoundingBox boundingBox = geometryFactory.newBoundingBox(minX, maxY, maxX, minY);
setBoundingBox(boundingBox);
}
void setDpi(final int... dpi);
void setRenderedImage(final RenderedImage image);
void setTiePoints(final List<MappedLocation> tiePoints);
}