Newer
Older
adjustgpx-gui / src / main / java / osm / jp / gpx / ImportPicture.java
@haya4 haya4 on 8 Mar 2020 29 KB JDK1.8
  1. package osm.jp.gpx;
  2.  
  3. import java.io.*;
  4. import java.nio.channels.FileChannel;
  5. import java.text.DateFormat;
  6. import java.text.DecimalFormat;
  7. import java.text.ParseException;
  8. import java.text.SimpleDateFormat;
  9. import java.util.ArrayList;
  10. import java.util.Arrays;
  11. import java.util.Calendar;
  12. import java.util.Comparator;
  13. import java.util.Date;
  14. import java.util.Map;
  15. import java.util.ResourceBundle;
  16. import java.util.TimeZone;
  17. import java.util.logging.LogManager;
  18. import java.util.logging.Logger;
  19.  
  20. import javax.xml.parsers.*;
  21. import javax.xml.transform.TransformerException;
  22.  
  23. import org.apache.commons.imaging.ImageReadException;
  24. import org.apache.commons.imaging.ImageWriteException;
  25. import org.apache.commons.imaging.Imaging;
  26. import org.apache.commons.imaging.common.ImageMetadata;
  27. import org.apache.commons.imaging.common.RationalNumber;
  28. import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
  29. import org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter;
  30. import org.apache.commons.imaging.formats.tiff.TiffImageMetadata;
  31. import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants;
  32. import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants;
  33. import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory;
  34. import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet;
  35. import org.w3c.dom.*;
  36. import org.xml.sax.SAXException;
  37.  
  38. public class ImportPicture extends Thread {
  39. /**
  40. * 実行中に発生したExceptionを保持する場所
  41. */
  42. public Exception ex = null;
  43. /**
  44. * ログ設定プロパティファイルのファイル内容
  45. */
  46. protected static final String LOGGING_PROPERTIES_DATA
  47. = "handlers=java.util.logging.ConsoleHandler\n"
  48. + ".level=FINEST\n"
  49. + "java.util.logging.ConsoleHandler.level=INFO\n"
  50. + "java.util.logging.ConsoleHandler.formatter=osm.jp.gpx.YuuLogFormatter";
  51.  
  52. /**
  53. * static initializer によるログ設定の初期化
  54. */
  55. public static final Logger LOGGER = Logger.getLogger("CommandLogging");
  56. static {
  57. InputStream inStream = null;
  58. try {
  59. inStream = new ByteArrayInputStream(LOGGING_PROPERTIES_DATA.getBytes("UTF-8"));
  60. try {
  61. LogManager.getLogManager().readConfiguration(inStream);
  62. // "ログ設定: LogManagerを設定しました。"
  63. LOGGER.config("LoggerSettings: LogManager setuped.");
  64. }
  65. catch (IOException e) {
  66. // LogManager設定の際に例外が発生しました.
  67. String str = "LoggerSettings: Exception occered:" + e.toString();
  68. LOGGER.warning(str);
  69. }
  70. }
  71. catch (UnsupportedEncodingException e) {
  72. String str = "LoggerSettings: Not supported 'UTF-8' encoding: " + e.toString();
  73. LOGGER.severe(str);
  74. }
  75. finally {
  76. try {
  77. if (inStream != null) {
  78. inStream.close();
  79. }
  80. } catch (IOException e) {
  81. String str = "LoggerSettings: Exception occored: "+ e.toString();
  82. LOGGER.warning(str);
  83. }
  84. }
  85. }
  86. /** メイン
  87. * 画像ファイルをGPXファイルに取り込みます。
  88. *
  89. * ・画像ファイルの更新日付をその画像の撮影日時とします。(Exi情報は無視します)
  90. * ※ 対象とするファイルは'*.jpg'のみ
  91. * ・精確な時刻との時差を入力することで、撮影日時を補正します。
  92. * ・画像ファイルの更新日付リストをCSV形式のファイルとして出力する。
  93. * ・・結果は、取り込み元のGPXファイルとは別に、元ファイル名にアンダーバー「_」を付加した.ファイルに出力します。
  94. *
  95. * exp) $ java -cp .:AdjustTime.jar:commons-imaging-1.0-SNAPSHOT.jar [AdjustTime.ini]
  96. * exp) > java -cp .;AdjustTime.jar;commons-imaging-1.0-SNAPSHOT.jar [AdjustTime.ini]
  97. *
  98. * @param argv
  99. * argv[0] = INIファイルのパス名
  100. *
  101. * argv[-] = dummy
  102. * argv[0] = 画像ファイルが格納されているディレクトリ --> imgDir
  103. * argv[1] = 時刻補正の基準とする画像ファイル --> baseFile
  104. * argv[2] = 基準画像ファイルの精確な撮影日時 "yyyy-MM-dd'T'HH:mm:ss" --> timeStr
  105. * argv[3] = 出力先フォルダ --> outDir
  106. * argv[4] = 撮影位置をロギングしたGPXファイル --> gpxDir
  107. *
  108. * @throws IOException
  109. * @throws ImageReadException
  110. */
  111. public static void main(String[] argv) throws Exception
  112. {
  113. ImportPicture obj = new ImportPicture();
  114. obj.setUp(((argv.length < 1) ? AppParameters.FILE_PATH : argv[0]));
  115. }
  116. public File gpxDir;
  117. public File imgDir;
  118. public File outDir;
  119. public long delta = 0;
  120. public boolean exif = false;
  121. public boolean exifBase = false;
  122. public ArrayList<File> gpxFiles = new ArrayList<>();
  123. public AppParameters params;
  124. public boolean param_GpxSplit = false;
  125. public static boolean param_GpxNoFirstNode = false;
  126. public boolean param_GpxReuse = false;
  127. public boolean param_GpxOutputWpt = true;
  128. public boolean param_ImgOutputAll = false;
  129. public String param_GpxSourceFolder = ".";
  130. public static final String TIME_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ss'Z'";
  131. private static final String EXIF_DATE_TIME_FORMAT_STRING = "yyyy:MM:dd HH:mm:ss";
  132. public ResourceBundle i18n = ResourceBundle.getBundle("i18n");
  133. public void setUp(String paramFilePath) throws Exception {
  134. System.out.println("Param File = '"+ paramFilePath +"'");
  135. this.params = new AppParameters(paramFilePath);
  136.  
  137. Date imgtime;
  138.  
  139. System.out.println(" - param: "+ AppParameters.IMG_TIME +"="+ this.params.getProperty(AppParameters.IMG_TIME) );
  140. System.out.println(" - param: "+ AppParameters.IMG_BASE_FILE +"="+ this.params.getProperty(AppParameters.IMG_BASE_FILE) );
  141. System.out.println(" - param: "+ AppParameters.GPX_BASETIME +"="+ this.params.getProperty(AppParameters.GPX_BASETIME) );
  142. System.out.println(" - param: "+ AppParameters.IMG_SOURCE_FOLDER +"="+ this.params.getProperty(AppParameters.IMG_SOURCE_FOLDER) );
  143. System.out.println(" - param: "+ AppParameters.IMG_OUTPUT_FOLDER +"="+ this.params.getProperty(AppParameters.IMG_OUTPUT_FOLDER) );
  144. System.out.println(" - param: "+ AppParameters.IMG_OUTPUT +"="+ this.params.getProperty(AppParameters.IMG_OUTPUT));
  145. System.out.println(" - param: "+ AppParameters.IMG_OUTPUT_ALL +"="+ this.param_ImgOutputAll);
  146. System.out.println(" - param: "+ AppParameters.IMG_OUTPUT_EXIF +"= "+ String.valueOf(this.exif));
  147. System.out.println(" - param: "+ AppParameters.GPX_SOURCE_FOLDER +"="+ this.param_GpxSourceFolder);
  148. System.out.println(" - param: "+ AppParameters.GPX_OUTPUT_WPT +"="+ this.param_GpxOutputWpt);
  149. System.out.println(" - param: "+ AppParameters.GPX_OVERWRITE_MAGVAR +"="+ Complementation.param_GpxOverwriteMagvar);
  150. System.out.println(" - param: "+ AppParameters.GPX_OUTPUT_SPEED +"="+ Complementation.param_GpxOutputSpeed);
  151. System.out.println(" - param: "+ AppParameters.GPX_GPXSPLIT +"="+ this.param_GpxSplit);
  152. System.out.println(" - param: "+ AppParameters.GPX_NO_FIRST_NODE +"="+ ImportPicture.param_GpxNoFirstNode);
  153. System.out.println(" - param: "+ AppParameters.GPX_REUSE +"="+ this.param_GpxReuse);
  154.  
  155. this.ex = null;
  156. // argv[0] --> AppParameters.IMG_SOURCE_FOLDER に置き換え
  157. this.imgDir = new File(this.params.getProperty(AppParameters.IMG_SOURCE_FOLDER));
  158.  
  159. // 基準時刻(ファイル更新日時 | EXIF撮影日時)
  160. this.exifBase = (this.params.getProperty(AppParameters.GPX_BASETIME).equals("EXIF_TIME"));
  161.  
  162. // 基準時刻ファイルの「更新日時」を使って時刻合わせを行う。
  163. // argv[1] --> AppParameters.IMG_BASE_FILE に置き換え
  164. imgtime = this.adjustTime(new File(this.imgDir, this.params.getProperty(AppParameters.IMG_BASE_FILE)));
  165.  
  166. // 出力ファイル
  167. // argv[3] --> AppParameters.IMG_OUTPUT に置き換え
  168. this.outDir = new File(this.params.getProperty(AppParameters.IMG_OUTPUT_FOLDER));
  169.  
  170. // その他のパラメータを読み取る
  171. String paramStr = this.params.getProperty(AppParameters.GPX_GPXSPLIT);
  172. if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
  173. this.param_GpxSplit = true;
  174. }
  175. paramStr = this.params.getProperty(AppParameters.GPX_NO_FIRST_NODE);
  176. if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
  177. ImportPicture.param_GpxNoFirstNode = true;
  178. }
  179. paramStr = this.params.getProperty(AppParameters.GPX_REUSE);
  180. if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
  181. this.param_GpxReuse = true;
  182. }
  183. paramStr = this.params.getProperty(AppParameters.IMG_OUTPUT_ALL);
  184. if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
  185. this.param_ImgOutputAll = true;
  186. }
  187.  
  188. paramStr = this.params.getProperty(AppParameters.GPX_OUTPUT_WPT);
  189. if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
  190. this.param_GpxOutputWpt = true;
  191. }
  192. paramStr = this.params.getProperty(AppParameters.GPX_OVERWRITE_MAGVAR);
  193. if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
  194. Complementation.param_GpxOverwriteMagvar = true;
  195. }
  196.  
  197. paramStr = this.params.getProperty(AppParameters.GPX_OUTPUT_SPEED);
  198. if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
  199. Complementation.param_GpxOutputSpeed = true;
  200. }
  201.  
  202. paramStr = this.params.getProperty(AppParameters.GPX_SOURCE_FOLDER);
  203. if (paramStr != null) {
  204. this.param_GpxSourceFolder = paramStr;
  205. this.gpxDir = new File(this.param_GpxSourceFolder);
  206. if (!this.gpxDir.exists()) {
  207. // GPXファイルまたはディレクトリが存在しません。('%s')
  208. System.out.println(
  209. String.format(i18n.getString("msg.100"), paramStr)
  210. );
  211. return;
  212. }
  213. }
  214. else {
  215. this.gpxDir = this.imgDir;
  216. }
  217.  
  218. // 指定されたディレクトリ内のGPXファイルすべてを対象とする
  219. if (this.gpxDir.isDirectory()) {
  220. File[] files = this.gpxDir.listFiles();
  221. if (files == null) {
  222. // 対象となるGPXファイルがありませんでした。('%s')
  223. System.out.println(
  224. String.format(i18n.getString("msg.110"), this.gpxDir.getAbsolutePath())
  225. );
  226. return;
  227. }
  228. if (this.param_ImgOutputAll && (files.length > 1)) {
  229. // "複数のGPXファイルがあるときには、'IMG.OUTPUT_ALL'オプションは指定できません。"
  230. System.out.println(
  231. i18n.getString("msg.120")
  232. );
  233. return;
  234. }
  235. java.util.Arrays.sort(
  236. files, new java.util.Comparator<File>() {
  237. @Override
  238. public int compare(File file1, File file2){
  239. return file1.getName().compareTo(file2.getName());
  240. }
  241. }
  242. );
  243. for (File file : files) {
  244. if (file.isFile()) {
  245. String filename = file.getName().toUpperCase();
  246. if (filename.toUpperCase().endsWith(".GPX")) {
  247. if (!filename.toUpperCase().endsWith("_.GPX") || this.param_GpxReuse) {
  248. this.gpxFiles.add(file);
  249. }
  250. }
  251. }
  252. }
  253. }
  254. else {
  255. this.gpxFiles.add(this.gpxDir);
  256. }
  257.  
  258. paramStr = this.params.getProperty(AppParameters.IMG_OUTPUT_EXIF);
  259. if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
  260. this.exif = true;
  261. }
  262. String timeStr = this.params.getProperty(AppParameters.IMG_TIME);
  263. try {
  264. Date t = toUTCDate(timeStr);
  265. this.delta = t.getTime() - imgtime.getTime();
  266. }
  267. catch (ParseException e) {
  268. // "'%s'の書式が違います(%s)"
  269. System.out.println(
  270. String.format(
  271. i18n.getString("msg.130"),
  272. timeStr,
  273. TIME_FORMAT_STRING
  274. )
  275. );
  276. return;
  277. }
  278.  
  279. this.start();
  280. try {
  281. this.join();
  282. } catch(InterruptedException end) {}
  283. if (this.ex != null) {
  284. throw this.ex;
  285. }
  286. }
  287. /**
  288. * @code{
  289. <wpt lat="35.25714922" lon="139.15490497">
  290. <ele>62.099998474121094</ele>
  291. <time>2012-06-11T00:44:38Z</time>
  292. <hdop>0.75</hdop>
  293. <name><![CDATA[写真]]></name>
  294. <cmt><![CDATA[精度: 3.0m]]></cmt>
  295. <link href="2012-06-11_09-44-38.jpg">
  296. <text>2012-06-11_09-44-38.jpg</text>
  297. </link>
  298. <sat>9</sat>
  299. </wpt>
  300. * }
  301. */
  302. @Override
  303. public void run() {
  304. try {
  305. if (params.getProperty(AppParameters.IMG_OUTPUT).equals(Boolean.toString(true))) {
  306. outDir = new File(outDir, imgDir.getName());
  307. }
  308. else {
  309. outDir = gpxDir;
  310. }
  311. for (File gpxFile : this.gpxFiles) {
  312. procGPXfile(new GpxFile(gpxFile));
  313. }
  314. }
  315. catch(ParserConfigurationException | DOMException | SAXException | IOException | ParseException | ImageReadException | ImageWriteException | IllegalArgumentException | TransformerException e) {
  316. e.printStackTrace();
  317. this.ex = new Exception(e);
  318. }
  319. }
  320. /**
  321. * 個別のGPXファイルを処理する
  322. *
  323. * @throws ParserConfigurationException
  324. * @throws IOException
  325. * @throws SAXException
  326. * @throws ParseException
  327. * @throws ImageWriteException
  328. * @throws ImageReadException
  329. * @throws TransformerException
  330. */
  331. void procGPXfile(GpxFile gpxFile) throws ParserConfigurationException, SAXException, IOException, ParseException, ImageReadException, ImageWriteException, TransformerException {
  332. System.gc();
  333.  
  334. System.out.println("time difference: "+ (delta / 1000) +"(sec)");
  335. System.out.println(" Target GPX: ["+ gpxFile.getAbsolutePath() +"]");
  336. System.out.println(" EXIF: "+ (exif ? ("convert to '" + outDir.getAbsolutePath() +"'") : "off"));
  337. System.out.println();
  338.  
  339. // imgDir内の画像ファイルを処理する
  340. System.out.println("|--------------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");
  341. System.out.println("| name | Camera Time | GPStime | Latitude | Longitude | ele |magvar| km/h |");
  342. System.out.println("|--------------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");
  343. boolean out = proc(imgDir, delta, gpxFile.mapTRKSEG, exif, gpxFile);
  344. System.out.println("|--------------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");
  345. if (out) {
  346. // GPX出力
  347. gpxFile.output(outDir);
  348. }
  349. }
  350. /**
  351. * 再帰メソッド
  352. * @throws ParseException
  353. * @throws IOException
  354. * @throws ImageReadException
  355. * @throws ImageWriteException
  356. */
  357. boolean proc(File dir, long delta, ElementMapTRKSEG mapTRKSEG, boolean exifWrite, GpxFile gpxFile) throws ParseException, ImageReadException, IOException, ImageWriteException {
  358. boolean ret = false;
  359. File[] files = dir.listFiles(new JpegFileFilter());
  360. Arrays.sort(files, new FileSort());
  361. for (File image : files) {
  362. System.out.print(String.format("|%-32s|", image.getName()));
  363. if (image.isDirectory()) {
  364. ret = proc(image, delta, mapTRKSEG, exifWrite, gpxFile);
  365. continue;
  366. }
  367. String imageName = image.getName();
  368. if (!checkFile(imageName)) {
  369. System.out.println(String.format("%20s ", "it is not image file."));
  370. continue;
  371. }
  372. Discripter result = procImageFile(image, delta, mapTRKSEG, exifWrite, gpxFile);
  373. ret |= result.ret;
  374. switch (result.control) {
  375. case Discripter.CONTINUE:
  376. continue;
  377. case Discripter.BREAK:
  378. break;
  379. }
  380. }
  381. return ret;
  382. }
  383. class Discripter {
  384. static final int NEXT = 0;
  385. static final int CONTINUE = -1;
  386. static final int BREAK = 1;
  387. public boolean ret;
  388. public int control;
  389. public Discripter(boolean ret) {
  390. this.ret = ret;
  391. this.control = Discripter.NEXT;
  392. }
  393. }
  394. Discripter procImageFile(File imageFile, long delta, ElementMapTRKSEG mapTRKSEG, boolean exifWrite, GpxFile gpxFile) throws ParseException, ImageReadException, IOException, ImageWriteException {
  395. Discripter result = new Discripter(false);
  396. // itime <-- 画像ファイルの撮影時刻
  397. // ファイルの更新日時/EXIFの撮影日時
  398. Date itime = new Date(imageFile.lastModified());
  399. if (this.exifBase) {
  400. ImageMetadata meta = Imaging.getMetadata(imageFile);
  401. JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;
  402. if (jpegMetadata == null) {
  403. // "'%s'にEXIF情報がありません"
  404. System.out.println(
  405. String.format(
  406. i18n.getString("msg.140"),
  407. imageFile.getAbsolutePath()
  408. )
  409. );
  410. result.control = Discripter.CONTINUE;
  411. return result;
  412. }
  413. TiffImageMetadata exif = jpegMetadata.getExif();
  414. if (exif == null) {
  415. // "'%s'にEXIF情報がありません"
  416. System.out.println(
  417. String.format(
  418. i18n.getString("msg.140"),
  419. imageFile.getAbsolutePath()
  420. )
  421. );
  422. result.control = Discripter.CONTINUE;
  423. return result;
  424. }
  425. String dateTimeOriginal = exif.getFieldValue(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL)[0];
  426. itime = ImportPicture.toEXIFDate(dateTimeOriginal);
  427. }
  428. System.out.print(String.format("%20s|", toUTCString(itime)));
  429.  
  430. // uktime <-- 画像撮影時刻に対応するGPX時刻(補正日時)
  431. Date correctedtime = new Date(itime.getTime() + delta);
  432. System.out.print(String.format("%20s|", toUTCString(correctedtime)));
  433.  
  434. // 時刻uktimeにおける<magver>をtrkptに追加する
  435. String eleStr = "-";
  436. String magvarStr = "-";
  437. String speedStr = "-";
  438. TagTrkpt trkptT = null;
  439.  
  440. for (Map.Entry<Date,ElementMapTRKPT> map : mapTRKSEG.entrySet()) {
  441. ElementMapTRKPT mapTRKPT = map.getValue();
  442. trkptT = mapTRKPT.getValue(correctedtime);
  443. if (trkptT != null) {
  444. break;
  445. }
  446. }
  447.  
  448. if (trkptT == null) {
  449. System.out.print(String.format("%-14s|%-14s|", "", ""));
  450. System.out.println(String.format("%8s|%6s|%6s|", "", "", ""));
  451. if (!this.param_ImgOutputAll) {
  452. result.control = Discripter.CONTINUE;
  453. return result;
  454. }
  455. }
  456. else {
  457. double latitude = trkptT.lat;
  458. double longitude = trkptT.lon;
  459. if (trkptT.eleStr != null) {
  460. eleStr = trkptT.eleStr;
  461. }
  462. if (trkptT.magvarStr != null) {
  463. magvarStr = trkptT.magvarStr;
  464. }
  465. if (trkptT.speedStr != null) {
  466. speedStr = trkptT.speedStr;
  467. }
  468. System.out.print(String.format("%14.10f|%14.10f|", latitude, longitude));
  469. System.out.println(String.format("%8s|%6s|%6s|", eleStr, magvarStr, speedStr));
  470. }
  471.  
  472. result.ret = true;
  473. outDir.mkdir();
  474.  
  475. if (exifWrite) {
  476. exifWrite(imageFile, correctedtime, trkptT);
  477.  
  478. if (Boolean.parseBoolean(params.getProperty(AppParameters.GPX_OUTPUT_WPT))) {
  479. if (trkptT != null) {
  480. Element temp = gpxFile.createWptTag(imageFile, imgDir, itime.getTime(), trkptT.trkpt);
  481. gpxFile.gpx.appendChild(temp);
  482. }
  483. }
  484. }
  485. else {
  486. if (this.param_ImgOutputAll) {
  487. // EXIFの変換を伴わない単純なファイルコピー
  488. FileInputStream sStream = new FileInputStream(imageFile);
  489. FileInputStream dStream = new FileInputStream(new File(outDir, imageFile.getName()));
  490. FileChannel srcChannel = sStream.getChannel();
  491. FileChannel destChannel = dStream.getChannel();
  492. try {
  493. srcChannel.transferTo(0, srcChannel.size(), destChannel);
  494. }
  495. finally {
  496. srcChannel.close();
  497. destChannel.close();
  498. sStream.close();
  499. dStream.close();
  500. }
  501. }
  502. }
  503. result.control = Discripter.NEXT;
  504. return result;
  505. }
  506. void exifWrite(File imageFile, Date correctedtime, TagTrkpt trkptT) throws ImageReadException, IOException, ImageWriteException {
  507. DecimalFormat yearFormatter = new DecimalFormat("0000");
  508. DecimalFormat monthFormatter = new DecimalFormat("00");
  509. DecimalFormat dayFormatter = new DecimalFormat("00");
  510. TiffOutputSet outputSet = null;
  511.  
  512. ImageMetadata meta = Imaging.getMetadata(imageFile);
  513. JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;
  514. if (jpegMetadata != null) {
  515. TiffImageMetadata exif = jpegMetadata.getExif();
  516. if (exif != null) {
  517. outputSet = exif.getOutputSet();
  518. }
  519. }
  520.  
  521. if (outputSet == null) {
  522. outputSet = new TiffOutputSet();
  523. }
  524.  
  525. //---- EXIF_TAG_DATE_TIME_ORIGINAL / 「撮影日時/オリジナル画像の生成日時」----
  526. TiffOutputDirectory exifDir = outputSet.getOrCreateExifDirectory();
  527. {
  528. Calendar cal = Calendar.getInstance();
  529. cal.setTimeZone(TimeZone.getTimeZone("UTC"));
  530. cal.setTime(correctedtime);
  531. exifDir.removeField(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
  532. exifDir.add(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL, ImportPicture.toEXIFString(cal.getTime()));
  533. }
  534.  
  535. //---- EXIF GPS_TIME_STAMP ----
  536. TiffOutputDirectory gpsDir = outputSet.getOrCreateGPSDirectory();
  537. {
  538. Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
  539. cal.setTimeZone(TimeZone.getTimeZone("GMT+00"));
  540. cal.setTime(correctedtime);
  541. final String yearStr = yearFormatter.format(cal.get(Calendar.YEAR));
  542. final String monthStr = monthFormatter.format(cal.get(Calendar.MONTH) + 1);
  543. final String dayStr = dayFormatter.format(cal.get(Calendar.DAY_OF_MONTH));
  544. final String dateStamp = yearStr +":"+ monthStr +":"+ dayStr;
  545.  
  546. gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_TIME_STAMP);
  547. gpsDir.add(
  548. GpsTagConstants.GPS_TAG_GPS_TIME_STAMP,
  549. RationalNumber.valueOf(cal.get(Calendar.HOUR_OF_DAY)),
  550. RationalNumber.valueOf(cal.get(Calendar.MINUTE)),
  551. RationalNumber.valueOf(cal.get(Calendar.SECOND))
  552. );
  553. gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_DATE_STAMP);
  554. gpsDir.add(GpsTagConstants.GPS_TAG_GPS_DATE_STAMP, dateStamp);
  555. }
  556.  
  557. if (trkptT != null) {
  558. //---- EXIF GPS elevation/ALTITUDE ----
  559. if (trkptT.eleStr != null) {
  560. final double altitude = Double.parseDouble(trkptT.eleStr);
  561. gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_ALTITUDE);
  562. gpsDir.add(GpsTagConstants.GPS_TAG_GPS_ALTITUDE, RationalNumber.valueOf(altitude));
  563. }
  564.  
  565. //---- EXIF GPS magvar/IMG_DIRECTION ----
  566. if (trkptT.magvarStr != null) {
  567. final double magvar = Double.parseDouble(trkptT.magvarStr);
  568. gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);
  569. gpsDir.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION, RationalNumber.valueOf(magvar));
  570. }
  571.  
  572. //---- EXIF GPS_ ----
  573. outputSet.setGPSInDegrees(trkptT.lon, trkptT.lat);
  574. }
  575.  
  576. ExifRewriter rewriter = new ExifRewriter();
  577. try (FileOutputStream fos = new FileOutputStream(new File(outDir, imageFile.getName()))) {
  578. rewriter.updateExifMetadataLossy(imageFile, fos, outputSet);
  579. }
  580. }
  581. // 基準時刻ファイルの「更新日時」を使って時刻合わせを行う。
  582. // argv[1] --> AppParameters.IMG_BASE_FILE に置き換え
  583. // File baseFile = new File(this.imgDir, this.params.getProperty(AppParameters.IMG_BASE_FILE));
  584. private Date adjustTime(File baseFile) throws ImageReadException, IOException, ParseException {
  585. if (exifBase) {
  586. ImageMetadata meta = Imaging.getMetadata(baseFile);
  587. JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;
  588. if (jpegMetadata == null) {
  589. // "'%s'にEXIF情報がありません"
  590. System.out.println(
  591. String.format(
  592. i18n.getString("msg.140"),
  593. baseFile.getAbsolutePath()
  594. )
  595. );
  596. return null;
  597. }
  598. TiffImageMetadata exif = jpegMetadata.getExif();
  599. if (exif == null) {
  600. // "'%s'にEXIF情報がありません"
  601. System.out.println(
  602. String.format(
  603. i18n.getString("msg.140"),
  604. baseFile.getAbsolutePath()
  605. )
  606. );
  607. return null;
  608. }
  609. String dateTimeOriginal = exif.getFieldValue(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL)[0];
  610. return new Date(ImportPicture.toEXIFDate(dateTimeOriginal).getTime());
  611. }
  612. else {
  613. return new Date(baseFile.lastModified());
  614. }
  615. }
  616.  
  617. /**
  618. * 対象は '*.JPG' のみ対象とする
  619. * @return
  620. * @param name
  621. */
  622. public static boolean checkFile(String name) {
  623. return ((name != null) && name.toUpperCase().endsWith(".JPG"));
  624. }
  625.  
  626. /**
  627. * DateをEXIFの文字列に変換する。
  628. * 注意:EXiFの撮影時刻はUTC時間ではない
  629. * @param localdate
  630. * @return
  631. */
  632. public static String toEXIFString(Date localdate) {
  633. DateFormat dfUTC = new SimpleDateFormat(EXIF_DATE_TIME_FORMAT_STRING);
  634. return dfUTC.format(localdate);
  635. }
  636. /**
  637. * EXIFの文字列をDateに変換する。
  638. * 注意:EXiFの撮影時刻はUTC時間ではない
  639. * @param timeStr
  640. * @return
  641. * @throws ParseException
  642. */
  643. public static Date toEXIFDate(String timeStr) throws ParseException {
  644. DateFormat dfUTC = new SimpleDateFormat(EXIF_DATE_TIME_FORMAT_STRING);
  645. //dfUTC.setTimeZone(TimeZone.getTimeZone("UTC"));
  646. return dfUTC.parse(timeStr);
  647. }
  648. public static String toUTCString(Date localdate) {
  649. DateFormat dfUTC = new SimpleDateFormat(TIME_FORMAT_STRING);
  650. dfUTC.setTimeZone(TimeZone.getTimeZone("UTC"));
  651. return dfUTC.format(localdate);
  652. }
  653. public static Date toUTCDate(String timeStr) throws ParseException {
  654. DateFormat dfUTC = new SimpleDateFormat(TIME_FORMAT_STRING);
  655. dfUTC.setTimeZone(TimeZone.getTimeZone("UTC"));
  656. return dfUTC.parse(timeStr);
  657. }
  658. static String getShortPathName(File dir, File iFile) {
  659. String dirPath = dir.getAbsolutePath();
  660. String filePath = iFile.getAbsolutePath();
  661. if (filePath.startsWith(dirPath)) {
  662. return filePath.substring(dirPath.length()+1);
  663. }
  664. else {
  665. return filePath;
  666. }
  667. }
  668. /**
  669. * ファイル名の順序に並び替えるためのソートクラス
  670. *
  671. * @author hayashi
  672. */
  673. static class FileSort implements Comparator<File> {
  674. @Override
  675. public int compare(File src, File target){
  676. int diff = src.getName().compareTo(target.getName());
  677. return diff;
  678. }
  679. }
  680.  
  681. /**
  682. * JPEGファイルフィルター
  683. * @author yuu
  684. */
  685. class JpegFileFilter implements FilenameFilter {
  686. @Override
  687. public boolean accept(File dir, String name) {
  688. return name.toUpperCase().matches(".*\\.JPG$");
  689. }
  690. }
  691. }