source: josm/trunk/src/org/openstreetmap/josm/data/projection/CustomProjection.java@ 5073

Last change on this file since 5073 was 5072, checked in by bastiK, 12 years ago

basic support for custom projections (see #7495)

  • Property svn:eol-style set to native
File size: 12.4 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 java.awt.GridBagLayout;
7import java.awt.event.ActionListener;
8import java.util.ArrayList;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.HashMap;
12import java.util.List;
13import java.util.Map;
14import java.util.regex.Matcher;
15import java.util.regex.Pattern;
16import javax.swing.JPanel;
17import javax.swing.JTextField;
18
19import org.openstreetmap.josm.data.Bounds;
20import org.openstreetmap.josm.data.coor.LatLon;
21import org.openstreetmap.josm.data.projection.datum.CentricDatum;
22import org.openstreetmap.josm.data.projection.datum.Datum;
23import org.openstreetmap.josm.data.projection.datum.SevenParameterDatum;
24import org.openstreetmap.josm.data.projection.datum.ThreeParameterDatum;
25import org.openstreetmap.josm.data.projection.proj.Proj;
26import org.openstreetmap.josm.data.projection.proj.ProjParameters;
27import org.openstreetmap.josm.tools.Utils;
28
29/**
30 * Custom projection
31 *
32 * Inspired by PROJ.4 and Proj4J.
33 */
34public class CustomProjection extends AbstractProjection implements ProjectionSubPrefs {
35
36 private String pref = "";
37
38 public void update(String pref) {
39 try {
40 Map<String, String> parameters = new HashMap<String, String>();
41 String[] parts = pref.trim().split("\\s+");
42 for (int i = 0; i < parts.length; i++) {
43 String part = parts[i];
44 if (part.charAt(0) != '+')
45 throw new ProjectionConfigurationException(tr("Parameter must begin with a ''+'' sign (found ''{0}'')", part));
46 Matcher m = Pattern.compile("\\+([a-zA-Z0-9_]+)(=(.*))?").matcher(part);
47 if (m.matches()) {
48 String key = m.group(1);
49 String value = null;
50 if (m.groupCount() >= 3) {
51 value = m.group(3);
52 }
53 parameters.put(key, value);
54 } else
55 throw new ProjectionConfigurationException(tr("Unexpected parameter format (''{0}'')", part));
56 }
57 ellps = parseEllipsoid(parameters);
58 datum = parseDatum(parameters, ellps);
59 proj = parseProjection(parameters, ellps);
60 String s = parameters.get("x_0");
61 if (s != null) {
62 this.x_0 = parseDouble(s, "x_0");
63 }
64 s = parameters.get("y_0");
65 if (s != null) {
66 this.y_0 = parseDouble(s, "y_0");
67 }
68 s = parameters.get("lon_0");
69 if (s != null) {
70 this.lon_0 = parseAngle(s, "lon_0");
71 }
72 s = parameters.get("k_0");
73 if (s != null) {
74 this.k_0 = parseDouble(s, "k_0");
75 }
76 } catch (ProjectionConfigurationException e) {
77 System.err.println(e.toString()); // FIXME
78 }
79 this.pref = pref;
80 }
81
82 public Ellipsoid parseEllipsoid(Map<String, String> parameters) throws ProjectionConfigurationException {
83 String code = parameters.get("ellps");
84 if (code != null) {
85 Ellipsoid ellipsoid = Projections.getEllipsoid(code);
86 if (ellipsoid == null) {
87 throw new ProjectionConfigurationException(tr("Ellipsoid ''{0}'' not supported.", code));
88 } else {
89 return ellipsoid;
90 }
91 }
92 String s = parameters.get("a");
93 if (s != null) {
94 double a = parseDouble(s, "a");
95 if (parameters.get("es") != null) {
96 double es = parseDouble(parameters, "es");
97 return Ellipsoid.create_a_es(a, es);
98 }
99 if (parameters.get("rf") != null) {
100 double rf = parseDouble(parameters, "rf");
101 return Ellipsoid.create_a_rf(a, rf);
102 }
103 if (parameters.get("f") != null) {
104 double f = parseDouble(parameters, "f");
105 return Ellipsoid.create_a_f(a, f);
106 }
107 if (parameters.get("b") != null) {
108 double b = parseDouble(parameters, "b");
109 return Ellipsoid.create_a_b(a, b);
110 }
111 }
112 if (parameters.containsKey("a") ||
113 parameters.containsKey("es") ||
114 parameters.containsKey("rf") ||
115 parameters.containsKey("f") ||
116 parameters.containsKey("b"))
117 throw new ProjectionConfigurationException(tr("Combination of ellipsoid parameters is not supported."));
118 // nothing specified, use WGS84 as default
119 return Ellipsoid.WGS84;
120 }
121
122 public Datum parseDatum(Map<String, String> parameters, Ellipsoid ellps) throws ProjectionConfigurationException {
123 String towgs84 = parameters.get("towgs84");
124 if (towgs84 != null)
125 return parseToWGS84(towgs84, ellps);
126
127 String datumId = parameters.get("datum");
128 if (datumId != null) {
129 Datum datum = Projections.getDatum(datumId);
130 if (datum == null) throw new ProjectionConfigurationException(tr("Unkown datum identifier: ''{0}''", datumId));
131 return datum;
132 } else
133 return new CentricDatum(null, null, ellps);
134 }
135
136 public Datum parseToWGS84(String paramList, Ellipsoid ellps) throws ProjectionConfigurationException {
137 String[] numStr = paramList.split(",");
138
139 if (numStr.length != 3 && numStr.length != 7)
140 throw new ProjectionConfigurationException(tr("Unexpected number of arguments for parameter ''towgs84'' (must be 3 or 7)"));
141 List<Double> towgs84Param = new ArrayList<Double>();
142 for (int i = 0; i < numStr.length; i++) {
143 try {
144 towgs84Param.add(Double.parseDouble(numStr[i]));
145 } catch (NumberFormatException e) {
146 throw new ProjectionConfigurationException(tr("Unable to parse value of parameter ''towgs84'' (''{0}'')", numStr[i]));
147 }
148 }
149 boolean is3Param = true;
150 for (int i = 3; i<towgs84Param.size(); i++) {
151 if (towgs84Param.get(i) != 0.0) {
152 is3Param = false;
153 break;
154 }
155 }
156 Datum datum = null;
157 if (is3Param) {
158 datum = new ThreeParameterDatum(null, null, ellps,
159 towgs84Param.get(0),
160 towgs84Param.get(1),
161 towgs84Param.get(2)
162 );
163 } else {
164 datum = new SevenParameterDatum(null, null, ellps,
165 towgs84Param.get(0),
166 towgs84Param.get(1),
167 towgs84Param.get(2),
168 towgs84Param.get(3),
169 towgs84Param.get(4),
170 towgs84Param.get(5),
171 towgs84Param.get(6)
172 );
173 }
174 return datum;
175 }
176
177 public Proj parseProjection(Map<String, String> parameters, Ellipsoid ellps) throws ProjectionConfigurationException {
178 String id = (String) parameters.get("proj");
179 if (id == null) throw new ProjectionConfigurationException(tr("Projection required (+proj=*)"));
180
181 Proj proj = Projections.getProjection(id);
182 if (proj == null) throw new ProjectionConfigurationException(tr("Unkown projection identifier: ''{0}''", id));
183
184 ProjParameters projParams = new ProjParameters();
185
186 projParams.ellps = ellps;
187
188 String s;
189 s = parameters.get("lat_0");
190 if (s != null) {
191 projParams.lat_0 = parseAngle(s, "lat_0");
192 }
193 s = parameters.get("lat_1");
194 if (s != null) {
195 projParams.lat_1 = parseAngle(s, "lat_1");
196 }
197 s = parameters.get("lat_2");
198 if (s != null) {
199 projParams.lat_2 = parseAngle(s, "lat_2");
200 }
201 proj.initialize(projParams);
202 return proj;
203
204 }
205
206 public double parseDouble(Map<String, String> parameters, String parameterName) throws ProjectionConfigurationException {
207 String doubleStr = parameters.get(parameterName);
208 if (doubleStr == null && parameters.containsKey(parameterName))
209 throw new ProjectionConfigurationException(
210 tr("Expected number argument for parameter ''{0}''", parameterName));
211 return parseDouble(doubleStr, parameterName);
212 }
213
214 public double parseDouble(String doubleStr, String parameterName) throws ProjectionConfigurationException {
215 try {
216 return Double.parseDouble(doubleStr);
217 } catch (NumberFormatException e) {
218 throw new ProjectionConfigurationException(
219 tr("Unable to parse value of ''{0}'' parameter ''{0}'' as number.", parameterName, doubleStr));
220 }
221 }
222
223 public double parseAngle(String angleStr, String parameterName) throws ProjectionConfigurationException {
224 String s = angleStr;
225 double value = 0;
226 boolean neg = false;
227 Matcher m = Pattern.compile("^-").matcher(s);
228 if (m.find()) {
229 neg = true;
230 s = s.substring(m.end());
231 }
232 final String FLOAT = "(\\d+(\\.\\d*)?)";
233 boolean dms = false;
234 // degrees
235 m = Pattern.compile("^"+FLOAT+"d").matcher(s);
236 if (m.find()) {
237 s = s.substring(m.end());
238 value += Double.parseDouble(m.group(1));
239 dms = true;
240 }
241 // minutes
242 m = Pattern.compile("^"+FLOAT+"'").matcher(s);
243 if (m.find()) {
244 s = s.substring(m.end());
245 value += Double.parseDouble(m.group(1)) / 60.0;
246 dms = true;
247 }
248 // seconds
249 m = Pattern.compile("^"+FLOAT+"\"").matcher(s);
250 if (m.find()) {
251 s = s.substring(m.end());
252 value += Double.parseDouble(m.group(1)) / 3600.0;
253 dms = true;
254 }
255 // plain number (in degrees)
256 if (!dms) {
257 m = Pattern.compile("^"+FLOAT).matcher(s);
258 if (m.find()) {
259 s = s.substring(m.end());
260 value += Double.parseDouble(m.group(1));
261 }
262 }
263 m = Pattern.compile("^(N|E)", Pattern.CASE_INSENSITIVE).matcher(s);
264 if (m.find()) {
265 s = s.substring(m.end());
266 } else {
267 m = Pattern.compile("^(S|W)", Pattern.CASE_INSENSITIVE).matcher(s);
268 if (m.find()) {
269 s = s.substring(m.end());
270 neg = !neg;
271 }
272 }
273 if (neg) {
274 value = -value;
275 }
276 if (!s.isEmpty()) {
277 throw new ProjectionConfigurationException(
278 tr("Unable to parse value of ''{0}'' parameter ''{0}'' as coordinate value.", parameterName, angleStr));
279 }
280 return value;
281 }
282
283 public void dump() {
284 System.err.println("x_0="+x_0);
285 System.err.println("y_0="+y_0);
286 System.err.println("lon_0="+lon_0);
287 System.err.println("k_0="+k_0);
288 System.err.println("ellps="+ellps);
289 System.err.println("proj="+proj);
290 System.err.println("datum="+datum);
291 }
292
293 @Override
294 public Integer getEpsgCode() {
295 return null;
296 }
297
298 @Override
299 public String toCode() {
300 return Utils.md5Hex(pref).substring(0, 10);
301 }
302
303 @Override
304 public String getCacheDirectoryName() {
305 return toCode();
306 }
307
308 @Override
309 public Bounds getWorldBoundsLatLon() {
310 return new Bounds(
311 new LatLon(-85.05112877980659, -180.0),
312 new LatLon(85.05112877980659, 180.0)); // FIXME
313 }
314
315 @Override
316 public String toString() {
317 return tr("Custom Projection (from PROJ.4 string)");
318 }
319
320 @Override
321 public void setupPreferencePanel(JPanel p, ActionListener listener) {
322 JTextField input = new JTextField(pref, 50);
323 p.setLayout(new GridBagLayout());
324 p.add(input);
325 }
326
327 @Override
328 public Collection<String> getPreferences(JPanel p) {
329 Object prefTf = p.getComponent(0);
330 if (!(prefTf instanceof JTextField))
331 return null;
332 String pref = ((JTextField) prefTf).getText();
333 return Collections.singleton(pref);
334 }
335
336 @Override
337 public void setPreferences(Collection<String> args) {
338 update(args.iterator().next());
339 }
340
341 @Override
342 public String[] allCodes() {
343 return new String[0];
344 }
345
346 @Override
347 public Collection<String> getPreferencesFromCode(String code) {
348 return null;
349 }
350
351}
Note: See TracBrowser for help on using the repository browser.