/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.autoplot.pngwalk; import java.awt.Point; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.net.URI; import java.text.ParseException; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JOptionPane; import org.das2.datum.Datum; import org.das2.datum.DatumRange; import org.das2.datum.DatumRangeUtil; import org.das2.datum.EnumerationUnits; import org.das2.datum.Units; import org.das2.datum.UnitsUtil; import org.das2.util.ImageUtil; import org.das2.util.LoggerManager; import org.das2.util.monitor.AlertNullProgressMonitor; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.das2.qds.DataSetOps; import org.das2.qds.DataSetUtil; import org.das2.qds.QDataSet; import org.das2.qds.SemanticOps; import org.autoplot.datasource.DataSetURI; import org.autoplot.datasource.DataSourceUtil; import org.das2.qds.ops.Ops; /** * Quick-n-dirty class for picking off points from images. The ClickDigitizer knows how to * grab JSON metadata from the image (http://autoplot.org/richPng) and invTransform the pixel * location to a dataset. * @author jbf */ public class ClickDigitizer { //WalkImageSequence seq; PngWalkView view; PngWalkTool viewer; private static final Logger logger= LoggerManager.getLogger("autoplot.pngwalk"); public ClickDigitizer( PngWalkView view ) { this.view= view; } void setViewer( PngWalkTool viewer ) { this.viewer= viewer; } private JSONObject getPlotContaining( JSONArray plots, int x, int y ) throws JSONException { for ( int i=0; it2 ) { // swap t2= yaxis.getInt("top"); t1= yaxis.getInt("bottom"); } if ( t1<=y && yt2 ) { // swap t2= yaxis.getInt("left"); t1= yaxis.getInt("right"); } if ( t1<=x && xStart Digitizer to record)"); } } } catch (JSONException ex) { Logger.getLogger(SinglePngWalkView.class.getName()).log(Level.SEVERE, null, ex); int h= view.seq.imageAt( view.seq.getIndex() ).getImage().getHeight(); Datum xx= Units.dimensionless.createDatum(x); Datum yy= Units.dimensionless.createDatum(h-y); view.seq.setStatus( "Pixel Coordinates: " + xx + ", "+ yy + " (unable to use JSON) " ); } } else { int h= view.seq.imageAt( view.seq.getIndex() ).getImage().getHeight(); Datum xx= Units.dimensionless.createDatum(x); Datum yy= Units.dimensionless.createDatum(h-y); if ( viewer!=null ) { view.seq.setStatus( "Pixel Coordinates: " + xx + ", "+ yy ); if ( release==false && viewer.digitizer!=null ) { try { viewer.digitizer.setSorted(false); viewer.digitizer.addDataPoint( xx, yy, meta ); //viewer.digitizer.addDataPoint( xx, yy ); } catch ( RuntimeException ex ) { // units conversion JOptionPane.showMessageDialog( viewer,ex.getMessage()); } } else { } } else { view.seq.setStatus( "Pixel Coordinates: " + xx + ", "+ yy + " (Options->Start Digitizer to record)"); } } } /** * return the coordinates for the click in data coordinates if the JSON * Rich PNG metadata is available, or just the pixel coordinates if it * is not, with the property "PlotNumber" indicating which plot number * in the JSON is used. The property "PlotNumber" * will be an integer equal -1 if the rich png metadata is not found, * or zero or positive int for valid clicks. If the x and y are not within * a plot, then -1 is returned for the plot number. * @param x the horizontal pixel coordinate * @param y the vertical pixel coordinate, with 0 at the top. * @return two-element bundle QDataSet with PlotNumber property. -1 * indicates no plot found at the location, and -99 means no rich png data. * @throws IOException * @throws ParseException */ public QDataSet pixelToDataTransform( int x, int y ) throws IOException, ParseException { if ( view==null ) { throw new IllegalArgumentException("view is not attached"); } URI uri= view.seq.imageAt( view.seq.getIndex() ).getUri(); File file = DataSetURI.getFile( uri, new AlertNullProgressMonitor("get image file") ); // assume it's local. String json= ImageUtil.getJSONMetadata( file ); return doTransformPoint(json, -1, x, y ); } /** * return the coordinates for the click in data coordinates if the JSON * Rich PNG metadata is available, or just the pixel coordinates if it * is not, with the property "PlotNumber" indicating which plot number * in the JSON is used. The property "PlotNumber" * will be an integer equal -1 if the rich png metadata is not found, * or zero or positive int for valid clicks. If the x and y are not within * a plot, then null (None) is returned. * @param iplot the plot number, or -1 to indicate whichever plot x and y are within. * @param x the horizontal position * @param y the vertical position, with 0 being the top of the plot. * @return two-element bundle QDataSet with PlotNumber property. -1 * indicates no plot found at the location, and -99 means no rich png data. * @throws IOException * @throws ParseException */ public QDataSet pixelToDataTransform( int iplot, int x, int y ) throws IOException, ParseException { if ( view==null ) { throw new IllegalArgumentException("view is not attached"); } URI uri= view.seq.imageAt( view.seq.getIndex() ).getUri(); File file = DataSetURI.getFile( uri, new AlertNullProgressMonitor("get image file") ); // assume it's local. String json= ImageUtil.getJSONMetadata( file ); return doTransformPoint(json, iplot, x, y ); } /** * return the pixel coordinates for a given data coordinates. * @param iplot the plot number * @param p bundle of x and y data coordinates. * @return int[2] for the x and y pixel coordinates (0,0 is upper left). * @throws IOException * @throws ParseException */ public int[] dataToPixelTransform( int iplot, QDataSet p ) throws IOException, ParseException { if ( view==null ) { throw new IllegalArgumentException("view is not attached"); } URI uri= view.seq.imageAt( view.seq.getIndex() ).getUri(); File file = DataSetURI.getFile( uri, new AlertNullProgressMonitor("get image file") ); // assume it's local. String json= ImageUtil.getJSONMetadata( file ); return doInvTransformPoint( json, iplot, p ); } /** * This will transform the point x,y to data coordinates. If iplot is * -1, then the x and y coordinates will pick the first plot which contains, * otherwise the plot number is used. The result is a two-element bundle * QDataSet with the property "PlotNumber" which will indicate the plot * number used. If iplot is -1 and x and y are not within a plot, then * the PlotNumber will be -1. If the richPng metadata is not available * (null passed in for json), then -99 is returned for the plot number and * the pixel coordinate is returned. * * @param json null or the JSON * @param iplot -1 or the plot number. * @param x x in the canvas frame. * @param y y in the canvas frame. * @return rank 1 bundle x,y. * @throws IOException * @throws ParseException */ private QDataSet doTransformPoint( String json, int iplot, int x, int y) throws IOException, ParseException { if ( json!=null ) { try { JSONObject jo = new JSONObject( json ); JSONArray plots= jo.getJSONArray("plots"); JSONObject plot; if ( iplot==-1 ) { plot= getPlotContaining( plots, x, y ); } else { plot= plots.getJSONObject( iplot ); } if ( plot!=null ) { JSONObject xaxis= plot.getJSONObject("xaxis"); QDataSet xx= invTransform( xaxis, x, "left", "right" ); JSONObject yaxis= plot.getJSONObject("yaxis"); QDataSet yy= invTransform( yaxis, y, "bottom", "top" ); QDataSet result= Ops.bundle( xx, yy ); for ( int i=0; i1 ) { QDataSet images= Ops.slice1(ds,1); EnumerationUnits eu= (EnumerationUnits)SemanticOps.getUnits(images); QDataSet r= Ops.where( Ops.eq( images, eu.createDatum(view.seq.getSelectedName()) ) ); if ( r.length()==0 ) { return null; } ds= DataSetOps.applyIndex( ds, 0, r, true ); ds= Ops.slice1(ds,0); } QDataSet dep0= (QDataSet) ds.property(QDataSet.DEPEND_0); if (dep0.rank()>1 ){ dep0= dep0.slice(0); } if ( json==null ) { BufferedImage im= view.seq.imageAt( view.seq.getIndex() ).getImageIfLoaded(); if ( im==null ) return null; return Ops.bundle( dep0, Ops.subtract( Ops.dataset(im.getHeight()), ds ) ); } else { QDataSet result=null; for ( int ii= 0; ii