OSDN Git Service

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