/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2012 - 2014, 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.gui.swing.render2d.control.edition;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import org.geotoolkit.storage.coverage.CoverageReference;
import org.geotoolkit.storage.coverage.CoverageUtilities;
import org.geotoolkit.coverage.grid.GridCoverage2D;
import org.geotoolkit.coverage.grid.GridCoverageBuilder;
import org.geotoolkit.coverage.io.*;
import org.geotoolkit.display2d.canvas.RenderingContext2D;
import org.geotoolkit.display2d.canvas.J2DCanvas;
import org.apache.sis.geometry.GeneralEnvelope;
import org.geotoolkit.gui.swing.render2d.JMap2D;
import org.geotoolkit.gui.swing.render2d.control.navigation.PanHandler;
import org.geotoolkit.gui.swing.render2d.decoration.AbstractMapDecoration;
import org.geotoolkit.gui.swing.render2d.decoration.MapDecoration;
import org.geotoolkit.gui.swing.util.JOptionDialog;
import org.geotoolkit.gui.swing.resource.MessageBundle;
import org.geotoolkit.internal.referencing.CRSUtilities;
import org.geotoolkit.map.CoverageMapLayer;
import org.apache.sis.referencing.CRS;
import org.geotoolkit.referencing.ReferencingUtilities;
import org.geotoolkit.referencing.crs.PredefinedCRS;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.storage.DataStoreException;
import org.geotoolkit.font.FontAwesomeIcons;
import org.geotoolkit.font.IconBuilder;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.referencing.operation.TransformException;
import org.opengis.util.FactoryException;
import org.apache.sis.geometry.Envelopes;
/**
* Coverage editor tool.
*
* @author Johann Sorel (Geomatys)
*/
public class CoverageEditionDelegate extends AbstractEditionDelegate {
private static final ImageIcon ICON_COMMIT = IconBuilder.createIcon(FontAwesomeIcons.ICON_FLOPPY_O, 16, FontAwesomeIcons.DEFAULT_COLOR);
private static final ImageIcon ICON_ROLLBACK = IconBuilder.createIcon(FontAwesomeIcons.ICON_UNDO, 16, FontAwesomeIcons.DEFAULT_COLOR);
private static final ImageIcon ICON_SELECT = IconBuilder.createIcon(FontAwesomeIcons.ICON_LOCATION_ARROW, 16, FontAwesomeIcons.DEFAULT_COLOR);
private final CoverageMapDecoration decoration;
private final CoverageMapLayer layer;
//mouse gesture variables
private int mouseX = 0;
private int mouseY = 0;
private boolean selectAction = false;
private Rectangle selectRectangle = null;
//coverage edition variables
private JMap2D map = null;
private RenderingContext2D context = null;
private GridCoverage2D coverage = null;
// Write parameters discovered when setting the coverage
private GridCoverageWriteParam writeParam = null;
private final GridCoverageReadParam gcrp = new GridCoverageReadParam();
private RenderedImage img;
private WritableRaster raster;
private CoordinateReferenceSystem lastObjCRS = null;
private double[] dataPoints;
private double[] objPoints;
private final Rectangle gridSelectionSize = new Rectangle();
//pixel which value has been changed
private final List<Point> editedPixels = new ArrayList<Point>();
public CoverageEditionDelegate(JMap2D map, CoverageMapLayer layer) {
super(map);
this.layer = layer;
final GeneralEnvelope layerEnv = new GeneralEnvelope(layer.getBounds());
this.decoration = new CoverageMapDecoration(map, layerEnv);
}
@Override
public MapDecoration getDecoration() {
return decoration;
}
@Override
public void mouseMoved(MouseEvent e) {
super.mouseMoved(e);
this.mouseX = e.getX();
this.mouseY = e.getY();
decoration.getComponent().repaint();
}
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
this.mouseX = e.getX();
this.mouseY = e.getY();
decoration.getComponent().repaint();
try {
final Point mouseGridPosition = getMouseGridPosition();
if(mouseGridPosition != null){
final JPanel panel = new JPanel(new GridLayout(0, 2));
panel.setFocusCycleRoot(true);
final int nbSample = img.getSampleModel().getNumBands();
final int sampleType = img.getSampleModel().getDataType();
final double[] samples = new double[nbSample];
raster.getPixel(mouseGridPosition.x, mouseGridPosition.y, samples);
final List<JSpinner> spinners = new ArrayList<JSpinner>();
for(int i=0;i<samples.length;i++){
panel.add(new JLabel(String.valueOf(i)));
final JSpinner spinner = new JSpinner();
if(i==0) spinner.requestFocus();
switch(sampleType){
case DataBuffer.TYPE_BYTE :
spinner.setModel(new SpinnerNumberModel((int)samples[i],0,255,1));
break;
case DataBuffer.TYPE_DOUBLE :
spinner.setModel(new SpinnerNumberModel(samples[i],Double.NEGATIVE_INFINITY,Double.POSITIVE_INFINITY,1));
break;
case DataBuffer.TYPE_FLOAT :
spinner.setModel(new SpinnerNumberModel(samples[i],Float.NEGATIVE_INFINITY,Float.POSITIVE_INFINITY,1));
break;
case DataBuffer.TYPE_INT :
spinner.setModel(new SpinnerNumberModel((int)samples[i],Integer.MIN_VALUE,Integer.MAX_VALUE,1));
break;
case DataBuffer.TYPE_SHORT :
spinner.setModel(new SpinnerNumberModel((int)samples[i],Short.MIN_VALUE,Short.MAX_VALUE,1));
break;
case DataBuffer.TYPE_USHORT :
spinner.setModel(new SpinnerNumberModel((int)samples[i],0,Short.MAX_VALUE*2,1));
break;
default :
spinner.setModel(new SpinnerNumberModel(samples[i],Double.NEGATIVE_INFINITY,Double.POSITIVE_INFINITY,1));
}
spinners.add(spinner);
panel.add(spinner);
}
final int res = JOptionDialog.show(map, panel, JOptionPane.OK_CANCEL_OPTION);
if(res == JOptionPane.OK_OPTION){
editedPixels.add(mouseGridPosition);
//update image
final CoverageReference ref = layer.getCoverageReference();
if(ref != null){
for(int i=0;i<samples.length;i++){
samples[i] = ((Number)spinners.get(i).getValue()).doubleValue();
}
raster.setPixel(mouseGridPosition.x, mouseGridPosition.y, samples);
}
}
}
} catch (FactoryException ex) {
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
} catch (TransformException ex) {
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
}
}
@Override
public void mouseDragged(MouseEvent e) {
if(selectAction){
if(selectRectangle == null){
selectRectangle = new Rectangle(e.getX(),e.getY(),1,1);
}
selectRectangle.width = e.getX() - selectRectangle.x;
selectRectangle.height = e.getY() - selectRectangle.y;
decoration.getComponent().repaint();
}else{
super.mouseDragged(e);
}
}
@Override
public void mouseReleased(MouseEvent e) {
if(selectAction){
selectAction = false;
final Envelope visibleArea = getMap().getCanvas().getVisibleEnvelope();
try {
gcrp.clear();
gcrp.setEnvelope(visibleArea);
final CoverageReference ref = layer.getCoverageReference();
final CoverageReader reader = ref.acquireReader();
final GridCoverage2D cov = (GridCoverage2D) reader.read(ref.getImageIndex(), gcrp);
ref.recycle(reader);
setCoverage(cov,selectRectangle);
writeParam = new GridCoverageWriteParam();
final MathTransform grid_To_Crs = cov.getGridGeometry().getGridToCRS(PixelInCell.CELL_CORNER);
GeneralEnvelope env = new GeneralEnvelope(PredefinedCRS.CARTESIAN_2D);
env.setEnvelope(gridSelectionSize.x, gridSelectionSize.y, gridSelectionSize.x + gridSelectionSize.width, gridSelectionSize.y + gridSelectionSize.height);
env = Envelopes.transform(grid_To_Crs, env);
final CoordinateReferenceSystem covCRS = cov.getCoordinateReferenceSystem();
GeneralEnvelope vAInCovArea = new GeneralEnvelope(visibleArea);
vAInCovArea = new GeneralEnvelope(ReferencingUtilities.transform2DCRS(vAInCovArea, CRSUtilities.getCRS2D(covCRS)));
int minOrdinate = CoverageUtilities.getMinOrdinate(vAInCovArea.getCoordinateReferenceSystem());
vAInCovArea.setRange(minOrdinate++, env.getMinimum(0), env.getMaximum(0));
vAInCovArea.setRange(minOrdinate, env.getMinimum(1), env.getMaximum(1));
writeParam.setEnvelope(vAInCovArea);
} catch (Exception ex) {
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
writeParam = null;
}
selectRectangle = null;
decoration.getComponent().repaint();
}
super.mouseReleased(e);
}
/**
* return the current editer area points.
*/
private double[] getObjPoints(CoordinateReferenceSystem objCRS) throws FactoryException, TransformException{
if(objPoints == null || lastObjCRS != objCRS){
lastObjCRS = objCRS;
objPoints = new double[dataPoints.length];
final MathTransform dataToObj = CRS.findOperation(coverage.getCoordinateReferenceSystem(), objCRS, null).getMathTransform();
dataToObj.transform(dataPoints, 0, objPoints, 0, dataPoints.length/2);
}
return objPoints;
}
private void setCoverage(GridCoverage2D coverage, Rectangle selectionRectangle)
throws FactoryException, NoninvertibleTransformException, TransformException {
this.editedPixels.clear();
this.objPoints = null;
this.dataPoints = null;
this.raster = null;
this.img = null;
this.coverage = coverage;
if(coverage == null || selectionRectangle == null) return;
this.img = coverage.getRenderedImage();
this.raster = img.copyData(null);
this.img = new BufferedImage(img.getColorModel(), raster, img.getColorModel().isAlphaPremultiplied(), null);
final MathTransform gridTodata = coverage.getGridGeometry().getGridToCRS(PixelInCell.CELL_CORNER);
final MathTransform dispToObj = context.getDisplayToObjective();
final MathTransform objToData = CRS.findOperation(context.getObjectiveCRS2D(), coverage.getCoordinateReferenceSystem(), null).getMathTransform();
final MathTransform dataToGrid = gridTodata.inverse();
final double[] coords = new double[8];
coords[0] = selectionRectangle.x; coords[1] = selectionRectangle.y;
coords[2] = selectionRectangle.x+selectRectangle.width; coords[3] = selectionRectangle.y;
coords[4] = selectionRectangle.x+selectRectangle.width; coords[5] = selectionRectangle.y+selectRectangle.height;
coords[6] = selectionRectangle.x; coords[7] = selectionRectangle.y+selectRectangle.height;
dispToObj.transform(coords, 0, coords, 0, 4);
objToData.transform(coords, 0, coords, 0, 4);
dataToGrid.transform(coords, 0, coords, 0, 4);
double minX = coords[0];
double maxX = coords[0];
double minY = coords[1];
double maxY = coords[1];
for(int i=0;i<8;i+=2){
minX = Math.min(minX,coords[i]);
maxX = Math.max(maxX,coords[i]);
minY = Math.min(minY,coords[i+1]);
maxY = Math.max(maxY,coords[i+1]);
}
final int imageHeight = img.getHeight();
final int imageWidth = img.getWidth();
final int startX = (int) minX;
final int endX = Math.min( (int)(maxX+0.5), imageWidth);
final int startY = (int) minY;
final int endY = Math.min( (int)(maxY+0.5), imageHeight);
final int height = endY-startY;
final int width = endX-startX;
if(height <=0 || width <=0){
//invalid edition area
this.raster = null;
this.img = null;
return;
}
gridSelectionSize.x = startX;
gridSelectionSize.y = startY;
gridSelectionSize.height = height;
gridSelectionSize.width = width;
dataPoints = new double[(width+1)*(height+1)*2];
int i=0;
for(int y=startY; y<=endY; y++){
for(int x=startX; x<=endX; x++){
dataPoints[i] = x;
i++;
dataPoints[i] = y;
i++;
}
}
try{
gridTodata.transform(dataPoints, 0, dataPoints, 0, i/2);
}catch(Exception ex){
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
}
}
private Point getMouseGridPosition() throws FactoryException, TransformException{
if(coverage == null) return null;
final MathTransform gridTodata = coverage.getGridGeometry().getGridToCRS(PixelInCell.CELL_CORNER);
final double[] coords = new double[2];
coords[0] = mouseX;
coords[1] = mouseY;
final MathTransform dispToObj = context.getDisplayToObjective();
final MathTransform objToData = CRS.findOperation(context.getObjectiveCRS2D(), coverage.getCoordinateReferenceSystem(), null).getMathTransform();
final MathTransform dataToGrid = gridTodata.inverse();
dispToObj.transform(coords, 0, coords, 0, 1);
objToData.transform(coords, 0, coords, 0, 1);
dataToGrid.transform(coords, 0, coords, 0, 1);
final int gridX = (int)coords[0];
final int gridY = (int)coords[1];
if(gridSelectionSize.contains(gridX, gridY)){
return new Point(gridX, gridY);
}
return null;
}
private void save() throws DataStoreException{
if (layer == null || coverage == null) {
return;
}
final CoverageReference ref = layer.getCoverageReference();
final GridCoverageWriter writer = ref.acquireWriter();
final GridCoverageBuilder gcb = new GridCoverageBuilder();
gcb.setRenderedImage(img);
gcb.setCoordinateReferenceSystem(coverage.getCoordinateReferenceSystem2D());
gcb.setGridToCRS(coverage.getGridGeometry().getGridToCRS2D());
writer.write(gcb.getGridCoverage2D(), writeParam);
ref.recycle(writer);
this.editedPixels.clear();
map.getCanvas().repaint();
}
private class CoverageMapDecoration extends AbstractMapDecoration implements PropertyChangeListener {
private final JPanel container = new GridComponent();
private GeneralEnvelope layerEnvelope;
private boolean selectEnable = true;
protected CoverageMapDecoration(JMap2D map, GeneralEnvelope layerEnvelope) {
map.getCanvas().addPropertyChangeListener(this);
this.layerEnvelope = layerEnvelope;
final Envelope visibleArea = getMap().getCanvas().getVisibleEnvelope();
checkEditable(new GeneralEnvelope(visibleArea));
initComponent();
}
/**
* Check if the current canvas envelope is contained in layer envelope.
*
* @param canvasEnv
*/
private void checkEditable (GeneralEnvelope canvasEnv) {
if (layerEnvelope != null) {
int canvasDims = canvasEnv.getDimension();
int layerDim = layerEnvelope.getDimension();
if (canvasDims == layerDim) {
try {
canvasEnv = new GeneralEnvelope(Envelopes.transform(canvasEnv, layerEnvelope.getCoordinateReferenceSystem()));
selectEnable = canvasEnv.intersects(layerEnvelope, true);
} catch (TransformException ex) {
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
selectEnable = false;
}
} else {
selectEnable = false;
}
return;
}
selectEnable = true;
}
private void initComponent() {
//clear
container.removeAll();
container.setLayout(new BorderLayout());
container.setOpaque(false);
container.setFocusable(false);
final JPanel guiRight = new JPanel(new BorderLayout(10, 10));
guiRight.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
//NORTH
//select area
final JButton guiSelect = new JButton(MessageBundle.format("select_area"));
guiSelect.setIcon(ICON_SELECT);
guiSelect.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
selectAction = true;
}
});
guiSelect.setEnabled(selectEnable);
//CENTER
final JPanel guiTools = new JPanel(new BorderLayout(0, 0));
guiTools.add(BorderLayout.NORTH,new JSeparator(SwingConstants.HORIZONTAL));
if (!selectEnable) {
final JPanel warningPanel = new JPanel(new BorderLayout(0, 0));
final JLabel warningMessage = new JLabel();
StringBuilder sb = new StringBuilder("<html><p width=\"120px\">");
sb.append(MessageBundle.format("edition_coverage_warrningdimension"));
sb.append("</p></html>");
warningMessage.setText(sb.toString());
warningPanel.add(BorderLayout.NORTH, warningMessage);
guiTools.add(BorderLayout.CENTER, warningPanel);
}
guiTools.add(BorderLayout.SOUTH,new JSeparator(SwingConstants.HORIZONTAL));
//SOUTH
final JPanel guiApplyPane = new JPanel(new GridLayout(0,1,2,2));
//save edition
final JButton guiSave = new JButton(MessageBundle.format("save"));
guiSave.setIcon(ICON_COMMIT);
guiSave.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
save();
} catch (DataStoreException ex) {
LOGGER.log(Level.WARNING, ex.getMessage(),ex);
}
}
});
guiApplyPane.add(guiSave);
//cancel edition
final JButton guiCancel = new JButton(MessageBundle.format("cancel"));
guiCancel.setIcon(ICON_ROLLBACK);
guiCancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
setCoverage(null,null);
map.setHandler(new PanHandler(map,false));
} catch (Exception ex) {
LOGGER.log(Level.WARNING, ex.getMessage(),ex);
}
}
});
guiApplyPane.add(guiCancel);
guiRight.add(BorderLayout.NORTH, guiSelect);
guiRight.add(BorderLayout.CENTER, guiTools);
guiRight.add(BorderLayout.SOUTH, guiApplyPane);
container.add(BorderLayout.EAST,guiRight);
}
@Override
public void refresh() {
initComponent();
container.revalidate();
container.repaint();
}
@Override
public void setMap2D(final JMap2D map) {
super.setMap2D(map);
CoverageEditionDelegate.this.map = map;
if(map != null && map.getCanvas() != null){
context = new RenderingContext2D(map.getCanvas());
}else{
context = null;
}
}
@Override
public JComponent getComponent() {
return container;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(J2DCanvas.ENVELOPE_KEY)) {
final Envelope newEnv = (Envelope) evt.getNewValue();
checkEditable(new GeneralEnvelope(newEnv));
this.refresh();
}
}
}
private class GridComponent extends JPanel{
@Override
protected void paintComponent(final Graphics gg) {
super.paintComponent(gg);
final Graphics2D g = (Graphics2D) gg;
//disable anti-aliasing
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
paintGrid(g);
if (selectRectangle != null) {
g.setColor(Color.ORANGE);
g.setStroke(new BasicStroke(2));
g.draw(selectRectangle);
}
}
/**
* Paint edit grid and mouse over pixel
*/
private void paintGrid(final Graphics2D g){
//check if the map has a Java2D canvas
if(map == null) return;
final J2DCanvas canvas = map.getCanvas();
if(!(canvas != null)) return;
canvas.prepareContext(context,(Graphics2D) g.create(), null);
if(raster == null) return;
final Rectangle area = context.getCanvasDisplayBounds();
try{
final MathTransform gridTodata = coverage.getGridGeometry().getGridToCRS(PixelInCell.CELL_CORNER);
final MathTransform dataToObj = CRS.findOperation(coverage.getCoordinateReferenceSystem(), context.getObjectiveCRS2D(), null).getMathTransform();
final MathTransform objToDisp = context.getObjectiveToDisplay();
final MathTransform gridToDisp = MathTransforms.concatenate(gridTodata, dataToObj, objToDisp);
// paint grid --------------------------------------------------
g.setColor(Color.GRAY);
g.setStroke(new BasicStroke(1));
final double[] pointObj = getObjPoints(context.getObjectiveCRS2D());
final double[] pointdisp = Arrays.copyOf(pointObj, pointObj.length);
objToDisp.transform(pointdisp, 0, pointdisp, 0, pointdisp.length/2);
final int height = gridSelectionSize.height;
final int width = gridSelectionSize.width + 1; //there is one extra point per line
final double[] coords = new double[8];
for(int y=0; y<height; y++){
for(int x=0; x<width-1; x++){
coords[0] = pointdisp[( y*width+x )*2]; coords[1] = pointdisp[( y*width+x )*2+1];
coords[2] = pointdisp[( y*width+x+1)*2]; coords[3] = pointdisp[( y*width+x+1)*2+1];
coords[4] = pointdisp[((y+1)*width+x+1)*2]; coords[5] = pointdisp[((y+1)*width+x+1)*2+1];
coords[6] = pointdisp[((y+1)*width+x )*2]; coords[7] = pointdisp[((y+1)*width+x )*2+1];
if( coords[2] < area.x || coords[0] > area.x+area.width
|| coords[5] < area.y || coords[1] > area.y+area.height){
continue;
}
g.drawLine((int)coords[2], (int)coords[3], (int)coords[4], (int)coords[5]);
if(x==0) g.drawLine((int)coords[6], (int)coords[7], (int)coords[0], (int)coords[1]);
g.drawLine((int)coords[4], (int)coords[5], (int)coords[6], (int)coords[7]);
if(y==0) g.drawLine((int)coords[0], (int)coords[1], (int)coords[2], (int)coords[3]);
}
}
//paint edited pixels ------------------------------------------
g.setStroke(new BasicStroke(1));
for(Point pt : editedPixels){
final int gridX = pt.x;
final int gridY = pt.y;
coords[0] = gridX; coords[1] = gridY;
coords[2] = gridX+1; coords[3] = gridY;
coords[4] = gridX+1; coords[5] = gridY+1;
coords[6] = gridX; coords[7] = gridY+1;
gridToDisp.transform(coords, 0, coords, 0, 4);
final GeneralPath path = new GeneralPath();
path.moveTo(coords[0], (int)coords[1]);
path.lineTo(coords[2], (int)coords[3]);
path.lineTo(coords[4], (int)coords[5]);
path.lineTo(coords[6], (int)coords[7]);
path.lineTo(coords[0], (int)coords[1]);
path.closePath();
g.setColor(Color.YELLOW);
g.fill(path);
g.setColor(Color.BLACK);
g.draw(path);
g.drawLine((int)coords[0], (int)coords[1], (int)coords[4], (int)coords[5]);
g.drawLine((int)coords[2], (int)coords[3], (int)coords[6], (int)coords[7]);
}
//paint mouse position -----------------------------------------
final Point mouseGridPosition = getMouseGridPosition();
if(mouseGridPosition != null){
final int gridX = mouseGridPosition.x;
final int gridY = mouseGridPosition.y;
coords[0] = gridX; coords[1] = gridY;
coords[2] = gridX+1; coords[3] = gridY;
coords[4] = gridX+1; coords[5] = gridY+1;
coords[6] = gridX; coords[7] = gridY+1;
gridToDisp.transform(coords, 0, coords, 0, 4);
final GeneralPath path = new GeneralPath();
path.moveTo(coords[0], (int)coords[1]);
path.lineTo(coords[2], (int)coords[3]);
path.lineTo(coords[4], (int)coords[5]);
path.lineTo(coords[6], (int)coords[7]);
path.lineTo(coords[0], (int)coords[1]);
path.closePath();
g.setColor(Color.RED);
g.fill(path);
final int nbSample = img.getSampleModel().getNumBands();
final double[] samples = new double[nbSample];
raster.getPixel(gridX, gridY, samples);
final String str = Arrays.toString(samples);
final FontMetrics fm = g.getFontMetrics();
final Rectangle rect = fm.getStringBounds(str, g).getBounds();
g.setColor(Color.WHITE);
rect.x = mouseX +4;
rect.y = mouseY - fm.getHeight();
rect.height = rect.height +4;
rect.width = rect.width +4;
g.fill(rect);
g.setColor(Color.BLACK);
g.drawString(str, mouseX+6, mouseY);
}
}catch(Exception ex ){
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
}
}
}
}