OSDN Git Service

0696de10413127e08d8a1eea6cb536543d06f2a1
[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.ParseException;\r
4 import java.text.SimpleDateFormat;\r
5 import java.util.ArrayList;\r
6 import java.util.Arrays;\r
7 import java.util.Comparator;\r
8 import java.util.Date;\r
9 import java.util.HashMap;\r
10 import java.util.Iterator;\r
11 import java.util.Locale;\r
12 import java.util.Set;\r
13 import java.util.logging.LogManager;\r
14 import java.util.logging.Logger;\r
15 \r
16 import javax.xml.parsers.*;\r
17 import javax.xml.transform.OutputKeys;\r
18 import javax.xml.transform.Transformer;\r
19 import javax.xml.transform.TransformerConfigurationException;\r
20 import javax.xml.transform.TransformerException;\r
21 import javax.xml.transform.TransformerFactory;\r
22 import javax.xml.transform.dom.DOMSource;\r
23 import javax.xml.transform.stream.StreamResult;\r
24 \r
25 import org.w3c.dom.*;\r
26 import org.xml.sax.SAXException;\r
27 \r
28 public class ImportPicture {\r
29         public static File gpxDir = new File(".");\r
30         \r
31     /**\r
32          * ログ設定プロパティファイルのファイル内容\r
33          */\r
34         protected static final String LOGGING_PROPERTIES_DATA\r
35             = "handlers=java.util.logging.ConsoleHandler\n"\r
36             + ".level=FINEST\n"\r
37             + "java.util.logging.ConsoleHandler.level=INFO\n"\r
38             + "java.util.logging.ConsoleHandler.formatter=hayashi.yuu.tools.logger.YuuLogFormatter";\r
39 \r
40         /**\r
41          * static initializer によるログ設定の初期化\r
42          */\r
43     public static final Logger logger = Logger.getLogger("SampleLogging");\r
44     static {\r
45         InputStream inStream = null;\r
46         try {\r
47             inStream = new ByteArrayInputStream(LOGGING_PROPERTIES_DATA.getBytes("UTF-8"));\r
48             try {\r
49                 LogManager.getLogManager().readConfiguration(inStream);\r
50                 logger.config("ログ設定: LogManagerを設定しました。");\r
51             }\r
52             catch (IOException e) {\r
53                 logger.warning("ログ設定: LogManager設定の際に例外が発生しました。:" + e.toString());\r
54             }\r
55         }\r
56         catch (UnsupportedEncodingException e) {\r
57             logger.severe("ログ設定: UTF-8エンコーディングがサポートされていません。:" + e.toString());\r
58         }\r
59         finally {\r
60             try {\r
61                 if (inStream != null) {\r
62                         inStream.close();\r
63                 }\r
64             } catch (IOException e) {\r
65                 logger.warning("ログ設定: ログ設定プロパティファイルのストリームクローズ時に例外が発生しました。:"+ e.toString());\r
66             }\r
67         }\r
68     }\r
69 \r
70         /** メイン\r
71          * 画像ファイルをGPXファイルに取り込みます。\r
72          * \r
73          * ・画像ファイルの更新日付をその画像の撮影日時とします。(Exi情報は無視します)\r
74          *    ※ 対象とするファイルは'*.jpg'のみ\r
75          * ・精確な時刻との時差を入力することで、撮影日時を補正します。\r
76          * ・画像ファイルの更新日付リストをCSV形式のファイルとして出力する。\r
77          * ・・結果は、取り込み元のGPXファイルとは別に、元ファイル名にアンダーバー「_」を付加した.ファイルに出力します。\r
78          *\r
79          * argv[-] = dummy\r
80          * argv[0] = 画像ファイルが格納されているディレクトリ\r
81          * argv[1] = 時刻補正の基準とする画像ファイル\r
82          * argv[2] = 基準画像ファイルの精確な撮影日時 "yyyy-MM-dd'T'HH:mm:ss"\r
83          * argv[3] = 撮影位置をロギングしたGPXファイル\r
84          * \r
85          * @throws IOException\r
86          */\r
87         public static void main(String[] argv) throws IOException\r
88         {\r
89                 Date jptime;\r
90                 \r
91                 if (argv.length > 0) {\r
92                         gpxDir = new File(argv[0]);\r
93                 }\r
94 \r
95                 if (argv.length < 3) {\r
96                         System.out.println("> java -jar importPicture.jar <targetDir> <time base image> <time> (gpx)");\r
97                         System.out.println("> java -jar importPicture.jar . IMG_01234.JPG 2012-06-15T12:52:22");\r
98                 }\r
99 \r
100                 // 基準時刻ファイルの「更新日時」を使って時刻合わせを行う。\r
101                 File baseFile = new File(gpxDir, argv[1]);\r
102                 jptime = new Date(baseFile.lastModified());\r
103                 \r
104                 // 第5引数が指定されなければ、指定されたディレクトリ内のGPXファイルすべてを対象とする\r
105                 ArrayList<File> gpxFiles = new ArrayList<File>();\r
106                 if (argv.length > 3) {\r
107                         gpxFiles.add(new File(gpxDir, argv[3]));\r
108                 }\r
109                 else {\r
110                         File[] files = gpxDir.listFiles();\r
111                         for (int i=0; i < files.length; i++) {\r
112                                 if (files[i].isFile()) {\r
113                                         String filename = files[i].getName().toUpperCase();\r
114                                         if (filename.endsWith(".GPX")) {\r
115                                                 if (!filename.endsWith("_.GPX")) {\r
116                                                         gpxFiles.add(files[i]);\r
117                                                 }\r
118                                         }\r
119                                 }\r
120                         }\r
121                 }\r
122 \r
123                 \r
124                 /**\r
125                  *\r
126                         <wpt lat="35.25714922" lon="139.15490497">\r
127                                 <ele>62.099998474121094</ele>\r
128                                 <time>2012-06-11T00:44:38Z</time>\r
129                                 <hdop>0.75</hdop>\r
130                                 <name><![CDATA[写真]]></name>\r
131                                 <cmt><![CDATA[精度: 3.0m]]></cmt>\r
132                                 <link href="2012-06-11_09-44-38.jpg">\r
133                                         <text>2012-06-11_09-44-38.jpg</text>\r
134                                 </link>\r
135                                 <sat>9</sat>\r
136                         </wpt>\r
137                  */\r
138                 DocumentBuilderFactory factory;\r
139                 DocumentBuilder        builder;\r
140                 Node gpx;\r
141                 \r
142                 try {\r
143                         for (int gpxCnt = 0; gpxCnt < gpxFiles.size(); gpxCnt++) {\r
144                                 File gpxFile = gpxFiles.get(gpxCnt);\r
145                                 String fileName = gpxFile.getName();\r
146                                 String iStr = fileName.substring(0, fileName.length() - 4);\r
147         \r
148                                 File outputFile = new File(gpxFile.getParent(), iStr +"_.gpx");\r
149                                 System.out.println(iStr + " => "+ outputFile.getName());\r
150 \r
151                                 factory = DocumentBuilderFactory.newInstance();\r
152                                 builder = factory.newDocumentBuilder();\r
153                                 factory.setIgnoringElementContentWhitespace(true);\r
154                                 factory.setIgnoringComments(true);\r
155                                 factory.setValidating(true);\r
156 \r
157                                 // GPX file --> Node root\r
158                                 DOMImplementation domImpl = builder.getDOMImplementation();\r
159                                 document = domImpl.createDocument("", "gpx", null);\r
160 \r
161                                 /*\r
162                                 <gpx>\r
163                                         <trk>\r
164                                                 <name><![CDATA[Tracked with OSMTracker for Android?]]></name>\r
165                                                 <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
166                                                 <trkseg>\r
167                                                         <trkpt lat="35.32123832" lon="139.56965631">\r
168                                                                 <ele>47.20000076293945</ele>\r
169                                                                 <time>2012-06-15T03:00:29Z</time>\r
170                                                                 <hdop>0.5</hdop>\r
171                                                         </trkpt>\r
172                                                 </trkseg>\r
173                                         </trk>\r
174                                         <wpt lat="35.2564461" lon="139.15437809">\r
175                                         </wpt>\r
176                                 </gpx>\r
177                                 */\r
178                                 Element trk = null;\r
179                                 gpx    = builder.parse(gpxFile).getFirstChild();\r
180                                 NodeList nodes = gpx.getChildNodes();\r
181                                 for (int i=0; i < nodes.getLength(); i++) {\r
182                                         Node node2 = nodes.item(i);\r
183                                         if (node2.getNodeName().equals("trk")) {\r
184                                                 trk = (Element) node2;\r
185                                         }\r
186                                 }\r
187 \r
188                                 if (trk != null) {\r
189                                         HashMap<Long,Element> map = trkptMap(trk);\r
190 \r
191                                         String timeStr = argv[2];\r
192                                         try {\r
193                                                 Date t = dfjp.parse(timeStr);\r
194                                                 long delta = t.getTime() - jptime.getTime();\r
195                                                 \r
196                                                 /*\r
197                                                  * GPXへ割りつける開始時刻と終了時刻を求める\r
198                                                  */\r
199                                                 long gpxStartTime = (new Date()).getTime();     // 対象とする開始時刻\r
200                                                 long gpxEndTime = 0L;                                                   // 対象とする終了時刻\r
201                                                 Set<Long> keySet = map.keySet();  //すべてのキー値を取得\r
202                                                 for (Iterator<Long> keyIte = keySet.iterator(); keyIte.hasNext();) {\r
203                                                         Long timeLong = (Long)keyIte.next();\r
204                                                         long gpxTime = timeLong.longValue();\r
205                                                         if (gpxStartTime > gpxTime) {\r
206                                                                 gpxStartTime = gpxTime;\r
207                                                         }\r
208                                                         if (gpxEndTime < gpxTime) {\r
209                                                                 gpxEndTime = gpxTime;\r
210                                                         }\r
211                                                 }\r
212                                                 \r
213                                                 System.out.println("           時差: "+ (delta / 1000) +"(sec)");\r
214                                                 System.out.println("    Target GPX: ["+ gpxFile.getName() +"]");\r
215                                                 System.out.println("GPX start time: "+ dfjp.format(new Date(gpxStartTime).getTime()));\r
216                                                 System.out.println("  GPX end time: "+ dfjp.format(new Date(gpxEndTime).getTime()));\r
217                                                 System.out.println();\r
218                                                 System.out.println("------------|--------------------|--------------------|--------|-------|--------");\r
219                                                 System.out.println(" name       | UpdateTime         | GPStime            | LAT    | LON   | ele");\r
220                                                 System.out.println("------------|--------------------|--------------------|--------|-------|--------");\r
221                                                 proc(gpxDir, delta, gpxStartTime, gpxEndTime, map, gpx);\r
222                                                 System.out.println("------------|--------------------|--------------------|--------|-------|--------");\r
223                                         }\r
224                                         catch (ParseException e) {\r
225                                                 System.out.println("'"+ timeStr +"' の書式が違います("+ TIME_FORMAT_STRING +")");\r
226                                         }\r
227                                 }\r
228 \r
229                                 // 出力\r
230                                 DOMSource source = new DOMSource(gpx);\r
231                                 FileOutputStream os = new FileOutputStream(outputFile);\r
232                                 StreamResult result = new StreamResult(os);\r
233                                 TransformerFactory transFactory = TransformerFactory.newInstance();\r
234                                 Transformer transformer = transFactory.newTransformer();\r
235                                 transformer.setOutputProperty(OutputKeys.INDENT, "yes");\r
236                                 transformer.setOutputProperty(OutputKeys.METHOD, "xml");\r
237                                 transformer.transform(source, result);\r
238                         }\r
239                 }\r
240                 catch (ParserConfigurationException e) {\r
241                         e.printStackTrace();\r
242                 }\r
243                 catch (SAXException e) {\r
244                         e.printStackTrace();\r
245                 }\r
246                 catch (TransformerConfigurationException e) {\r
247                         e.printStackTrace();\r
248                 }\r
249                 catch (TransformerException e) {\r
250                         e.printStackTrace();\r
251                 } catch (ParseException e) {\r
252                         e.printStackTrace();\r
253                 }\r
254                 finally {\r
255                 }\r
256         }\r
257         \r
258         /**\r
259          * 再帰メソッド\r
260          * @throws ParseException \r
261          */\r
262         static void proc(File dir, long delta, long gpxStartTime, long gpxEndTime, HashMap<Long,Element> map, Node gpx) throws ParseException {\r
263                 File[] files = dir.listFiles();\r
264                 Arrays.sort(files, new FileSort());\r
265                 for (File image : files) {\r
266                         if (image.isDirectory()) {\r
267                                 proc(image, delta, gpxStartTime, gpxEndTime, map, gpx);\r
268                         }\r
269                         else {\r
270                                 String imageName = image.getName();\r
271                                 if (checkFile(imageName)) {\r
272                                         Date itime = new Date(image.lastModified());\r
273                                         Date uktime = new Date(itime.getTime() + delta);\r
274                                         if ((uktime.getTime() >= gpxStartTime) && (uktime.getTime() <= gpxEndTime)) {\r
275                                                 Element trkpt = trkpt(map, uktime);\r
276                                                 if (trkpt != null) {\r
277                                                         System.out.print(String.format("%12s ", image.getName()));\r
278                                                         System.out.print(String.format("%19s ", dfjp.format(itime)));\r
279                                                         System.out.print(String.format("%19s ", dfjp.format(uktime)));\r
280 \r
281                                                         Element wpt = createWptTag(image, uktime.getTime(), trkpt);\r
282                                                         System.out.print(String.format("%12s ", wpt.getAttribute("lat")));\r
283                                                         System.out.print(String.format("%12s "+ wpt.getAttribute("lon")));\r
284                                                         \r
285                                                         NodeList nodes = wpt.getChildNodes();      // 子ノードを取得\r
286                                                         for (int i4=0; i4< nodes.getLength(); i4++) {\r
287                                                                 Node node = nodes.item(i4);\r
288                                                                 if (node != null) {\r
289                                                                         if (node.getNodeType() == Node.TEXT_NODE) {\r
290                                                                                 String eleStr = node.getNodeValue();\r
291                                                                                 System.out.println(eleStr);\r
292                                                                         }\r
293                                                             }\r
294                                                         }\r
295 \r
296                                                         Element temp = getCopy(gpx.getOwnerDocument(), wpt);\r
297                                                         gpx.appendChild(temp);\r
298                                                 }\r
299                                         }\r
300                                 }\r
301                         }\r
302                 }\r
303 \r
304         }\r
305 \r
306         static Document document;\r
307 \r
308         /**\r
309          * 2012-06-10T05:09:46Z  (日本時間の'2012-06-10T14:09:46')\r
310          */\r
311         public static final String TIME_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ss";\r
312         public static SimpleDateFormat dfjp = new SimpleDateFormat(TIME_FORMAT_STRING);\r
313         public static SimpleDateFormat dfuk = new SimpleDateFormat(TIME_FORMAT_STRING +"'Z'", Locale.UK);\r
314 \r
315         /**\r
316          * XMLエレメント<trkpt>をTIMEでキー付したHashMapを生成する<br>\r
317          * \r
318          *      <trk><trkseg><trkpt><time>2014-01-01T00:59:09Z</time></trkpt></trkseg></trk>\r
319          * \r
320          * @param trk\r
321          * @return\r
322          * @throws ParseException\r
323          */\r
324         public static HashMap<Long,Element> trkptMap(Element trk) throws ParseException {\r
325                 HashMap<Long,Element> map = new HashMap<Long,Element>();\r
326 \r
327                 NodeList nodes1 = trk.getChildNodes();\r
328                 for (int i1=0; i1 < nodes1.getLength(); i1++) {\r
329                         Node node2 = nodes1.item(i1);\r
330                         if (node2.getNodeName().equals("trkseg")) {\r
331                                 Element trkseg = (Element) node2;\r
332                                 NodeList nodes2 = trkseg.getChildNodes();\r
333                                 for (int i2=0; i2 < nodes2.getLength(); i2++) {\r
334                                         Node node3 = nodes2.item(i2);\r
335                                         if (node3.getNodeName().equals("trkpt")) {\r
336                                                 Element trkpt = (Element) node3;\r
337 \r
338                                                 NodeList nodes3 = trkpt.getChildNodes();\r
339                                                 for (int i3=0; i3 < nodes3.getLength(); i3++) {\r
340                                                         Node node4 = nodes3.item(i3);\r
341                                                         if (node4.getNodeName().equals("time")) {\r
342                                                                 Element time = (Element) node4;\r
343                                                                 NodeList nodes4 = time.getChildNodes();      // 子ノードを取得\r
344                                                                 for (int i4=0; i4< nodes4.getLength(); i4++) {\r
345                                                                         Node node5 = nodes4.item(i4);\r
346                                                                         if (node5 != null) {\r
347                                                                                 if (node5.getNodeType() == Node.TEXT_NODE) {\r
348                                                                                         String timeStr = node5.getNodeValue();\r
349                                                                                         long t = dfuk.parse(timeStr).getTime() + (9L * 3600000L);\r
350                                                                                         map.put(new Long(t), getCopy(trk.getOwnerDocument(), trkpt));\r
351                                                                                 }\r
352                                                                     }\r
353                                                                 }\r
354                                                         }\r
355                                                 }\r
356                                         }\r
357                                 }\r
358                         }\r
359                 }\r
360                 return map;\r
361         }\r
362 \r
363         public static Element trkpt(HashMap<Long,Element> map, Date jptime) throws ParseException {\r
364                 long sa = 2L * 3600000L;\r
365                 long jpt = jptime.getTime();\r
366                 Element ret = null;\r
367 \r
368                 Set<Long> keySet = map.keySet();  //すべてのキー値を取得\r
369                 Iterator<Long> keyIte = keySet.iterator();\r
370                 while (keyIte.hasNext()) {    //ループ。反復子iteratorによる キー 取得\r
371                         Long time = keyIte.next();\r
372                         long t = time.longValue();\r
373                         if (Math.abs(jpt - t) < sa) {\r
374                                 sa = Math.abs(jpt - t);\r
375                                 ret = map.get(time);\r
376                         }\r
377                 }\r
378 \r
379                 if (sa < (60000L * 10L)) {\r
380                 System.out.println(dfuk.format(jpt) +" ("+ sa +")");\r
381                 return ret;\r
382         }\r
383                 return null;\r
384         }\r
385 \r
386         /**\r
387          * 対象は '*.JPG' のみ対象とする\r
388          */\r
389         public static boolean checkFile(String name) {\r
390                 if (name != null && name.toUpperCase().endsWith(".JPG")) {\r
391                         return true;\r
392                 }\r
393                 return false;\r
394         }\r
395 \r
396         /**\r
397          *      <wpt lat="35.25714922" lon="139.15490497">\r
398          *              <ele>62.099998474121094</ele>\r
399          *              <time>2012-06-11T00:44:38Z</time>\r
400          *              <name><![CDATA[写真]]></name>\r
401          *              <link href="2012-06-11_09-44-38.jpg">\r
402          *                      <text>2012-06-11_09-44-38.jpg</text>\r
403          *              </link>\r
404          *      </wpt>\r
405          *\r
406          *      <trkpt lat="35.32123832" lon="139.56965631">\r
407          *              <ele>47.20000076293945</ele>\r
408          *              <time>2012-06-15T03:00:29Z</time>\r
409          *      </trkpt>\r
410          *\r
411          * @param dir\r
412          * @param csvfile\r
413          * @param delta\r
414          * @throws IOException\r
415          */\r
416         public static Element createWptTag(File iFile, long timestamp, Element trkpt) {\r
417                 Element wpt = document.createElement("wpt");\r
418 \r
419                 NamedNodeMap nodeMap = trkpt.getAttributes();\r
420                 if (null != nodeMap) {\r
421                         for (int j=0; j < nodeMap.getLength(); j++ ) {\r
422                                 if (nodeMap.item(j).getNodeName().equals("lat")) {\r
423                                         String lat = nodeMap.item(j).getNodeValue();\r
424                                         wpt.setAttribute("lat", lat);\r
425                                 }\r
426                                 else if (nodeMap.item(j).getNodeName().equals("lon")) {\r
427                                         String lon = nodeMap.item(j).getNodeValue();\r
428                                         wpt.setAttribute("lon", lon);\r
429                                 }\r
430                         }\r
431                 }\r
432 \r
433                 NodeList nodes1 = trkpt.getChildNodes();\r
434                 for (int i1=0; i1 < nodes1.getLength(); i1++) {\r
435                         Node node1 = nodes1.item(i1);\r
436                         if (node1.getNodeName().equals("ele")) {\r
437                                 NodeList nodes2 = node1.getChildNodes();\r
438                                 for (int i2=0; i2 < nodes2.getLength(); i2++) {\r
439                                         Node node2 = nodes2.item(i2);\r
440                                         if (node2 != null) {\r
441                                                 if (node2.getNodeType() == Node.TEXT_NODE) {\r
442                                                         String eleStr = node2.getNodeValue();\r
443                                                         Element eleE = document.createElement("ele");\r
444                                                         eleE.setTextContent(eleStr);\r
445                                                         wpt.appendChild(eleE);\r
446                                                 }\r
447                                     }\r
448                                 }\r
449                         }\r
450                         else if (node1.getNodeName().equals("time")) {\r
451                                 NodeList nodes2 = node1.getChildNodes();\r
452                                 for (int i2=0; i2 < nodes2.getLength(); i2++) {\r
453                                         Node node2 = nodes2.item(i2);\r
454                                         if (node2 != null) {\r
455                                                 if (node2.getNodeType() == Node.TEXT_NODE) {\r
456                                                         String timeStr = node2.getNodeValue();\r
457                                                         Element timeE = document.createElement("time");\r
458                                                         timeE.setTextContent(timeStr);\r
459                                                         wpt.appendChild(timeE);\r
460                                                 }\r
461                                     }\r
462                                 }\r
463                         }\r
464                 }\r
465 \r
466                 Element name = document.createElement("name");\r
467                 name.appendChild(document.createCDATASection("写真"));\r
468                 wpt.appendChild(name);\r
469 \r
470                 Element link = document.createElement("link");\r
471                 link.setAttribute("href", getShortPathName(gpxDir, iFile));\r
472                 Element text = document.createElement("text");\r
473                 text.setTextContent(iFile.getName());\r
474                 link.appendChild(text);\r
475                 wpt.appendChild(link);\r
476 \r
477                 return wpt;\r
478         }\r
479         \r
480         static String getShortPathName(File dir, File iFile) {\r
481                 String dirPath = dir.getAbsolutePath();\r
482                 String filePath = iFile.getAbsolutePath();\r
483                 if (filePath.startsWith(dirPath)) {\r
484                         return filePath.substring(dirPath.length()+1);\r
485                 }\r
486                 else {\r
487                         return filePath;\r
488                 }\r
489         }\r
490 \r
491         public static Element getCopy(Document doc, Node node) {\r
492                 Element root = doc.createElement(node.getNodeName());\r
493 \r
494                 NamedNodeMap nodeMap = node.getAttributes();\r
495                 if (null != nodeMap) {\r
496                         for (int j=0; j < nodeMap.getLength(); j++ ) {\r
497                                 root.setAttribute(nodeMap.item(j).getNodeName(), nodeMap.item(j).getNodeValue());\r
498                         }\r
499                 }\r
500 \r
501                 NodeList nodes = node.getChildNodes();\r
502                 for (int i=0; i < nodes.getLength(); i++) {\r
503                         Node node2 = nodes.item(i);\r
504                         if (node2.getNodeType() == Node.ELEMENT_NODE) {\r
505                                 root.appendChild(getCopy(doc, node2));\r
506                         }\r
507                         else if (node2.getNodeType() == Node.TEXT_NODE) {\r
508                                 String str = node2.getNodeValue();\r
509                                 Text textContents = doc.createTextNode(str);\r
510                                 root.appendChild(textContents);\r
511                         }\r
512                         else if (node2.getNodeType() == Node.CDATA_SECTION_NODE) {\r
513                                 String str = node2.getNodeValue();\r
514                                 CDATASection cdataSection = doc.createCDATASection(str);\r
515                                 root.appendChild(cdataSection);\r
516                         }\r
517                 }\r
518                 return root;\r
519         }\r
520         \r
521         /**\r
522          * ファイル名の順序に並び替えるためのソートクラス\r
523          * \r
524          * @author hayashi\r
525          */\r
526         static class FileSort implements Comparator<File>{\r
527                 public int compare(File src, File target){\r
528                         int diff = src.getName().compareTo(target.getName());\r
529                         return diff;\r
530                 }\r
531         }\r
532 \r
533 }