source: josm/trunk/src/org/openstreetmap/josm/data/projection/ProjectionCLI.java @ 12792

Last change on this file since 12792 was 12792, checked in by bastiK, 3 months ago

closes #15273, see #15229, see #15182 - add command line interface module for projections

  • run josm project --help to see the options
  • extracts parser from LatLon and CustomProjection into LatLonParser
File size: 7.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.projection;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import gnu.getopt.Getopt;
7import gnu.getopt.LongOpt;
8
9import java.io.BufferedReader;
10import java.io.IOException;
11import java.io.InputStreamReader;
12import java.nio.charset.Charset;
13import java.nio.charset.StandardCharsets;
14import java.nio.file.Files;
15import java.nio.file.Paths;
16import java.util.ArrayList;
17import java.util.List;
18import java.util.function.Function;
19
20import org.openstreetmap.josm.data.coor.EastNorth;
21import org.openstreetmap.josm.data.coor.LatLon;
22import org.openstreetmap.josm.CLIModule;
23import org.openstreetmap.josm.data.coor.conversion.LatLonParser;
24
25import org.openstreetmap.josm.tools.Utils;
26
27/**
28 * Command line interface for projecting coordinates.
29 * @since 12792
30 */
31public class ProjectionCLI implements CLIModule {
32
33    public static final ProjectionCLI INSTANCE = new ProjectionCLI();
34
35    private boolean argInverse = false;
36    private boolean argSwitchInput = false;
37    private boolean argSwitchOutput = false;
38
39    @Override
40    public String getActionKeyword() {
41        return "project";
42    }
43
44    @Override
45    public void processArguments(String[] argArray) {
46        Getopt getopt = new Getopt("JOSM projection", argArray, "Irh", new LongOpt[] {
47                new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h')});
48
49        int c;
50        while ((c = getopt.getopt()) != -1) {
51            switch (c) {
52            case 'h':
53                showHelp();
54                System.exit(0);
55            case 'I':
56                argInverse = true;
57                break;
58            case 'r':
59                argSwitchInput = true;
60                break;
61            case 's':
62                argSwitchOutput = true;
63                break;
64            }
65        }
66
67        List<String> projParamFrom = new ArrayList<>();
68        List<String> projParamTo = new ArrayList<>();
69        List<String> otherPositional = new ArrayList<>();
70        boolean toTokenSeen = false;
71        // positional arguments:
72        for (int i = getopt.getOptind(); i < argArray.length; ++i) {
73            String arg = argArray[i];
74            if (arg.isEmpty()) throw new IllegalArgumentException("non-empty argument expected");
75            if (arg.startsWith("+")) {
76                if (arg.equals("+to")) {
77                    toTokenSeen = true;
78                } else {
79                    (toTokenSeen ? projParamTo : projParamFrom).add(arg);
80                }
81            } else {
82                otherPositional.add(arg);
83            }
84        }
85        String fromStr = Utils.join(" ", projParamFrom);
86        String toStr = Utils.join(" ", projParamTo);
87        try {
88            run(fromStr, toStr, otherPositional);
89        } catch (ProjectionConfigurationException | IllegalArgumentException | IOException ex) {
90            System.err.println(tr("Error: {0}", ex.getMessage()));
91            System.exit(1);
92        }
93        System.exit(0);
94    }
95
96    /**
97     * Displays help on the console
98     */
99    public static void showHelp() {
100        System.out.println(getHelp());
101    }
102
103    private static String getHelp() {
104        return tr("JOSM projection command line interface")+"\n\n"+
105                tr("Usage")+":\n"+
106                "\tjava -jar josm.jar project <options> <crs> +to <crs> [file]\n\n"+
107                tr("Description")+":\n"+
108                tr("Converts coordinates from one coordinate reference system to another.")+"\n\n"+
109                tr("Options")+":\n"+
110                "\t--help|-h         "+tr("Show this help")+"\n"+
111                "\t-I                "+tr("Switch input and output crs")+"\n"+
112                "\t-r                "+tr("Switch order of input coordinates (east/north, lon/lat)")+"\n"+
113                "\t-s                "+tr("Switch order of output coordinates (east/north, lon/lat)")+"\n\n"+
114                tr("<crs>")+":\n"+
115                tr("The format for input and output coordinate reference system"
116                        + " is similar to that of the PROJ.4 software.")+"\n\n"+
117                tr("[file]")+":\n"+
118                tr("Reads input data from one or more files listed as positional arguments. "
119                + "When no files are given, or the filename is \"-\", data is read from "
120                + "standard input.")+"\n\n"+
121                tr("Examples")+":\n"+
122                "    java -jar josm.jar project +init=epsg:4326 +to +init=epsg:3857 <<<\"11.232274 50.5685716\"\n"+
123                "       => 1250371.1334500168 6545331.055189664\n\n"+
124                "    java -jar josm.jar project +proj=lonlat +datum=WGS84 +to +proj=merc +a=6378137 +b=6378137 +nadgrids=@null <<EOF\n" +
125                "    11d13'56.19\"E 50d34'6.86\"N\n" +
126                "    118d39'30.42\"W 37d20'18.76\"N\n"+
127                "    EOF\n"+
128                "       => 1250371.1334500168 6545331.055189664\n" +
129                "          -1.3208998232319113E7 4486401.160664663\n" +
130                "";
131    }
132
133    private void run(String fromStr, String toStr, List<String> files) throws ProjectionConfigurationException, IOException {
134        CustomProjection fromProj = createProjection(fromStr);
135        CustomProjection toProj = createProjection(toStr);
136        if (this.argInverse) {
137            CustomProjection tmp = fromProj;
138            fromProj = toProj;
139            toProj = tmp;
140        }
141
142        if (files.isEmpty() || files.get(0).equals("-")) {
143            processInput(fromProj, toProj, new BufferedReader(new InputStreamReader(System.in, Charset.defaultCharset())));
144        } else {
145            for (String file : files) {
146                try (BufferedReader br = Files.newBufferedReader(Paths.get(file), StandardCharsets.UTF_8)) {
147                    processInput(fromProj, toProj, br);
148                }
149            }
150        }
151    }
152
153    private void processInput(CustomProjection fromProj, CustomProjection toProj, BufferedReader reader) throws IOException {
154        String line;
155        while ((line = reader.readLine()) != null) {
156            line = line.trim();
157            if (line.isEmpty() || line.startsWith("#"))
158                continue;
159            EastNorth enIn;
160            if (fromProj.isGeographic()) {
161                enIn = parseEastNorth(line, LatLonParser::parseCoordinate);
162            } else {
163                enIn = parseEastNorth(line, ProjectionCLI::parseDouble);
164            }
165            LatLon ll = fromProj.eastNorth2latlon(enIn);
166            EastNorth enOut = toProj.latlon2eastNorth(ll);
167            double cOut1 = argSwitchOutput ? enOut.north() : enOut.east();
168            double cOut2 = argSwitchOutput ? enOut.east() : enOut.north();
169            System.out.println(Double.toString(cOut1) + " " + Double.toString(cOut2));
170            System.out.flush();
171        }
172    }
173
174    private CustomProjection createProjection(String params) throws ProjectionConfigurationException {
175        CustomProjection proj = new CustomProjection();
176        proj.update(params);
177        return proj;
178    }
179
180    private EastNorth parseEastNorth(String s, Function<String, Double> parser) {
181        String[] en = s.split("[;, ]+");
182        if (en.length != 2)
183            throw new IllegalArgumentException(tr("Expected two coordinates, separated by white space, found {0} in ''{1}''", en.length, s));
184        double east = parser.apply(en[0]);
185        double north = parser.apply(en[1]);
186        if (this.argSwitchInput)
187            return new EastNorth(north, east);
188        else
189            return new EastNorth(east, north);
190    }
191
192    private static double parseDouble(String s) {
193        try {
194            return Double.parseDouble(s);
195        } catch (NumberFormatException nfe) {
196            throw new IllegalArgumentException(tr("Unable to parse number ''{0}''", s));
197        }
198    }
199
200    /**
201     * Main class to run just the projection CLI.
202     * @param args command line arguments
203     */
204    public static void main(String[] args) {
205        ProjectionCLI.INSTANCE.processArguments(args);
206    }
207}
Note: See TracBrowser for help on using the repository browser.