Package net.sourceforge.gpstools.xmp

Source Code of net.sourceforge.gpstools.xmp.XMPHandler

package net.sourceforge.gpstools.xmp;

/* gpsdings
* Copyright (C) 2007 Moritz Ringler
* $Id: XMPHandler.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.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.Date;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import net.sourceforge.gpstools.gpx.Wpt;

/** A ContentHandler for parsing the GPS information contained in an
        XMP document. **/
class XMPHandler extends DefaultHandler{
    private final static String XMP_EXIF_NAMESPACE = "http://ns.adobe.com/exif/1.0/";
    private final static String XMP_TIFF_NAMESPACE = "http://ns.adobe.com/tiff/1.0/";
    private XMPProperties xmp;
    private transient String element;
    private transient String namespace;
    private transient StringBuilder contents;

    @Override
    public void startDocument(){
        xmp = new XMPProperties();
        element = null;
        namespace = null;
        contents = null;
    }

    @Override
    public void startElement(String uri, String localname, String rawname,
                            Attributes atts){
        if(isKnownNamespace(uri)){
            element = localname;
            namespace = uri;
            contents = null;
        } else {
            element = null;
            namespace = null;
            contents = null;
        }
    }

    @Override
    public void characters(char[] ch, int start, int length){
        if(element != null){
            if(contents == null){
                contents = new StringBuilder();
            }
            contents.append(ch, start, length);
        }
    }

    private static boolean isKnownNamespace(String ns){
        return XMP_EXIF_NAMESPACE.equals(ns) || XMP_TIFF_NAMESPACE.equals(ns);
    }

    private void setResultProperty(String namespace, String key, String valuethrows ParseException{
        try{
            if(XMP_EXIF_NAMESPACE.equals(namespace)){
                setExifProperty(key, value);
            } else if(XMP_TIFF_NAMESPACE.equals(namespace)){
                setTiffProperty(key, value);
            }
        } catch (NumberFormatException nfx){
            ParseException px = new ParseException(nfx.getMessage(), 0);
            px.initCause(nfx);
            throw px;
        }
    }

    @Override
    public void endElement(String uri, String localname, String rawname) throws SAXException{
        if(uri != null && uri.equals(namespace)){
            if(localname.equals(element)){
                try{
                    setResultProperty(uri, localname, contents.toString().trim());
                } catch (ParseException ex){
                    throw new SAXException(ex);
                }
                contents = null;
                namespace = null;
                element = null;
            } else {
                throw new SAXException("Not well-formed:" +
                    "element <" + element + "> terminated by </" +
                    localname +">");
            }
        }
    }

    public XMPProperties getParseResult(){
        return xmp;
    }

    /** Regular expression used to parse XMP Exif Rationals. **/
    private final static Pattern RATIONAL_PATTERN = Pattern.compile("(\\d+)/(\\d+)?");
    /** Parses an XMP Exif Rational. **/
    private static BigDecimal parseRational(String key, String valuethrows ParseException{
        Matcher m = RATIONAL_PATTERN.matcher(value);
        BigDecimal dresult = null;
        if(m.matches()){
            int numerator = Integer.parseInt(m.group(1));
            int denominator = Integer.parseInt(m.group(2));
            if(denominator == 0){
                throw new ParseException(key + " value " + value + "has a zero denominator.",0);
            }
            dresult = new BigDecimal(1.0 * numerator /denominator);
        } else {
            throw new ParseException(key + " value " + value + "is not a Rational.",0);
        }
        return dresult;
    }

    /** Regular expression used to parse XMP Exif GPSCoordinates. **/
    private final static Pattern GPS_COORDINATE_PATTERN_A =
        Pattern.compile("(\\d+),(\\d+),(\\d+)([NWSE])");
    /** Regular expression used to parse XMP Exif GPSCoordinates. **/
    private final static Pattern GPS_COORDINATE_PATTERN_B =
        Pattern.compile("(\\d+),(\\d+\\.\\d+)([NWSE])");
    /** Parses an XMP Exif GPSCoordinate. **/
    private static BigDecimal parseCoordinate(String key, String value) throws ParseException{
        Matcher m = GPS_COORDINATE_PATTERN_A.matcher(value);
        if(m.matches()){
            int degrees = Integer.parseInt(m.group(1));
            int minutes = Integer.parseInt(m.group(2));
            int seconds = Integer.parseInt(m.group(3));
            double dval = degrees + (minutes + (seconds/60.))/60.;
            char hemi = m.group(4).charAt(0);
            if(hemi == 'S' || hemi == 'W'){
                dval = dval * -1;
            }
            return new BigDecimal(dval);
        }
        m = GPS_COORDINATE_PATTERN_B.matcher(value);
        if(m.matches()){
            int degrees = Integer.parseInt(m.group(1));
            double minutes = Double.parseDouble(m.group(2));
            double dval = degrees + minutes/60.;
            char hemi = m.group(3).charAt(0);
            if(hemi == 'S' || hemi == 'W'){
                dval = dval * -1;
            }
            return new BigDecimal(dval);
        }
        throw new ParseException(key + " value " + value + "is not a GPSCoordinate.",0);
    }

    /** Regular expression used to parse XMP Exif Dates. **/
    private final static Pattern DATE_PATTERN =
        Pattern.compile(
            "(\\d{4})" + //year: group 1
            "(?:-(\\d{2})" + //month: group 2
                "(?:-(\\d{2})" + //day: group 3
                    "(?:T(\\d{2}):(\\d{2})" + //hours and minutes: group 4 and 5
                        "(?::(\\d{2}(?:\\.\\d+)?)" + //seconds: group 6
                        ")?"+
                        "(?:([+-]\\d{2}:\\d{2})|Z)" + //time zone: group 7
                    ")?" +
                ")?" +
            ")?");
    /** Parses an XMP Exif Date. **/
    final static Calendar parseDate(String value) throws ParseException{
        Matcher m = DATE_PATTERN.matcher(value);
        Calendar dateTime = new GregorianCalendar();
        dateTime.setTimeZone(TimeZone.getTimeZone("UTC"));
        dateTime.setLenient(false);
        dateTime.setTime(new Date(0l));
        try{
        if(m.matches()){
            if( m.group(7) != null){
                dateTime.setTimeZone(TimeZone.getTimeZone("GMT"+m.group(7)));
            }
            dateTime.set(Calendar.YEAR, Integer.parseInt(m.group(1)));
            if( m.group(2) != null){
                dateTime.set(Calendar.MONTH, Integer.parseInt(m.group(2)) - 1);
            }
            if( m.group(3) != null){
                dateTime.set(Calendar.DAY_OF_MONTH, Integer.parseInt(m.group(3)));
            }
            if( m.group(4) != null){
                dateTime.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m.group(4)));
            }
            if( m.group(5) != null){
                dateTime.set(Calendar.MINUTE, Integer.parseInt(m.group(5)));
            }
            if( m.group(6) != null){
                float fseconds = Float.parseFloat(m.group(6));
                int seconds = (int) fseconds;
                dateTime.set(Calendar.SECOND, seconds);
                int millis = Math.round((fseconds - seconds) * 1000);
                if(millis != 0){
                    dateTime.set(Calendar.MILLISECOND, millis);
                }
            }
        }
        } catch (ArrayIndexOutOfBoundsException xx){
            ParseException pex = new ParseException("Error parsing date " + value, 0);
            pex.initCause(xx);
            throw pex;
        }
        return dateTime;
    }

    /** Sets an Exif specific property from a raw XMP tag key-value-pair.*/
    private void setExifProperty(String key, String value) throws ParseException{
        if(key.startsWith("GPS")){
            setGPSProperty(key, value);
        } else if("DateTimeOriginal".equals(key)){
            xmp.dateTimeOriginal = parseDate(value);
        } else if("PixelXDimension".equals(key)){
            xmp.setDimension(Integer.valueOf(value), null);
        } else if("PixelYDimension".equals(key)){
            xmp.setDimension(null,Integer.valueOf(value));
        }
    }

    /** Sets an Exif schema for TIFF property from a raw XMP tag key-value-pair.*/
    private void setTiffProperty(String key, String value) throws ParseException{
        if("ImageWidth".equals(key)){
            xmp.setDimension(Integer.valueOf(value), null);
        } else if("ImageHeight".equals(key)){
            xmp.setDimension(null, Integer.valueOf(value));
        }
    }

    /** Sets the location Wpt properties from a raw XMP tag key-value-pair.*/
    private void setGPSProperty(String key, String value) throws ParseException{
        Wpt location = xmp.location;
        if(key.equals("GPSAltitude")){
            BigDecimal val = parseRational(key, value);
            if (location.getEle() != null){
                val = location.getEle().multiply(val);
            }
            location.setEle(val);
        } else if (key.equals("GPSAltitudeRef")){
            int val = Integer.parseInt(value);
            switch(val){
                case 0: //above sea level: nothing to do
                    break;
                case 1: //below sea level
                    BigDecimal dval = new BigDecimal(-1);
                    if(location.getEle() == null){
                        location.setEle(dval);
                    } else {
                        location.setEle(dval.multiply(location.getEle()));
                    }
                    break;
                default:
                    throw new ParseException("Illegal value " + value + " for GPSAltitudeRef.",0);
            }
        } else if (key.equals("GPSLatitude")){
            location.setLat(parseCoordinate(key, value));
        } else if (key.equals("GPSLongitude")){
            location.setLon(parseCoordinate(key, value));
        } else if (key.equals("GPSMapDatum")){
            if(value == null || value.equals("")){
                System.err.println("Warning: GPS datum missing. Assuming WGS84.");
            } else if(!value.toUpperCase().replaceAll("[^0-9A-Z]", "").equals("WGS84")){
                throw new ParseException("Unsupported map datum: " + value.toString() +
                ". Currently only the WGS84 map datum is supported.", 0);
            }
        } else if (key.equals("GPSVersionID")){
            if(!"2.0.0.0".equals(value)){
                throw new ParseException("Unsupported GPSVersionID: " + value,0);
            }
        } else if (key.equals("GPSTimeStamp")){
            location.setTime(parseDate(value).getTime());
        }
    }
}
TOP

Related Classes of net.sourceforge.gpstools.xmp.XMPHandler

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.