OSDN Git Service

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