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