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

Last change on this file since 9135 was 9135, checked in by Don-vip, 8 years ago

checkstyle

  • Property svn:eol-style set to native
File size: 27.0 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.concurrent.ConcurrentHashMap;
11import java.util.regex.Matcher;
12import java.util.regex.Pattern;
13
14import org.openstreetmap.josm.Main;
15import org.openstreetmap.josm.data.Bounds;
16import org.openstreetmap.josm.data.coor.LatLon;
17import org.openstreetmap.josm.data.projection.datum.CentricDatum;
18import org.openstreetmap.josm.data.projection.datum.Datum;
19import org.openstreetmap.josm.data.projection.datum.NTV2Datum;
20import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileWrapper;
21import org.openstreetmap.josm.data.projection.datum.NullDatum;
22import org.openstreetmap.josm.data.projection.datum.SevenParameterDatum;
23import org.openstreetmap.josm.data.projection.datum.ThreeParameterDatum;
24import org.openstreetmap.josm.data.projection.datum.WGS84Datum;
25import org.openstreetmap.josm.data.projection.proj.Mercator;
26import org.openstreetmap.josm.data.projection.proj.Proj;
27import org.openstreetmap.josm.data.projection.proj.ProjParameters;
28import org.openstreetmap.josm.tools.Utils;
29
30/**
31 * Custom projection.
32 *
33 * Inspired by PROJ.4 and Proj4J.
34 * @since 5072
35 */
36public class CustomProjection extends AbstractProjection {
37
38 private static final double METER_PER_UNIT_DEGREE = 2 * Math.PI * 6370997 / 360;
39 private static final Map<String, Double> UNITS_TO_METERS = getUnitsToMeters();
40 private static final Map<String, Double> PRIME_MERIDANS = getPrimeMeridians();
41
42 /**
43 * pref String that defines the projection
44 *
45 * null means fall back mode (Mercator)
46 */
47 protected String pref;
48 protected String name;
49 protected String code;
50 protected String cacheDir;
51 protected Bounds bounds;
52 private double metersPerUnit = METER_PER_UNIT_DEGREE; // default to degrees
53 private String axis = "enu"; // default axis orientation is East, North, Up
54
55 /**
56 * Proj4-like projection parameters. See <a href="https://trac.osgeo.org/proj/wiki/GenParms">reference</a>.
57 * @since 7370 (public)
58 */
59 public enum Param {
60
61 /** False easting */
62 x_0("x_0", true),
63 /** False northing */
64 y_0("y_0", true),
65 /** Central meridian */
66 lon_0("lon_0", true),
67 /** Prime meridian */
68 pm("pm", true),
69 /** Scaling factor */
70 k_0("k_0", true),
71 /** Ellipsoid name (see {@code proj -le}) */
72 ellps("ellps", true),
73 /** Semimajor radius of the ellipsoid axis */
74 a("a", true),
75 /** Eccentricity of the ellipsoid squared */
76 es("es", true),
77 /** Reciprocal of the ellipsoid flattening term (e.g. 298) */
78 rf("rf", true),
79 /** Flattening of the ellipsoid = 1-sqrt(1-e^2) */
80 f("f", true),
81 /** Semiminor radius of the ellipsoid axis */
82 b("b", true),
83 /** Datum name (see {@code proj -ld}) */
84 datum("datum", true),
85 /** 3 or 7 term datum transform parameters */
86 towgs84("towgs84", true),
87 /** Filename of NTv2 grid file to use for datum transforms */
88 nadgrids("nadgrids", true),
89 /** Projection name (see {@code proj -l}) */
90 proj("proj", true),
91 /** Latitude of origin */
92 lat_0("lat_0", true),
93 /** Latitude of first standard parallel */
94 lat_1("lat_1", true),
95 /** Latitude of second standard parallel */
96 lat_2("lat_2", true),
97 /** the exact proj.4 string will be preserved in the WKT representation */
98 wktext("wktext", false), // ignored
99 /** meters, US survey feet, etc. */
100 units("units", true),
101 /** Don't use the /usr/share/proj/proj_def.dat defaults file */
102 no_defs("no_defs", false),
103 init("init", true),
104 /** crs units to meter multiplier */
105 to_meter("to_meter", true),
106 /** definition of axis for projection */
107 axis("axis", true),
108 /** UTM zone */
109 zone("zone", true),
110 /** indicate southern hemisphere for UTM */
111 south("south", false),
112 /** vertical units - ignore, as we don't use height information */
113 vunits("vunits", true),
114 // JOSM extensions, not present in PROJ.4
115 wmssrs("wmssrs", true),
116 bounds("bounds", true);
117
118 /** Parameter key */
119 public final String key;
120 /** {@code true} if the parameter has a value */
121 public final boolean hasValue;
122
123 /** Map of all parameters by key */
124 static final Map<String, Param> paramsByKey = new ConcurrentHashMap<>();
125 static {
126 for (Param p : Param.values()) {
127 paramsByKey.put(p.key, p);
128 }
129 }
130
131 Param(String key, boolean hasValue) {
132 this.key = key;
133 this.hasValue = hasValue;
134 }
135 }
136
137 /**
138 * Constructs a new empty {@code CustomProjection}.
139 */
140 public CustomProjection() {
141 // contents can be set later with update()
142 }
143
144 /**
145 * Constructs a new {@code CustomProjection} with given parameters.
146 * @param pref String containing projection parameters
147 * (ex: "+proj=tmerc +lon_0=-3 +k_0=0.9996 +x_0=500000 +ellps=WGS84 +datum=WGS84 +bounds=-8,-5,2,85")
148 */
149 public CustomProjection(String pref) {
150 this(null, null, pref, null);
151 }
152
153 /**
154 * Constructs a new {@code CustomProjection} with given name, code and parameters.
155 *
156 * @param name describe projection in one or two words
157 * @param code unique code for this projection - may be null
158 * @param pref the string that defines the custom projection
159 * @param cacheDir cache directory name
160 */
161 public CustomProjection(String name, String code, String pref, String cacheDir) {
162 this.name = name;
163 this.code = code;
164 this.pref = pref;
165 this.cacheDir = cacheDir;
166 try {
167 update(pref);
168 } catch (ProjectionConfigurationException ex) {
169 try {
170 update(null);
171 } catch (ProjectionConfigurationException ex1) {
172 throw new RuntimeException(ex1);
173 }
174 }
175 }
176
177 /**
178 * Updates this {@code CustomProjection} with given parameters.
179 * @param pref String containing projection parameters (ex: "+proj=lonlat +ellps=WGS84 +datum=WGS84 +bounds=-180,-90,180,90")
180 * @throws ProjectionConfigurationException if {@code pref} cannot be parsed properly
181 */
182 public final void update(String pref) throws ProjectionConfigurationException {
183 this.pref = pref;
184 if (pref == null) {
185 ellps = Ellipsoid.WGS84;
186 datum = WGS84Datum.INSTANCE;
187 proj = new Mercator();
188 bounds = new Bounds(
189 -85.05112877980659, -180.0,
190 85.05112877980659, 180.0, true);
191 } else {
192 Map<String, String> parameters = parseParameterList(pref, false);
193 parameters = resolveInits(parameters, false);
194 ellps = parseEllipsoid(parameters);
195 datum = parseDatum(parameters, ellps);
196 if (ellps == null) {
197 ellps = datum.getEllipsoid();
198 }
199 proj = parseProjection(parameters, ellps);
200 // "utm" is a shortcut for a set of parameters
201 if ("utm".equals(parameters.get(Param.proj.key))) {
202 String zoneStr = parameters.get(Param.zone.key);
203 Integer zone;
204 if (zoneStr == null)
205 throw new ProjectionConfigurationException(tr("UTM projection (''+proj=utm'') requires ''+zone=...'' parameter."));
206 try {
207 zone = Integer.valueOf(zoneStr);
208 } catch (NumberFormatException e) {
209 zone = null;
210 }
211 if (zone == null || zone < 1 || zone > 60)
212 throw new ProjectionConfigurationException(tr("Expected integer value in range 1-60 for ''+zone=...'' parameter."));
213 this.lon0 = 6 * zone - 183;
214 this.k0 = 0.9996;
215 this.x0 = 500000;
216 this.y0 = parameters.containsKey(Param.south.key) ? 10000000 : 0;
217 }
218 String s = parameters.get(Param.x_0.key);
219 if (s != null) {
220 this.x0 = parseDouble(s, Param.x_0.key);
221 }
222 s = parameters.get(Param.y_0.key);
223 if (s != null) {
224 this.y0 = parseDouble(s, Param.y_0.key);
225 }
226 s = parameters.get(Param.lon_0.key);
227 if (s != null) {
228 this.lon0 = parseAngle(s, Param.lon_0.key);
229 }
230 s = parameters.get(Param.pm.key);
231 if (s != null) {
232 if (PRIME_MERIDANS.containsKey(s)) {
233 this.pm = PRIME_MERIDANS.get(s);
234 } else {
235 this.pm = parseAngle(s, Param.pm.key);
236 }
237 }
238 s = parameters.get(Param.k_0.key);
239 if (s != null) {
240 this.k0 = parseDouble(s, Param.k_0.key);
241 }
242 s = parameters.get(Param.bounds.key);
243 if (s != null) {
244 this.bounds = parseBounds(s);
245 }
246 s = parameters.get(Param.wmssrs.key);
247 if (s != null) {
248 this.code = s;
249 }
250 s = parameters.get(Param.units.key);
251 if (s != null) {
252 s = Utils.strip(s, "\"");
253 if (UNITS_TO_METERS.containsKey(s)) {
254 this.metersPerUnit = UNITS_TO_METERS.get(s);
255 } else {
256 Main.warn("No metersPerUnit found for: " + s);
257 }
258 }
259 s = parameters.get(Param.to_meter.key);
260 if (s != null) {
261 this.metersPerUnit = parseDouble(s, Param.to_meter.key);
262 }
263 s = parameters.get(Param.axis.key);
264 if (s != null) {
265 this.axis = s;
266 }
267 }
268 }
269
270 /**
271 * Parse a parameter list to key=value pairs.
272 *
273 * @param pref the parameter list
274 * @param ignoreUnknownParameter true, if unknown parameter should not raise exception
275 * @return parameters map
276 * @throws ProjectionConfigurationException in case of invalid parameter
277 */
278 public static Map<String, String> parseParameterList(String pref, boolean ignoreUnknownParameter) throws ProjectionConfigurationException {
279 Map<String, String> parameters = new HashMap<>();
280 String[] parts = Utils.WHITE_SPACES_PATTERN.split(pref.trim());
281 if (pref.trim().isEmpty()) {
282 parts = new String[0];
283 }
284 for (String part : parts) {
285 if (part.isEmpty() || part.charAt(0) != '+')
286 throw new ProjectionConfigurationException(tr("Parameter must begin with a ''+'' character (found ''{0}'')", part));
287 Matcher m = Pattern.compile("\\+([a-zA-Z0-9_]+)(=(.*))?").matcher(part);
288 if (m.matches()) {
289 String key = m.group(1);
290 // alias
291 if ("k".equals(key)) {
292 key = Param.k_0.key;
293 }
294 String value = null;
295 if (m.groupCount() >= 3) {
296 value = m.group(3);
297 // some aliases
298 if (key.equals(Param.proj.key)) {
299 if ("longlat".equals(value) || "latlon".equals(value) || "latlong".equals(value)) {
300 value = "lonlat";
301 }
302 }
303 }
304 if (!Param.paramsByKey.containsKey(key)) {
305 if (!ignoreUnknownParameter)
306 throw new ProjectionConfigurationException(tr("Unknown parameter: ''{0}''.", key));
307 } else {
308 if (Param.paramsByKey.get(key).hasValue && value == null)
309 throw new ProjectionConfigurationException(tr("Value expected for parameter ''{0}''.", key));
310 if (!Param.paramsByKey.get(key).hasValue && value != null)
311 throw new ProjectionConfigurationException(tr("No value expected for parameter ''{0}''.", key));
312 }
313 parameters.put(key, value);
314 } else
315 throw new ProjectionConfigurationException(tr("Unexpected parameter format (''{0}'')", part));
316 }
317 return parameters;
318 }
319
320 /**
321 * Recursive resolution of +init includes.
322 *
323 * @param parameters parameters map
324 * @param ignoreUnknownParameter true, if unknown parameter should not raise exception
325 * @return parameters map with +init includes resolved
326 * @throws ProjectionConfigurationException in case of invalid parameter
327 */
328 public static Map<String, String> resolveInits(Map<String, String> parameters, boolean ignoreUnknownParameter)
329 throws ProjectionConfigurationException {
330 // recursive resolution of +init includes
331 String initKey = parameters.get(Param.init.key);
332 if (initKey != null) {
333 String init = Projections.getInit(initKey);
334 if (init == null)
335 throw new ProjectionConfigurationException(tr("Value ''{0}'' for option +init not supported.", initKey));
336 Map<String, String> initp;
337 try {
338 initp = parseParameterList(init, ignoreUnknownParameter);
339 initp = resolveInits(initp, ignoreUnknownParameter);
340 } catch (ProjectionConfigurationException ex) {
341 throw new ProjectionConfigurationException(initKey+": "+ex.getMessage(), ex);
342 }
343 initp.putAll(parameters);
344 return initp;
345 }
346 return parameters;
347 }
348
349 public Ellipsoid parseEllipsoid(Map<String, String> parameters) throws ProjectionConfigurationException {
350 String code = parameters.get(Param.ellps.key);
351 if (code != null) {
352 Ellipsoid ellipsoid = Projections.getEllipsoid(code);
353 if (ellipsoid == null) {
354 throw new ProjectionConfigurationException(tr("Ellipsoid ''{0}'' not supported.", code));
355 } else {
356 return ellipsoid;
357 }
358 }
359 String s = parameters.get(Param.a.key);
360 if (s != null) {
361 double a = parseDouble(s, Param.a.key);
362 if (parameters.get(Param.es.key) != null) {
363 double es = parseDouble(parameters, Param.es.key);
364 return Ellipsoid.create_a_es(a, es);
365 }
366 if (parameters.get(Param.rf.key) != null) {
367 double rf = parseDouble(parameters, Param.rf.key);
368 return Ellipsoid.create_a_rf(a, rf);
369 }
370 if (parameters.get(Param.f.key) != null) {
371 double f = parseDouble(parameters, Param.f.key);
372 return Ellipsoid.create_a_f(a, f);
373 }
374 if (parameters.get(Param.b.key) != null) {
375 double b = parseDouble(parameters, Param.b.key);
376 return Ellipsoid.create_a_b(a, b);
377 }
378 }
379 if (parameters.containsKey(Param.a.key) ||
380 parameters.containsKey(Param.es.key) ||
381 parameters.containsKey(Param.rf.key) ||
382 parameters.containsKey(Param.f.key) ||
383 parameters.containsKey(Param.b.key))
384 throw new ProjectionConfigurationException(tr("Combination of ellipsoid parameters is not supported."));
385 return null;
386 }
387
388 public Datum parseDatum(Map<String, String> parameters, Ellipsoid ellps) throws ProjectionConfigurationException {
389 String datumId = parameters.get(Param.datum.key);
390 if (datumId != null) {
391 Datum datum = Projections.getDatum(datumId);
392 if (datum == null) throw new ProjectionConfigurationException(tr("Unknown datum identifier: ''{0}''", datumId));
393 return datum;
394 }
395 if (ellps == null) {
396 if (parameters.containsKey(Param.no_defs.key))
397 throw new ProjectionConfigurationException(tr("Ellipsoid required (+ellps=* or +a=*, +b=*)"));
398 // nothing specified, use WGS84 as default
399 ellps = Ellipsoid.WGS84;
400 }
401
402 String nadgridsId = parameters.get(Param.nadgrids.key);
403 if (nadgridsId != null) {
404 if (nadgridsId.startsWith("@")) {
405 nadgridsId = nadgridsId.substring(1);
406 }
407 if ("null".equals(nadgridsId))
408 return new NullDatum(null, ellps);
409 NTV2GridShiftFileWrapper nadgrids = Projections.getNTV2Grid(nadgridsId);
410 if (nadgrids == null)
411 throw new ProjectionConfigurationException(tr("Grid shift file ''{0}'' for option +nadgrids not supported.", nadgridsId));
412 return new NTV2Datum(nadgridsId, null, ellps, nadgrids);
413 }
414
415 String towgs84 = parameters.get(Param.towgs84.key);
416 if (towgs84 != null)
417 return parseToWGS84(towgs84, ellps);
418
419 return new NullDatum(null, ellps);
420 }
421
422 public Datum parseToWGS84(String paramList, Ellipsoid ellps) throws ProjectionConfigurationException {
423 String[] numStr = paramList.split(",");
424
425 if (numStr.length != 3 && numStr.length != 7)
426 throw new ProjectionConfigurationException(tr("Unexpected number of arguments for parameter ''towgs84'' (must be 3 or 7)"));
427 List<Double> towgs84Param = new ArrayList<>();
428 for (String str : numStr) {
429 try {
430 towgs84Param.add(Double.valueOf(str));
431 } catch (NumberFormatException e) {
432 throw new ProjectionConfigurationException(tr("Unable to parse value of parameter ''towgs84'' (''{0}'')", str), e);
433 }
434 }
435 boolean isCentric = true;
436 for (Double param : towgs84Param) {
437 if (param != 0) {
438 isCentric = false;
439 break;
440 }
441 }
442 if (isCentric)
443 return new CentricDatum(null, null, ellps);
444 boolean is3Param = true;
445 for (int i = 3; i < towgs84Param.size(); i++) {
446 if (towgs84Param.get(i) != 0) {
447 is3Param = false;
448 break;
449 }
450 }
451 if (is3Param)
452 return new ThreeParameterDatum(null, null, ellps,
453 towgs84Param.get(0),
454 towgs84Param.get(1),
455 towgs84Param.get(2));
456 else
457 return new SevenParameterDatum(null, null, ellps,
458 towgs84Param.get(0),
459 towgs84Param.get(1),
460 towgs84Param.get(2),
461 towgs84Param.get(3),
462 towgs84Param.get(4),
463 towgs84Param.get(5),
464 towgs84Param.get(6));
465 }
466
467 public Proj parseProjection(Map<String, String> parameters, Ellipsoid ellps) throws ProjectionConfigurationException {
468 String id = parameters.get(Param.proj.key);
469 if (id == null) throw new ProjectionConfigurationException(tr("Projection required (+proj=*)"));
470
471 // "utm" is not a real projection, but a shortcut for a set of parameters
472 if ("utm".equals(id)) {
473 id = "tmerc";
474 }
475 Proj proj = Projections.getBaseProjection(id);
476 if (proj == null) throw new ProjectionConfigurationException(tr("Unknown projection identifier: ''{0}''", id));
477
478 ProjParameters projParams = new ProjParameters();
479
480 projParams.ellps = ellps;
481
482 String s;
483 s = parameters.get(Param.lat_0.key);
484 if (s != null) {
485 projParams.lat0 = parseAngle(s, Param.lat_0.key);
486 }
487 s = parameters.get(Param.lat_1.key);
488 if (s != null) {
489 projParams.lat1 = parseAngle(s, Param.lat_1.key);
490 }
491 s = parameters.get(Param.lat_2.key);
492 if (s != null) {
493 projParams.lat2 = parseAngle(s, Param.lat_2.key);
494 }
495 proj.initialize(projParams);
496 return proj;
497 }
498
499 public static Bounds parseBounds(String boundsStr) throws ProjectionConfigurationException {
500 String[] numStr = boundsStr.split(",");
501 if (numStr.length != 4)
502 throw new ProjectionConfigurationException(tr("Unexpected number of arguments for parameter ''+bounds'' (must be 4)"));
503 return new Bounds(parseAngle(numStr[1], "minlat (+bounds)"),
504 parseAngle(numStr[0], "minlon (+bounds)"),
505 parseAngle(numStr[3], "maxlat (+bounds)"),
506 parseAngle(numStr[2], "maxlon (+bounds)"), false);
507 }
508
509 public static double parseDouble(Map<String, String> parameters, String parameterName) throws ProjectionConfigurationException {
510 if (!parameters.containsKey(parameterName))
511 throw new ProjectionConfigurationException(tr("Unknown parameter ''{0}''", parameterName));
512 String doubleStr = parameters.get(parameterName);
513 if (doubleStr == null)
514 throw new ProjectionConfigurationException(
515 tr("Expected number argument for parameter ''{0}''", parameterName));
516 return parseDouble(doubleStr, parameterName);
517 }
518
519 public static double parseDouble(String doubleStr, String parameterName) throws ProjectionConfigurationException {
520 try {
521 return Double.parseDouble(doubleStr);
522 } catch (NumberFormatException e) {
523 throw new ProjectionConfigurationException(
524 tr("Unable to parse value ''{1}'' of parameter ''{0}'' as number.", parameterName, doubleStr), e);
525 }
526 }
527
528 public static double parseAngle(String angleStr, String parameterName) throws ProjectionConfigurationException {
529 String s = angleStr;
530 double value = 0;
531 boolean neg = false;
532 Matcher m = Pattern.compile("^-").matcher(s);
533 if (m.find()) {
534 neg = true;
535 s = s.substring(m.end());
536 }
537 final String FLOAT = "(\\d+(\\.\\d*)?)";
538 boolean dms = false;
539 double deg = 0.0, min = 0.0, sec = 0.0;
540 // degrees
541 m = Pattern.compile("^"+FLOAT+"d").matcher(s);
542 if (m.find()) {
543 s = s.substring(m.end());
544 deg = Double.parseDouble(m.group(1));
545 dms = true;
546 }
547 // minutes
548 m = Pattern.compile("^"+FLOAT+"'").matcher(s);
549 if (m.find()) {
550 s = s.substring(m.end());
551 min = Double.parseDouble(m.group(1));
552 dms = true;
553 }
554 // seconds
555 m = Pattern.compile("^"+FLOAT+"\"").matcher(s);
556 if (m.find()) {
557 s = s.substring(m.end());
558 sec = Double.parseDouble(m.group(1));
559 dms = true;
560 }
561 // plain number (in degrees)
562 if (dms) {
563 value = deg + (min/60.0) + (sec/3600.0);
564 } else {
565 m = Pattern.compile("^"+FLOAT).matcher(s);
566 if (m.find()) {
567 s = s.substring(m.end());
568 value += Double.parseDouble(m.group(1));
569 }
570 }
571 m = Pattern.compile("^(N|E)", Pattern.CASE_INSENSITIVE).matcher(s);
572 if (m.find()) {
573 s = s.substring(m.end());
574 } else {
575 m = Pattern.compile("^(S|W)", Pattern.CASE_INSENSITIVE).matcher(s);
576 if (m.find()) {
577 s = s.substring(m.end());
578 neg = !neg;
579 }
580 }
581 if (neg) {
582 value = -value;
583 }
584 if (!s.isEmpty()) {
585 throw new ProjectionConfigurationException(
586 tr("Unable to parse value ''{1}'' of parameter ''{0}'' as coordinate value.", parameterName, angleStr));
587 }
588 return value;
589 }
590
591 @Override
592 public Integer getEpsgCode() {
593 if (code != null && code.startsWith("EPSG:")) {
594 try {
595 return Integer.valueOf(code.substring(5));
596 } catch (NumberFormatException e) {
597 Main.warn(e);
598 }
599 }
600 return null;
601 }
602
603 @Override
604 public String toCode() {
605 return code != null ? code : "proj:" + (pref == null ? "ERROR" : pref);
606 }
607
608 @Override
609 public String getCacheDirectoryName() {
610 return cacheDir != null ? cacheDir : "proj-"+Utils.md5Hex(pref == null ? "" : pref).substring(0, 4);
611 }
612
613 @Override
614 public Bounds getWorldBoundsLatLon() {
615 if (bounds != null) return bounds;
616 Bounds ab = proj.getAlgorithmBounds();
617 if (ab != null) {
618 double minlon = Math.max(ab.getMinLon() + lon0 + pm, -180);
619 double maxlon = Math.min(ab.getMaxLon() + lon0 + pm, 180);
620 return new Bounds(ab.getMinLat(), minlon, ab.getMaxLat(), maxlon, false);
621 } else {
622 return new Bounds(
623 new LatLon(-90.0, -180.0),
624 new LatLon(90.0, 180.0));
625 }
626 }
627
628 @Override
629 public String toString() {
630 return name != null ? name : tr("Custom Projection");
631 }
632
633 @Override
634 public double getMetersPerUnit() {
635 return metersPerUnit;
636 }
637
638 @Override
639 public boolean switchXY() {
640 // TODO: support for other axis orientation such as West South, and Up Down
641 return this.axis.startsWith("ne");
642 }
643
644 private static Map<String, Double> getUnitsToMeters() {
645 Map<String, Double> ret = new ConcurrentHashMap<>();
646 ret.put("km", 1000d);
647 ret.put("m", 1d);
648 ret.put("dm", 1d/10);
649 ret.put("cm", 1d/100);
650 ret.put("mm", 1d/1000);
651 ret.put("kmi", 1852.0);
652 ret.put("in", 0.0254);
653 ret.put("ft", 0.3048);
654 ret.put("yd", 0.9144);
655 ret.put("mi", 1609.344);
656 ret.put("fathom", 1.8288);
657 ret.put("chain", 20.1168);
658 ret.put("link", 0.201168);
659 ret.put("us-in", 1d/39.37);
660 ret.put("us-ft", 0.304800609601219);
661 ret.put("us-yd", 0.914401828803658);
662 ret.put("us-ch", 20.11684023368047);
663 ret.put("us-mi", 1609.347218694437);
664 ret.put("ind-yd", 0.91439523);
665 ret.put("ind-ft", 0.30479841);
666 ret.put("ind-ch", 20.11669506);
667 ret.put("degree", METER_PER_UNIT_DEGREE);
668 return ret;
669 }
670
671 private static Map<String, Double> getPrimeMeridians() {
672 Map<String, Double> ret = new ConcurrentHashMap<>();
673 try {
674 ret.put("greenwich", 0.0);
675 ret.put("lisbon", parseAngle("9d07'54.862\"W", null));
676 ret.put("paris", parseAngle("2d20'14.025\"E", null));
677 ret.put("bogota", parseAngle("74d04'51.3\"W", null));
678 ret.put("madrid", parseAngle("3d41'16.58\"W", null));
679 ret.put("rome", parseAngle("12d27'8.4\"E", null));
680 ret.put("bern", parseAngle("7d26'22.5\"E", null));
681 ret.put("jakarta", parseAngle("106d48'27.79\"E", null));
682 ret.put("ferro", parseAngle("17d40'W", null));
683 ret.put("brussels", parseAngle("4d22'4.71\"E", null));
684 ret.put("stockholm", parseAngle("18d3'29.8\"E", null));
685 ret.put("athens", parseAngle("23d42'58.815\"E", null));
686 ret.put("oslo", parseAngle("10d43'22.5\"E", null));
687 } catch (ProjectionConfigurationException ex) {
688 throw new RuntimeException();
689 }
690 return ret;
691 }
692}
Note: See TracBrowser for help on using the repository browser.