3 import java.nio.channels.FileChannel;
\r
4 import java.text.DecimalFormat;
\r
5 import java.text.ParseException;
\r
6 import java.text.SimpleDateFormat;
\r
7 import java.util.ArrayList;
\r
8 import java.util.Arrays;
\r
9 import java.util.Calendar;
\r
10 import java.util.Comparator;
\r
11 import java.util.Date;
\r
12 import java.util.GregorianCalendar;
\r
13 import java.util.Iterator;
\r
14 import java.util.Set;
\r
15 import java.util.TimeZone;
\r
16 import java.util.TreeMap;
\r
17 import java.util.logging.LogManager;
\r
18 import java.util.logging.Logger;
\r
20 import javax.xml.parsers.*;
\r
21 import javax.xml.transform.OutputKeys;
\r
22 import javax.xml.transform.Transformer;
\r
23 import javax.xml.transform.TransformerException;
\r
24 import javax.xml.transform.TransformerFactory;
\r
25 import javax.xml.transform.dom.DOMSource;
\r
26 import javax.xml.transform.stream.StreamResult;
\r
28 import org.apache.commons.imaging.ImageReadException;
\r
29 import org.apache.commons.imaging.ImageWriteException;
\r
30 import org.apache.commons.imaging.Imaging;
\r
31 import org.apache.commons.imaging.common.ImageMetadata;
\r
32 import org.apache.commons.imaging.common.RationalNumber;
\r
33 import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
\r
34 import org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter;
\r
35 import org.apache.commons.imaging.formats.tiff.TiffImageMetadata;
\r
36 import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants;
\r
37 import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants;
\r
38 import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory;
\r
39 import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet;
\r
40 import org.w3c.dom.*;
\r
41 import org.xml.sax.SAXException;
\r
43 public class ImportPicture extends Thread {
\r
45 * 実行中に発生したExceptionを保持する場所
\r
47 public Exception ex = null;
\r
50 * ログ設定プロパティファイルのファイル内容
\r
52 protected static final String LOGGING_PROPERTIES_DATA
\r
53 = "handlers=java.util.logging.ConsoleHandler\n"
\r
55 + "java.util.logging.ConsoleHandler.level=INFO\n"
\r
56 + "java.util.logging.ConsoleHandler.formatter=osm.jp.gpx.YuuLogFormatter";
\r
59 * static initializer によるログ設定の初期化
\r
61 public static final Logger logger = Logger.getLogger("CommandLogging");
\r
63 InputStream inStream = null;
\r
65 inStream = new ByteArrayInputStream(LOGGING_PROPERTIES_DATA.getBytes("UTF-8"));
\r
67 LogManager.getLogManager().readConfiguration(inStream);
\r
68 logger.config("ログ設定: LogManagerを設定しました。");
\r
70 catch (IOException e) {
\r
71 logger.warning("ログ設定: LogManager設定の際に例外が発生しました。:" + e.toString());
\r
74 catch (UnsupportedEncodingException e) {
\r
75 logger.severe("ログ設定: UTF-8エンコーディングがサポートされていません。:" + e.toString());
\r
79 if (inStream != null) {
\r
82 } catch (IOException e) {
\r
83 logger.warning("ログ設定: ログ設定プロパティファイルのストリームクローズ時に例外が発生しました。:"+ e.toString());
\r
91 * 画像ファイルをGPXファイルに取り込みます。
\r
93 * ・画像ファイルの更新日付をその画像の撮影日時とします。(Exi情報は無視します)
\r
94 * ※ 対象とするファイルは'*.jpg'のみ
\r
95 * ・精確な時刻との時差を入力することで、撮影日時を補正します。
\r
96 * ・画像ファイルの更新日付リストをCSV形式のファイルとして出力する。
\r
97 * ・・結果は、取り込み元のGPXファイルとは別に、元ファイル名にアンダーバー「_」を付加した.ファイルに出力します。
\r
101 * argv[0] = 画像ファイルが格納されているディレクトリ --> imgDir
\r
102 * argv[1] = 時刻補正の基準とする画像ファイル --> baseFile
\r
103 * argv[2] = 基準画像ファイルの精確な撮影日時 "yyyy-MM-dd'T'HH:mm:ss" --> timeStr
\r
104 * argv[3] = 出力先フォルダ --> outDir
\r
105 * argv[4] = 撮影位置をロギングしたGPXファイル --> gpxDir
\r
107 * @throws IOException
\r
108 * @throws ImageReadException
\r
110 public static void main(String[] argv) throws Exception
\r
114 ImportPicture obj = new ImportPicture();
\r
117 if (argv.length > 0) {
\r
118 obj.imgDir = new File(argv[0]);
\r
121 if (argv.length < 4) {
\r
122 System.out.println("!!! Illigal command call. !!!");
\r
123 System.out.println("> java -cp .:AdjustTime.jar:commons-imaging-1.0-SNAPSHOT.jar <targetDir> <time base image> <time> {EXIF/not} (gpx)");
\r
124 System.out.println("> java -cp .:AdjustTime.jar:commons-imaging-1.0-SNAPSHOT.jar. IMG_01234.JPG 2012-06-15T12:52:22 EXIF");
\r
125 System.out.println("> java -cp .:AdjustTime.jar . IMG_01234.JPG 2012-06-15T12:52:22 not");
\r
126 System.out.println();
\r
130 obj.params = new AppParameters();
\r
132 // 基準時刻(ファイル更新日時 | EXIF撮影日時)
\r
133 obj.exifBase = (obj.params.getProperty(AppParameters.GPX_BASETIME).equals("EXIF_TIME") ? true : false);
\r
135 // 基準時刻ファイルの「更新日時」を使って時刻合わせを行う。
\r
136 File baseFile = new File(obj.imgDir, argv[1]);
\r
137 if (obj.exifBase) {
\r
138 ImageMetadata meta = Imaging.getMetadata(baseFile);
\r
139 JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;
\r
140 if (jpegMetadata == null) {
\r
141 System.out.println("'"+ baseFile.getAbsolutePath() +"' にEXIF情報がありません");
\r
144 TiffImageMetadata exif = jpegMetadata.getExif();
\r
145 if (exif == null) {
\r
146 System.out.println("'"+ baseFile.getAbsolutePath() +"' にEXIF情報がありません");
\r
149 String dateTimeOriginal = exif.getFieldValue(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL)[0];
\r
150 long lastModifyTime = (new SimpleDateFormat("yyyy:MM:dd HH:mm:ss")).parse(dateTimeOriginal).getTime();
\r
151 jptime = new Date(lastModifyTime);
\r
154 jptime = new Date(baseFile.lastModified());
\r
157 String timeStr = argv[2];
\r
159 dfjp.setTimeZone(TimeZone.getTimeZone("JST"));
\r
160 Date t = dfjp.parse(timeStr);
\r
161 obj.delta = t.getTime() - jptime.getTime();
\r
163 catch (ParseException e) {
\r
164 System.out.println("'"+ timeStr +"' の書式が違います("+ TIME_FORMAT_STRING +")");
\r
169 obj.outDir = new File(argv[3]);
\r
172 String paramStr = obj.params.getProperty(AppParameters.GPX_GPXSPLIT);
\r
173 if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
\r
174 obj.param_GpxSplit = true;
\r
177 paramStr = obj.params.getProperty(AppParameters.GPX_NO_FIRST_NODE);
\r
178 if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
\r
179 obj.param_GpxNoFirstNode = true;
\r
182 paramStr = obj.params.getProperty(AppParameters.GPX_REUSE);
\r
183 if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
\r
184 obj.param_GpxReuse = true;
\r
187 paramStr = obj.params.getProperty(AppParameters.IMG_OUTPUT_ALL);
\r
188 if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
\r
189 obj.param_ImgOutputAll = true;
\r
192 paramStr = obj.params.getProperty(AppParameters.GPX_OUTPUT_WPT);
\r
193 if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
\r
194 obj.param_GpxOutputWpt = true;
\r
197 paramStr = obj.params.getProperty(AppParameters.GPX_OVERWRITE_MAGVAR);
\r
198 if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
\r
199 Complementation.param_GpxOverwriteMagvar = true;
\r
202 paramStr = obj.params.getProperty(AppParameters.GPX_OUTPUT_SPEED);
\r
203 if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
\r
204 Complementation.param_GpxOutputSpeed = true;
\r
207 paramStr = obj.params.getProperty(AppParameters.GPX_SOURCE_FOLDER);
\r
208 if (paramStr != null) {
\r
209 obj.param_GpxSourceFolder = new String(paramStr);
\r
210 obj.gpxDir = new File(obj.param_GpxSourceFolder);
\r
211 if (!obj.gpxDir.exists()) {
\r
212 // GPXファイルまたはディレクトリが存在しません。
\r
213 System.out.println("GPXファイルまたはディレクトリが存在しません。('"+ paramStr +"')");
\r
218 obj.gpxDir = obj.imgDir;
\r
221 // 指定されたディレクトリ内のGPXファイルすべてを対象とする
\r
222 if (obj.gpxDir.isDirectory()) {
\r
223 File[] files = obj.gpxDir.listFiles();
\r
224 if (files == null) {
\r
225 // 対象となるGPXファイルがありませんでした。
\r
226 System.out.println("対象となるGPXファイルがありませんでした。('"+ obj.gpxDir.getAbsolutePath() +"')");
\r
229 if (obj.param_ImgOutputAll && (files.length > 1)) {
\r
230 System.out.println("複数のGPXファイルがあるときには、'IMG.OUTPUT_ALL'オプションは指定できません。");
\r
234 java.util.Arrays.sort(files, new java.util.Comparator<File>() {
\r
235 public int compare(File file1, File file2){
\r
236 return file1.getName().compareTo(file2.getName());
\r
239 for (File file : files) {
\r
240 if (file.isFile()) {
\r
241 String filename = file.getName().toUpperCase();
\r
242 if (filename.toUpperCase().endsWith(".GPX")) {
\r
243 if (!filename.toUpperCase().endsWith("_.GPX") || obj.param_GpxReuse) {
\r
244 obj.gpxFiles.add(file);
\r
251 obj.gpxFiles.add(obj.gpxDir);
\r
254 paramStr = obj.params.getProperty(AppParameters.IMG_OUTPUT_EXIF);
\r
255 if ((paramStr != null) && (paramStr.equals(Boolean.toString(true)))) {
\r
259 System.out.println(" - param: imgDir = '"+ obj.imgDir.getAbsolutePath() +"'");
\r
260 System.out.println(" - param: outDir = '"+ (obj.outDir == null ? "" : obj.outDir.getAbsolutePath()) +"'");
\r
261 System.out.println(" - param: gpxDir = '"+ (obj.gpxDir == null ? "" : obj.gpxDir.getAbsolutePath()) +"'");
\r
262 System.out.println(" - param: number of gpxFiles = '"+ obj.gpxFiles.size() +"'");
\r
263 System.out.println(" - param: "+ AppParameters.GPX_GPXSPLIT +"="+ obj.param_GpxSplit);
\r
264 System.out.println(" - param: "+ AppParameters.GPX_NO_FIRST_NODE +"="+ obj.param_GpxNoFirstNode);
\r
265 System.out.println(" - param: "+ AppParameters.GPX_REUSE +"="+ obj.param_GpxReuse);
\r
266 System.out.println(" - param: "+ AppParameters.IMG_BASE_FILE +"="+ obj.params.getProperty(AppParameters.IMG_BASE_FILE) );
\r
267 System.out.println(" - param: "+ AppParameters.GPX_BASETIME +"="+ obj.params.getProperty(AppParameters.GPX_BASETIME) );
\r
268 System.out.println(" - param: "+ AppParameters.IMG_OUTPUT +"="+ obj.params.getProperty(AppParameters.IMG_OUTPUT));
\r
269 System.out.println(" - param: "+ AppParameters.IMG_OUTPUT_ALL +"="+ obj.param_ImgOutputAll);
\r
270 System.out.println(" - param: "+ AppParameters.IMG_OUTPUT_EXIF +"= "+ String.valueOf(obj.exif));
\r
271 System.out.println(" - param: "+ AppParameters.GPX_SOURCE_FOLDER +"="+ obj.param_GpxSourceFolder);
\r
272 System.out.println(" - param: "+ AppParameters.GPX_OUTPUT_WPT +"="+ obj.param_GpxOutputWpt);
\r
273 System.out.println(" - param: "+ AppParameters.GPX_OVERWRITE_MAGVAR +"="+ Complementation.param_GpxOverwriteMagvar);
\r
274 System.out.println(" - param: "+ AppParameters.GPX_OUTPUT_SPEED +"="+ Complementation.param_GpxOutputSpeed);
\r
278 } catch(InterruptedException end) {}
\r
279 if (obj.ex != null) {
\r
284 public File gpxDir;
\r
285 public File imgDir;
\r
286 public File outDir;
\r
287 public long delta = 0;
\r
288 public boolean exif = false;
\r
289 public boolean exifBase = false;
\r
290 public ArrayList<File> gpxFiles = new ArrayList<>();
\r
291 public AppParameters params;
\r
292 public boolean param_GpxSplit = false;
\r
293 public boolean param_GpxNoFirstNode = false;
\r
294 public boolean param_GpxReuse = false;
\r
295 //public boolean Complementation.param_GpxOutputSpeed = false;
\r
296 //public boolean Complementation.param_GpxOverwriteMagvar = false;
\r
297 public boolean param_GpxOutputWpt = true;
\r
298 public boolean param_ImgOutputAll = false;
\r
299 public String param_GpxSourceFolder = ".";
\r
300 public static final long DIFF_MAE_TIME = 3000L; // before 3 secound
\r
303 public void run() {
\r
306 <wpt lat="35.25714922" lon="139.15490497">
\r
307 <ele>62.099998474121094</ele>
\r
308 <time>2012-06-11T00:44:38Z</time>
\r
310 <name><![CDATA[写真]]></name>
\r
311 <cmt><![CDATA[精度: 3.0m]]></cmt>
\r
312 <link href="2012-06-11_09-44-38.jpg">
\r
313 <text>2012-06-11_09-44-38.jpg</text>
\r
319 if (params.getProperty(AppParameters.IMG_OUTPUT).equals(Boolean.toString(true))) {
\r
320 outDir = new File(outDir, imgDir.getName());
\r
326 for (File gpxFile : this.gpxFiles) {
\r
327 procGPXfile(gpxFile);
\r
330 catch(ParserConfigurationException | DOMException | SAXException | IOException | ParseException | ImageReadException | ImageWriteException | IllegalArgumentException | TransformerException e) {
\r
331 e.printStackTrace();
\r
332 this.ex = new Exception(e);
\r
339 * @throws ParserConfigurationException
\r
340 * @throws IOException
\r
341 * @throws SAXException
\r
342 * @throws ParseException
\r
343 * @throws ImageWriteException
\r
344 * @throws ImageReadException
\r
345 * @throws TransformerException
\r
347 void procGPXfile(File gpxFile) throws ParserConfigurationException, SAXException, IOException, ParseException, ImageReadException, ImageWriteException, TransformerException {
\r
348 DocumentBuilderFactory factory;
\r
349 DocumentBuilder builder;
\r
352 String fileName = gpxFile.getName();
\r
353 String iStr = fileName.substring(0, fileName.length() - 4);
\r
355 File outputFile = new File(outDir, iStr +"_.gpx");
\r
356 System.out.println(gpxFile.getAbsolutePath() + " => "+ outputFile.getAbsolutePath());
\r
357 System.out.println(" 時差: "+ (delta / 1000) +"(sec)");
\r
358 System.out.println(" Target GPX: ["+ gpxFile.getAbsolutePath() +"]");
\r
359 System.out.println(" EXIF: "+ (exif ? ("convert to '" + outDir.getAbsolutePath() +"'") : "off"));
\r
360 System.out.println();
\r
362 factory = DocumentBuilderFactory.newInstance();
\r
363 builder = factory.newDocumentBuilder();
\r
364 factory.setIgnoringElementContentWhitespace(true);
\r
365 factory.setIgnoringComments(true);
\r
366 factory.setValidating(true);
\r
368 // GPX file --> Node root
\r
369 DOMImplementation domImpl = builder.getDOMImplementation();
\r
370 document = domImpl.createDocument("", "gpx", null);
\r
375 * <name><![CDATA[Tracked with OSMTracker for Android?]]></name>
\r
376 * <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
378 * <trkpt lat="35.32123832" lon="139.56965631">
\r
379 * <ele>47.20000076293945</ele>
\r
380 * <time>2012-06-15T03:00:29Z</time>
\r
385 * <wpt lat="35.2564461" lon="139.15437809"></wpt>
\r
388 TreeMap<Long,Element> map = new TreeMap<Long, Element>();
\r
389 TreeMap<Long,Element> mapTRKSEG = new TreeMap<>();
\r
390 Element trk = null;
\r
391 //Element maeTRKPT = null;
\r
392 gpx = builder.parse(gpxFile).getFirstChild();
\r
393 document = gpx.getOwnerDocument();
\r
394 NodeList nodes = gpx.getChildNodes();
\r
395 for (int i=0; i < nodes.getLength(); i++) {
\r
396 Node node2 = nodes.item(i);
\r
397 if (node2.getNodeName().equals("trk")) {
\r
398 trk = (Element) node2;
\r
400 NodeList nodes1 = trk.getChildNodes();
\r
401 int trksegCounter = 0;
\r
402 for (int i1=0; i1 < nodes1.getLength(); i1++) {
\r
403 Node nodeTRKSEG = nodes1.item(i1);
\r
404 if (nodeTRKSEG.getNodeName().equals("trkseg")) {
\r
406 Element newTRKSEG = document.createElement("trkseg");
\r
407 Element trkseg = (Element) nodeTRKSEG;
\r
408 NodeList nodes2 = trkseg.getChildNodes();
\r
409 for (int i2=0; i2 < nodes2.getLength(); i2++) {
\r
410 Node nodeTRKPT = nodes2.item(i2);
\r
411 if (nodeTRKPT.getNodeName().equals("trkpt")) {
\r
412 if (param_GpxNoFirstNode && (i2 == 0)) {
\r
415 newTRKSEG.appendChild(getCopy(document, nodeTRKPT));
\r
418 mapTRKSEG.put(new Long(trksegCounter), getCopy(document, newTRKSEG));
\r
420 // <trk>から<trkseg>を削除する。
\r
421 trk.removeChild(nodeTRKSEG);
\r
426 Iterator<Long> keyIte = mapTRKSEG.keySet().iterator();
\r
427 while (keyIte.hasNext()) { //ループ。反復子iteratorによる キー 取得
\r
429 // <trk>に、新たな<trkseg>を追加する。
\r
430 Element newTRKSEG = mapTRKSEG.get(keyIte.next());
\r
431 trk.appendChild(newTRKSEG);
\r
433 // mapに、<trkpt>を割り付ける
\r
434 trkptMap(newTRKSEG, map);
\r
440 * GPXへ割りつける開始時刻と終了時刻を求める
\r
442 long gpxStartTime = (new Date()).getTime(); // 対象とする開始時刻(現在時刻)
\r
443 long gpxEndTime = 0L; // 対象とする終了時刻
\r
444 Set<Long> keySet = map.keySet(); //すべてのキー値を取得
\r
445 for (Long timeLong : keySet) {
\r
446 long gpxTime = timeLong;
\r
447 if (gpxStartTime > gpxTime) {
\r
448 gpxStartTime = gpxTime;
\r
450 if (gpxEndTime < gpxTime) {
\r
451 gpxEndTime = gpxTime;
\r
457 * 条件: SPEEDを書き出すフラグがONの時
\r
458 * 条件: オリジナルのSPEEDがある場合「上書きする/変更しない」(GPX_OUTPUT_SPEED)
\r
461 TreeMap<Long,Element> map2 = new TreeMap<Long, Element>();
\r
462 if (Boolean.parseBoolean(params.getProperty(AppParameters.GPX_OUTPUT_SPEED))) {
\r
463 for (Long timeL : keySet) {
\r
464 Element trkptE = trkpt(map, new Date(timeL));
\r
465 if (trkptE != null) {
\r
466 map2.put(timeL, trkptE);
\r
469 map2.put(timeL, map.get(timeL));
\r
474 map2 = (TreeMap<Long, Element>) map.clone();
\r
478 System.out.println("GPX start time: "+ dfjp.format(new Date(gpxStartTime)) + "\t[GMT " + dfuk.format(new Date(gpxStartTime))+"]");
\r
479 System.out.println(" GPX end time: "+ dfjp.format(new Date(gpxEndTime)) + "\t[GMT " + dfuk.format(new Date(gpxEndTime))+"]");
\r
480 System.out.println("------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");
\r
481 System.out.println(" name | UpdateTime | GPStime | Latitude | Longitude | ele |magvar| km/h |");
\r
482 System.out.println("------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");
\r
483 proc(imgDir, delta, gpxStartTime, gpxEndTime, map, exif, gpx);
\r
484 System.out.println("------------------------|--------------------|--------------------|--------------|--------------|--------|------|------|");
\r
487 outputFile.getParentFile().mkdirs();
\r
488 DOMSource source = new DOMSource(gpx);
\r
489 FileOutputStream os = new FileOutputStream(outputFile);
\r
490 StreamResult result = new StreamResult(os);
\r
491 TransformerFactory transFactory = TransformerFactory.newInstance();
\r
492 Transformer transformer = transFactory.newTransformer();
\r
493 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
\r
494 transformer.setOutputProperty(OutputKeys.METHOD, "xml");
\r
495 transformer.transform(source, result);
\r
497 os = new FileOutputStream(outputFile);
\r
498 result = new StreamResult(os);
\r
499 transformer.transform(source, result);
\r
505 * @throws ParseException
\r
506 * @throws IOException
\r
507 * @throws ImageReadException
\r
508 * @throws ImageWriteException
\r
510 boolean proc(File dir, long delta, long gpxStartTime, long gpxEndTime, TreeMap<Long,Element> map, boolean exifWrite, Node gpx) throws ParseException, ImageReadException, IOException, ImageWriteException {
\r
511 DecimalFormat yearFormatter = new DecimalFormat("0000");
\r
512 DecimalFormat monthFormatter = new DecimalFormat("00");
\r
513 DecimalFormat dayFormatter = new DecimalFormat("00");
\r
515 boolean ret = false;
\r
516 File[] files = dir.listFiles(new JpegFileFilter());
\r
517 Arrays.sort(files, new FileSort());
\r
518 for (File image : files) {
\r
519 System.out.print(String.format("%-24s|", image.getName()));
\r
520 if (image.isDirectory()) {
\r
521 ret = proc(image, delta, gpxStartTime, gpxEndTime, map, exifWrite, gpx);
\r
525 String imageName = image.getName();
\r
526 if (!checkFile(imageName)) {
\r
527 System.out.println(String.format("%20s ", "it is not image file."));
\r
531 Date itime = new Date(image.lastModified());
\r
532 if (this.exifBase) {
\r
533 ImageMetadata meta = Imaging.getMetadata(image);
\r
534 JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;
\r
535 if (jpegMetadata == null) {
\r
536 System.out.println("'"+ image.getAbsolutePath() +"' にEXIF情報がありません");
\r
539 TiffImageMetadata exif = jpegMetadata.getExif();
\r
540 if (exif == null) {
\r
541 System.out.println("'"+ image.getAbsolutePath() +"' にEXIF情報がありません");
\r
544 String dateTimeOriginal = exif.getFieldValue(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL)[0];
\r
545 long lastModifyTime = (new SimpleDateFormat("yyyy:MM:dd HH:mm:ss")).parse(dateTimeOriginal).getTime();
\r
546 itime = new Date(lastModifyTime);
\r
549 // uktime <-- 画像撮影時刻に対応するGPX時刻
\r
550 Date uktime = new Date(itime.getTime() + delta);
\r
551 System.out.print(String.format("%20s ", dfjp.format(itime)));
\r
552 System.out.print(String.format("%20s|", dfjp.format(uktime)));
\r
553 if ((uktime.getTime() < gpxStartTime) || (uktime.getTime() > gpxEndTime)) {
\r
554 System.out.println(String.format("%20s ", "out of time."));
\r
555 if (!this.param_ImgOutputAll) {
\r
560 // 時刻uktimeにおける<magver>をtrkptに追加する
\r
561 Element trkptE = trkpt(map, uktime);
\r
562 String eleStr = "-";
\r
563 String magvarStr = "-";
\r
564 String speedStr = "-";
\r
565 double latitude = 90.5D;
\r
566 double longitude = 180.5D;
\r
567 TagTrkpt trkptT = null;
\r
568 if (trkptE == null) {
\r
569 System.out.println(String.format("%20s ", "Out of GPX logging time."));
\r
570 if (!this.param_ImgOutputAll) {
\r
575 trkptT = new TagTrkpt(trkptE);
\r
577 //Element wpt = createWptTag(image, uktime.getTime(), trkptE);
\r
578 //String latStr = trkptT.lat.toString();
\r
579 //String lonStr = trkptT.lon.toString();
\r
580 latitude = trkptT.lat;
\r
581 longitude = trkptT.lon;
\r
583 if (trkptT.eleStr != null) {
\r
584 eleStr = new String(trkptT.eleStr);
\r
587 if (trkptT.magvarStr != null) {
\r
588 magvarStr = new String(trkptT.magvarStr);
\r
591 if (trkptT.speedStr != null) {
\r
592 speedStr = new String(trkptT.speedStr);
\r
594 System.out.print(String.format("%-14s %-14s|", (new Double(latitude)).toString(), (new Double(longitude)).toString()));
\r
595 System.out.println(String.format("%8s|%6s|%6s|", eleStr, magvarStr, speedStr));
\r
599 FileOutputStream fos = null;
\r
603 TiffOutputSet outputSet = null;
\r
605 ImageMetadata meta = Imaging.getMetadata(image);
\r
606 JpegImageMetadata jpegMetadata = (JpegImageMetadata)meta;
\r
607 if (jpegMetadata != null) {
\r
608 TiffImageMetadata exif = jpegMetadata.getExif();
\r
609 if (exif != null) {
\r
610 outputSet = exif.getOutputSet();
\r
614 if (outputSet == null) {
\r
615 System.out.println("added : new tiff output set");
\r
616 outputSet = new TiffOutputSet();
\r
619 //---- EXIF_TAG_DATE_TIME_ORIGINAL / 「撮影日時/オリジナル画像の生成日時」----
\r
620 TiffOutputDirectory exifDir = outputSet.getOrCreateExifDirectory();
\r
622 Calendar cal = GregorianCalendar.getInstance();
\r
623 cal.setTime(uktime);
\r
624 exifDir.removeField(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
\r
625 exifDir.add(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL, new SimpleDateFormat("yyyy:MM:dd HH:mm:ss").format(cal.getTime()));
\r
628 //---- EXIF GPS_TIME_STAMP ----
\r
629 TiffOutputDirectory gpsDir = outputSet.getOrCreateGPSDirectory();
\r
631 Calendar cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC"));
\r
632 cal.setTime(uktime);
\r
633 final String yearStr = yearFormatter.format(cal.get(Calendar.YEAR));
\r
634 final String monthStr = monthFormatter.format(cal.get(Calendar.MONTH) + 1);
\r
635 final String dayStr = dayFormatter.format(cal.get(Calendar.DAY_OF_MONTH));
\r
636 final String dateStamp = yearStr +":"+ monthStr +":"+ dayStr;
\r
638 gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_TIME_STAMP);
\r
639 gpsDir.add(GpsTagConstants.GPS_TAG_GPS_TIME_STAMP,
\r
640 RationalNumber.valueOf(cal.get(Calendar.HOUR_OF_DAY)),
\r
641 RationalNumber.valueOf(cal.get(Calendar.MINUTE)),
\r
642 RationalNumber.valueOf(cal.get(Calendar.SECOND)));
\r
643 gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_DATE_STAMP);
\r
644 gpsDir.add(GpsTagConstants.GPS_TAG_GPS_DATE_STAMP, dateStamp);
\r
647 if (trkptE != null) {
\r
648 //---- EXIF GPS elevation/ALTITUDE ----
\r
649 if (eleStr.equals("-") == false) {
\r
650 final double altitude = Double.parseDouble(eleStr);
\r
651 gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_ALTITUDE);
\r
652 gpsDir.add(GpsTagConstants.GPS_TAG_GPS_ALTITUDE, RationalNumber.valueOf(altitude));
\r
655 //---- EXIF GPS magvar/IMG_DIRECTION ----
\r
656 if (magvarStr.equals("-") == false) {
\r
657 final double magvar = Double.parseDouble(magvarStr);
\r
658 gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);
\r
659 gpsDir.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION, RationalNumber.valueOf(magvar));
\r
662 //---- EXIF GPS_ ----
\r
663 final String longitudeRef = (longitude < 0 ? "W" : "E");
\r
664 longitude = Math.abs(longitude);
\r
665 final String latitudeRef = (latitude < 0 ? "S" : "N");
\r
666 latitude = Math.abs(latitude);
\r
668 gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF);
\r
669 gpsDir.add(GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF, longitudeRef);
\r
670 gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF);
\r
671 gpsDir.add(GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF, latitudeRef);
\r
673 double value = longitude;
\r
674 final double longitudeDegrees = (long) value;
\r
677 final double longitudeMinutes = (long) value;
\r
680 final double longitudeSeconds = value;
\r
681 gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_LONGITUDE);
\r
682 gpsDir.add(GpsTagConstants.GPS_TAG_GPS_LONGITUDE,
\r
683 RationalNumber.valueOf(longitudeDegrees),
\r
684 RationalNumber.valueOf(longitudeMinutes),
\r
685 RationalNumber.valueOf(longitudeSeconds));
\r
688 double value = latitude;
\r
689 final double latitudeDegrees = (long) value;
\r
692 final double latitudeMinutes = (long) value;
\r
695 final double latitudeSeconds = value;
\r
696 gpsDir.removeField(GpsTagConstants.GPS_TAG_GPS_LATITUDE);
\r
697 gpsDir.add(GpsTagConstants.GPS_TAG_GPS_LATITUDE,
\r
698 RationalNumber.valueOf(latitudeDegrees),
\r
699 RationalNumber.valueOf(latitudeMinutes),
\r
700 RationalNumber.valueOf(latitudeSeconds));
\r
704 ExifRewriter rewriter = new ExifRewriter();
\r
706 fos = new FileOutputStream(new File(outDir, imageName));
\r
707 rewriter.updateExifMetadataLossy(image, fos, outputSet);
\r
715 if (Boolean.parseBoolean(params.getProperty(AppParameters.GPX_OUTPUT_WPT))) {
\r
716 if (trkptT != null) {
\r
717 Element temp = createWptTag(image, itime.getTime(), trkptT.trkpt);
\r
718 gpx.appendChild(temp);
\r
723 if (this.param_ImgOutputAll) {
\r
724 // EXIFの変換を伴わない単純なファイルコピー
\r
725 FileInputStream sStream = new FileInputStream(image);
\r
726 FileInputStream dStream = new FileInputStream(new File(outDir, imageName));
\r
727 FileChannel srcChannel = sStream.getChannel();
\r
728 FileChannel destChannel = dStream.getChannel();
\r
730 srcChannel.transferTo(0, srcChannel.size(), destChannel);
\r
733 srcChannel.close();
\r
734 destChannel.close();
\r
744 static Document document;
\r
748 * 2012-06-10T05:09:46Z (日本時間の'2012-06-10T14:09:46')
\r
750 public static final String TIME_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ss";
\r
751 public static SimpleDateFormat dfjp = new SimpleDateFormat(TIME_FORMAT_STRING);
\r
752 public static SimpleDateFormat dfuk = new SimpleDateFormat(TIME_FORMAT_STRING +"'Z'");
\r
755 * XMLエレメント<trkpt>をTIMEでキー付したHashMapを生成する<br>
\r
758 * <trkpt lat="34.976635" lon="138.466228">
\r
759 * <ele>267.291</ele>
\r
760 * <magvar>359</magvar>
\r
762 * <time>2016-07-02T08:25:18Z</time>
\r
768 * @throws ParseException
\r
770 public void trkptMap(Element trkseg, TreeMap<Long,Element> map) throws ParseException {
\r
771 dfuk.setTimeZone(TimeZone.getTimeZone("GMT"));
\r
773 NodeList nodes2 = trkseg.getChildNodes();
\r
774 for (int i2=0; i2 < nodes2.getLength(); i2++) {
\r
775 Node node3 = nodes2.item(i2);
\r
776 if (node3.getNodeName().equals("trkpt")) {
\r
777 Element trkpt = (Element) node3;
\r
779 NodeList nodes3 = trkpt.getChildNodes();
\r
780 for (int i3=0; i3 < nodes3.getLength(); i3++) {
\r
781 Node node4 = nodes3.item(i3);
\r
782 if (node4.getNodeName().equals("time")) {
\r
783 Element time = (Element) node4;
\r
784 NodeList nodes4 = time.getChildNodes(); // 子ノードを取得
\r
785 for (int i4=0; i4< nodes4.getLength(); i4++) {
\r
786 Node node5 = nodes4.item(i4);
\r
787 if (node5 != null) {
\r
788 if (node5.getNodeType() == Node.TEXT_NODE) {
\r
789 String timeStr = node5.getNodeValue();
\r
790 long t = dfuk.parse(timeStr).getTime();
\r
791 map.put(t, getCopy(trkseg.getOwnerDocument(), trkpt));
\r
803 * <trkpt lat="35.32123832" lon="139.56965631">
\r
804 * <ele>47.20000076293945</ele>
\r
805 * <time>2012-06-15T03:00:29Z</time>
\r
810 * @param jptime 画像ファイルの撮影日時 ミリ秒(日本時間)
\r
811 * @throws ParseException
\r
813 public Element trkpt(TreeMap<Long,Element> map, Date jptime) throws ParseException {
\r
814 // 指定した時刻のエレメント(imaTrkpt)を取得する
\r
815 Element imaE = getTrkpt(map, jptime);
\r
816 if (imaE != null) {
\r
817 Element maeE = getMaeTrkpt(map, new TagTrkpt(imaE));
\r
818 if (maeE != null) {
\r
819 Complementation comp = new Complementation(imaE, maeE);
\r
822 // 直前の位置と、現在地から進行方向を求める
\r
823 // 経度(longitude)と経度から進行方向を求める
\r
824 if (Complementation.param_GpxOverwriteMagvar) {
\r
825 comp.complementationMagvar();
\r
828 // 緯度・経度と時間差から速度(km/h)を求める
\r
829 if (Complementation.param_GpxOutputSpeed) {
\r
830 comp.complementationSpeed();
\r
832 imaE = (Element)(comp.imaTag.trkpt.cloneNode(true));
\r
839 * [map]から指定した時刻の<trkpt>エレメントを取り出す。
\r
840 * GPX時刻との差が10分以上は無効
\r
845 * @throws ParseException
\r
847 public Element getTrkpt(TreeMap<Long,Element> map, Date jptime) throws ParseException {
\r
848 long sa = 2L * 3600000L;
\r
849 long jpt = jptime.getTime();
\r
851 Element ret = null;
\r
853 Set<Long> keySet = map.keySet(); //すべてのキー値を取得
\r
854 Iterator<Long> keyIte = keySet.iterator();
\r
855 while (keyIte.hasNext()) { //ループ。反復子iteratorによる キー 取得
\r
856 Long time = keyIte.next();
\r
859 if (Math.abs(jpt - t) < sa) {
\r
860 sa = Math.abs(jpt - t);
\r
861 ret = map.get(time);
\r
865 if (sa < (60000L * 10L)) {
\r
866 // GPX時刻との差が10分以内なら有効
\r
872 public Element getMaeTrkpt(TreeMap<Long,Element> map, TagTrkpt imaTrkpt) throws ParseException {
\r
873 Element ret = null;
\r
874 long diffTime = 2L * 3600000L; // 2時間
\r
875 long jpt = imaTrkpt.time.getTime() - DIFF_MAE_TIME;
\r
877 Set<Long> keySet = map.keySet(); //すべてのキー値を取得
\r
878 Iterator<Long> keyIte = keySet.iterator();
\r
879 while (keyIte.hasNext()) { //ループ。反復子iteratorによる キー 取得
\r
880 Long time = keyIte.next();
\r
883 if (Math.abs(jpt - t) < diffTime) {
\r
884 diffTime = Math.abs(jpt - t);
\r
885 ret = map.get(time);
\r
889 if (diffTime < (60000L * 10L)) {
\r
890 // GPX時刻との差が10分以内なら有効
\r
891 if (diffTime < (imaTrkpt.time.getTime() - 1000)) {
\r
892 // 元の時刻との差が1秒以上あること
\r
900 * 対象は '*.JPG' のみ対象とする
\r
904 public static boolean checkFile(String name) {
\r
905 return ((name != null) && name.toUpperCase().endsWith(".JPG"));
\r
909 * <wpt lat="35.25714922" lon="139.15490497">
\r
910 * <ele>62.099998474121094</ele>
\r
911 * <time>2012-06-11T00:44:38Z</time>
\r
912 * <name><![CDATA[写真]]></name>
\r
913 * <link href="2012-06-11_09-44-38.jpg">
\r
914 * <text>2012-06-11_09-44-38.jpg</text>
\r
918 * <trkpt lat="35.32123832" lon="139.56965631">
\r
919 * <ele>47.20000076293945</ele>
\r
920 * <time>2012-06-15T03:00:29Z</time>
\r
928 public Element createWptTag(File iFile, long timestamp, Element trkpt) {
\r
929 Element wpt = document.createElement("wpt");
\r
931 NamedNodeMap nodeMap = trkpt.getAttributes();
\r
932 if (null != nodeMap) {
\r
933 for (int j=0; j < nodeMap.getLength(); j++ ) {
\r
934 switch (nodeMap.item(j).getNodeName()) {
\r
936 String lat = nodeMap.item(j).getNodeValue();
\r
937 wpt.setAttribute("lat", lat);
\r
940 String lon = nodeMap.item(j).getNodeValue();
\r
941 wpt.setAttribute("lon", lon);
\r
947 NodeList nodes1 = trkpt.getChildNodes();
\r
948 for (int i1=0; i1 < nodes1.getLength(); i1++) {
\r
949 Node node1 = nodes1.item(i1);
\r
950 NodeList nodes2 = node1.getChildNodes();
\r
951 switch (node1.getNodeName()) {
\r
953 for (int i2=0; i2 < nodes2.getLength(); i2++) {
\r
954 Node node2 = nodes2.item(i2);
\r
955 if (node2 != null) {
\r
956 if (node2.getNodeType() == Node.TEXT_NODE) {
\r
957 String eleStr = node2.getNodeValue();
\r
958 Element eleE = document.createElement("ele");
\r
959 eleE.setTextContent(eleStr);
\r
960 wpt.appendChild(eleE);
\r
966 for (int i2=0; i2 < nodes2.getLength(); i2++) {
\r
967 Node node2 = nodes2.item(i2);
\r
968 if (node2 != null) {
\r
969 if (node2.getNodeType() == Node.TEXT_NODE) {
\r
970 String timeStr = node2.getNodeValue();
\r
971 Element timeE = document.createElement("time");
\r
972 timeE.setTextContent(timeStr);
\r
973 wpt.appendChild(timeE);
\r
979 for (int i2=0; i2 < nodes2.getLength(); i2++) {
\r
980 Node node2 = nodes2.item(i2);
\r
981 if (node2 != null) {
\r
982 if (node2.getNodeType() == Node.TEXT_NODE) {
\r
983 String magvarStr = node2.getNodeValue();
\r
984 Element magvarE = document.createElement("magvar");
\r
985 magvarE.setTextContent(magvarStr);
\r
986 wpt.appendChild(magvarE);
\r
992 for (int i2=0; i2 < nodes2.getLength(); i2++) {
\r
993 Node node2 = nodes2.item(i2);
\r
994 if (node2 != null) {
\r
995 if (node2.getNodeType() == Node.TEXT_NODE) {
\r
996 String speedStr = node2.getNodeValue();
\r
997 Element speedE = document.createElement("speed");
\r
998 speedE.setTextContent(speedStr);
\r
999 wpt.appendChild(speedE);
\r
1007 Element name = document.createElement("name");
\r
1008 name.appendChild(document.createCDATASection("写真"));
\r
1009 wpt.appendChild(name);
\r
1011 Element link = document.createElement("link");
\r
1012 link.setAttribute("href", getShortPathName(imgDir, iFile));
\r
1013 Element text = document.createElement("text");
\r
1014 text.setTextContent(iFile.getName());
\r
1015 link.appendChild(text);
\r
1016 wpt.appendChild(link);
\r
1021 static String getShortPathName(File dir, File iFile) {
\r
1022 String dirPath = dir.getAbsolutePath();
\r
1023 String filePath = iFile.getAbsolutePath();
\r
1024 if (filePath.startsWith(dirPath)) {
\r
1025 return filePath.substring(dirPath.length()+1);
\r
1032 public static Element getCopy(Document doc, Node node) {
\r
1033 Element root = doc.createElement(node.getNodeName());
\r
1035 NamedNodeMap nodeMap = node.getAttributes();
\r
1036 if (null != nodeMap) {
\r
1037 for (int j=0; j < nodeMap.getLength(); j++ ) {
\r
1038 root.setAttribute(nodeMap.item(j).getNodeName(), nodeMap.item(j).getNodeValue());
\r
1042 NodeList nodes = node.getChildNodes();
\r
1043 for (int i=0; i < nodes.getLength(); i++) {
\r
1044 Node node2 = nodes.item(i);
\r
1045 if (node2.getNodeType() == Node.ELEMENT_NODE) {
\r
1046 root.appendChild(getCopy(doc, node2));
\r
1048 else if (node2.getNodeType() == Node.TEXT_NODE) {
\r
1049 String str = node2.getNodeValue();
\r
1050 Text textContents = doc.createTextNode(str);
\r
1051 root.appendChild(textContents);
\r
1053 else if (node2.getNodeType() == Node.CDATA_SECTION_NODE) {
\r
1054 String str = node2.getNodeValue();
\r
1055 CDATASection cdataSection = doc.createCDATASection(str);
\r
1056 root.appendChild(cdataSection);
\r
1063 * ファイル名の順序に並び替えるためのソートクラス
\r
1067 static class FileSort implements Comparator<File>{
\r
1069 public int compare(File src, File target){
\r
1070 int diff = src.getName().compareTo(target.getName());
\r
1079 class JpegFileFilter implements FilenameFilter{
\r
1080 public boolean accept(File dir, String name) {
\r
1081 if (name.toUpperCase().matches(".*\\.JPG$")) {
\r