Newer
Older
adjustgpx-gui / src / main / java / osm / jp / gpx / ImgFile.java
@haya4 haya4 on 30 Apr 2020 11 KB fixed: 5.2
  1. package osm.jp.gpx;
  2.  
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import java.nio.channels.FileChannel;
  8. import java.text.DecimalFormat;
  9. import java.text.ParseException;
  10. import java.util.Calendar;
  11. import java.util.Date;
  12. import java.util.Map;
  13. import java.util.TimeZone;
  14.  
  15. import javax.xml.parsers.ParserConfigurationException;
  16.  
  17. import org.apache.commons.imaging.ImageReadException;
  18. import org.apache.commons.imaging.ImageWriteException;
  19. import org.apache.commons.imaging.Imaging;
  20. import org.apache.commons.imaging.common.ImageMetadata;
  21. import org.apache.commons.imaging.common.RationalNumber;
  22. import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
  23. import org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter;
  24. import org.apache.commons.imaging.formats.tiff.TiffImageMetadata;
  25. import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants;
  26. import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants;
  27. import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory;
  28. import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet;
  29. import org.xml.sax.SAXException;
  30.  
  31. @SuppressWarnings("serial")
  32. public class ImgFile extends File {
  33. boolean done = false;
  34. Date imgtime = null;
  35. Date gpstime = null;
  36. double latitude = 0.0D;
  37. double longitude = 0.0D;
  38. String eleStr = "-";
  39. String magvarStr = "-";
  40. String speedStr = "-";
  41.  
  42. public ImgFile(File file) {
  43. super(file.getParentFile(), file.getName());
  44. }
  45. public void setDone(boolean done) {
  46. this.done = done;
  47. }
  48. public boolean isDone() {
  49. return this.done;
  50. }
  51. /**
  52. * 対象は '*.JPG' のみ対象とする
  53. * @return
  54. * @param name
  55. */
  56. public boolean isImageFile() {
  57. String name = this.getName();
  58. return ((name != null) && name.toUpperCase().endsWith(".JPG"));
  59. }
  60. public boolean procImageFile(AppParameters params, long delta, GpxFile gpxFile, File outDir) throws ParseException, ImageReadException, IOException, ImageWriteException, ParserConfigurationException, SAXException {
  61. //ElementMapTRKSEG mapTRKSEG = gpxFile.parse();
  62. boolean exifWrite = params.isImgOutputExif();
  63. // itime <-- 画像ファイルの撮影時刻
  64. // ファイルの更新日時/EXIFの撮影日時
  65. imgtime = getDate(params);
  66. // uktime <-- 画像撮影時刻に対応するGPX時刻(補正日時)
  67. gpstime = new Date(imgtime.getTime() + delta);
  68.  
  69. // 時刻uktimeにおける<magver>をtrkptに追加する
  70. TagTrkpt trkptT = null;
  71.  
  72. for (Map.Entry<Date,ElementMapTRKPT> map : gpxFile.getTrksegMap().entrySet()) {
  73. ElementMapTRKPT mapTRKPT = map.getValue();
  74. trkptT = mapTRKPT.getValue(gpstime);
  75. if (trkptT != null) {
  76. break;
  77. }
  78. }
  79.  
  80. if (trkptT == null) {
  81. if (!params.isImgOutputAll()) {
  82. return false;
  83. }
  84. }
  85. else {
  86. latitude = trkptT.lat;
  87. longitude = trkptT.lon;
  88. if (trkptT.eleStr != null) {
  89. eleStr = trkptT.eleStr;
  90. }
  91. if (trkptT.magvarStr != null) {
  92. magvarStr = trkptT.magvarStr;
  93. }
  94. if (trkptT.speedStr != null) {
  95. speedStr = trkptT.speedStr;
  96. }
  97. }
  98.  
  99. outDir.mkdir();
  100. if (exifWrite) {
  101. exifWrite(this, gpstime, trkptT, outDir);
  102. }
  103. else {
  104. if (params.isImgOutputAll()) {
  105. // EXIFの変換を伴わない単純なファイルコピー
  106. FileInputStream sStream = new FileInputStream(this);
  107. FileInputStream dStream = new FileInputStream(new File(outDir, this.getName()));
  108. FileChannel srcChannel = sStream.getChannel();
  109. FileChannel destChannel = dStream.getChannel();
  110. try {
  111. srcChannel.transferTo(0, srcChannel.size(), destChannel);
  112. }
  113. finally {
  114. srcChannel.close();
  115. destChannel.close();
  116. sStream.close();
  117. dStream.close();
  118. }
  119. }
  120. }
  121. return true;
  122. }
  123. void exifWrite(File imageFile, Date correctedtime, TagTrkpt trkptT, File outDir) throws ImageReadException, IOException, ImageWriteException {
  124. DecimalFormat yearFormatter = new DecimalFormat("0000");
  125. DecimalFormat monthFormatter = new DecimalFormat("00");
  126. DecimalFormat dayFormatter = new DecimalFormat("00");
  127. TiffOutputSet outputSet = null;
  128.  
  129. ImageMetadata meta = Imaging.getMetadata(imageFile);
  130. JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;
  131. if (jpegMetadata != null) {
  132. TiffImageMetadata exif = jpegMetadata.getExif();
  133. if (exif != null) {
  134. outputSet = exif.getOutputSet();
  135. }
  136. }
  137.  
  138. if (outputSet == null) {
  139. outputSet = new TiffOutputSet();
  140. }
  141.  
  142. //---- EXIF_TAG_DATE_TIME_ORIGINAL / 「撮影日時/オリジナル画像の生成日時」----
  143. TiffOutputDirectory exifDir = outputSet.getOrCreateExifDirectory();
  144. {
  145. Calendar cal = Calendar.getInstance();
  146. cal.setTimeZone(TimeZone.getTimeZone("UTC"));
  147. cal.setTime(correctedtime);
  148. exifDir.removeField(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
  149. exifDir.add(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL, ImportPicture.toEXIFString(cal.getTime()));
  150. }
  151.  
  152. //---- EXIF GPS_TIME_STAMP ----
  153. TiffOutputDirectory gpsDir = outputSet.getOrCreateGPSDirectory();
  154. {
  155. Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
  156. cal.setTimeZone(TimeZone.getTimeZone("GMT+00"));
  157. cal.setTime(correctedtime);
  158. final String yearStr = yearFormatter.format(cal.get(Calendar.YEAR));
  159. final String monthStr = monthFormatter.format(cal.get(Calendar.MONTH) + 1);
  160. final String dayStr = dayFormatter.format(cal.get(Calendar.DAY_OF_MONTH));
  161. final String dateStamp = yearStr +":"+ monthStr +":"+ dayStr;
  162.  
  163. gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_TIME_STAMP);
  164. gpsDir.add(
  165. GpsTagConstants.GPS_TAG_GPS_TIME_STAMP,
  166. RationalNumber.valueOf(cal.get(Calendar.HOUR_OF_DAY)),
  167. RationalNumber.valueOf(cal.get(Calendar.MINUTE)),
  168. RationalNumber.valueOf(cal.get(Calendar.SECOND))
  169. );
  170. gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_DATE_STAMP);
  171. gpsDir.add(GpsTagConstants.GPS_TAG_GPS_DATE_STAMP, dateStamp);
  172. }
  173.  
  174. if (trkptT != null) {
  175. //---- EXIF GPS elevation/ALTITUDE ----
  176. if (trkptT.eleStr != null) {
  177. final double altitude = Double.parseDouble(trkptT.eleStr);
  178. gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_ALTITUDE);
  179. gpsDir.add(GpsTagConstants.GPS_TAG_GPS_ALTITUDE, RationalNumber.valueOf(altitude));
  180. }
  181.  
  182. //---- EXIF GPS magvar/IMG_DIRECTION ----
  183. if (trkptT.magvarStr != null) {
  184. final double magvar = Double.parseDouble(trkptT.magvarStr);
  185. gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);
  186. gpsDir.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION, RationalNumber.valueOf(magvar));
  187. }
  188.  
  189. //---- EXIF GPS_ ----
  190. outputSet.setGPSInDegrees(trkptT.lon, trkptT.lat);
  191. }
  192.  
  193. ExifRewriter rewriter = new ExifRewriter();
  194. try (FileOutputStream fos = new FileOutputStream(new File(outDir, imageFile.getName()))) {
  195. rewriter.updateExifMetadataLossy(imageFile, fos, outputSet);
  196. }
  197. }
  198. /**
  199. * 基準時刻ファイルの「更新日時」を取得する
  200. * @param baseFile = new File(this.imgDir, this.params.getProperty(AppParameters.IMG_BASE_FILE));
  201. * @return
  202. * @throws ImageReadException
  203. * @throws IOException
  204. * @throws ParseException
  205. */
  206. Date getDate(AppParameters params) throws ImageReadException, IOException, ParseException {
  207. return getDate(params, this);
  208. }
  209.  
  210. /**
  211. * 基準時刻ファイルの「更新日時」を取得する
  212. * @param baseFile = new File(this.imgDir, this.params.getProperty(AppParameters.IMG_BASE_FILE));
  213. * @return
  214. * @throws ImageReadException
  215. * @throws IOException
  216. * @throws ParseException
  217. */
  218. static Date getDate(AppParameters params, File baseFile) throws ImageReadException, IOException, ParseException {
  219. if (params.isExifBase()) {
  220. // 基準時刻(EXIF撮影日時)
  221. ImageMetadata meta = Imaging.getMetadata(baseFile);
  222. JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;
  223. if (jpegMetadata == null) {
  224. // "'%s'にEXIF情報がありません"
  225. throw new ImageReadException(
  226. String.format(
  227. ImportPicture.i18n.getString("msg.140"),
  228. baseFile.getAbsolutePath()
  229. )
  230. );
  231. }
  232. TiffImageMetadata exif = jpegMetadata.getExif();
  233. if (exif == null) {
  234. // "'%s'にEXIF情報がありません"
  235. throw new ImageReadException(
  236. String.format(
  237. ImportPicture.i18n.getString("msg.140"),
  238. baseFile.getAbsolutePath()
  239. )
  240. );
  241. }
  242. String dateTimeOriginal = exif.getFieldValue(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL)[0];
  243. return new Date(ImportPicture.toEXIFDate(dateTimeOriginal).getTime());
  244. }
  245. else {
  246. // 基準時刻(ファイル更新日時)
  247. return new Date(baseFile.lastModified());
  248. }
  249. }
  250. /**
  251. * ImgFileインスタンスの状態をTEXT化
  252. * @return 1行
  253. */
  254. String toText() {
  255. String ret = "";
  256. if (isDone()) {
  257. ret += (String.format("|%-32s|", this.getName()));
  258. ret += (String.format("%20s|", (imgtime==null ? "" : ImportPicture.toUTCString(imgtime))));
  259. ret += (String.format("%20s|", (gpstime==null ? "" : ImportPicture.toUTCString(gpstime))));
  260. ret += (String.format("%14.10f|%14.10f|", latitude, longitude));
  261. ret += (String.format("%8s|%6s|%6s|", eleStr, magvarStr, speedStr));
  262. }
  263. else {
  264. ret += (String.format("|%-32s|", this.getName()));
  265. ret += (String.format("%20s|", (imgtime==null ? "" : ImportPicture.toUTCString(imgtime))));
  266. ret += (String.format("%20s|", (gpstime==null ? "" : ImportPicture.toUTCString(gpstime))));
  267. ret += (String.format("%-14s|%-14s|", "", ""));
  268. ret += (String.format("%8s|%6s|%6s|", "", "", ""));
  269. }
  270. return ret;
  271. }
  272. public void printinfo() {
  273. System.out.println(toText());
  274. }
  275. public static void printheader() {
  276. System.out.println("|--------------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");
  277. System.out.println("| name | Camera Time | GPStime | Latitude | Longitude | ele |magvar| km/h |");
  278. System.out.println("|--------------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");
  279. }
  280. public static void printfooter() {
  281. System.out.println("|--------------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");
  282. System.out.println();
  283. }
  284. }