com.mycompany.jpegrenamer.MetaDataReader.java Source code

Java tutorial

Introduction

Here is the source code for com.mycompany.jpegrenamer.MetaDataReader.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.mycompany.jpegrenamer;

import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.lang.GeoLocation;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;
import com.drew.metadata.exif.ExifSubIFDDirectory;
import com.drew.metadata.exif.GpsDirectory;
import com.drew.metadata.exif.makernotes.PanasonicMakernoteDirectory;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.ImageWriteException;
import org.apache.commons.imaging.Imaging;
import org.apache.commons.imaging.common.ImageMetadata;
import org.apache.commons.imaging.common.RationalNumber;
import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
import org.apache.commons.imaging.formats.tiff.TiffField;
import org.apache.commons.imaging.formats.tiff.TiffImageMetadata;
import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants;
import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants;
import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author eperson
 */
public class MetaDataReader {

    private static final Logger logger = LoggerFactory.getLogger(MetaDataReader.class);

    private static void printTagValue(final JpegImageMetadata jpegMetadata, final TagInfo tagInfo) {
        final TiffField field = jpegMetadata.findEXIFValueWithExactMatch(tagInfo);
        if (field == null) {
            logger.info(tagInfo.name + ": " + "Not Found.");
        } else {
            logger.info(tagInfo.name + ": " + field.getValueDescription());
        }
    }

    private static Date getDateTagValue(final JpegImageMetadata jpegMetadata, final TagInfo tagInfo)
            throws ImageReadException {
        final TiffField field = jpegMetadata.findEXIFValueWithExactMatch(tagInfo);
        if (field == null) {
            return null;
        } else {
            Date d = (Date) field.getValue();
            return d;
        }
    }

    private static String getTagValue(final JpegImageMetadata jpegMetadata, final TagInfo tagInfo) {
        final TiffField field = jpegMetadata.findEXIFValueWithExactMatch(tagInfo);
        if (field == null) {
            return "";
        } else {
            return field.getValueDescription();
        }
    }

    public Map<String, String> readMetaData2(String f) {
        logger.info("**************************************************");
        logger.info("Parsing file " + f);
        Map<String, String> s = new HashMap();
        ;
        try {
            s = getExifMetadata(new File(f));
        } catch (ImageReadException ex) {
            logger.error("", ex);
        } catch (IOException ex) {
            logger.error("", ex);
        } catch (ImageWriteException ex) {
            logger.error("", ex);
        }
        return s;
    }

    private Map<String, String> getAddressByGpsCoordinates(String lat, String lng)
            throws MalformedURLException, IOException, org.json.simple.parser.ParseException {
        Map<String, String> res = new HashMap();
        URL url = new URL(
                "http://maps.googleapis.com/maps/api/geocode/json?latlng=" + lat + "," + lng + "&sensor=true");
        logger.info(url.toString());
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        String formattedAddress = null;
        PrintStream out = new PrintStream(System.out, true, "UTF-8");
        try {
            InputStream in = url.openStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
            String result;
            String line = reader.readLine();
            result = line;
            while ((line = reader.readLine()) != null) {
                result += line;
            }
            JSONParser parser = new JSONParser();
            JSONObject rsp = (JSONObject) parser.parse(result);
            logger.debug("JSON " + rsp.toString());
            if (rsp.containsKey("error_message")) {
                JSONObject msg = (JSONObject) rsp.get("error_message");
                logger.error("Error response from Google Maps: " + msg);
                res.put("formatted_address", msg.toString());
            } else if (rsp.containsKey("results")) {
                JSONArray matches = (JSONArray) rsp.get("results");
                String premise = null;
                String administrative_area_level_1 = null;
                String administrative_area_level_2 = null;
                String sublocality_level_1 = null;
                String locality = null;
                String postal_town = null;
                String country = null;
                List<String> types = null;
                String short_name = null;
                String long_name = null;
                for (int i = 0; i < matches.size(); i++) {
                    JSONObject data = (JSONObject) matches.get(i); //TODO: check if idx=0 exists
                    if (formattedAddress == null) {
                        formattedAddress = (String) data.get("formatted_address");
                        res.put("formatted_address", formattedAddress);
                    }
                    JSONObject comp = (JSONObject) ((JSONArray) data.get("address_components")).get(0);
                    logger.debug("JSON " + comp.toString());
                    types = (List<String>) ((JSONArray) comp.get("types"));
                    short_name = (String) comp.get("short_name");
                    long_name = (String) comp.get("long_name");
                    logger.debug("JSON types" + types);
                    logger.debug("JSON short_name" + short_name);
                    logger.debug("JSON long_name" + long_name);
                    if (types.contains("premise")) {
                        premise = long_name;
                        res.put("premise", premise);
                    } else if (types.contains("sublocality_level_1")) {
                        sublocality_level_1 = long_name;
                        res.put("sublocality_level_1", sublocality_level_1);
                    } else if (types.contains("postal_town")) {
                        postal_town = long_name;
                        res.put("postal_town", postal_town);
                    } else if (types.contains("country")) {
                        logger.debug("Setting country to " + long_name);
                        country = long_name;
                        res.put("country", country);
                    } else if (types.contains("locality") && locality == null) {
                        logger.debug("Setting locality to " + long_name);
                        locality = long_name;
                        res.put("locality", locality);
                    } else if (types.contains("administrative_area_level_1")) {
                        logger.debug("Setting administrative_area_level_1 to " + long_name);
                        administrative_area_level_1 = long_name;
                        res.put("administrative_area_level_1", administrative_area_level_1);
                    }
                }
            }
        } finally {
            urlConnection.disconnect();
            logger.debug("Reverse geocode returns " + res);
            logger.debug("Reverse geocode returns formatted_address = " + formattedAddress);
            return res;
        }
    }

    Map<String, String> readMetaData(String f) {
        logger.info("**************************************************");
        logger.info("Parsing file " + f);
        Map<String, String> s = new HashMap();
        ;
        try {
            s = getMetaData(new File(f));
        } catch (IOException ex) {
            logger.error("", ex);
        } catch (ImageProcessingException ex) {
            logger.error("", ex);
        }
        return s;
    }

    public Map<String, String> getExifMetadata(final File jpegImageFile)
            throws ImageReadException, IOException, ImageWriteException {
        Map<String, String> res = new HashMap<>();
        Map<String, String> location = new HashMap();
        ;
        // note that metadata might be null if no metadata is found.
        final ImageMetadata metadata = Imaging.getMetadata(jpegImageFile);
        boolean isClassOk = metadata instanceof JpegImageMetadata;
        if (!isClassOk || (null == metadata)) {
            return res;
        }
        final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
        if (jpegMetadata == null) {
            logger.info("File does not contain metadata - " + jpegImageFile.getCanonicalPath());
            return res;
        }
        String dateOfCaptureString = getTagValue(jpegMetadata, TiffTagConstants.TIFF_TAG_DATE_TIME);
        SimpleDateFormat sdf = new SimpleDateFormat("''yyyy:MM:dd hh:mm:ss''");
        Date dateOfCapture = null;
        try {
            dateOfCapture = sdf.parse(dateOfCaptureString);
            final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH.mm.ss", Locale.ENGLISH);
            res.put("date", df.format(dateOfCapture));
        } catch (ParseException ex) {
            logger.error("", ex);
        }
        //        s = getTagValue(jpegMetadata, TiffTagConstants.TIFF_TAG_DATE_TIME);
        printTagValue(jpegMetadata, TiffTagConstants.TIFF_TAG_DATE_TIME);
        printTagValue(jpegMetadata, ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
        printTagValue(jpegMetadata, ExifTagConstants.EXIF_TAG_DATE_TIME_DIGITIZED);
        printTagValue(jpegMetadata, GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF);
        printTagValue(jpegMetadata, GpsTagConstants.GPS_TAG_GPS_LATITUDE);
        printTagValue(jpegMetadata, GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF);
        printTagValue(jpegMetadata, GpsTagConstants.GPS_TAG_GPS_LONGITUDE);
        if (null != jpegMetadata) {
            // note that exif might be null if no Exif metadata is found.
            final TiffImageMetadata exif = jpegMetadata.getExif();
            //logger.info(jpegMetadata.toString());
            if (null != exif) {
                //logger.info(exif.toString());
                final TiffImageMetadata.GPSInfo gpsInfo = exif.getGPS();
                if (null != gpsInfo) {
                    final String gpsDescription = gpsInfo.toString();
                    final double longitude = gpsInfo.getLongitudeAsDegreesEast();
                    final double latitude = gpsInfo.getLatitudeAsDegreesNorth();
                    logger.info("    " + "GPS Description: " + gpsDescription);
                    logger.info("    " + "GPS Longitude (Degrees East): " + longitude);
                    logger.info("    " + "GPS Latitude (Degrees North): " + latitude);
                    try {
                        Locale fmtLocale = Locale.US;
                        NumberFormat formatter = NumberFormat.getNumberInstance(fmtLocale);
                        formatter.setGroupingUsed(false);
                        location = getAddressByGpsCoordinates(formatter.format(latitude),
                                formatter.format(longitude));
                        if (location != null) {
                            res.putAll(location);
                        }
                    } catch (MalformedURLException ex) {
                        logger.error("", ex);
                    } catch (org.json.simple.parser.ParseException ex) {
                        logger.error("", ex);
                    }
                    logger.info("    " + location);
                }
                // more specific example of how to manually access GPS values
                final TiffField gpsLatitudeRefField = jpegMetadata
                        .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF);
                final TiffField gpsLatitudeField = jpegMetadata
                        .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LATITUDE);
                final TiffField gpsLongitudeRefField = jpegMetadata
                        .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF);
                final TiffField gpsLongitudeField = jpegMetadata
                        .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LONGITUDE);
                if (gpsLatitudeRefField != null && gpsLatitudeField != null && gpsLongitudeRefField != null
                        && gpsLongitudeField != null) {
                    // all of these values are strings.
                    final String gpsLatitudeRef = (String) gpsLatitudeRefField.getValue();
                    final RationalNumber[] gpsLatitude = (RationalNumber[]) (gpsLatitudeField.getValue());
                    final String gpsLongitudeRef = (String) gpsLongitudeRefField.getValue();
                    final RationalNumber[] gpsLongitude = (RationalNumber[]) gpsLongitudeField.getValue();
                    final RationalNumber gpsLatitudeDegrees = gpsLatitude[0];
                    final RationalNumber gpsLatitudeMinutes = gpsLatitude[1];
                    final RationalNumber gpsLatitudeSeconds = gpsLatitude[2];
                    final RationalNumber gpsLongitudeDegrees = gpsLongitude[0];
                    final RationalNumber gpsLongitudeMinutes = gpsLongitude[1];
                    final RationalNumber gpsLongitudeSeconds = gpsLongitude[2];
                    // This will format the gps info like so:
                    //
                    // gpsLatitude: 8 degrees, 40 minutes, 42.2 seconds S
                    // gpsLongitude: 115 degrees, 26 minutes, 21.8 seconds E
                    logger.info("    " + "GPS Latitude: " + gpsLatitudeDegrees.toDisplayString() + " degrees, "
                            + gpsLatitudeMinutes.toDisplayString() + " minutes, "
                            + gpsLatitudeSeconds.toDisplayString() + " seconds " + gpsLatitudeRef);
                    logger.info("    " + "GPS Longitude: " + gpsLongitudeDegrees.toDisplayString() + " degrees, "
                            + gpsLongitudeMinutes.toDisplayString() + " minutes, "
                            + gpsLongitudeSeconds.toDisplayString() + " seconds " + gpsLongitudeRef);
                }
                logger.info("");
                final List<ImageMetadata.ImageMetadataItem> items = jpegMetadata.getItems();
                for (int i = 0; i < items.size(); i++) {
                    final ImageMetadata.ImageMetadataItem item = items.get(i);
                    // logger.info("    " + "item: " + item);
                }
                logger.info("");
            }
        }
        return res;
    }

    public Map<String, String> getMetaData(final File jpegImageFile) throws ImageProcessingException, IOException {
        Metadata metadata = ImageMetadataReader.readMetadata(jpegImageFile);
        Map<String, String> res = new HashMap<>();
        res.put("date", "Pinding");
        for (Directory directory : metadata.getDirectories()) {
            for (Tag tag : directory.getTags()) {
                logger.debug("{} - {} = {}", directory.getName(), tag.getTagName(), tag.getDescription());
            }
            if (directory.hasErrors()) {
                for (String error : directory.getErrors()) {
                    logger.debug("ERROR: {}", error);
                }
            }
        }
        // obtain the Exif directory
        ExifSubIFDDirectory directory = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
        // query the tag's value
        if (directory == null) {
            return res;
        }
        PanasonicMakernoteDirectory pmd = metadata.getFirstDirectoryOfType(PanasonicMakernoteDirectory.class);
        if (pmd != null) {
            String country = "";
            String city = "";
            String landmark = "";
            for (Tag tag : pmd.getTags()) {

                if (tag.getTagType() == PanasonicMakernoteDirectory.TAG_COUNTRY) {
                    country = tag.getDescription();
                    if (!country.equals("---")) {
                        res.put("country", country);
                    }
                } else if (tag.getTagType() == PanasonicMakernoteDirectory.TAG_CITY) {

                    city = tag.getDescription();
                    if (!city.equals("---")) {
                        res.put("locality", city);
                    }
                } else if (tag.getTagType() == PanasonicMakernoteDirectory.TAG_LANDMARK) {

                    landmark = tag.getDescription();
                    if (!landmark.equals("---")) {
                        res.put("premise", landmark);
                    }
                }
            }
            logger.info("Panasonic fafa " + country + " x " + city + " x " + landmark);
        }

        Date dateOfCapture = directory.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL);
        final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH.mm.ss", Locale.ENGLISH);
        String s = null;
        if (dateOfCapture != null) {
            s = df.format(dateOfCapture);
        } else {
            return res;
        }
        logger.info("Date " + dateOfCapture.toString());
        logger.info("Date f " + s);
        res.put("date", s);

        //        if (res.get("country") != null) {
        //            // We have some data from Panasonic Makernotes, so no need to lookup GPS
        //            return res;
        //        }
        // See whether it has GPS data
        Collection<GpsDirectory> gpsDirectories = metadata.getDirectoriesOfType(GpsDirectory.class);
        if (gpsDirectories == null) {
            return res;
        }
        for (GpsDirectory gpsDirectory : gpsDirectories) {
            // Try to read out the location, making sure it's non-zero
            GeoLocation geoLocation = gpsDirectory.getGeoLocation();
            if (geoLocation != null && !geoLocation.isZero()) {
                // Add to our collection for use below
                logger.info("Geo " + geoLocation.toString());
                double lat = geoLocation.getLatitude();
                double lon = geoLocation.getLongitude();
                Locale fmtLocale = Locale.US;
                NumberFormat formatter = NumberFormat.getNumberInstance(fmtLocale);
                formatter.setGroupingUsed(false);
                Map<String, String> location = new HashMap();
                ;
                try {
                    location = getAddressByGpsCoordinates(formatter.format(lat), formatter.format(lon));
                    logger.info("Location " + location);
                    res.putAll(location);
                } catch (MalformedURLException ex) {
                    logger.error("", ex);
                } catch (org.json.simple.parser.ParseException ex) {
                    logger.error("", ex);
                }
                break;
            }
        }
        return res;
    }

}