OSDN Git Service

7d41febf0b9759bec24337099f161daf8ab7cd3f
[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.OutputKeys;\r
21 import javax.xml.transform.Transformer;\r
22 import javax.xml.transform.TransformerException;\r
23 import javax.xml.transform.TransformerFactory;\r
24 import javax.xml.transform.dom.DOMSource;\r
25 import javax.xml.transform.stream.StreamResult;\r
26 \r
27 import org.apache.commons.imaging.ImageReadException;\r
28 import org.apache.commons.imaging.ImageWriteException;\r
29 import org.apache.commons.imaging.Imaging;\r
30 import org.apache.commons.imaging.common.ImageMetadata;\r
31 import org.apache.commons.imaging.common.RationalNumber;\r
32 import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;\r
33 import org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter;\r
34 import org.apache.commons.imaging.formats.tiff.TiffImageMetadata;\r
35 import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants;\r
36 import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants;\r
37 import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory;\r
38 import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet;\r
39 import org.w3c.dom.*;\r
40 import org.xml.sax.SAXException;\r
41 \r
42 public class ImportPicture extends Thread {\r
43         /**\r
44          * 実行中に発生したExceptionを保持する場所\r
45          */\r
46         public Exception ex = null;\r
47         \r
48     /**\r
49      * ログ設定プロパティファイルのファイル内容\r
50      */\r
51     protected static final String LOGGING_PROPERTIES_DATA\r
52            = "handlers=java.util.logging.ConsoleHandler\n"\r
53            + ".level=FINEST\n"\r
54            + "java.util.logging.ConsoleHandler.level=INFO\n"\r
55            + "java.util.logging.ConsoleHandler.formatter=osm.jp.gpx.YuuLogFormatter";\r
56 \r
57     /**\r
58      * static initializer によるログ設定の初期化\r
59      */\r
60     public static final Logger logger = Logger.getLogger("CommandLogging");\r
61     static {\r
62         InputStream inStream = null;\r
63         try {\r
64             inStream = new ByteArrayInputStream(LOGGING_PROPERTIES_DATA.getBytes("UTF-8"));\r
65             try {\r
66                 LogManager.getLogManager().readConfiguration(inStream);\r
67                 logger.config("ログ設定: LogManagerを設定しました。");\r
68             }\r
69             catch (IOException e) {\r
70                 logger.warning("ログ設定: LogManager設定の際に例外が発生しました。:" + e.toString());\r
71             }\r
72         }\r
73         catch (UnsupportedEncodingException e) {\r
74             logger.severe("ログ設定: UTF-8エンコーディングがサポートされていません。:" + e.toString());\r
75         }\r
76         finally {\r
77             try {\r
78                 if (inStream != null) {\r
79                     inStream.close();\r
80                 }\r
81             } catch (IOException e) {\r
82                 logger.warning("ログ設定: ログ設定プロパティファイルのストリームクローズ時に例外が発生しました。:"+ e.toString());\r
83             }\r
84         }\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         Date imgtime;\r
117 \r
118         String paramFilePath = ((argv.length < 1) ? AppParameters.FILE_PATH : argv[0]);\r
119         System.out.println("Param File = '"+ paramFilePath +"'");\r
120         ImportPicture obj = new ImportPicture();\r
121         obj.params = new AppParameters(paramFilePath);\r
122 \r
123         System.out.println(" - param: "+ AppParameters.IMG_TIME +"="+ obj.params.getProperty(AppParameters.IMG_TIME) );\r
124         System.out.println(" - param: "+ AppParameters.IMG_BASE_FILE +"="+ obj.params.getProperty(AppParameters.IMG_BASE_FILE) );\r
125         System.out.println(" - param: "+ AppParameters.GPX_BASETIME +"="+ obj.params.getProperty(AppParameters.GPX_BASETIME) );\r
126         System.out.println(" - param: "+ AppParameters.IMG_SOURCE_FOLDER +"="+ obj.params.getProperty(AppParameters.IMG_SOURCE_FOLDER) );\r
127         System.out.println(" - param: "+ AppParameters.IMG_OUTPUT_FOLDER +"="+ obj.params.getProperty(AppParameters.IMG_OUTPUT_FOLDER) );\r
128         System.out.println(" - param: "+ AppParameters.IMG_OUTPUT +"="+ obj.params.getProperty(AppParameters.IMG_OUTPUT));     \r
129         System.out.println(" - param: "+ AppParameters.IMG_OUTPUT_ALL +"="+ obj.param_ImgOutputAll);\r
130         System.out.println(" - param: "+ AppParameters.IMG_OUTPUT_EXIF +"= "+ String.valueOf(obj.exif));\r
131         System.out.println(" - param: "+ AppParameters.GPX_SOURCE_FOLDER +"="+ obj.param_GpxSourceFolder);\r
132         System.out.println(" - param: "+ AppParameters.GPX_OUTPUT_WPT +"="+ obj.param_GpxOutputWpt);\r
133         System.out.println(" - param: "+ AppParameters.GPX_OVERWRITE_MAGVAR +"="+ Complementation.param_GpxOverwriteMagvar);\r
134         System.out.println(" - param: "+ AppParameters.GPX_OUTPUT_SPEED +"="+ Complementation.param_GpxOutputSpeed);\r
135         System.out.println(" - param: "+ AppParameters.GPX_GPXSPLIT +"="+ obj.param_GpxSplit);\r
136         System.out.println(" - param: "+ AppParameters.GPX_NO_FIRST_NODE +"="+ ImportPicture.param_GpxNoFirstNode);        \r
137         System.out.println(" - param: "+ AppParameters.GPX_REUSE +"="+ obj.param_GpxReuse);\r
138 \r
139         obj.ex = null;\r
140         // argv[0] --> AppParameters.IMG_SOURCE_FOLDER に置き換え\r
141         obj.imgDir = new File(obj.params.getProperty(AppParameters.IMG_SOURCE_FOLDER));\r
142 \r
143         // 基準時刻(ファイル更新日時 | EXIF撮影日時)\r
144         obj.exifBase = (obj.params.getProperty(AppParameters.GPX_BASETIME).equals("EXIF_TIME") ? true : false);\r
145 \r
146         // 基準時刻ファイルの「更新日時」を使って時刻合わせを行う。\r
147         // argv[1] --> AppParameters.IMG_BASE_FILE に置き換え\r
148         File baseFile = new File(obj.imgDir, obj.params.getProperty(AppParameters.IMG_BASE_FILE));\r
149         if (obj.exifBase) {\r
150             ImageMetadata meta = Imaging.getMetadata(baseFile);\r
151             JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;\r
152             if (jpegMetadata == null) {\r
153                 System.out.println("'"+ baseFile.getAbsolutePath() +"' にEXIF情報がありません");\r
154                 return;\r
155             }\r
156             TiffImageMetadata exif = jpegMetadata.getExif();\r
157             if (exif == null) {\r
158                 System.out.println("'"+ baseFile.getAbsolutePath() +"' にEXIF情報がありません");\r
159                 return;\r
160             }\r
161                 String dateTimeOriginal = exif.getFieldValue(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL)[0];\r
162                 long lastModifyTime = ImportPicture.toEXIFDate(dateTimeOriginal).getTime();\r
163                 imgtime = new Date(lastModifyTime);\r
164         }\r
165         else {\r
166             imgtime = new Date(baseFile.lastModified());\r
167         }\r
168 \r
169         // 出力ファイル\r
170         // argv[3] --> AppParameters.IMG_OUTPUT に置き換え\r
171         obj.outDir = new File(obj.params.getProperty(AppParameters.IMG_OUTPUT_FOLDER));\r
172 \r
173         // その他のパラメータを読み取る\r
174         String paramStr = obj.params.getProperty(AppParameters.GPX_GPXSPLIT);\r
175         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
176                 obj.param_GpxSplit = true;\r
177         }\r
178         \r
179         paramStr = obj.params.getProperty(AppParameters.GPX_NO_FIRST_NODE);\r
180         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
181                 ImportPicture.param_GpxNoFirstNode = true;\r
182         }\r
183         \r
184         paramStr = obj.params.getProperty(AppParameters.GPX_REUSE);\r
185         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
186                 obj.param_GpxReuse = true;\r
187         }\r
188         \r
189         paramStr = obj.params.getProperty(AppParameters.IMG_OUTPUT_ALL);\r
190         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
191                 obj.param_ImgOutputAll = true;\r
192         }\r
193 \r
194         paramStr = obj.params.getProperty(AppParameters.GPX_OUTPUT_WPT);\r
195         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
196                 obj.param_GpxOutputWpt = true;\r
197         }\r
198         \r
199         paramStr = obj.params.getProperty(AppParameters.GPX_OVERWRITE_MAGVAR);\r
200         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
201                 Complementation.param_GpxOverwriteMagvar = true;\r
202         }\r
203 \r
204         paramStr = obj.params.getProperty(AppParameters.GPX_OUTPUT_SPEED);\r
205         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
206                 Complementation.param_GpxOutputSpeed = true;\r
207         }\r
208 \r
209         paramStr = obj.params.getProperty(AppParameters.GPX_SOURCE_FOLDER);\r
210         if (paramStr != null) {\r
211                 obj.param_GpxSourceFolder = new String(paramStr);\r
212             obj.gpxDir = new File(obj.param_GpxSourceFolder);\r
213             if (!obj.gpxDir.exists()) {\r
214                 // GPXファイルまたはディレクトリが存在しません。\r
215                 System.out.println("GPXファイルまたはディレクトリが存在しません。('"+ paramStr +"')");\r
216                 return;\r
217             }\r
218         }\r
219         else {\r
220             obj.gpxDir = obj.imgDir;\r
221         }\r
222 \r
223         // 指定されたディレクトリ内のGPXファイルすべてを対象とする\r
224         if (obj.gpxDir.isDirectory()) {\r
225             File[] files = obj.gpxDir.listFiles();\r
226             if (files == null) {\r
227                 // 対象となるGPXファイルがありませんでした。\r
228                 System.out.println("対象となるGPXファイルがありませんでした。('"+ obj.gpxDir.getAbsolutePath() +"')");\r
229                 return;\r
230             }\r
231             if (obj.param_ImgOutputAll && (files.length > 1)) {\r
232                 System.out.println("複数のGPXファイルがあるときには、'IMG.OUTPUT_ALL'オプションは指定できません。");\r
233                 return;\r
234             }\r
235             \r
236             java.util.Arrays.sort(files, new java.util.Comparator<File>() {\r
237                         @Override\r
238                                 public int compare(File file1, File file2){\r
239                             return file1.getName().compareTo(file2.getName());\r
240                         }\r
241             });\r
242             for (File file : files) {\r
243                 if (file.isFile()) {\r
244                     String filename = file.getName().toUpperCase();\r
245                     if (filename.toUpperCase().endsWith(".GPX")) {\r
246                         if (!filename.toUpperCase().endsWith("_.GPX") || obj.param_GpxReuse) {\r
247                             obj.gpxFiles.add(file);\r
248                         }\r
249                     }\r
250                 }\r
251             }\r
252         }\r
253         else {\r
254             obj.gpxFiles.add(obj.gpxDir);\r
255         }\r
256 \r
257         paramStr = obj.params.getProperty(AppParameters.IMG_OUTPUT_EXIF);\r
258         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
259                 obj.exif = true;\r
260         }\r
261         \r
262         String timeStr = obj.params.getProperty(AppParameters.IMG_TIME);\r
263         try {\r
264             Date t = toUTCDate(timeStr);\r
265             obj.delta = t.getTime() - imgtime.getTime();\r
266         }\r
267         catch (ParseException e) {\r
268             System.out.println("'"+ timeStr +"' の書式が違います("+ TIME_FORMAT_STRING +")");\r
269             return;\r
270         }\r
271 \r
272         obj.start();\r
273         try {\r
274             obj.join();\r
275         } catch(InterruptedException end) {}\r
276         if (obj.ex != null) {\r
277                 throw obj.ex;\r
278         }\r
279     }\r
280     \r
281     public File gpxDir;\r
282     public File imgDir;\r
283     public File outDir;\r
284     public long delta = 0;\r
285     public boolean exif = false;\r
286     public boolean exifBase = false;\r
287     public ArrayList<File> gpxFiles = new ArrayList<>();\r
288     public AppParameters params;\r
289         public boolean param_GpxSplit = false;\r
290         public static boolean param_GpxNoFirstNode = false;\r
291         public boolean param_GpxReuse = false;\r
292         public boolean param_GpxOutputWpt = true;\r
293         public boolean param_ImgOutputAll = false;\r
294         public String param_GpxSourceFolder = ".";\r
295     Document document;\r
296         \r
297     public static final String TIME_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ss'Z'";\r
298     private static final String EXIF_DATE_TIME_FORMAT_STRING = "yyyy:MM:dd HH:mm:ss";\r
299 \r
300     @Override\r
301     public void run() {\r
302         /**\r
303          *\r
304                 <wpt lat="35.25714922" lon="139.15490497">\r
305                         <ele>62.099998474121094</ele>\r
306                         <time>2012-06-11T00:44:38Z</time>\r
307                         <hdop>0.75</hdop>\r
308                         <name><![CDATA[写真]]></name>\r
309                         <cmt><![CDATA[精度: 3.0m]]></cmt>\r
310                         <link href="2012-06-11_09-44-38.jpg">\r
311                                 <text>2012-06-11_09-44-38.jpg</text>\r
312                         </link>\r
313                         <sat>9</sat>\r
314                 </wpt>\r
315          */\r
316         try {\r
317                 if (params.getProperty(AppParameters.IMG_OUTPUT).equals(Boolean.toString(true))) {\r
318                 outDir = new File(outDir, imgDir.getName());\r
319                 }\r
320                 else {\r
321                 outDir = gpxDir;\r
322                 }\r
323 \r
324             for (File gpxFile : this.gpxFiles) {\r
325                 procGPXfile(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(File gpxFile) throws ParserConfigurationException, SAXException, IOException, ParseException, ImageReadException, ImageWriteException, TransformerException {\r
346         DocumentBuilderFactory factory = null;\r
347         DocumentBuilder        builder = null;\r
348         ElementMapTRKSEG mapTRKSEG = null;\r
349         Node gpx = null;\r
350         \r
351         System.gc();\r
352 \r
353         String fileName = gpxFile.getName();\r
354         String iStr = fileName.substring(0, fileName.length() - 4);\r
355 \r
356         File outputFile = new File(outDir, iStr +"_.gpx");\r
357         System.out.println(gpxFile.getAbsolutePath() + " => "+ outputFile.getAbsolutePath());\r
358         System.out.println("           時差: "+ (delta / 1000) +"(sec)");\r
359         System.out.println("    Target GPX: ["+ gpxFile.getAbsolutePath() +"]");\r
360         System.out.println("          EXIF: "+ (exif ? ("convert to '" + outDir.getAbsolutePath() +"'") : "off"));\r
361         System.out.println();\r
362 \r
363         factory = DocumentBuilderFactory.newInstance();\r
364         builder = factory.newDocumentBuilder();\r
365         factory.setIgnoringElementContentWhitespace(true);\r
366         factory.setIgnoringComments(true);\r
367         factory.setValidating(true);\r
368 \r
369         // GPXファイルをパースする\r
370         mapTRKSEG = new ElementMapTRKSEG();\r
371         document = mapTRKSEG.parse(gpxFile);\r
372         \r
373         // パースされた mapTRKSEG の中身を出力する\r
374         mapTRKSEG.printinfo();\r
375         \r
376         // GPX file --> Node root\r
377         gpx = builder.parse(gpxFile).getFirstChild();\r
378 \r
379         // imgDir内の画像ファイルを処理する\r
380                 System.out.println("|--------------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");\r
381                 System.out.println("| name                           | Camera Time        | GPStime            |   Latitude   |   Longitude  | ele    |magvar| km/h |");\r
382                 System.out.println("|--------------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");\r
383                 proc(imgDir, delta, mapTRKSEG, exif, gpx);\r
384                 System.out.println("|--------------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");\r
385 \r
386         // 出力\r
387         outputFile.getParentFile().mkdirs();\r
388         DOMSource source = new DOMSource(gpx);\r
389         FileOutputStream os = new FileOutputStream(outputFile);\r
390         StreamResult result = new StreamResult(os);\r
391         TransformerFactory transFactory = TransformerFactory.newInstance();\r
392         Transformer transformer = transFactory.newTransformer();\r
393         transformer.setOutputProperty(OutputKeys.INDENT, "yes");\r
394         transformer.setOutputProperty(OutputKeys.METHOD, "xml");\r
395         transformer.transform(source, result);         \r
396 \r
397         os = new FileOutputStream(outputFile);\r
398         result = new StreamResult(os);\r
399         transformer.transform(source, result);\r
400     }\r
401     \r
402         \r
403     /**\r
404      * 再帰メソッド\r
405      * @throws ParseException \r
406      * @throws IOException \r
407      * @throws ImageReadException \r
408      * @throws ImageWriteException \r
409      */\r
410     boolean proc(File dir, long delta, ElementMapTRKSEG mapTRKSEG, boolean exifWrite, Node gpx) throws ParseException, ImageReadException, IOException, ImageWriteException {\r
411         boolean ret = false;\r
412         File[] files = dir.listFiles(new JpegFileFilter());\r
413         Arrays.sort(files, new FileSort());\r
414         for (File image : files) {\r
415             System.out.print(String.format("|%-32s|", image.getName()));\r
416             if (image.isDirectory()) {\r
417                 ret = proc(image, delta, mapTRKSEG, exifWrite, gpx);\r
418                 continue;\r
419             }\r
420             \r
421             String imageName = image.getName();\r
422             if (!checkFile(imageName)) {\r
423                 System.out.println(String.format("%20s ", "it is not image file."));\r
424                 continue;\r
425             }\r
426             \r
427             Discripter result = procImageFile(image, delta, mapTRKSEG, exifWrite, gpx);\r
428             ret |= result.ret;\r
429             switch (result.control) {\r
430             case Discripter.CONTINUE:\r
431                 continue;\r
432             case Discripter.BREAK:\r
433                 break;\r
434             }\r
435         }\r
436         return ret;\r
437     }\r
438     \r
439     class Discripter {\r
440         static final int NEXT = 0;\r
441         static final int CONTINUE = -1;\r
442         static final int BREAK = 1;\r
443         \r
444         public boolean ret;\r
445         public int control;\r
446         public Discripter(boolean ret) {\r
447                 this.ret = ret;\r
448                 this.control = Discripter.NEXT;\r
449         }\r
450     }\r
451     \r
452     Discripter procImageFile(File imageFile, long delta, ElementMapTRKSEG mapTRKSEG, boolean exifWrite, Node gpx) throws ParseException, ImageReadException, IOException, ImageWriteException {\r
453         Discripter result = new Discripter(false);\r
454         \r
455         // itime <-- 画像ファイルの撮影時刻\r
456         //                      ファイルの更新日時/EXIFの撮影日時\r
457         Date itime = new Date(imageFile.lastModified());\r
458         if (this.exifBase) {\r
459             ImageMetadata meta = Imaging.getMetadata(imageFile);\r
460             JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;\r
461             if (jpegMetadata == null) {\r
462                 System.out.println("'"+ imageFile.getAbsolutePath() +"' にEXIF情報がありません");\r
463                 result.control = Discripter.CONTINUE;\r
464                 return result;\r
465             }\r
466             TiffImageMetadata exif = jpegMetadata.getExif();\r
467             if (exif == null) {\r
468                 System.out.println("'"+ imageFile.getAbsolutePath() +"' にEXIF情報がありません");\r
469                 result.control = Discripter.CONTINUE;\r
470                 return result;\r
471             }\r
472                 String dateTimeOriginal = exif.getFieldValue(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL)[0];\r
473                 itime = ImportPicture.toEXIFDate(dateTimeOriginal);\r
474         }\r
475         System.out.print(String.format("%20s|", toUTCString(itime)));\r
476 \r
477         // uktime <-- 画像撮影時刻に対応するGPX時刻(補正日時)\r
478         Date correctedtime = new Date(itime.getTime() + delta);\r
479         System.out.print(String.format("%20s|", toUTCString(correctedtime)));\r
480 \r
481                 // 時刻uktimeにおける<magver>をtrkptに追加する\r
482         String eleStr = "-";\r
483         String magvarStr = "-";\r
484         String speedStr = "-";\r
485         double latitude = 90.5D;\r
486         double longitude = 180.5D;\r
487         TagTrkpt trkptT = null;\r
488 \r
489         for (Map.Entry<Date,ElementMapTRKPT> map : mapTRKSEG.entrySet()) {\r
490                 ElementMapTRKPT mapTRKPT = map.getValue();\r
491                 trkptT = mapTRKPT.getValue(correctedtime);\r
492             if (trkptT != null) {\r
493                 break;\r
494             }\r
495         }\r
496 \r
497         if (trkptT == null) {\r
498             System.out.print(String.format("%-14s|%-14s|", "", ""));\r
499             System.out.println(String.format("%8s|%6s|%6s|", "", "", ""));\r
500             if (!this.param_ImgOutputAll) {\r
501                 result.control = Discripter.CONTINUE;\r
502                 return result;\r
503             }\r
504         }\r
505         else {\r
506             latitude = trkptT.lat;\r
507             longitude = trkptT.lon;\r
508             \r
509             if (trkptT.eleStr != null) {\r
510                 eleStr = new String(trkptT.eleStr);\r
511             }\r
512             \r
513             if (trkptT.magvarStr != null) {\r
514                 magvarStr = new String(trkptT.magvarStr);\r
515             }\r
516             \r
517             if (trkptT.speedStr != null) {\r
518                 speedStr = new String(trkptT.speedStr);\r
519             }\r
520             //System.out.print(String.format("%-14s|%-14s|", (new Double(latitude)).toString(), (new Double(longitude)).toString()));\r
521             System.out.print(String.format("%14.10f|%14.10f|", latitude, longitude));\r
522             System.out.println(String.format("%8s|%6s|%6s|", eleStr, magvarStr, speedStr));\r
523         }\r
524 \r
525         result.ret = true;\r
526         outDir.mkdir();\r
527 \r
528         if (exifWrite) {\r
529                 exifWrite(imageFile, correctedtime, trkptT);\r
530 \r
531             if (Boolean.parseBoolean(params.getProperty(AppParameters.GPX_OUTPUT_WPT))) {\r
532                 if (trkptT != null) {\r
533                         Element temp = createWptTag(imageFile, itime.getTime(), trkptT.trkpt);\r
534                     gpx.appendChild(temp);\r
535                 }\r
536             }\r
537         }\r
538         else {\r
539                 if (this.param_ImgOutputAll) {\r
540                 // EXIFの変換を伴わない単純なファイルコピー\r
541                         FileInputStream sStream = new FileInputStream(imageFile);\r
542                         FileInputStream dStream = new FileInputStream(new File(outDir, imageFile.getName()));\r
543                 FileChannel srcChannel = sStream.getChannel();\r
544                 FileChannel destChannel = dStream.getChannel();\r
545                 try {\r
546                     srcChannel.transferTo(0, srcChannel.size(), destChannel);\r
547                 }\r
548                 finally {\r
549                     srcChannel.close();\r
550                     destChannel.close();\r
551                     sStream.close();\r
552                     dStream.close();\r
553                 }\r
554                 }\r
555         }\r
556         result.control = Discripter.NEXT;\r
557         return result;\r
558     }\r
559     \r
560     void exifWrite(File imageFile, Date correctedtime, TagTrkpt trkptT) throws ImageReadException, IOException, ImageWriteException {\r
561         DecimalFormat yearFormatter = new DecimalFormat("0000");\r
562         DecimalFormat monthFormatter = new DecimalFormat("00");\r
563         DecimalFormat dayFormatter = new DecimalFormat("00");\r
564         \r
565         TiffOutputSet outputSet = null;\r
566 \r
567         ImageMetadata meta = Imaging.getMetadata(imageFile);\r
568         JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;\r
569         if (jpegMetadata != null) {\r
570             TiffImageMetadata exif = jpegMetadata.getExif();\r
571             if (exif != null) {\r
572                 outputSet = exif.getOutputSet();\r
573             }\r
574         }\r
575 \r
576         if (outputSet == null) {\r
577             System.out.println("added : new tiff output set");\r
578             outputSet = new TiffOutputSet();\r
579         }\r
580 \r
581         //---- EXIF_TAG_DATE_TIME_ORIGINAL / 「撮影日時/オリジナル画像の生成日時」----\r
582         TiffOutputDirectory exifDir = outputSet.getOrCreateExifDirectory();\r
583         {\r
584             Calendar cal = Calendar.getInstance();\r
585             cal.setTimeZone(TimeZone.getTimeZone("UTC"));\r
586             cal.setTime(correctedtime);\r
587             exifDir.removeField(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);\r
588             exifDir.add(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL, ImportPicture.toEXIFString(cal.getTime()));\r
589         }\r
590 \r
591         //---- EXIF GPS_TIME_STAMP ----\r
592         TiffOutputDirectory gpsDir = outputSet.getOrCreateGPSDirectory();\r
593         {\r
594             Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));\r
595             cal.setTimeZone(TimeZone.getTimeZone("GMT+00"));\r
596             cal.setTime(correctedtime);\r
597             final String yearStr = yearFormatter.format(cal.get(Calendar.YEAR));\r
598             final String monthStr = monthFormatter.format(cal.get(Calendar.MONTH) + 1);\r
599             final String dayStr = dayFormatter.format(cal.get(Calendar.DAY_OF_MONTH));\r
600             final String dateStamp = yearStr +":"+ monthStr +":"+ dayStr;\r
601 \r
602             gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_TIME_STAMP);\r
603             gpsDir.add(GpsTagConstants.GPS_TAG_GPS_TIME_STAMP,\r
604                     RationalNumber.valueOf(cal.get(Calendar.HOUR_OF_DAY)),\r
605                     RationalNumber.valueOf(cal.get(Calendar.MINUTE)),\r
606                     RationalNumber.valueOf(cal.get(Calendar.SECOND)));\r
607             gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_DATE_STAMP);\r
608             gpsDir.add(GpsTagConstants.GPS_TAG_GPS_DATE_STAMP, dateStamp);\r
609         }\r
610 \r
611         if (trkptT != null) {\r
612             //---- EXIF GPS elevation/ALTITUDE ----\r
613             if (trkptT.eleStr != null) {\r
614                 final double altitude = Double.parseDouble(trkptT.eleStr);\r
615                 gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_ALTITUDE);\r
616                 gpsDir.add(GpsTagConstants.GPS_TAG_GPS_ALTITUDE, RationalNumber.valueOf(altitude));\r
617             }\r
618 \r
619             //---- EXIF GPS magvar/IMG_DIRECTION ----\r
620             if (trkptT.magvarStr != null) {\r
621                 final double magvar = Double.parseDouble(trkptT.magvarStr);\r
622                 gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);\r
623                 gpsDir.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION, RationalNumber.valueOf(magvar));\r
624             }\r
625 \r
626             //---- EXIF GPS_ ----\r
627             outputSet.setGPSInDegrees(trkptT.lon.doubleValue(), trkptT.lat.doubleValue());\r
628         }\r
629 \r
630         ExifRewriter rewriter = new ExifRewriter();\r
631         try (FileOutputStream fos = new FileOutputStream(new File(outDir, imageFile.getName()))) {\r
632             rewriter.updateExifMetadataLossy(imageFile, fos, outputSet);\r
633         }\r
634 \r
635     }\r
636         \r
637     /**\r
638      * 対象は '*.JPG' のみ対象とする\r
639      * @return \r
640      * @param name\r
641      */\r
642     public static boolean checkFile(String name) {\r
643         return ((name != null) && name.toUpperCase().endsWith(".JPG"));\r
644     }\r
645 \r
646     /**\r
647      *  <wpt lat="35.25714922" lon="139.15490497">\r
648      *          <ele>62.099998474121094</ele>\r
649      *          <time>2012-06-11T00:44:38Z</time>\r
650      *          <name><![CDATA[写真]]></name>\r
651      *          <link href="2012-06-11_09-44-38.jpg">\r
652      *                  <text>2012-06-11_09-44-38.jpg</text>\r
653      *          </link>\r
654      *  </wpt>\r
655      *\r
656      *  <trkpt lat="35.32123832" lon="139.56965631">\r
657      *          <ele>47.20000076293945</ele>\r
658      *          <time>2012-06-15T03:00:29Z</time>\r
659      *  </trkpt>\r
660      *\r
661      * @return\r
662      * @param iFile\r
663      * @param timestamp\r
664      * @param trkpt\r
665      */\r
666     public Element createWptTag(File iFile, long timestamp, Element trkpt) {\r
667         Element wpt = document.createElement("wpt");\r
668 \r
669         NamedNodeMap nodeMap = trkpt.getAttributes();\r
670         if (null != nodeMap) {\r
671             for (int j=0; j < nodeMap.getLength(); j++ ) {\r
672                 switch (nodeMap.item(j).getNodeName()) {\r
673                 case "lat":\r
674                     String lat = nodeMap.item(j).getNodeValue();\r
675                     wpt.setAttribute("lat", lat);\r
676                     break;\r
677                 case "lon":\r
678                     String lon = nodeMap.item(j).getNodeValue();\r
679                     wpt.setAttribute("lon", lon);\r
680                     break;\r
681                 }\r
682             }\r
683         }\r
684 \r
685         NodeList nodes1 = trkpt.getChildNodes();\r
686         for (int i1=0; i1 < nodes1.getLength(); i1++) {\r
687             Node node1 = nodes1.item(i1);\r
688             NodeList nodes2 = node1.getChildNodes();\r
689             switch (node1.getNodeName()) {\r
690             case "ele":\r
691                 for (int i2=0; i2 < nodes2.getLength(); i2++) {\r
692                     Node node2 = nodes2.item(i2);\r
693                     if (node2 != null) {\r
694                         if (node2.getNodeType() == Node.TEXT_NODE) {\r
695                             String eleStr = node2.getNodeValue();\r
696                             Element eleE = document.createElement("ele");\r
697                             eleE.setTextContent(eleStr);\r
698                             wpt.appendChild(eleE);\r
699                         }\r
700                     }\r
701                 }\r
702                 break;\r
703             case "time":\r
704                 for (int i2=0; i2 < nodes2.getLength(); i2++) {\r
705                     Node node2 = nodes2.item(i2);\r
706                     if (node2 != null) {\r
707                         if (node2.getNodeType() == Node.TEXT_NODE) {\r
708                             String timeStr = node2.getNodeValue();\r
709                             Element timeE = document.createElement("time");\r
710                             timeE.setTextContent(timeStr);\r
711                             wpt.appendChild(timeE);\r
712                         }\r
713                     }\r
714                 }\r
715                 break;\r
716             case "magvar":\r
717                 for (int i2=0; i2 < nodes2.getLength(); i2++) {\r
718                     Node node2 = nodes2.item(i2);\r
719                     if (node2 != null) {\r
720                         if (node2.getNodeType() == Node.TEXT_NODE) {\r
721                             String magvarStr = node2.getNodeValue();\r
722                             Element magvarE = document.createElement("magvar");\r
723                             magvarE.setTextContent(magvarStr);\r
724                             wpt.appendChild(magvarE);\r
725                         }\r
726                     }\r
727                 }\r
728                 break;\r
729             case "speed":\r
730                 for (int i2=0; i2 < nodes2.getLength(); i2++) {\r
731                     Node node2 = nodes2.item(i2);\r
732                     if (node2 != null) {\r
733                         if (node2.getNodeType() == Node.TEXT_NODE) {\r
734                             String speedStr = node2.getNodeValue();\r
735                             Element speedE = document.createElement("speed");\r
736                             speedE.setTextContent(speedStr);\r
737                             wpt.appendChild(speedE);\r
738                         }\r
739                     }\r
740                 }\r
741                 break;\r
742             }\r
743         }\r
744 \r
745         Element name = document.createElement("name");\r
746         name.appendChild(document.createCDATASection("写真"));\r
747         wpt.appendChild(name);\r
748 \r
749         Element link = document.createElement("link");\r
750         link.setAttribute("href", ImportPicture.getShortPathName(imgDir, iFile));\r
751         Element text = document.createElement("text");\r
752         text.setTextContent(iFile.getName());\r
753         link.appendChild(text);\r
754         wpt.appendChild(link);\r
755 \r
756         return wpt;\r
757     }\r
758     \r
759     /**\r
760      * DateをEXIFの文字列に変換する。\r
761      * 注意:EXiFの撮影時刻はUTC時間ではない\r
762      * @param localdate\r
763      * @return\r
764      */\r
765     public static String toEXIFString(Date localdate) {\r
766         DateFormat dfUTC = new SimpleDateFormat(EXIF_DATE_TIME_FORMAT_STRING);\r
767         return dfUTC.format(localdate);\r
768     }\r
769     \r
770     /**\r
771      * EXIFの文字列をDateに変換する。\r
772      * 注意:EXiFの撮影時刻はUTC時間ではない\r
773      * @param timeStr\r
774      * @return\r
775      * @throws ParseException\r
776      */\r
777     public static Date toEXIFDate(String timeStr) throws ParseException {\r
778         DateFormat dfUTC = new SimpleDateFormat(EXIF_DATE_TIME_FORMAT_STRING);\r
779         //dfUTC.setTimeZone(TimeZone.getTimeZone("UTC"));\r
780         return dfUTC.parse(timeStr);\r
781     }\r
782         \r
783     public static String toUTCString(Date localdate) {\r
784         DateFormat dfUTC = new SimpleDateFormat(TIME_FORMAT_STRING);\r
785         dfUTC.setTimeZone(TimeZone.getTimeZone("UTC"));\r
786         return dfUTC.format(localdate);\r
787     }\r
788         \r
789     public static Date toUTCDate(String timeStr) throws ParseException {\r
790         DateFormat dfUTC = new SimpleDateFormat(TIME_FORMAT_STRING);\r
791         dfUTC.setTimeZone(TimeZone.getTimeZone("UTC"));\r
792         return dfUTC.parse(timeStr);\r
793     }\r
794         \r
795     static String getShortPathName(File dir, File iFile) {\r
796         String dirPath = dir.getAbsolutePath();\r
797         String filePath = iFile.getAbsolutePath();\r
798         if (filePath.startsWith(dirPath)) {\r
799             return filePath.substring(dirPath.length()+1);\r
800         }\r
801         else {\r
802             return filePath;\r
803         }\r
804     }\r
805     \r
806     /**\r
807      * ファイル名の順序に並び替えるためのソートクラス\r
808      * \r
809      * @author hayashi\r
810      */\r
811     static class FileSort implements Comparator<File> {\r
812         @Override\r
813         public int compare(File src, File target){\r
814             int diff = src.getName().compareTo(target.getName());\r
815             return diff;\r
816         }\r
817     }\r
818 \r
819     /**\r
820      * JPEGファイルフィルター\r
821      * @author yuu\r
822      */\r
823         class JpegFileFilter implements FilenameFilter {\r
824         @Override\r
825                 public boolean accept(File dir, String name) {\r
826                         if (name.toUpperCase().matches(".*\\.JPG$")) {\r
827                                 return true;\r
828                         }\r
829                         return false;\r
830         }\r
831         }\r
832 }