package osm.jp.gpx;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.TimeZone;
import javax.xml.parsers.ParserConfigurationException;
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.jpeg.exif.ExifRewriter;
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.write.TiffOutputDirectory;
import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet;
import org.xml.sax.SAXException;
@SuppressWarnings("serial")
public class ImgFile extends File {
boolean done = false;
Date imgtime = null;
Date gpstime = null;
double latitude = 0.0D;
double longitude = 0.0D;
String eleStr = "-";
String magvarStr = "-";
String speedStr = "-";
public ImgFile(File file) {
super(file.getParentFile(), file.getName());
}
public void setDone(boolean done) {
this.done = done;
}
public boolean isDone() {
return this.done;
}
/**
* 対象は '*.JPG' のみ対象とする
* @return
* @param name
*/
public boolean isImageFile() {
String name = this.getName();
return ((name != null) && name.toUpperCase().endsWith(".JPG"));
}
public boolean procImageFile(AppParameters params, long delta, GpxFile gpxFile, File outDir) throws ParseException, ImageReadException, IOException, ImageWriteException, ParserConfigurationException, SAXException {
//ElementMapTRKSEG mapTRKSEG = gpxFile.parse();
boolean exifWrite = params.isImgOutputExif();
// itime <-- 画像ファイルの撮影時刻
// ファイルの更新日時/EXIFの撮影日時
imgtime = getDate(params);
// uktime <-- 画像撮影時刻に対応するGPX時刻(補正日時)
gpstime = new Date(imgtime.getTime() + delta);
// 時刻uktimeにおける<magver>をtrkptに追加する
TagTrkpt trkptT = null;
for (Map.Entry<Date,ElementMapTRKPT> map : gpxFile.getTrksegMap().entrySet()) {
ElementMapTRKPT mapTRKPT = map.getValue();
trkptT = mapTRKPT.getValue(gpstime);
if (trkptT != null) {
break;
}
}
if (trkptT == null) {
if (!params.isImgOutputAll()) {
return false;
}
}
else {
latitude = trkptT.lat;
longitude = trkptT.lon;
if (trkptT.eleStr != null) {
eleStr = trkptT.eleStr;
}
if (trkptT.magvarStr != null) {
magvarStr = trkptT.magvarStr;
}
if (trkptT.speedStr != null) {
speedStr = trkptT.speedStr;
}
}
outDir.mkdir();
if (exifWrite) {
exifWrite(this, gpstime, trkptT, outDir);
}
else {
if (params.isImgOutputAll()) {
// EXIFの変換を伴わない単純なファイルコピー
FileInputStream sStream = new FileInputStream(this);
FileInputStream dStream = new FileInputStream(new File(outDir, this.getName()));
FileChannel srcChannel = sStream.getChannel();
FileChannel destChannel = dStream.getChannel();
try {
srcChannel.transferTo(0, srcChannel.size(), destChannel);
}
finally {
srcChannel.close();
destChannel.close();
sStream.close();
dStream.close();
}
}
}
return true;
}
void exifWrite(File imageFile, Date correctedtime, TagTrkpt trkptT, File outDir) throws ImageReadException, IOException, ImageWriteException {
DecimalFormat yearFormatter = new DecimalFormat("0000");
DecimalFormat monthFormatter = new DecimalFormat("00");
DecimalFormat dayFormatter = new DecimalFormat("00");
TiffOutputSet outputSet = null;
ImageMetadata meta = Imaging.getMetadata(imageFile);
JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;
if (jpegMetadata != null) {
TiffImageMetadata exif = jpegMetadata.getExif();
if (exif != null) {
outputSet = exif.getOutputSet();
}
}
if (outputSet == null) {
outputSet = new TiffOutputSet();
}
//---- EXIF_TAG_DATE_TIME_ORIGINAL / 「撮影日時/オリジナル画像の生成日時」----
TiffOutputDirectory exifDir = outputSet.getOrCreateExifDirectory();
{
Calendar cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
cal.setTime(correctedtime);
exifDir.removeField(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
exifDir.add(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL, ImportPicture.toEXIFString(cal.getTime()));
}
//---- EXIF GPS_TIME_STAMP ----
TiffOutputDirectory gpsDir = outputSet.getOrCreateGPSDirectory();
{
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeZone(TimeZone.getTimeZone("GMT+00"));
cal.setTime(correctedtime);
final String yearStr = yearFormatter.format(cal.get(Calendar.YEAR));
final String monthStr = monthFormatter.format(cal.get(Calendar.MONTH) + 1);
final String dayStr = dayFormatter.format(cal.get(Calendar.DAY_OF_MONTH));
final String dateStamp = yearStr +":"+ monthStr +":"+ dayStr;
gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_TIME_STAMP);
gpsDir.add(
GpsTagConstants.GPS_TAG_GPS_TIME_STAMP,
RationalNumber.valueOf(cal.get(Calendar.HOUR_OF_DAY)),
RationalNumber.valueOf(cal.get(Calendar.MINUTE)),
RationalNumber.valueOf(cal.get(Calendar.SECOND))
);
gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_DATE_STAMP);
gpsDir.add(GpsTagConstants.GPS_TAG_GPS_DATE_STAMP, dateStamp);
}
if (trkptT != null) {
//---- EXIF GPS elevation/ALTITUDE ----
if (trkptT.eleStr != null) {
final double altitude = Double.parseDouble(trkptT.eleStr);
gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_ALTITUDE);
gpsDir.add(GpsTagConstants.GPS_TAG_GPS_ALTITUDE, RationalNumber.valueOf(altitude));
}
//---- EXIF GPS magvar/IMG_DIRECTION ----
if (trkptT.magvarStr != null) {
final double magvar = Double.parseDouble(trkptT.magvarStr);
gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);
gpsDir.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION, RationalNumber.valueOf(magvar));
}
//---- EXIF GPS_ ----
outputSet.setGPSInDegrees(trkptT.lon, trkptT.lat);
}
ExifRewriter rewriter = new ExifRewriter();
try (FileOutputStream fos = new FileOutputStream(new File(outDir, imageFile.getName()))) {
rewriter.updateExifMetadataLossy(imageFile, fos, outputSet);
}
}
/**
* 基準時刻ファイルの「更新日時」を取得する
* @param baseFile = new File(this.imgDir, this.params.getProperty(AppParameters.IMG_BASE_FILE));
* @return
* @throws ImageReadException
* @throws IOException
* @throws ParseException
*/
Date getDate(AppParameters params) throws ImageReadException, IOException, ParseException {
return getDate(params, this);
}
/**
* 基準時刻ファイルの「更新日時」を取得する
* @param baseFile = new File(this.imgDir, this.params.getProperty(AppParameters.IMG_BASE_FILE));
* @return
* @throws ImageReadException
* @throws IOException
* @throws ParseException
*/
static Date getDate(AppParameters params, File baseFile) throws ImageReadException, IOException, ParseException {
if (params.isExifBase()) {
// 基準時刻(EXIF撮影日時)
ImageMetadata meta = Imaging.getMetadata(baseFile);
JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;
if (jpegMetadata == null) {
// "'%s'にEXIF情報がありません"
throw new ImageReadException(
String.format(
ImportPicture.i18n.getString("msg.140"),
baseFile.getAbsolutePath()
)
);
}
TiffImageMetadata exif = jpegMetadata.getExif();
if (exif == null) {
// "'%s'にEXIF情報がありません"
throw new ImageReadException(
String.format(
ImportPicture.i18n.getString("msg.140"),
baseFile.getAbsolutePath()
)
);
}
String dateTimeOriginal = exif.getFieldValue(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL)[0];
return new Date(ImportPicture.toEXIFDate(dateTimeOriginal).getTime());
}
else {
// 基準時刻(ファイル更新日時)
return new Date(baseFile.lastModified());
}
}
/**
* ImgFileインスタンスの状態をTEXT化
* @return 1行
*/
String toText() {
String ret = "";
if (isDone()) {
ret += (String.format("|%-32s|", this.getName()));
ret += (String.format("%20s|", (imgtime==null ? "" : ImportPicture.toUTCString(imgtime))));
ret += (String.format("%20s|", (gpstime==null ? "" : ImportPicture.toUTCString(gpstime))));
ret += (String.format("%14.10f|%14.10f|", latitude, longitude));
ret += (String.format("%8s|%6s|%6s|", eleStr, magvarStr, speedStr));
}
else {
ret += (String.format("|%-32s|", this.getName()));
ret += (String.format("%20s|", (imgtime==null ? "" : ImportPicture.toUTCString(imgtime))));
ret += (String.format("%20s|", (gpstime==null ? "" : ImportPicture.toUTCString(gpstime))));
ret += (String.format("%-14s|%-14s|", "", ""));
ret += (String.format("%8s|%6s|%6s|", "", "", ""));
}
return ret;
}
public void printinfo() {
System.out.println(toText());
}
public static void printheader() {
System.out.println("|--------------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");
System.out.println("| name | Camera Time | GPStime | Latitude | Longitude | ele |magvar| km/h |");
System.out.println("|--------------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");
}
public static void printfooter() {
System.out.println("|--------------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");
System.out.println();
}
}