| [19939] | 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 | } | 
|---|