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