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

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

improvements for custom projection

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