Package net.sourceforge.gpstools.exif

Source Code of net.sourceforge.gpstools.exif.CalibratedExifReader$TimeCalibrator$TimeCalibration

package net.sourceforge.gpstools.exif;

/* gpsdings
* Copyright (C) 2006 Moritz Ringler
* $Id: CalibratedExifReader.java 441 2010-12-13 20:04:20Z ringler $
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  This program 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 General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
import java.awt.Dimension;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.Date;
import java.util.Collections;
import java.util.TreeMap;
import java.util.SortedMap;
import net.sourceforge.gpstools.gpx.Wpt;
import org.apache.commons.math.FunctionEvaluationException;
import net.sourceforge.gpstools.math.LinearFunction;
import org.apache.commons.math.analysis.SplineInterpolator;
import org.apache.commons.math.analysis.UnivariateRealFunction;

/** Adds a time calibration to a GPSExifReader that allows to read dates
in UTC. This addresses two problems: A. The camera may be off with respect
to the correct time for the time zone it is in. B. The time zone is not stored
in Exif.
**/
public class CalibratedExifReader implements GpsExifReader{
    private static final UnivariateRealFunction IDENTITY = LinearFunction.withUnitySlope(0.0);
    private static final Long LONG_ZERO = new Long(0l);
    private static class TimeCalibrator extends SplineInterpolator{
        private static class TimeCalibration implements UnivariateRealFunction{
            private final double minx;
            private final double minOffset;
            private final double maxx;
            private final double maxOffset;
            private final UnivariateRealFunction inter;

            public TimeCalibration(double minX, double minY, double maxX, double maxY, UnivariateRealFunction interpol){
                minx = minX;
                maxx = maxX;
                minOffset = minY - minX;
                maxOffset = maxY - maxX;
                inter = interpol;
            }

            @Override
            public double value(double x) throws FunctionEvaluationException{
                double result = 0;
                if( x < minx){
                    result = x + minOffset;
                } else if (x > maxx){
                    result = x + maxOffset;
                } else {
                    result = inter.value(x);
                }
                return result;
            }
        }

        @Override
        public UnivariateRealFunction interpolate(double[] x, double[] y){
           final int n = x.length;
           if(y.length != n || n == 0){
               throw new IllegalArgumentException("Both arrays must have the same length > 0");
           }
           double minX = x[0];
           double minY = y[0];
           double maxX = x[0];
           double maxY = y[0];
           for(int i=1; i< n; i++){
               if(x[i] < minX){
                   minX = x[i];
                   minY = y[i];
               } else if (x[i] > maxX){
                   maxX = x[i];
                   maxY = y[i];
               }
           }
           UnivariateRealFunction result = null;
           switch(n){
               case 1:
                   result = LinearFunction.withUnitySlope(y[0] - x[0]);
                   break;
               case 2:
                   if(minX == maxX){
                       throw new IllegalArgumentException("Identical x-values.");
                   }
                   double y0 = (maxX * minY - minX * maxY)/(maxX - minX);
                   double m = (maxY - minY)/(maxX - minX);
                   result = new TimeCalibration(minX, minY, maxX, maxY, new LinearFunction(y0, m));
                   break;
               default:
                   result = new TimeCalibration(minX, minY, maxX, maxY, super.interpolate(x, y));
           }
           return result;
        }
    }
    private UnivariateRealFunction fCalib = IDENTITY;
    private final GpsExifReader delegate;
    private final SortedMap<Long, Long> calibration =
        Collections.synchronizedSortedMap(new TreeMap<Long, Long>());
    private final Object sync = new Object();

    /** Constructs a new CalibratedExifReader with an new
        GpsExifReader delegate of unspecified type.
     **/
    public CalibratedExifReader(){
        this((new ReaderFactory()).getReader());
    }

    /** Constructs a new CalibratedExifReader that uses the
        specified GpsExifReader delegate to access raw Exif information.
     **/
    public CalibratedExifReader(GpsExifReader delegate){
        this.delegate = delegate;
        calibration.put(LONG_ZERO, LONG_ZERO);
    }

    /** Returns the OriginalDateTime from the exif header of the jpeg
        file adjusted to UTC using the current time calibration. */
    public Date readOriginalUTCTime(File jpegthrows IOException, ParseException, FunctionEvaluationException{
        return adjustDate(readOriginalTime(jpeg));
    }

    /** Adjusts the Date d to UTC using the current time calibration. **/
    protected Date adjustDate(Date d) throws FunctionEvaluationException{
        synchronized(sync){
            long millis = d.getTime();
            millis = Math.round(fCalib.value(millis));
            d.setTime(millis);
        }
        return d;
    }

    /** Calibrates this exif reader using a linear calibration function
        with unity slope. The time returned by readOriginalUTCTime(jpeg) will be
        <pre> result = readOriginalTime(jpeg) - millis </pre>
     **/
    public void setUTCOffset(final long millis){
        synchronized(sync){
            fCalib = LinearFunction.withUnitySlope(- millis);
            calibration.clear();
            calibration.put(LONG_ZERO, -millis);
        }
    }

    /** Calibrates this exif reader by interpolating
        calibration points supplied as two Date arrays of equal length.
        @throws IllegalArgumentException if cameraTimes or utcTimes is
        empty or if they have unequal lengths
        @throws NullPointerException if any of the arguments is <code>null</code>
     **/
    public void calibrate(Date[] cameraTimes, Date[] utcTimes){
        final int nc = cameraTimes.length;
        if( utcTimes.length != nc){
            throw new IllegalArgumentException("Both Date arrays must have the same length.");
        }
        double[] camera = new double[nc];
        double[] utc = new double[nc];
        synchronized(sync){
            long lcam, lutc;
            calibration.clear();
            for(int i=0; i<nc; i++){
                lcam = cameraTimes[i].getTime();
                lutc = utcTimes[i].getTime();
                camera[i] = lcam;
                utc[i] = lutc;
                calibration.put(lcam, lutc); //auto-boxing
            }
        }
        fCalib = (new TimeCalibrator()).interpolate(camera, utc);
    }

    /** Calibrates this exif reader by interpolating
        calibration points supplied as two long arrays of equal length.
        The long values are understood as milliseconds since the epoch
        (January 1, 1970, 00:00:00 GMT).
        @throws IllegalArgumentException if cameraTimes or utcTimes is
        empty or if they have unequal lengths
        @throws NullPointerException if any of the arguments is <code>null</code>
     **/
    public void calibrate(long[] cameraTimes, long[] utcTimes){
        final int n = cameraTimes.length;
        if(n != utcTimes.length){
            throw new IllegalArgumentException("cameraTimes and utcTimes must have the same length.");
        }
        double[] camera = new double[n];
        double[] utc = new double[n];
        synchronized(sync){
            calibration.clear();
            for(int i=0; i<n; i++){
                camera[i] = cameraTimes[i];
                utc[i] = utcTimes[i];
                calibration.put(cameraTimes[i], utcTimes[i]); //uses auto-boxing
            }
            fCalib = (new TimeCalibrator()).interpolate(camera, utc);
        }
    }

    /** Calibrates this exif reader by interpolating
        calibration points supplied as a sorted map of Dates.
        @throws IllegalArgumentException if calib is empty
        @throws NullPointerException if calib is <code>null</code>
     **/
    public void calibrateFromDateMap(SortedMap<Date, Date> calib){
        final int n = calib.size();
        calibrate(
            calib.keySet().toArray(new Date[n]),
            calib.values().toArray(new Date[n])
        );
    }

    /** Calibrates this exif reader by interpolating
        calibration points supplied as a sorted map of longs.
        The long values are understood as milliseconds since the epoch
        (January 1, 1970, 00:00:00 GMT).
        @throws IllegalArgumentException if calib is empty
        @throws NullPointerException if calib is <code>null</code>
    **/
    public void calibrateFromLongMap(SortedMap<Long, Long> calib){
        final int n = calib.size();
        double[] cam = new double[n];
        double[] utc = new double[n];
        int i = 0;
        for(Long key : calib.keySet()){
            cam[i] = key.doubleValue();
            utc[i] = calib.get(key).doubleValue();
            i++;
        }
        synchronized(sync){
            calibration.clear();
            calibration.putAll(calib);
            fCalib = (new TimeCalibrator()).interpolate(cam, utc);
        }
    }

    /** Returns an unmodifiable view of this CalibratedExifReader's
        calibration points. When the calibration is changed the
        returned Map will change, too. */
    public SortedMap<Long, Long> calibration(){
        synchronized(sync){
            return Collections.unmodifiableSortedMap(calibration);
        }
    }

    @Override
    public Date parseExifDate(String str) throws ParseException{
        return delegate.parseExifDate(str);
    }

    @Override
    public Date readOriginalTime(File jpeg) throws IOException, ParseException{
        Date d = delegate.readOriginalTime(jpeg);
        if (d == null){
            System.err.println("No original date-time information found in " + jpeg.getName());
        }
        return d;
    }

    @Override
    public Wpt readGPSTag(File jpeg) throws IOException, ParseException{
        Wpt result = delegate.readGPSTag(jpeg);
        if(result != null && result.getTime() == null){
           try{
               result.setTime(readOriginalUTCTime(jpeg));
           } catch (Exception ex){
               throw new Error(ex);
           }
        }
        return result;
    }

    @Override
    public Dimension readJPEGDimension(File f) throws IOException, ParseException{
        return delegate.readJPEGDimension(f);
    }

}
TOP

Related Classes of net.sourceforge.gpstools.exif.CalibratedExifReader$TimeCalibrator$TimeCalibration

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.