OSDN Git Service

SpeedとMagverを表示
[importpicture/importpicture.git] / importPicture / src / osm / jp / gpx / ImportPicture.java
1 package osm.jp.gpx;\r
2 import java.io.*;\r
3 import java.text.DecimalFormat;\r
4 import java.text.ParseException;\r
5 import java.text.SimpleDateFormat;\r
6 import java.util.ArrayList;\r
7 import java.util.Arrays;\r
8 import java.util.Calendar;\r
9 import java.util.Comparator;\r
10 import java.util.Date;\r
11 import java.util.GregorianCalendar;\r
12 import java.util.Iterator;\r
13 import java.util.Set;\r
14 import java.util.TimeZone;\r
15 import java.util.TreeMap;\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      * ログ設定プロパティファイルのファイル内容\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      * @param argv\r
94      * argv[-] = dummy\r
95      * argv[0] = 画像ファイルが格納されているディレクトリ           --> imgDir\r
96      * argv[1] = 時刻補正の基準とする画像ファイル                               --> baseFile\r
97      * argv[2] = 基準画像ファイルの精確な撮影日時 "yyyy-MM-dd'T'HH:mm:ss"       --> timeStr\r
98      * argv[3] = 出力先フォルダ                                                          --> outDir\r
99      * argv[4] = 撮影位置をロギングしたGPXファイル                               --> gpxDir\r
100      * \r
101      * @throws IOException\r
102      * @throws ImageReadException \r
103      */\r
104     public static void main(String[] argv) throws Exception\r
105     {\r
106         Date jptime;\r
107 \r
108         ImportPicture obj = new ImportPicture();\r
109 \r
110         if (argv.length > 0) {\r
111             obj.imgDir = new File(argv[0]);\r
112         }\r
113 \r
114         if (argv.length < 4) {\r
115             System.out.println("!!! Illigal command call. !!!");\r
116             System.out.println("> java -cp .:AdjustTime.jar:commons-imaging-1.0-SNAPSHOT.jar <targetDir> <time base image> <time> {EXIF/not} (gpx)");\r
117             System.out.println("> java -cp .:AdjustTime.jar:commons-imaging-1.0-SNAPSHOT.jar. IMG_01234.JPG 2012-06-15T12:52:22 EXIF");\r
118             System.out.println("> java -cp .:AdjustTime.jar . IMG_01234.JPG 2012-06-15T12:52:22 not");\r
119             System.out.println();\r
120             return;\r
121         }\r
122 \r
123         obj.params = new AppParameters();\r
124 \r
125         // 基準時刻(ファイル更新日時 | EXIF撮影日時)\r
126         obj.exifBase = (obj.params.getProperty(AppParameters.GPX_BASETIME).equals("EXIF_TIME") ? true : false);\r
127 \r
128         // 基準時刻ファイルの「更新日時」を使って時刻合わせを行う。\r
129         File baseFile = new File(obj.imgDir, argv[1]);\r
130         if (obj.exifBase) {\r
131             ImageMetadata meta = Imaging.getMetadata(baseFile);\r
132             JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;\r
133             if (jpegMetadata == null) {\r
134                 System.out.println("'"+ baseFile.getAbsolutePath() +"' にEXIF情報がありません");\r
135                 return;\r
136             }\r
137             TiffImageMetadata exif = jpegMetadata.getExif();\r
138             if (exif == null) {\r
139                 System.out.println("'"+ baseFile.getAbsolutePath() +"' にEXIF情報がありません");\r
140                 return;\r
141             }\r
142                 String dateTimeOriginal = exif.getFieldValue(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL)[0];\r
143                 long lastModifyTime = (new SimpleDateFormat("yyyy:MM:dd HH:mm:ss")).parse(dateTimeOriginal).getTime();\r
144                 jptime = new Date(lastModifyTime);\r
145         }\r
146         else {\r
147             jptime = new Date(baseFile.lastModified());\r
148         }\r
149 \r
150         String timeStr = argv[2];\r
151         try {\r
152             dfjp.setTimeZone(TimeZone.getTimeZone("JST"));\r
153             Date t = dfjp.parse(timeStr);\r
154             obj.delta = t.getTime() - jptime.getTime();\r
155         }\r
156         catch (ParseException e) {\r
157             System.out.println("'"+ timeStr +"' の書式が違います("+ TIME_FORMAT_STRING +")");\r
158             return;\r
159         }\r
160 \r
161         // 出力ファイル\r
162         obj.outDir = new File(argv[3]);\r
163 \r
164         // その他のパラメータを読み取る\r
165         String paramStr = obj.params.getProperty(AppParameters.GPX_GPXSPLIT);\r
166         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
167                 obj.param_GpxSplit = true;\r
168         }\r
169         \r
170         paramStr = obj.params.getProperty(AppParameters.GPX_NO_FIRST_NODE);\r
171         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
172                 obj.param_GpxNoFirstNode = true;\r
173         }\r
174         \r
175         paramStr = obj.params.getProperty(AppParameters.GPX_REUSE);\r
176         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
177                 obj.param_GpxReuse = true;\r
178         }\r
179         \r
180         paramStr = obj.params.getProperty(AppParameters.GPX_OUTPUT_WPT);\r
181         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
182                 obj.param_GpxOutputWpt = true;\r
183         }\r
184         \r
185         paramStr = obj.params.getProperty(AppParameters.GPX_OVERWRITE_MAGVAR);\r
186         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
187                 Complementation.param_GpxOverwriteMagvar = true;\r
188         }\r
189 \r
190         paramStr = obj.params.getProperty(AppParameters.GPX_OUTPUT_SPEED);\r
191         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
192                 Complementation.param_GpxOutputSpeed = true;\r
193         }\r
194 \r
195         paramStr = obj.params.getProperty(AppParameters.GPX_SOURCE_FOLDER);\r
196         if (paramStr != null) {\r
197                 obj.param_GpxSourceFolder = new String(paramStr);\r
198             obj.gpxDir = new File(obj.param_GpxSourceFolder);\r
199             if (!obj.gpxDir.exists()) {\r
200                 // GPXファイルまたはディレクトリが存在しません。\r
201                 System.out.println("GPXファイルまたはディレクトリが存在しません。('"+ paramStr +"')");\r
202                 return;\r
203             }\r
204         }\r
205         else {\r
206             obj.gpxDir = obj.imgDir;\r
207         }\r
208 \r
209         // 指定されたディレクトリ内のGPXファイルすべてを対象とする\r
210         if (obj.gpxDir.isDirectory()) {\r
211             File[] files = obj.gpxDir.listFiles();\r
212             if (files == null) {\r
213                 // 対象となるGPXファイルがありませんでした。\r
214                 System.out.println("対象となるGPXファイルがありませんでした。('"+ obj.gpxDir.getAbsolutePath() +"')");\r
215                 return;\r
216             }\r
217             for (File file : files) {\r
218                 if (file.isFile()) {\r
219                     String filename = file.getName().toUpperCase();\r
220                     if (filename.toUpperCase().endsWith(".GPX")) {\r
221                         if (!filename.toUpperCase().endsWith("_.GPX") || obj.param_GpxReuse) {\r
222                             obj.gpxFiles.add(file);\r
223                         }\r
224                     }\r
225                 }\r
226             }\r
227         }\r
228         else {\r
229             obj.gpxFiles.add(obj.gpxDir);\r
230         }\r
231 \r
232         paramStr = obj.params.getProperty(AppParameters.IMG_OUTPUT_EXIF);\r
233         if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {\r
234                 obj.exif = true;\r
235         }\r
236 \r
237         System.out.println(" - param: imgDir = '"+ obj.imgDir.getAbsolutePath() +"'");\r
238         System.out.println(" - param: outDir = '"+ (obj.outDir == null ? "" : obj.outDir.getAbsolutePath()) +"'");\r
239         System.out.println(" - param: gpxDir = '"+ (obj.gpxDir == null ? "" : obj.gpxDir.getAbsolutePath()) +"'");\r
240         System.out.println(" - param: number of gpxFiles = '"+ obj.gpxFiles.size() +"'");\r
241         System.out.println(" - param: "+ AppParameters.GPX_GPXSPLIT +"="+ obj.param_GpxSplit);\r
242         System.out.println(" - param: "+ AppParameters.GPX_NO_FIRST_NODE +"="+ obj.param_GpxNoFirstNode);        \r
243         System.out.println(" - param: "+ AppParameters.GPX_REUSE +"="+ obj.param_GpxReuse);        \r
244         System.out.println(" - param: "+ AppParameters.GPX_BASETIME +"="+ obj.params.getProperty(AppParameters.GPX_BASETIME) );\r
245         System.out.println(" - param: "+ AppParameters.IMG_OUTPUT +"="+ obj.params.getProperty(AppParameters.IMG_OUTPUT));     \r
246         System.out.println(" - param: "+ AppParameters.IMG_OUTPUT_EXIF +"= "+ String.valueOf(obj.exif));\r
247         System.out.println(" - param: "+ AppParameters.GPX_SOURCE_FOLDER +"="+ obj.param_GpxSourceFolder);\r
248         System.out.println(" - param: "+ AppParameters.GPX_OUTPUT_WPT +"="+ obj.param_GpxOutputWpt);\r
249         System.out.println(" - param: "+ AppParameters.GPX_OVERWRITE_MAGVAR +"="+ Complementation.param_GpxOverwriteMagvar);\r
250         System.out.println(" - param: "+ AppParameters.GPX_OUTPUT_SPEED +"="+ Complementation.param_GpxOutputSpeed);\r
251         obj.start();\r
252         try {\r
253             obj.join();                            \r
254         } catch(InterruptedException end) {}\r
255     }\r
256     \r
257     public File gpxDir;\r
258     public File imgDir;\r
259     public File outDir;\r
260     public long delta = 0;\r
261     public boolean exif = false;\r
262     public boolean exifBase = false;\r
263     public ArrayList<File> gpxFiles = new ArrayList<>();\r
264     public AppParameters params;\r
265         public boolean param_GpxSplit = false;\r
266         public boolean param_GpxNoFirstNode = false;\r
267         public boolean param_GpxReuse = false;\r
268         //public boolean Complementation.param_GpxOutputSpeed = false;\r
269         //public boolean Complementation.param_GpxOverwriteMagvar = false;\r
270         public boolean param_GpxOutputWpt = true;\r
271         public String param_GpxSourceFolder = ".";\r
272         public static final long DIFF_MAE_TIME = 3000L; // before 3 secound\r
273         \r
274     @Override\r
275     public void run() {\r
276         /**\r
277          *\r
278                 <wpt lat="35.25714922" lon="139.15490497">\r
279                         <ele>62.099998474121094</ele>\r
280                         <time>2012-06-11T00:44:38Z</time>\r
281                         <hdop>0.75</hdop>\r
282                         <name><![CDATA[写真]]></name>\r
283                         <cmt><![CDATA[精度: 3.0m]]></cmt>\r
284                         <link href="2012-06-11_09-44-38.jpg">\r
285                                 <text>2012-06-11_09-44-38.jpg</text>\r
286                         </link>\r
287                         <sat>9</sat>\r
288                 </wpt>\r
289          */\r
290         try {\r
291                 if (params.getProperty(AppParameters.IMG_OUTPUT).equals(Boolean.toString(true))) {\r
292                 outDir = new File(outDir, imgDir.getName());\r
293                 }\r
294                 else {\r
295                 outDir = gpxDir;\r
296                 }\r
297 \r
298             for (File gpxFile : this.gpxFiles) {\r
299                 procGPXfile(gpxFile);\r
300             }\r
301         }\r
302         catch(ParserConfigurationException | DOMException | SAXException | IOException | ParseException | ImageReadException | ImageWriteException | IllegalArgumentException | TransformerException e) {\r
303         }\r
304     }\r
305     \r
306     /**\r
307      * 個別のGPXファイルを処理する\r
308      * \r
309      * @throws ParserConfigurationException \r
310      * @throws IOException \r
311      * @throws SAXException \r
312      * @throws ParseException \r
313      * @throws ImageWriteException \r
314      * @throws ImageReadException \r
315      * @throws TransformerException \r
316      */\r
317     void procGPXfile(File gpxFile) throws ParserConfigurationException, SAXException, IOException, ParseException, ImageReadException, ImageWriteException, TransformerException {\r
318         DocumentBuilderFactory factory;\r
319         DocumentBuilder        builder;\r
320         Node gpx;\r
321 \r
322         String fileName = gpxFile.getName();\r
323         String iStr = fileName.substring(0, fileName.length() - 4);\r
324 \r
325         File outputFile = new File(outDir, iStr +"_.gpx");\r
326         System.out.println(gpxFile.getAbsolutePath() + " => "+ outputFile.getAbsolutePath());\r
327         System.out.println("           時差: "+ (delta / 1000) +"(sec)");\r
328         System.out.println("    Target GPX: ["+ gpxFile.getAbsolutePath() +"]");\r
329         System.out.println("          EXIF: "+ (exif ? ("convert to '" + outDir.getAbsolutePath() +"'") : "off"));\r
330         System.out.println();\r
331 \r
332         factory = DocumentBuilderFactory.newInstance();\r
333         builder = factory.newDocumentBuilder();\r
334         factory.setIgnoringElementContentWhitespace(true);\r
335         factory.setIgnoringComments(true);\r
336         factory.setValidating(true);\r
337 \r
338         // GPX file --> Node root\r
339         DOMImplementation domImpl = builder.getDOMImplementation();\r
340         document = domImpl.createDocument("", "gpx", null);\r
341 \r
342         /*\r
343          * <gpx>\r
344          *   <trk>\r
345          *     <name><![CDATA[Tracked with OSMTracker for Android?]]></name>\r
346          *     <cmt><![CDATA[警告: HDOP values aren't the HDOP as returned by the GPS device. They're approximated from the location accuracy in meters.]]></cmt>\r
347          *     <trkseg>\r
348          *       <trkpt lat="35.32123832" lon="139.56965631">\r
349          *         <ele>47.20000076293945</ele>\r
350          *         <time>2012-06-15T03:00:29Z</time>\r
351          *         <hdop>0.5</hdop>\r
352          *       </trkpt>\r
353          *     </trkseg>\r
354          *   </trk>\r
355          *   <wpt lat="35.2564461" lon="139.15437809"></wpt>\r
356          * </gpx>\r
357          */\r
358         TreeMap<Long,Element> map = new TreeMap<>();\r
359         TreeMap<Long,Element> mapTRKSEG = new TreeMap<>();\r
360         Element trk = null;\r
361         //Element maeTRKPT = null;\r
362         gpx    = builder.parse(gpxFile).getFirstChild();\r
363         Document doc = gpx.getOwnerDocument();\r
364         NodeList nodes = gpx.getChildNodes();\r
365         for (int i=0; i < nodes.getLength(); i++) {\r
366             Node node2 = nodes.item(i);\r
367             if (node2.getNodeName().equals("trk")) {\r
368                 trk = (Element) node2;\r
369                 \r
370                 NodeList nodes1 = trk.getChildNodes();\r
371                 int trksegCounter = 0;\r
372                 for (int i1=0; i1 < nodes1.getLength(); i1++) {\r
373                     Node nodeTRKSEG = nodes1.item(i1);\r
374                     if (nodeTRKSEG.getNodeName().equals("trkseg")) {\r
375                         trksegCounter++;\r
376                         Element newTRKSEG = doc.createElement("trkseg");\r
377                         Element trkseg = (Element) nodeTRKSEG;\r
378                         NodeList nodes2 = trkseg.getChildNodes();\r
379                         for (int i2=0; i2 < nodes2.getLength(); i2++) {\r
380                             Node nodeTRKPT = nodes2.item(i2);\r
381                             if (nodeTRKPT.getNodeName().equals("trkpt")) {\r
382                                 if (param_GpxNoFirstNode && (i2 == 0)) {\r
383                                         continue;\r
384                                 }\r
385                                 newTRKSEG.appendChild(getCopy(doc, nodeTRKPT));\r
386                             }\r
387                         }\r
388                         mapTRKSEG.put(new Long(trksegCounter), getCopy(doc, newTRKSEG));\r
389 \r
390                         // <trk>から<trkseg>を削除する。\r
391                         trk.removeChild(nodeTRKSEG);\r
392                     }\r
393                 }\r
394                 \r
395                 // <trkseg>毎に実行する\r
396                 Iterator<Long> keyIte = mapTRKSEG.keySet().iterator();\r
397                 while (keyIte.hasNext()) {    //ループ。反復子iteratorによる キー 取得\r
398 \r
399                         // <trk>に、新たな<trkseg>を追加する。\r
400                         Element newTRKSEG = mapTRKSEG.get(keyIte.next());\r
401                     trk.appendChild(newTRKSEG);\r
402                     \r
403                     // mapに、<trkpt>を割り付ける\r
404                     trkptMap(newTRKSEG, map);\r
405                 }\r
406             }\r
407         }\r
408         \r
409         /*\r
410          * GPXへ割りつける開始時刻と終了時刻を求める\r
411          */\r
412                 long gpxStartTime = (new Date()).getTime();             // 対象とする開始時刻(現在時刻)\r
413                 long gpxEndTime = 0L;                                                   // 対象とする終了時刻\r
414                 Set<Long> keySet = map.keySet();  //すべてのキー値を取得\r
415                 for (Long timeLong : keySet) {\r
416                         long gpxTime = timeLong;\r
417                         if (gpxStartTime > gpxTime) {\r
418                              gpxStartTime = gpxTime;\r
419                         }\r
420                         if (gpxEndTime < gpxTime) {\r
421                              gpxEndTime = gpxTime;\r
422                         }\r
423                 }\r
424                  \r
425                 System.out.println("GPX start time: "+ dfjp.format(new Date(gpxStartTime)) + "\t[GMT " + dfuk.format(new Date(gpxStartTime))+"]");\r
426                 System.out.println("  GPX end time: "+ dfjp.format(new Date(gpxEndTime)) + "\t[GMT " + dfuk.format(new Date(gpxEndTime))+"]");\r
427                 System.out.println("------------|--------------------|--------------------|------------|------------|--------|------|------|");\r
428                 System.out.println(" name       | UpdateTime         | GPStime            | Latitude   | Longitude  | ele    |magvar| km/h |");\r
429                 System.out.println("------------|--------------------|--------------------|------------|------------|--------|------|------|");\r
430                 proc(imgDir, delta, gpxStartTime, gpxEndTime, map, exif, gpx);\r
431                 System.out.println("------------|--------------------|--------------------|------------|------------|--------|------|------|");\r
432 \r
433         // 出力\r
434         DOMSource source = new DOMSource(gpx);\r
435         FileOutputStream os = new FileOutputStream(outputFile);\r
436         StreamResult result = new StreamResult(os);\r
437         TransformerFactory transFactory = TransformerFactory.newInstance();\r
438         Transformer transformer = transFactory.newTransformer();\r
439         transformer.setOutputProperty(OutputKeys.INDENT, "yes");\r
440         transformer.setOutputProperty(OutputKeys.METHOD, "xml");\r
441         transformer.transform(source, result);         \r
442 \r
443         outputFile = new File(outDir, iStr +"_.gpx");\r
444         os = new FileOutputStream(outputFile);\r
445         result = new StreamResult(os);\r
446         transformer.transform(source, result);\r
447 \r
448     }\r
449     \r
450         \r
451     /**\r
452      * 再帰メソッド\r
453      * @throws ParseException \r
454      * @throws IOException \r
455      * @throws ImageReadException \r
456      * @throws ImageWriteException \r
457      */\r
458     boolean proc(File dir, long delta, long gpxStartTime, long gpxEndTime, TreeMap<Long,Element> map, boolean exifWrite, Node gpx) throws ParseException, ImageReadException, IOException, ImageWriteException {\r
459         DecimalFormat yearFormatter = new DecimalFormat("0000");\r
460         DecimalFormat monthFormatter = new DecimalFormat("00");\r
461         DecimalFormat dayFormatter = new DecimalFormat("00");\r
462 \r
463         boolean ret = false;\r
464         File[] files = dir.listFiles(new JpegFileFilter());\r
465         Arrays.sort(files, new FileSort());\r
466         for (File image : files) {\r
467             System.out.print(String.format("%12s|", image.getName()));\r
468             if (image.isDirectory()) {\r
469                 ret = proc(image, delta, gpxStartTime, gpxEndTime, map, exifWrite, gpx);\r
470                 continue;\r
471             }\r
472             \r
473             String imageName = image.getName();\r
474             if (!checkFile(imageName)) {\r
475                 System.out.println(String.format("%20s ", "it is not image file."));\r
476                 continue;\r
477             }\r
478             \r
479             Date itime = new Date(image.lastModified());\r
480             if (this.exifBase) {\r
481                 ImageMetadata meta = Imaging.getMetadata(image);\r
482                 JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;\r
483                 if (jpegMetadata == null) {\r
484                     System.out.println("'"+ image.getAbsolutePath() +"' にEXIF情報がありません");\r
485                     continue;\r
486                 }\r
487                 TiffImageMetadata exif = jpegMetadata.getExif();\r
488                 if (exif == null) {\r
489                     System.out.println("'"+ image.getAbsolutePath() +"' にEXIF情報がありません");\r
490                     continue;\r
491                 }\r
492                 String dateTimeOriginal = exif.getFieldValue(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL)[0];\r
493                 long lastModifyTime = (new SimpleDateFormat("yyyy:MM:dd HH:mm:ss")).parse(dateTimeOriginal).getTime();\r
494                 itime = new Date(lastModifyTime);\r
495             }\r
496 \r
497             // uktime <-- 画像撮影時刻に対応するGPX時刻\r
498             Date uktime = new Date(itime.getTime() + delta);\r
499             System.out.print(String.format("%20s ", dfjp.format(itime)));\r
500             System.out.print(String.format("%20s|", dfjp.format(uktime)));\r
501             if ((uktime.getTime() < gpxStartTime) || (uktime.getTime() > gpxEndTime)) {\r
502                 System.out.println(String.format("%20s ", "out of time."));\r
503                 continue;\r
504             }\r
505 \r
506                 // 時刻uktimeにおける<magver>をtrkptに追加する\r
507             Element trkptE = trkpt(map, uktime);\r
508             if (trkptE == null) {\r
509                 System.out.println(String.format("%20s ", "Out of GPX logging time."));\r
510                 continue;\r
511             }\r
512 \r
513             TagTrkpt trkptT = new TagTrkpt(trkptE);\r
514 \r
515                 //Element wpt = createWptTag(image, uktime.getTime(), trkptE);\r
516             //String latStr = trkptT.lat.toString();\r
517             //String lonStr = trkptT.lon.toString();\r
518             double latitude = trkptT.lat;\r
519             double longitude = trkptT.lon;\r
520             \r
521             String eleStr = "-";\r
522             if (trkptT.eleStr != null) {\r
523                 eleStr = new String(trkptT.eleStr);\r
524             }\r
525             \r
526             String magvarStr = "-";\r
527             if (trkptT.magvarStr != null) {\r
528                 magvarStr = new String(trkptT.magvarStr);\r
529             }\r
530             \r
531             String speedStr = "-";\r
532             if (trkptT.speedStr != null) {\r
533                 speedStr = new String(trkptT.speedStr);\r
534             }\r
535 \r
536             System.out.print(String.format("%12s %12s|", (new Double(latitude)).toString(), (new Double(longitude)).toString()));\r
537             System.out.println(String.format("%8s|%6s|%6s|", eleStr, magvarStr, speedStr));\r
538             ret = true;\r
539 \r
540             if (exifWrite) {\r
541                 TiffOutputSet outputSet = null;\r
542                 FileOutputStream fos = null;\r
543 \r
544                 ImageMetadata meta = Imaging.getMetadata(image);\r
545                 JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;\r
546                 if (jpegMetadata != null) {\r
547                     TiffImageMetadata exif = jpegMetadata.getExif();\r
548                     if (exif != null) {\r
549                         outputSet = exif.getOutputSet();\r
550                     }\r
551                 }\r
552 \r
553                 if (outputSet == null) {\r
554                     System.out.println("added : new tiff output set");\r
555                     outputSet = new TiffOutputSet();\r
556                 }\r
557 \r
558                 //---- EXIF_TAG_DATE_TIME_ORIGINAL / 「撮影日時/オリジナル画像の生成日時」----\r
559                 TiffOutputDirectory exifDir = outputSet.getOrCreateExifDirectory();\r
560                 {\r
561                     Calendar cal = GregorianCalendar.getInstance();\r
562                     cal.setTime(uktime);\r
563                     exifDir.removeField(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);\r
564                     exifDir.add(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL, new SimpleDateFormat("yyyy:MM:dd HH:mm:ss").format(cal.getTime()));\r
565                 }\r
566 \r
567                 //---- EXIF GPS_TIME_STAMP ----\r
568                 TiffOutputDirectory gpsDir = outputSet.getOrCreateGPSDirectory();\r
569                 {\r
570                     Calendar cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC"));\r
571                     cal.setTime(uktime);\r
572                     final String yearStr = yearFormatter.format(cal.get(Calendar.YEAR));\r
573                     final String monthStr = monthFormatter.format(cal.get(Calendar.MONTH) + 1);\r
574                     final String dayStr = dayFormatter.format(cal.get(Calendar.DAY_OF_MONTH));\r
575                     final String dateStamp = yearStr +":"+ monthStr +":"+ dayStr;\r
576 \r
577                     gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_TIME_STAMP);\r
578                     gpsDir.add(GpsTagConstants.GPS_TAG_GPS_TIME_STAMP,\r
579                             RationalNumber.valueOf(cal.get(Calendar.HOUR_OF_DAY)),\r
580                             RationalNumber.valueOf(cal.get(Calendar.MINUTE)),\r
581                             RationalNumber.valueOf(cal.get(Calendar.SECOND)));\r
582                     gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_DATE_STAMP);\r
583                     gpsDir.add(GpsTagConstants.GPS_TAG_GPS_DATE_STAMP, dateStamp);\r
584                 }\r
585 \r
586                 //---- EXIF GPS elevation/ALTITUDE ----\r
587                 if (eleStr.equals("-") == false) {\r
588                     final double altitude = Double.parseDouble(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 (magvarStr.equals("-") == false) {\r
595                     final double magvar = Double.parseDouble(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                 final String longitudeRef = (longitude < 0 ? "W" : "E");\r
602                 longitude = Math.abs(longitude);\r
603                 final String latitudeRef = (latitude < 0 ? "S" : "N");\r
604                 latitude = Math.abs(latitude);\r
605 \r
606                 gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF);\r
607                 gpsDir.add(GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF, longitudeRef);\r
608                 gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF);\r
609                 gpsDir.add(GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF, latitudeRef);\r
610                 {\r
611                     double value = longitude;\r
612                     final double longitudeDegrees = (long) value;\r
613                     value %= 1;\r
614                     value *= 60.0;\r
615                     final double longitudeMinutes = (long) value;\r
616                     value %= 1;\r
617                     value *= 60.0;\r
618                     final double longitudeSeconds = value;\r
619                     gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_LONGITUDE);\r
620                     gpsDir.add(GpsTagConstants.GPS_TAG_GPS_LONGITUDE,\r
621                             RationalNumber.valueOf(longitudeDegrees),\r
622                             RationalNumber.valueOf(longitudeMinutes),\r
623                             RationalNumber.valueOf(longitudeSeconds));\r
624                 }\r
625                 {\r
626                     double value = latitude;\r
627                     final double latitudeDegrees = (long) value;\r
628                     value %= 1;\r
629                     value *= 60.0;\r
630                     final double latitudeMinutes = (long) value;\r
631                     value %= 1;\r
632                     value *= 60.0;\r
633                     final double latitudeSeconds = value;\r
634                     gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_LATITUDE);\r
635                     gpsDir.add(GpsTagConstants.GPS_TAG_GPS_LATITUDE,\r
636                             RationalNumber.valueOf(latitudeDegrees),\r
637                             RationalNumber.valueOf(latitudeMinutes),\r
638                             RationalNumber.valueOf(latitudeSeconds));\r
639                 }\r
640 \r
641                 outDir.mkdir();\r
642                 ExifRewriter rewriter = new ExifRewriter();\r
643                 try {\r
644                     fos = new FileOutputStream(new File(outDir, imageName));\r
645                     rewriter.updateExifMetadataLossy(image, fos, outputSet);\r
646                 }\r
647                 finally {\r
648                     if (fos != null) {\r
649                         fos.close();\r
650                     }\r
651                 }\r
652             }\r
653 \r
654             if (Boolean.parseBoolean(params.getProperty(AppParameters.GPX_OUTPUT_WPT))) {\r
655                 Element temp = getCopy(gpx.getOwnerDocument(), trkptT.trkpt);\r
656                 gpx.appendChild(temp);\r
657             }\r
658         }\r
659         return ret;\r
660     }\r
661         \r
662     static Document document;\r
663 \r
664     /**\r
665      * 2012-06-10T05:09:46Z  (日本時間の'2012-06-10T14:09:46')\r
666      */\r
667     public static final String TIME_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ss";\r
668     public static SimpleDateFormat dfjp = new SimpleDateFormat(TIME_FORMAT_STRING);\r
669     public static SimpleDateFormat dfuk = new SimpleDateFormat(TIME_FORMAT_STRING +"'Z'");\r
670 \r
671     /**\r
672      * XMLエレメント<trkpt>をTIMEでキー付したHashMapを生成する<br>\r
673      * \r
674      * <trkseg>\r
675      *  <trkpt lat="34.976635" lon="138.466228">\r
676      *          <ele>267.291</ele>\r
677      *          <magvar>359</magvar>\r
678      *          <speed></speed>\r
679      *          <time>2016-07-02T08:25:18Z</time>\r
680      *  </trkpt>\r
681      * </trkseg>\r
682      * \r
683      * @param trk\r
684      * @param map\r
685      * @throws ParseException\r
686      */\r
687     public void trkptMap(Element trkseg, TreeMap<Long,Element> map) throws ParseException {\r
688         dfuk.setTimeZone(TimeZone.getTimeZone("GMT"));\r
689 \r
690         NodeList nodes2 = trkseg.getChildNodes();\r
691         for (int i2=0; i2 < nodes2.getLength(); i2++) {\r
692             Node node3 = nodes2.item(i2);\r
693             if (node3.getNodeName().equals("trkpt")) {\r
694                 Element trkpt = (Element) node3;\r
695 \r
696                 NodeList nodes3 = trkpt.getChildNodes();\r
697                 for (int i3=0; i3 < nodes3.getLength(); i3++) {\r
698                     Node node4 = nodes3.item(i3);\r
699                     if (node4.getNodeName().equals("time")) {\r
700                         Element time = (Element) node4;\r
701                         NodeList nodes4 = time.getChildNodes();      // 子ノードを取得\r
702                         for (int i4=0; i4< nodes4.getLength(); i4++) {\r
703                             Node node5 = nodes4.item(i4);\r
704                             if (node5 != null) {\r
705                                 if (node5.getNodeType() == Node.TEXT_NODE) {\r
706                                     String timeStr = node5.getNodeValue();\r
707                                     long t = dfuk.parse(timeStr).getTime();\r
708                                     map.put(t, getCopy(trkseg.getOwnerDocument(), trkpt));\r
709                                 }\r
710                             }\r
711                         }\r
712                     }\r
713                 }\r
714             }\r
715         }\r
716     }\r
717 \r
718     \r
719     /**\r
720      *  <trkpt lat="35.32123832" lon="139.56965631">\r
721      *          <ele>47.20000076293945</ele>\r
722      *          <time>2012-06-15T03:00:29Z</time>\r
723      *  </trkpt>\r
724      *DIFF_MAE_TIME\r
725      * @return\r
726      * @param map\r
727      * @param jptime    画像ファイルの撮影日時 ミリ秒(日本時間)\r
728      * @throws ParseException\r
729      */\r
730     public Element trkpt(TreeMap<Long,Element> map, Date jptime) throws ParseException {\r
731         // 指定した時刻のエレメント(imaTrkpt)を取得する\r
732         Element imaE = getTrkpt(map, jptime);\r
733         if (imaE != null) {\r
734                 Element maeE = getMaeTrkpt(map, new TagTrkpt(imaE));\r
735             if (maeE != null) {\r
736                 Complementation comp = new Complementation(imaE, maeE);\r
737 \r
738                 // <MAGVAR>がなければ、\r
739                 // 直前の位置と、現在地から進行方向を求める\r
740                 // 経度(longitude)と経度から進行方向を求める\r
741                 if (Complementation.param_GpxOverwriteMagvar) {\r
742                         comp.complementationMagvar();\r
743                 }\r
744 \r
745                 // 緯度・経度と時間差から速度(km/h)を求める\r
746                 if (Complementation.param_GpxOutputSpeed) {\r
747                         comp.complementationSpeed();\r
748                 }\r
749                 imaE = (Element)(comp.imaTag.trkpt.cloneNode(true));\r
750             }\r
751         }\r
752         return imaE;\r
753     }\r
754 \r
755     /**\r
756      * [map]から指定した時刻の<trkpt>エレメントを取り出す。\r
757      * GPX時刻との差が10分以上は無効\r
758      * \r
759      * @param map\r
760      * @param jptime\r
761      * @return\r
762      * @throws ParseException\r
763      */\r
764     public Element getTrkpt(TreeMap<Long,Element> map, Date jptime) throws ParseException {\r
765         long sa = 2L * 3600000L;\r
766         long jpt = jptime.getTime();\r
767         \r
768         Element ret = null;\r
769 \r
770         Set<Long> keySet = map.keySet();  //すべてのキー値を取得\r
771         Iterator<Long> keyIte = keySet.iterator();\r
772         while (keyIte.hasNext()) {    //ループ。反復子iteratorによる キー 取得\r
773             Long time = keyIte.next();\r
774             long t = time;\r
775 \r
776             if (Math.abs(jpt - t) < sa) {\r
777                 sa = Math.abs(jpt - t);\r
778                 ret = map.get(time);\r
779             }\r
780         }\r
781 \r
782         if (sa < (60000L * 10L)) {\r
783                 // GPX時刻との差が10分以内なら有効\r
784             return ret;\r
785         }\r
786         return null;\r
787     }\r
788 \r
789     public Element getMaeTrkpt(TreeMap<Long,Element> map, TagTrkpt imaTrkpt) throws ParseException {\r
790         Element ret = null;\r
791         long diffTime = 2L * 3600000L;          // 2時間\r
792         long jpt = imaTrkpt.time.getTime() - DIFF_MAE_TIME;\r
793 \r
794         Set<Long> keySet = map.keySet();  //すべてのキー値を取得\r
795         Iterator<Long> keyIte = keySet.iterator();\r
796         while (keyIte.hasNext()) {    //ループ。反復子iteratorによる キー 取得\r
797             Long time = keyIte.next();\r
798             long t = time;\r
799 \r
800             if (Math.abs(jpt - t) < diffTime) {\r
801                 diffTime = Math.abs(jpt - t);\r
802                 ret = map.get(time);\r
803             }\r
804         }\r
805 \r
806         if (diffTime < (60000L * 10L)) {\r
807                 // GPX時刻との差が10分以内なら有効\r
808                 if (diffTime < (imaTrkpt.time.getTime() - 1000)) {\r
809                         // 元の時刻との差が1秒以上あること\r
810                 return ret;\r
811                 }\r
812         }\r
813         return null;\r
814     }\r
815     \r
816     /**\r
817      * 対象は '*.JPG' のみ対象とする\r
818      * @return \r
819      * @param name\r
820      */\r
821     public static boolean checkFile(String name) {\r
822         return ((name != null) && name.toUpperCase().endsWith(".JPG"));\r
823     }\r
824 \r
825     /**\r
826      *  <wpt lat="35.25714922" lon="139.15490497">\r
827      *          <ele>62.099998474121094</ele>\r
828      *          <time>2012-06-11T00:44:38Z</time>\r
829      *          <name><![CDATA[写真]]></name>\r
830      *          <link href="2012-06-11_09-44-38.jpg">\r
831      *                  <text>2012-06-11_09-44-38.jpg</text>\r
832      *          </link>\r
833      *  </wpt>\r
834      *\r
835      *  <trkpt lat="35.32123832" lon="139.56965631">\r
836      *          <ele>47.20000076293945</ele>\r
837      *          <time>2012-06-15T03:00:29Z</time>\r
838      *  </trkpt>\r
839      *\r
840      * @return\r
841      * @param iFile\r
842      * @param timestamp\r
843      * @param trkpt\r
844      */\r
845     public Element createWptTag(File iFile, long timestamp, Element trkpt) {\r
846         Element wpt = document.createElement("wpt");\r
847 \r
848         NamedNodeMap nodeMap = trkpt.getAttributes();\r
849         if (null != nodeMap) {\r
850             for (int j=0; j < nodeMap.getLength(); j++ ) {\r
851                 switch (nodeMap.item(j).getNodeName()) {\r
852                 case "lat":\r
853                     String lat = nodeMap.item(j).getNodeValue();\r
854                     wpt.setAttribute("lat", lat);\r
855                     break;\r
856                 case "lon":\r
857                     String lon = nodeMap.item(j).getNodeValue();\r
858                     wpt.setAttribute("lon", lon);\r
859                     break;\r
860                 }\r
861             }\r
862         }\r
863 \r
864         NodeList nodes1 = trkpt.getChildNodes();\r
865         for (int i1=0; i1 < nodes1.getLength(); i1++) {\r
866             Node node1 = nodes1.item(i1);\r
867             NodeList nodes2 = node1.getChildNodes();\r
868             switch (node1.getNodeName()) {\r
869             case "ele":\r
870                 for (int i2=0; i2 < nodes2.getLength(); i2++) {\r
871                     Node node2 = nodes2.item(i2);\r
872                     if (node2 != null) {\r
873                         if (node2.getNodeType() == Node.TEXT_NODE) {\r
874                             String eleStr = node2.getNodeValue();\r
875                             Element eleE = document.createElement("ele");\r
876                             eleE.setTextContent(eleStr);\r
877                             wpt.appendChild(eleE);\r
878                         }\r
879                     }\r
880                 }\r
881                 break;\r
882             case "time":\r
883                 for (int i2=0; i2 < nodes2.getLength(); i2++) {\r
884                     Node node2 = nodes2.item(i2);\r
885                     if (node2 != null) {\r
886                         if (node2.getNodeType() == Node.TEXT_NODE) {\r
887                             String timeStr = node2.getNodeValue();\r
888                             Element timeE = document.createElement("time");\r
889                             timeE.setTextContent(timeStr);\r
890                             wpt.appendChild(timeE);\r
891                         }\r
892                     }\r
893                 }\r
894                 break;\r
895             case "magvar":\r
896                 for (int i2=0; i2 < nodes2.getLength(); i2++) {\r
897                     Node node2 = nodes2.item(i2);\r
898                     if (node2 != null) {\r
899                         if (node2.getNodeType() == Node.TEXT_NODE) {\r
900                             String magvarStr = node2.getNodeValue();\r
901                             Element magvarE = document.createElement("magvar");\r
902                             magvarE.setTextContent(magvarStr);\r
903                             wpt.appendChild(magvarE);\r
904                         }\r
905                     }\r
906                 }\r
907                 break;\r
908             case "speed":\r
909                 for (int i2=0; i2 < nodes2.getLength(); i2++) {\r
910                     Node node2 = nodes2.item(i2);\r
911                     if (node2 != null) {\r
912                         if (node2.getNodeType() == Node.TEXT_NODE) {\r
913                             String speedStr = node2.getNodeValue();\r
914                             Element speedE = document.createElement("speed");\r
915                             speedE.setTextContent(speedStr);\r
916                             wpt.appendChild(speedE);\r
917                         }\r
918                     }\r
919                 }\r
920                 break;\r
921             }\r
922         }\r
923 \r
924         Element name = document.createElement("name");\r
925         name.appendChild(document.createCDATASection("写真"));\r
926         wpt.appendChild(name);\r
927 \r
928         Element link = document.createElement("link");\r
929         link.setAttribute("href", getShortPathName(imgDir, iFile));\r
930         Element text = document.createElement("text");\r
931         text.setTextContent(iFile.getName());\r
932         link.appendChild(text);\r
933         wpt.appendChild(link);\r
934 \r
935         return wpt;\r
936     }\r
937         \r
938     static String getShortPathName(File dir, File iFile) {\r
939         String dirPath = dir.getAbsolutePath();\r
940         String filePath = iFile.getAbsolutePath();\r
941         if (filePath.startsWith(dirPath)) {\r
942             return filePath.substring(dirPath.length()+1);\r
943         }\r
944         else {\r
945             return filePath;\r
946         }\r
947     }\r
948 \r
949     public static Element getCopy(Document doc, Node node) {\r
950         Element root = doc.createElement(node.getNodeName());\r
951 \r
952         NamedNodeMap nodeMap = node.getAttributes();\r
953         if (null != nodeMap) {\r
954             for (int j=0; j < nodeMap.getLength(); j++ ) {\r
955                 root.setAttribute(nodeMap.item(j).getNodeName(), nodeMap.item(j).getNodeValue());\r
956             }\r
957         }\r
958 \r
959         NodeList nodes = node.getChildNodes();\r
960         for (int i=0; i < nodes.getLength(); i++) {\r
961             Node node2 = nodes.item(i);\r
962             if (node2.getNodeType() == Node.ELEMENT_NODE) {\r
963                 root.appendChild(getCopy(doc, node2));\r
964             }\r
965             else if (node2.getNodeType() == Node.TEXT_NODE) {\r
966                 String str = node2.getNodeValue();\r
967                 Text textContents = doc.createTextNode(str);\r
968                 root.appendChild(textContents);\r
969             }\r
970             else if (node2.getNodeType() == Node.CDATA_SECTION_NODE) {\r
971                 String str = node2.getNodeValue();\r
972                 CDATASection cdataSection = doc.createCDATASection(str);\r
973                 root.appendChild(cdataSection);\r
974             }\r
975         }\r
976         return root;\r
977     }\r
978         \r
979     /**\r
980      * ファイル名の順序に並び替えるためのソートクラス\r
981      * \r
982      * @author hayashi\r
983      */\r
984     static class FileSort implements Comparator<File>{\r
985         @Override\r
986         public int compare(File src, File target){\r
987             int diff = src.getName().compareTo(target.getName());\r
988             return diff;\r
989         }\r
990     }\r
991 \r
992     /**\r
993      * JPEGファイルフィルター\r
994      * @author yuu\r
995      */\r
996         class JpegFileFilter implements FilenameFilter{\r
997         public boolean accept(File dir, String name) {\r
998                         if (name.toUpperCase().matches(".*\\.JPG$")) {\r
999                                 return true;\r
1000                         }\r
1001                         return false;\r
1002         }\r
1003         }\r
1004 }