OSDN Git Service

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