1 | package ch.guggis.haiti.radiotv;
|
---|
2 |
|
---|
3 | import java.io.OutputStreamWriter;
|
---|
4 | import java.io.StringReader;
|
---|
5 | import java.io.BufferedReader;
|
---|
6 | import javax.xml.xpath.*
|
---|
7 | import groovy.util.IndentPrinter;
|
---|
8 | import groovy.util.XmlParser
|
---|
9 | import groovy.xml.MarkupBuilder;
|
---|
10 | import groovy.xml.DOMBuilder;
|
---|
11 | import javax.xml.parsers.DocumentBuilderFactory
|
---|
12 | import org.xml.sax.InputSource
|
---|
13 | import groovy.xml.StreamingMarkupBuilder
|
---|
14 | import org.w3c.dom.Node
|
---|
15 | import java.text.SimpleDateFormat
|
---|
16 | import groovy.util.CliBuilder
|
---|
17 | import groovy.xml.XmlUtil
|
---|
18 |
|
---|
19 | /**
|
---|
20 | * Takes a file with the newest data for studios from IMS and creates an OSM file
|
---|
21 | * with property ids, version info and action="modify" attributes, in order to open
|
---|
22 | * it in JOSM and upload it to OSM.
|
---|
23 | *
|
---|
24 | */
|
---|
25 | class UpdateFileBuilder {
|
---|
26 | private static def xpath = XPathFactory.newInstance().newXPath()
|
---|
27 | def lastChangesetId
|
---|
28 | def reader
|
---|
29 | def writer
|
---|
30 |
|
---|
31 | def queryApi(url) {
|
---|
32 | def builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
---|
33 | def doc = builder.parse(
|
---|
34 | new InputSource(
|
---|
35 | new InputStreamReader(
|
---|
36 | new URL(url).openStream(),
|
---|
37 | "UTF-8"
|
---|
38 | )
|
---|
39 | )
|
---|
40 | ).documentElement
|
---|
41 | return doc
|
---|
42 | }
|
---|
43 | /**
|
---|
44 | * Retrieves the nodes which have been updated and create in the last update of
|
---|
45 | * the studio dataset
|
---|
46 | *
|
---|
47 | */
|
---|
48 | def fetchChangeset() {
|
---|
49 | return queryApi("http://api.openstreetmap.org/api/0.6/changeset/${lastChangesetId}/download")
|
---|
50 | }
|
---|
51 |
|
---|
52 |
|
---|
53 | /**
|
---|
54 | * Fetch the current version for all nodes representing IMS studios
|
---|
55 | *
|
---|
56 | */
|
---|
57 | def currentIMSStudioObjectsFromOsm(changeset) {
|
---|
58 | def ids = []
|
---|
59 | xpath.evaluate("//node", changeset, XPathConstants.NODESET).each { node ->
|
---|
60 | ids << xpath.evaluate("./@id", node, XPathConstants.STRING)
|
---|
61 | }
|
---|
62 | return queryApi("http://api.openstreetmap.org/api/0.6/nodes?nodes=" + ids.join(","))
|
---|
63 | }
|
---|
64 |
|
---|
65 | /**
|
---|
66 | * Load the new data from the OSM file
|
---|
67 | *
|
---|
68 | */
|
---|
69 | def loadNewStudios() {
|
---|
70 | def builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
---|
71 | def doc = builder.parse(
|
---|
72 | new InputSource(
|
---|
73 | reader
|
---|
74 | )
|
---|
75 | ).documentElement
|
---|
76 | return doc
|
---|
77 | }
|
---|
78 |
|
---|
79 | /**
|
---|
80 | * Update the id and version in the studio objects.
|
---|
81 | */
|
---|
82 | def updateIdsAndVersions(newStudios, oldStudios) {
|
---|
83 | xpath.evaluate("//node", newStudios, XPathConstants.NODESET).each { newStudio ->
|
---|
84 | def imsid = xpath.evaluate("./tag[@k = 'ims:id']/@v", newStudio, XPathConstants.STRING)
|
---|
85 | println "Processing studio '${imsid}' ..."
|
---|
86 | def osmid = xpath.evaluate("//tag[@k = 'ims:id'][@v = '${imsid}']/../@id", oldStudios, XPathConstants.STRING)
|
---|
87 | def osmversion = xpath.evaluate("//tag[@k = 'pid'][@v = '${imsid}']/../@version", oldStudios, XPathConstants.STRING)
|
---|
88 | if (!osmid) {
|
---|
89 | println "Warning: didn't find an existing node for studio '${imsid}'. Adding the studio instead of updating it."
|
---|
90 | return
|
---|
91 | }
|
---|
92 | newStudio.setAttribute("id", osmid)
|
---|
93 | newStudio.setAttribute("version", osmversion)
|
---|
94 | newStudio.setAttribute("action", "modify")
|
---|
95 | }
|
---|
96 | return newStudios
|
---|
97 | }
|
---|
98 |
|
---|
99 |
|
---|
100 | def process() {
|
---|
101 | println "Fetching changeset '${lastChangesetId}' from OSM API ..."
|
---|
102 | def changeset = fetchChangeset()
|
---|
103 | println "Fetching the current versions of the nodes from the OSM API ..."
|
---|
104 | def oldStudios = currentIMSStudioObjectsFromOsm(changeset)
|
---|
105 | println "Loading the new nodes for IMS studios ..."
|
---|
106 | def newStudios = loadNewStudios()
|
---|
107 | println "Updating the ids and versions of the new studios ..."
|
---|
108 | updateIdsAndVersions(newStudios, oldStudios)
|
---|
109 | println "Writing the update changeset file ..."
|
---|
110 | writer.println newStudios
|
---|
111 | writer.flush()
|
---|
112 | }
|
---|
113 |
|
---|
114 | def usage() {
|
---|
115 | println """
|
---|
116 | groovy ch.guggis.haiti.radiotv.UpdateFileBuilder [options]
|
---|
117 | Options:
|
---|
118 | -cs, --last-changeset the changeset id used in the last upload. Mandatory.
|
---|
119 | -h, --help show help information
|
---|
120 | -i, --input-file the input file. Reads from stdin if missing
|
---|
121 | -o, --output-file the output file. Writes to stdout if missing.
|
---|
122 | """
|
---|
123 | }
|
---|
124 |
|
---|
125 | def fail(msg) {
|
---|
126 | println msg
|
---|
127 | usage()
|
---|
128 | System.exit(1)
|
---|
129 | }
|
---|
130 |
|
---|
131 | def processCommandLineOptions(argArray) {
|
---|
132 | def inputFile
|
---|
133 | def outputFile
|
---|
134 | def args = Arrays.asList(argArray)
|
---|
135 | args = args.reverse()
|
---|
136 | def arg = args.pop()
|
---|
137 | while(arg != null) {
|
---|
138 | switch(arg) {
|
---|
139 | case "-i":
|
---|
140 | case "--input-file":
|
---|
141 | inputFile = args.pop()
|
---|
142 | if (inputFile == null) {
|
---|
143 | fail "Error: missing input file"
|
---|
144 | }
|
---|
145 | break
|
---|
146 | case "-o":
|
---|
147 | case "--output-file":
|
---|
148 | outputFile = args.pop()
|
---|
149 | if (outputFile == null) {
|
---|
150 | fail "Error: missing output file"
|
---|
151 | }
|
---|
152 | break
|
---|
153 | case "-cs":
|
---|
154 | case "--last-changeset":
|
---|
155 | lastChangesetId = args.pop()
|
---|
156 | if (lastChangesetId == null) {
|
---|
157 | fail "Error: missing changeset id"
|
---|
158 | }
|
---|
159 | break
|
---|
160 |
|
---|
161 | case "-h":
|
---|
162 | case "--help":
|
---|
163 | usage()
|
---|
164 | System.exit(0)
|
---|
165 | break
|
---|
166 |
|
---|
167 | default:
|
---|
168 | fail "Illegal argument ${arg}"
|
---|
169 | }
|
---|
170 | arg = args.empty ? null : args.pop()
|
---|
171 | }
|
---|
172 |
|
---|
173 | if (!lastChangesetId) {
|
---|
174 | fail("Mandatory command line option '-cs' missing.")
|
---|
175 | System.exit(1)
|
---|
176 | }
|
---|
177 |
|
---|
178 | if (inputFile) {
|
---|
179 | reader = new File(inputFile).newReader("UTF-8")
|
---|
180 | } else {
|
---|
181 | reader = new BufferedReader(new InputStreamReader(System.in, "UTF-8"))
|
---|
182 | }
|
---|
183 | if (outputFile) {
|
---|
184 | writer = new File(outputFile).newWriter("UTF-8")
|
---|
185 | } else {
|
---|
186 | writer = new PrintWriter(new OutputStreamWriter(System.out, "UTF-8"))
|
---|
187 | }
|
---|
188 | }
|
---|
189 |
|
---|
190 | static public void main(args) {
|
---|
191 | def task = new UpdateFileBuilder()
|
---|
192 | task.processCommandLineOptions(args)
|
---|
193 | task.process()
|
---|
194 | }
|
---|
195 | }
|
---|