source: josm/trunk/scripts/SyncEditorLayerIndex.groovy @ 11964

Last change on this file since 11964 was 11964, checked in by stoecker, 2 years ago

add maps sanitity check (will cause new red entries) for double points in bounds, first part for proper formatted XML output in sync script to make copying data easier

  • Property svn:eol-style set to native
File size: 26.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2/**
3 * Compare and analyse the differences of the editor layer index and the JOSM imagery list.
4 * The goal is to keep both lists in sync.
5 *
6 * The editor layer index project (https://github.com/osmlab/editor-layer-index)
7 * provides also a version in the JOSM format, but the GEOJSON is the original source
8 * format, so we read that.
9 *
10 * How to run:
11 * -----------
12 *
13 * Main JOSM binary needs to be in classpath, e.g.
14 *
15 * $ groovy -cp ../dist/josm-custom.jar SyncEditorLayerIndex.groovy
16 *
17 * Add option "-h" to show the available command line flags.
18 */
19import javax.json.Json
20import javax.json.JsonArray
21import javax.json.JsonObject
22import javax.json.JsonReader
23
24import org.openstreetmap.josm.data.imagery.ImageryInfo
25import org.openstreetmap.josm.data.imagery.Shape
26import org.openstreetmap.josm.io.imagery.ImageryReader
27
28class SyncEditorLayerIndex {
29
30    List<ImageryInfo> josmEntries;
31    JsonArray eliEntries;
32
33    def eliUrls = new HashMap<String, JsonObject>()
34    def josmUrls = new HashMap<String, ImageryInfo>()
35    def josmMirrors = new HashMap<String, ImageryInfo>()
36
37    static String eliInputFile = 'imagery.geojson'
38    static String josmInputFile = 'maps.xml'
39    static String ignoreInputFile = 'maps_ignores.txt'
40    static FileWriter outputFile = null
41    static BufferedWriter outputStream = null
42    def skip = [:]
43
44    static def options
45
46    /**
47     * Main method.
48     */
49    static main(def args) {
50        parse_command_line_arguments(args)
51        def script = new SyncEditorLayerIndex()
52        script.loadSkip()
53        script.start()
54        script.loadJosmEntries()
55        if(options.josmxml) {
56            def file = new FileWriter(options.josmxml)
57            def stream = new BufferedWriter(file)
58            script.printentries(script.josmEntries, stream)
59        }
60        script.loadELIEntries()
61        if(options.elixml) {
62            def file = new FileWriter(options.elixml)
63            def stream = new BufferedWriter(file)
64            script.printentries(script.eliEntries, stream)
65        }
66        script.checkInOneButNotTheOther()
67        script.checkCommonEntries()
68        script.end()
69        if(outputStream != null) {
70            outputStream.close();
71        }
72        if(outputFile != null) {
73            outputFile.close();
74        }
75    }
76
77    /**
78     * Parse command line arguments.
79     */
80    static void parse_command_line_arguments(args) {
81        def cli = new CliBuilder(width: 160)
82        cli.o(longOpt:'output', args:1, argName: "output", "Output file, - prints to stdout (default: -)")
83        cli.e(longOpt:'eli_input', args:1, argName:"eli_input", "Input file for the editor layer index (geojson). Default is $eliInputFile (current directory).")
84        cli.j(longOpt:'josm_input', args:1, argName:"josm_input", "Input file for the JOSM imagery list (xml). Default is $josmInputFile (current directory).")
85        cli.i(longOpt:'ignore_input', args:1, argName:"ignore_input", "Input file for the ignore list. Default is $ignoreInputFile (current directory).")
86        cli.s(longOpt:'shorten', "shorten the output, so it is easier to read in a console window")
87        cli.n(longOpt:'noskip', argName:"noskip", "don't skip known entries")
88        cli.x(longOpt:'xhtmlbody', argName:"xhtmlbody", "create XHTML body for display in a web page")
89        cli.X(longOpt:'xhtml', argName:"xhtml", "create XHTML for display in a web page")
90        cli.p(longOpt:'elixml', args:1, argName:"elixml", "ELI entries for use in JOSM as XML file (incomplete)")
91        cli.q(longOpt:'josmxml', args:1, argName:"josmxml", "JOSM entries reoutput as XML file (incomplete)")
92        cli.m(longOpt:'nomissingeli', argName:"nomissingeli", "don't show missing editor layer index entries")
93        cli.h(longOpt:'help', "show this help")
94        options = cli.parse(args)
95
96        if (options.h) {
97            cli.usage()
98            System.exit(0)
99        }
100        if (options.eli_input) {
101            eliInputFile = options.eli_input
102        }
103        if (options.josm_input) {
104            josmInputFile = options.josm_input
105        }
106        if (options.ignore_input) {
107            ignoreInputFile = options.ignore_input
108        }
109        if (options.output && options.output != "-") {
110            outputFile = new FileWriter(options.output)
111            outputStream = new BufferedWriter(outputFile)
112        }
113    }
114
115    void loadSkip() {
116        FileReader fr = new FileReader(ignoreInputFile)
117        def line
118
119        while((line = fr.readLine()) != null) {
120            def res = (line =~ /^\|\| *(ELI|Ignore) *\|\| *\{\{\{(.+)\}\}\} *\|\|/)
121            if(res.count)
122            {
123                if(res[0][1].equals("Ignore")) {
124                    skip[res[0][2]] = "green"
125                } else {
126                    skip[res[0][2]] = "darkgoldenrod"
127                }
128            }
129        }
130    }
131
132    void myprintlnfinal(String s) {
133        if(outputStream != null) {
134            outputStream.write(s)
135            outputStream.newLine()
136        } else {
137            println s
138        }
139    }
140
141    void myprintln(String s) {
142        if(skip.containsKey(s)) {
143            String color = skip.get(s)
144            skip.remove(s)
145            if(options.xhtmlbody || options.xhtml) {
146                s = "<pre style=\"margin:3px;color:"+color+"\">"+s.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;")+"</pre>"
147            }
148            if (!options.noskip) {
149                return
150            }
151        } else if(options.xhtmlbody || options.xhtml) {
152            String color = s.startsWith("***") ? "black" : ((s.startsWith("+ ") || s.startsWith("+++ ELI")) ? "blue" : "red")
153            s = "<pre style=\"margin:3px;color:"+color+"\">"+s.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;")+"</pre>"
154        }
155        myprintlnfinal(s)
156    }
157
158    void start() {
159        if (options.xhtml) {
160            myprintlnfinal "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
161            myprintlnfinal "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/><title>JOSM - ELI differences</title></head><body>\n"
162        }
163    }
164
165    void end() {
166        for (def s: skip.keySet()) {
167            myprintln "+++ Obsolete skip entry: " + s
168        }
169        if (options.xhtml) {
170            myprintlnfinal "</body></html>\n"
171        }
172    }
173
174    void loadELIEntries() {
175        FileReader fr = new FileReader(eliInputFile)
176        JsonReader jr = Json.createReader(fr)
177        eliEntries = jr.readObject().get("features")
178        jr.close()
179
180        for (def e : eliEntries) {
181            def url = getUrl(e)
182            if (url.contains("{z}")) {
183                myprintln "+++ ELI-URL uses {z} instead of {zoom}: "+url
184                url = url.replace("{z}","{zoom}")
185            }
186            if (eliUrls.containsKey(url)) {
187                myprintln "+++ ELI-URL is not unique: "+url
188            } else {
189                eliUrls.put(url, e)
190            }
191        }
192        myprintln "*** Loaded ${eliEntries.size()} entries (ELI). ***"
193    }
194
195    void printentries(def entries, def stream) {
196        stream.write "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
197        stream.write "<imagery xmlns=\"http://josm.openstreetmap.de/maps-1.0\">\n"
198        for (def e : entries) {
199            def best = "eli-best".equals(getQuality(e))
200            stream.write "    <entry"+(best ? " eli-best=\"true\"" : "" )+">\n"
201            stream.write "        <name>${getName(e)}</name>\n"
202            stream.write "        <id>${getId(e)}</id>\n"
203            def minlat = 1000
204            def minlon = 1000
205            def maxlat = -1000
206            def maxlon = -1000
207            def shapes = ""
208            def sep = "\n            "
209            for(def s: getShapes(e)) {
210                shapes += "            <shape>"
211                def i = 0
212                for(def p: s.getPoints()) {
213                    def lat = p.getLat()
214                    def lon = p.getLon()
215                    if(lat > maxlat) maxlat = lat
216                    if(lon > maxlon) maxlon = lon
217                    if(lat < minlat) minlat = lat
218                    if(lon < minlon) minlon = lon
219                    if(!(i++%3)) {
220                        shapes += sep + "    "
221                    }
222                    shapes += "<point lat='${String.format(Locale.ROOT, "%.7f",lat)}' lon='${String.format(Locale.ROOT, "%.7f",lon)}'/>"
223                }
224                shapes += sep + "</shape>\n"
225            }
226            if(shapes) {
227                stream.write "        <bounds min-lat='${minlat}' min-lon='${minlon}' max-lat='${maxlat}' max-lon='${maxlon}'>\n"
228                stream.write shapes + "        </bounds>\n"
229                stream.write "    </entry>\n"
230            }
231        }
232        stream.write "</imagery>\n"
233        stream.close()
234    }
235
236    void loadJosmEntries() {
237        def reader = new ImageryReader(josmInputFile)
238        josmEntries = reader.parse()
239
240        for (def e : josmEntries) {
241            def url = getUrl(e)
242            if (url.contains("{z}")) {
243                myprintln "+++ JOSM-URL uses {z} instead of {zoom}: "+url
244                url = url.replace("{z}","{zoom}")
245            }
246            if (josmUrls.containsKey(url)) {
247                myprintln "+++ JOSM-URL is not unique: "+url
248            } else {
249                josmUrls.put(url, e)
250            }
251            for (def m : e.getMirrors()) {
252                url = getUrl(m)
253                m.origName = m.getOriginalName().replaceAll(" mirror server( \\d+)?","")
254                if (josmUrls.containsKey(url)) {
255                    myprintln "+++ JOSM-Mirror-URL is not unique: "+url
256                } else {
257                    josmUrls.put(url, m)
258                    josmMirrors.put(url, m)
259                }
260            }
261        }
262        myprintln "*** Loaded ${josmEntries.size()} entries (JOSM). ***"
263    }
264
265    List inOneButNotTheOther(Map m1, Map m2) {
266        def l = []
267        for (def url : m1.keySet()) {
268            if (!m2.containsKey(url)) {
269                def name = getName(m1.get(url))
270                l += "  "+getDescription(m1.get(url))
271            }
272        }
273        l.sort()
274    }
275
276    void checkInOneButNotTheOther() {
277        def l1 = inOneButNotTheOther(eliUrls, josmUrls)
278        myprintln "*** URLs found in ELI but not in JOSM (${l1.size()}): ***"
279        if (!l1.isEmpty()) {
280            for (def l : l1) {
281                myprintln "-" + l
282            }
283        }
284
285        if (options.nomissingeli)
286            return
287        def l2 = inOneButNotTheOther(josmUrls, eliUrls)
288        myprintln "*** URLs found in JOSM but not in ELI (${l2.size()}): ***"
289        if (!l2.isEmpty()) {
290            for (def l : l2) {
291                myprintln "+" + l
292            }
293        }
294    }
295
296    void checkCommonEntries() {
297        myprintln "*** Same URL, but different name: ***"
298        for (def url : eliUrls.keySet()) {
299            def e = eliUrls.get(url)
300            if (!josmUrls.containsKey(url)) continue
301            def j = josmUrls.get(url)
302            def ename = getName(e).replace("'","’")
303            def jname = getName(j).replace("'","’")
304            if (!ename.equals(jname)) {
305                myprintln "* Name differs ('${getName(e)}' != '${getName(j)}'): $url"
306            }
307        }
308
309        myprintln "*** Same URL, but different type: ***"
310        for (def url : eliUrls.keySet()) {
311            def e = eliUrls.get(url)
312            if (!josmUrls.containsKey(url)) continue
313            def j = josmUrls.get(url)
314            if (!getType(e).equals(getType(j))) {
315                myprintln "* Type differs (${getType(e)} != ${getType(j)}): ${getName(j)} - $url"
316            }
317        }
318
319        myprintln "*** Same URL, but different zoom bounds: ***"
320        for (def url : eliUrls.keySet()) {
321            def e = eliUrls.get(url)
322            if (!josmUrls.containsKey(url)) continue
323            def j = josmUrls.get(url)
324
325            Integer eMinZoom = getMinZoom(e)
326            Integer jMinZoom = getMinZoom(j)
327            if (eMinZoom != jMinZoom  && !(eMinZoom == 0 && jMinZoom == null)) {
328                myprintln "* Minzoom differs (${eMinZoom} != ${jMinZoom}): ${getDescription(j)}"
329            }
330            Integer eMaxZoom = getMaxZoom(e)
331            Integer jMaxZoom = getMaxZoom(j)
332            if (eMaxZoom != jMaxZoom) {
333                myprintln "* Maxzoom differs (${eMaxZoom} != ${jMaxZoom}): ${getDescription(j)}"
334            }
335        }
336
337        myprintln "*** Same URL, but different country code: ***"
338        for (def url : eliUrls.keySet()) {
339            def e = eliUrls.get(url)
340            if (!josmUrls.containsKey(url)) continue
341            def j = josmUrls.get(url)
342            if (!getCountryCode(e).equals(getCountryCode(j))) {
343                myprintln "* Country code differs (${getCountryCode(e)} != ${getCountryCode(j)}): ${getDescription(j)}"
344            }
345        }
346        myprintln "*** Same URL, but different quality: ***"
347        for (def url : eliUrls.keySet()) {
348            def e = eliUrls.get(url)
349            if (!josmUrls.containsKey(url)) {
350              def q = getQuality(e)
351              if("eli-best".equals(q)) {
352                myprintln "- Quality best entry not in JOSM for ${getDescription(e)}"
353              }
354              continue
355            }
356            def j = josmUrls.get(url)
357            if (!getQuality(e).equals(getQuality(j))) {
358                myprintln "* Quality differs (${getQuality(e)} != ${getQuality(j)}): ${getDescription(j)}"
359            }
360        }
361        myprintln "*** Same URL, but different dates: ***"
362        for (def url : eliUrls.keySet()) {
363            def ed = getDate(eliUrls.get(url))
364            if (!josmUrls.containsKey(url)) continue
365            def j = josmUrls.get(url)
366            def jd = getDate(j)
367            // The forms 2015;- or -;2015 or 2015;2015 are handled equal to 2015
368            String ef = ed.replaceAll("\\A-;","").replaceAll(";-\\z","").replaceAll("\\A([0-9-]+);\\1\\z","\$1")
369            // ELI has a strange and inconsistent used end_date definition, so we try again with subtraction by one
370            String ed2 = ed
371            def reg = (ed =~ /^(.*;)(\d\d\d\d)(-(\d\d)(-(\d\d))?)?$/)
372            if(reg != null && reg.count == 1) {
373                Calendar cal = Calendar.getInstance()
374                cal.set(reg[0][2] as Integer, reg[0][4] == null ? 0 : (reg[0][4] as Integer)-1, reg[0][6] == null ? 1 : reg[0][6] as Integer)
375                cal.add(Calendar.DAY_OF_MONTH, -1)
376                ed2 = reg[0][1] + cal.get(Calendar.YEAR)
377                if (reg[0][4] != null)
378                    ed2 += "-" + String.format("%02d", cal.get(Calendar.MONTH)+1)
379                if (reg[0][6] != null)
380                    ed2 += "-" + String.format("%02d", cal.get(Calendar.DAY_OF_MONTH))
381            }
382            String ef2 = ed2.replaceAll("\\A-;","").replaceAll(";-\\z","").replaceAll("\\A([0-9-]+);\\1\\z","\$1")
383            if (!ed.equals(jd) && !ef.equals(jd) && !ed2.equals(jd) && !ef2.equals(jd)) {
384                String t = "'${ed}'"
385                if (!ed.equals(ef)) {
386                    t += " or '${ef}'"
387                }
388                if (jd.isEmpty()) {
389                    myprintln "- Missing JOSM date (${t}): ${getDescription(j)}"
390                } else if (!ed.isEmpty()) {
391                    myprintln "* Date differs (${t} != '${jd}'): ${getDescription(j)}"
392                } else if (!options.nomissingeli) {
393                    myprintln "+ Missing ELI date ('${jd}'): ${getDescription(j)}"
394                }
395            }
396        }
397        myprintln "*** Mismatching shapes: ***"
398        for (def url : josmUrls.keySet()) {
399            def j = josmUrls.get(url)
400            def num = 1
401            for (def shape : getShapes(j)) {
402                def p = shape.getPoints()
403                if(!p[0].equals(p[p.size()-1])) {
404                    myprintln "+++ JOSM shape $num unclosed: ${getDescription(j)}"
405                }
406                for (def nump = 1; nump < p.size(); ++nump) {
407                    if (p[nump-1] == p[nump]) {
408                        myprintln "+++ JOSM shape $num double point at ${nump-1}: ${getDescription(j)}"
409                    }
410                }
411                ++num
412            }
413        }
414        for (def url : eliUrls.keySet()) {
415            def e = eliUrls.get(url)
416            def num = 1
417            def s = getShapes(e)
418            for (def shape : s) {
419                def p = shape.getPoints()
420                if(!p[0].equals(p[p.size()-1]) && !options.nomissingeli) {
421                    myprintln "+++ ELI shape $num unclosed: ${getDescription(e)}"
422                }
423                for (def nump = 1; nump < p.size(); ++nump) {
424                    if (p[nump-1] == p[nump]) {
425                        myprintln "+++ ELI shape $num double point at ${nump-1}: ${getDescription(e)}"
426                    }
427                }
428                ++num
429            }
430            if (!josmUrls.containsKey(url)) {
431                continue
432            }
433            def j = josmUrls.get(url)
434            def js = getShapes(j)
435            if(!s.size() && js.size()) {
436                if(!options.nomissingeli) {
437                    myprintln "+ No ELI shape: ${getDescription(j)}"
438                }
439            } else if(!js.size() && s.size()) {
440                // don't report boundary like 5 point shapes as difference
441                if (s.size() != 1 || s[0].getPoints().size() != 5) {
442                    myprintln "- No JOSM shape: ${getDescription(j)}"
443                }
444            } else if(s.size() != js.size()) {
445                myprintln "* Different number of shapes (${s.size()} != ${js.size()}): ${getDescription(j)}"
446            } else {
447                for(def nums = 0; nums < s.size(); ++nums) {
448                    def ep = s[nums].getPoints()
449                    def jp = js[nums].getPoints()
450                    if(ep.size() != jp.size()) {
451                        myprintln "* Different number of points for shape ${nums+1} (${ep.size()} ! = ${jp.size()})): ${getDescription(j)}"
452                    } else {
453                        for(def nump = 0; nump < ep.size(); ++nump) {
454                            def ept = ep[nump]
455                            def jpt = jp[nump]
456                            if(Math.abs(ept.getLat()-jpt.getLat()) > 0.000001 || Math.abs(ept.getLon()-jpt.getLon()) > 0.000001) {
457                                myprintln "* Different coordinate for point ${nump+1} of shape ${nums+1}: ${getDescription(j)}"
458                                nump = ep.size()
459                                num = s.size()
460                            }
461                        }
462                    }
463                }
464            }
465        }
466        myprintln "*** Mismatching icons: ***"
467        for (def url : eliUrls.keySet()) {
468            def e = eliUrls.get(url)
469            if (!josmUrls.containsKey(url)) {
470                continue
471            }
472            def j = josmUrls.get(url)
473            def ij = getIcon(j)
474            def ie = getIcon(e)
475            if(ij != null && ie == null) {
476                if(!options.nomissingeli) {
477                    myprintln "+ No ELI icon: ${getDescription(j)}"
478                }
479            } else if(ij == null && ie != null) {
480                myprintln "- No JOSM icon: ${getDescription(j)}"
481            } else if(!ij.equals(ie)) {
482                myprintln "* Different icons: ${getDescription(j)}"
483            }
484        }
485        myprintln "*** Miscellaneous checks: ***"
486        def josmIds = new HashMap<String, ImageryInfo>()
487        for (def url : josmUrls.keySet()) {
488            def j = josmUrls.get(url)
489            def id = getId(j)
490            if(josmMirrors.containsKey(url)) {
491                continue
492            }
493            if(id == null) {
494                myprintln "* No JOSM-ID: ${getDescription(j)}"
495            } else if(josmIds.containsKey(id)) {
496                myprintln "* JOSM-ID ${id} not unique: ${getDescription(j)}"
497            } else {
498                josmIds.put(id, j)
499            }
500            def d = getDate(j)
501            if(!d.isEmpty()) {
502                def reg = (d =~ /^(-|(\d\d\d\d)(-(\d\d)(-(\d\d))?)?)(;(-|(\d\d\d\d)(-(\d\d)(-(\d\d))?)?))?$/)
503                if(reg == null || reg.count != 1) {
504                    myprintln "* JOSM-Date '${d}' is strange: ${getDescription(j)}"
505                } else {
506                    try {
507                        def first = verifyDate(reg[0][2],reg[0][4],reg[0][6])
508                        def second = verifyDate(reg[0][9],reg[0][11],reg[0][13])
509                        if(second.compareTo(first) < 0) {
510                            myprintln "* JOSM-Date '${d}' is strange (second earlier than first): ${getDescription(j)}"
511                        }
512                    }
513                    catch (Exception e) {
514                        myprintln "* JOSM-Date '${d}' is strange (${e.getMessage()}): ${getDescription(j)}"
515                    }
516                }
517            }
518            def js = getShapes(j)
519            if(js.size()) {
520                def minlat = 1000
521                def minlon = 1000
522                def maxlat = -1000
523                def maxlon = -1000
524                for(def s: js) {
525                    for(def p: s.getPoints()) {
526                        def lat = p.getLat()
527                        def lon = p.getLon()
528                        if(lat > maxlat) maxlat = lat
529                        if(lon > maxlon) maxlon = lon
530                        if(lat < minlat) minlat = lat
531                        if(lon < minlon) minlon = lon
532                    }
533                }
534                def b = j.getBounds()
535                if(b.getMinLat() != minlat || b.getMinLon() != minlon || b.getMaxLat() != maxlat || b.getMaxLon() != maxlon) {
536                    myprintln "* Bounds do not match shape (is ${b.getMinLat()},${b.getMinLon()},${b.getMaxLat()},${b.getMaxLon()}, calculated <bounds min-lat='${minlat}' min-lon='${minlon}' max-lat='${maxlat}' max-lon='${maxlon}'>): ${getDescription(j)}"
537                }
538            }
539        }
540    }
541
542    /**
543     * Utility functions that allow uniform access for both ImageryInfo and JsonObject.
544     */
545    static String getUrl(Object e) {
546        if (e instanceof ImageryInfo) return e.url
547        return e.get("properties").getString("url")
548    }
549    static String getDate(Object e) {
550        if (e instanceof ImageryInfo) return e.date ? e.date : ""
551        def p = e.get("properties")
552        def start = p.containsKey("start_date") ? p.getString("start_date") : ""
553        def end = p.containsKey("end_date") ? p.getString("end_date") : ""
554        if(!start.isEmpty() && !end.isEmpty())
555            return start+";"+end
556        else if(!start.isEmpty())
557            return start+";-"
558        else if(!end.isEmpty())
559            return "-;"+end
560        return ""
561    }
562    static Date verifyDate(String year, String month, String day) {
563        def date
564        if(year == null) {
565            date = "3000-01-01"
566        } else {
567            date = year + "-" + (month == null ? "01" : month) + "-" + (day == null ? "01" : day)
568        }
569        def df = new java.text.SimpleDateFormat("yyyy-MM-dd")
570        df.setLenient(false)
571        return df.parse(date)
572    }
573    static String getId(Object e) {
574        if (e instanceof ImageryInfo) return e.getId()
575        return e.get("properties").getString("id")
576    }
577    static String getName(Object e) {
578        if (e instanceof ImageryInfo) return e.getOriginalName()
579        return e.get("properties").getString("name")
580    }
581    static List<Shape> getShapes(Object e) {
582        if (e instanceof ImageryInfo) {
583            def bounds = e.getBounds()
584            if(bounds != null) {
585                return bounds.getShapes()
586            }
587            return []
588        }
589        if(!e.isNull("geometry")) {
590            def ex = e.get("geometry")
591            if(ex != null && !ex.isNull("coordinates")) {
592                def poly = ex.get("coordinates")
593                List<Shape> l = []
594                for(def shapes: poly) {
595                    def s = new Shape()
596                    for(def point: shapes) {
597                        def lon = point[0].toString()
598                        def lat = point[1].toString()
599                        s.addPoint(lat, lon)
600                    }
601                    l.add(s)
602                }
603                return l
604            }
605        }
606        return []
607    }
608    static String getType(Object e) {
609        if (e instanceof ImageryInfo) return e.getImageryType().getTypeString()
610        return e.get("properties").getString("type")
611    }
612    static Integer getMinZoom(Object e) {
613        if (e instanceof ImageryInfo) {
614            int mz = e.getMinZoom()
615            return mz == 0 ? null : mz
616        } else {
617            def num = e.get("properties").getJsonNumber("min_zoom")
618            if (num == null) return null
619            return num.intValue()
620        }
621    }
622    static Integer getMaxZoom(Object e) {
623        if (e instanceof ImageryInfo) {
624            int mz = e.getMaxZoom()
625            return mz == 0 ? null : mz
626        } else {
627            def num = e.get("properties").getJsonNumber("max_zoom")
628            if (num == null) return null
629            return num.intValue()
630        }
631    }
632    static String getCountryCode(Object e) {
633        if (e instanceof ImageryInfo) return "".equals(e.getCountryCode()) ? null : e.getCountryCode()
634        return e.get("properties").getString("country_code", null)
635    }
636    static String getQuality(Object e) {
637        if (e instanceof ImageryInfo) return e.isBestMarked() ? "eli-best" : null
638        return (e.get("properties").containsKey("best")
639            && e.get("properties").getBoolean("best")) ? "eli-best" : null
640    }
641    static String getIcon(Object e) {
642        if (e instanceof ImageryInfo) return e.getIcon()
643        return e.get("properties").getString("icon", null)
644    }
645    String getDescription(Object o) {
646        def url = getUrl(o)
647        def cc = getCountryCode(o)
648        if (cc == null) {
649            def j = josmUrls.get(url)
650            if (j != null) cc = getCountryCode(j)
651            if (cc == null) {
652                def e = eliUrls.get(url)
653                if (e != null) cc = getCountryCode(e)
654            }
655        }
656        if (cc == null) {
657            cc = ''
658        } else {
659            cc = "[$cc] "
660        }
661        def d = cc + getName(o) + " - " + getUrl(o)
662        if (options.shorten) {
663            def MAXLEN = 140
664            if (d.length() > MAXLEN) d = d.substring(0, MAXLEN-1) + "..."
665        }
666        return d
667    }
668}
Note: See TracBrowser for help on using the repository browser.