source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/projection/ProjectionPreference.java@ 8444

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

remove extra whitespaces

  • Property svn:eol-style set to native
File size: 19.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences.projection;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.awt.GridBagLayout;
8import java.awt.event.ActionEvent;
9import java.awt.event.ActionListener;
10import java.util.ArrayList;
11import java.util.Collection;
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.List;
15import java.util.Map;
16
17import javax.swing.BorderFactory;
18import javax.swing.JLabel;
19import javax.swing.JOptionPane;
20import javax.swing.JPanel;
21import javax.swing.JScrollPane;
22import javax.swing.JSeparator;
23
24import org.openstreetmap.josm.Main;
25import org.openstreetmap.josm.data.Bounds;
26import org.openstreetmap.josm.data.SystemOfMeasurement;
27import org.openstreetmap.josm.data.coor.CoordinateFormat;
28import org.openstreetmap.josm.data.preferences.CollectionProperty;
29import org.openstreetmap.josm.data.preferences.StringProperty;
30import org.openstreetmap.josm.data.projection.CustomProjection;
31import org.openstreetmap.josm.data.projection.Projection;
32import org.openstreetmap.josm.gui.NavigatableComponent;
33import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
34import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
35import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
36import org.openstreetmap.josm.gui.preferences.SubPreferenceSetting;
37import org.openstreetmap.josm.gui.preferences.TabPreferenceSetting;
38import org.openstreetmap.josm.gui.widgets.JosmComboBox;
39import org.openstreetmap.josm.tools.GBC;
40
41/**
42 * Projection preferences.
43 *
44 * How to add new Projections:
45 * - Find EPSG code for the projection.
46 * - Look up the parameter string for Proj4, e.g. on http://spatialreference.org/
47 * and add it to the file 'data/projection/epsg' in JOSM trunk
48 * - Search for official references and verify the parameter values. These
49 * documents are often available in the local language only.
50 * - Use {@link #registerProjectionChoice}, to make the entry known to JOSM.
51 *
52 * In case there is no EPSG code:
53 * - override {@link AbstractProjectionChoice#getProjection()} and provide
54 * a manual implementation of the projection. Use {@link CustomProjection}
55 * if possible.
56 */
57public class ProjectionPreference implements SubPreferenceSetting {
58
59 /**
60 * Factory used to create a new {@code ProjectionPreference}.
61 */
62 public static class Factory implements PreferenceSettingFactory {
63 @Override
64 public PreferenceSetting createPreferenceSetting() {
65 return new ProjectionPreference();
66 }
67 }
68
69 private static List<ProjectionChoice> projectionChoices = new ArrayList<>();
70 private static Map<String, ProjectionChoice> projectionChoicesById = new HashMap<>();
71
72 // some ProjectionChoices that are referenced from other parts of the code
73 public static final ProjectionChoice wgs84, mercator, lambert, utm_france_dom, lambert_cc9;
74
75 static {
76
77 /************************
78 * Global projections.
79 */
80
81 /**
82 * WGS84: Directly use latitude / longitude values as x/y.
83 */
84 wgs84 = registerProjectionChoice(tr("WGS84 Geographic"), "core:wgs84", 4326, "epsg4326");
85
86 /**
87 * Mercator Projection.
88 *
89 * The center of the mercator projection is always the 0 grad
90 * coordinate.
91 *
92 * See also USGS Bulletin 1532
93 * (http://pubs.usgs.gov/bul/1532/report.pdf)
94 * initially EPSG used 3785 but that has been superseded by 3857,
95 * see https://www.epsg-registry.org/
96 */
97 mercator = registerProjectionChoice(tr("Mercator"), "core:mercator", 3857);
98
99 /**
100 * UTM.
101 */
102 registerProjectionChoice(new UTMProjectionChoice());
103
104 /************************
105 * Regional - alphabetical order by country code.
106 */
107
108 /**
109 * Belgian Lambert 72 projection.
110 *
111 * As specified by the Belgian IGN in this document:
112 * http://www.ngi.be/Common/Lambert2008/Transformation_Geographic_Lambert_FR.pdf
113 *
114 * @author Don-vip
115 */
116 registerProjectionChoice(tr("Belgian Lambert 1972"), "core:belgianLambert1972", 31370); // BE
117
118 /**
119 * Belgian Lambert 2008 projection.
120 *
121 * As specified by the Belgian IGN in this document:
122 * http://www.ngi.be/Common/Lambert2008/Transformation_Geographic_Lambert_FR.pdf
123 *
124 * @author Don-vip
125 */
126 registerProjectionChoice(tr("Belgian Lambert 2008"), "core:belgianLambert2008", 3812); // BE
127
128 /**
129 * SwissGrid CH1903 / L03, see https://en.wikipedia.org/wiki/Swiss_coordinate_system.
130 *
131 * Actually, what we have here, is CH1903+ (EPSG:2056), but without
132 * the additional false easting of 2000km and false northing 1000 km.
133 *
134 * To get to CH1903, a shift file is required. So currently, there are errors
135 * up to 1.6m (depending on the location).
136 */
137 registerProjectionChoice(new SwissGridProjectionChoice()); // CH
138
139 registerProjectionChoice(new GaussKruegerProjectionChoice()); // DE
140
141 /**
142 * Estonian Coordinate System of 1997.
143 *
144 * Thanks to Johan Montagnat and its geoconv java converter application
145 * (https://www.i3s.unice.fr/~johan/gps/ , published under GPL license)
146 * from which some code and constants have been reused here.
147 */
148 registerProjectionChoice(tr("Lambert Zone (Estonia)"), "core:lambertest", 3301); // EE
149
150 /**
151 * Lambert conic conform 4 zones using the French geodetic system NTF.
152 *
153 * This newer version uses the grid translation NTF<->RGF93 provided by IGN for a submillimetric accuracy.
154 * (RGF93 is the French geodetic system similar to WGS84 but not mathematically equal)
155 *
156 * Source: http://geodesie.ign.fr/contenu/fichiers/Changement_systeme_geodesique.pdf
157 * @author Pieren
158 */
159 registerProjectionChoice(lambert = new LambertProjectionChoice()); // FR
160
161 /**
162 * Lambert 93 projection.
163 *
164 * As specified by the IGN in this document
165 * http://geodesie.ign.fr/contenu/fichiers/documentation/rgf93/Lambert-93.pdf
166 * @author Don-vip
167 */
168 registerProjectionChoice(tr("Lambert 93 (France)"), "core:lambert93", 2154); // FR
169
170 /**
171 * Lambert Conic Conform 9 Zones projection.
172 *
173 * As specified by the IGN in this document
174 * http://geodesie.ign.fr/contenu/fichiers/documentation/rgf93/cc9zones.pdf
175 * @author Pieren
176 */
177 registerProjectionChoice(lambert_cc9 = new LambertCC9ZonesProjectionChoice()); // FR
178
179 /**
180 * French departements in the Caribbean Sea and Indian Ocean.
181 *
182 * Using the UTM transvers Mercator projection and specific geodesic settings.
183 */
184 registerProjectionChoice(utm_france_dom = new UTMFranceDOMProjectionChoice()); // FR
185
186 /**
187 * LKS-92/ Latvia TM projection.
188 *
189 * Based on data from spatialreference.org.
190 * http://spatialreference.org/ref/epsg/3059/
191 *
192 * @author Viesturs Zarins
193 */
194 registerProjectionChoice(tr("LKS-92 (Latvia TM)"), "core:tmerclv", 3059); // LV
195
196 /**
197 * PUWG 1992 and 2000 are the official cordinate systems in Poland.
198 *
199 * They use the same math as UTM only with different constants.
200 *
201 * @author steelman
202 */
203 registerProjectionChoice(new PuwgProjectionChoice()); // PL
204
205 /**
206 * SWEREF99 13 30 projection. Based on data from spatialreference.org.
207 * http://spatialreference.org/ref/epsg/3008/
208 *
209 * @author Hanno Hecker
210 */
211 registerProjectionChoice(tr("SWEREF99 13 30 / EPSG:3008 (Sweden)"), "core:sweref99", 3008); // SE
212
213 /************************
214 * Projection by Code.
215 */
216 registerProjectionChoice(new CodeProjectionChoice());
217
218 /************************
219 * Custom projection.
220 */
221 registerProjectionChoice(new CustomProjectionChoice());
222 }
223
224 public static void registerProjectionChoice(ProjectionChoice c) {
225 projectionChoices.add(c);
226 projectionChoicesById.put(c.getId(), c);
227 }
228
229 public static ProjectionChoice registerProjectionChoice(String name, String id, Integer epsg, String cacheDir) {
230 ProjectionChoice pc = new SingleProjectionChoice(name, id, "EPSG:"+epsg, cacheDir);
231 registerProjectionChoice(pc);
232 return pc;
233 }
234
235 private static ProjectionChoice registerProjectionChoice(String name, String id, Integer epsg) {
236 ProjectionChoice pc = new SingleProjectionChoice(name, id, "EPSG:"+epsg);
237 registerProjectionChoice(pc);
238 return pc;
239 }
240
241 public static List<ProjectionChoice> getProjectionChoices() {
242 return Collections.unmodifiableList(projectionChoices);
243 }
244
245 private static final StringProperty PROP_PROJECTION = new StringProperty("projection", mercator.getId());
246 private static final StringProperty PROP_COORDINATES = new StringProperty("coordinates", null);
247 private static final CollectionProperty PROP_SUB_PROJECTION = new CollectionProperty("projection.sub", null);
248 public static final StringProperty PROP_SYSTEM_OF_MEASUREMENT = new StringProperty("system_of_measurement", "Metric");
249 private static final String[] unitsValues = (new ArrayList<>(SystemOfMeasurement.ALL_SYSTEMS.keySet())).toArray(new String[0]);
250 private static final String[] unitsValuesTr = new String[unitsValues.length];
251 static {
252 for (int i=0; i<unitsValues.length; ++i) {
253 unitsValuesTr[i] = tr(unitsValues[i]);
254 }
255 }
256
257 /**
258 * Combobox with all projections available
259 */
260 private final JosmComboBox<ProjectionChoice> projectionCombo = new JosmComboBox<>(projectionChoices.toArray(new ProjectionChoice[0]));
261
262 /**
263 * Combobox with all coordinate display possibilities
264 */
265 private final JosmComboBox<CoordinateFormat> coordinatesCombo = new JosmComboBox<>(CoordinateFormat.values());
266
267 private final JosmComboBox<String> unitsCombo = new JosmComboBox<>(unitsValuesTr);
268
269 /**
270 * This variable holds the JPanel with the projection's preferences. If the
271 * selected projection does not implement this, it will be set to an empty
272 * Panel.
273 */
274 private JPanel projSubPrefPanel;
275 private JPanel projSubPrefPanelWrapper = new JPanel(new GridBagLayout());
276
277 private JLabel projectionCodeLabel;
278 private Component projectionCodeGlue;
279 private JLabel projectionCode = new JLabel();
280 private JLabel projectionNameLabel;
281 private Component projectionNameGlue;
282 private JLabel projectionName = new JLabel();
283 private JLabel bounds = new JLabel();
284
285 /**
286 * This is the panel holding all projection preferences
287 */
288 private final JPanel projPanel = new JPanel(new GridBagLayout());
289
290 /**
291 * The GridBagConstraints for the Panel containing the ProjectionSubPrefs.
292 * This is required twice in the code, creating it here keeps both occurrences
293 * in sync
294 */
295 private static final GBC projSubPrefPanelGBC = GBC.std().fill(GBC.BOTH).weight(1.0, 1.0);
296
297 @Override
298 public void addGui(PreferenceTabbedPane gui) {
299 ProjectionChoice pc = setupProjectionCombo();
300
301 for (int i = 0; i < coordinatesCombo.getItemCount(); ++i) {
302 if (coordinatesCombo.getItemAt(i).name().equals(PROP_COORDINATES.get())) {
303 coordinatesCombo.setSelectedIndex(i);
304 break;
305 }
306 }
307
308 for (int i = 0; i < unitsValues.length; ++i) {
309 if (unitsValues[i].equals(PROP_SYSTEM_OF_MEASUREMENT.get())) {
310 unitsCombo.setSelectedIndex(i);
311 break;
312 }
313 }
314
315 projPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
316 projPanel.setLayout(new GridBagLayout());
317 projPanel.add(new JLabel(tr("Projection method")), GBC.std().insets(5,5,0,5));
318 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
319 projPanel.add(projectionCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
320 projPanel.add(projectionCodeLabel = new JLabel(tr("Projection code")), GBC.std().insets(25,5,0,5));
321 projPanel.add(projectionCodeGlue = GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
322 projPanel.add(projectionCode, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
323 projPanel.add(projectionNameLabel = new JLabel(tr("Projection name")), GBC.std().insets(25,5,0,5));
324 projPanel.add(projectionNameGlue = GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
325 projPanel.add(projectionName, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
326 projPanel.add(new JLabel(tr("Bounds")), GBC.std().insets(25,5,0,5));
327 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
328 projPanel.add(bounds, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
329 projPanel.add(projSubPrefPanelWrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(20,5,5,5));
330
331 projectionCodeLabel.setLabelFor(projectionCode);
332 projectionNameLabel.setLabelFor(projectionName);
333
334 projPanel.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0,5,0,10));
335 projPanel.add(new JLabel(tr("Display coordinates as")), GBC.std().insets(5,5,0,5));
336 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
337 projPanel.add(coordinatesCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
338 projPanel.add(new JLabel(tr("System of measurement")), GBC.std().insets(5,5,0,5));
339 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
340 projPanel.add(unitsCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
341 projPanel.add(GBC.glue(1,1), GBC.std().fill(GBC.HORIZONTAL).weight(1.0, 1.0));
342
343 JScrollPane scrollpane = new JScrollPane(projPanel);
344 gui.getMapPreference().addSubTab(this, tr("Map Projection"), scrollpane);
345
346 selectedProjectionChanged(pc);
347 }
348
349 private void updateMeta(ProjectionChoice pc) {
350 pc.setPreferences(pc.getPreferences(projSubPrefPanel));
351 Projection proj = pc.getProjection();
352 projectionCode.setText(proj.toCode());
353 projectionName.setText(proj.toString());
354 Bounds b = proj.getWorldBoundsLatLon();
355 CoordinateFormat cf = CoordinateFormat.getDefaultFormat();
356 bounds.setText(b.getMin().lonToString(cf)+", "+b.getMin().latToString(cf)+" : "+b.getMax().lonToString(cf)+", "+b.getMax().latToString(cf));
357 boolean showCode = true;
358 boolean showName = false;
359 if (pc instanceof SubPrefsOptions) {
360 showCode = ((SubPrefsOptions) pc).showProjectionCode();
361 showName = ((SubPrefsOptions) pc).showProjectionName();
362 }
363 projectionCodeLabel.setVisible(showCode);
364 projectionCodeGlue.setVisible(showCode);
365 projectionCode.setVisible(showCode);
366 projectionNameLabel.setVisible(showName);
367 projectionNameGlue.setVisible(showName);
368 projectionName.setVisible(showName);
369 }
370
371 @Override
372 public boolean ok() {
373 ProjectionChoice pc = (ProjectionChoice) projectionCombo.getSelectedItem();
374
375 String id = pc.getId();
376 Collection<String> prefs = pc.getPreferences(projSubPrefPanel);
377
378 setProjection(id, prefs);
379
380 if(PROP_COORDINATES.put(((CoordinateFormat)coordinatesCombo.getSelectedItem()).name())) {
381 CoordinateFormat.setCoordinateFormat((CoordinateFormat)coordinatesCombo.getSelectedItem());
382 }
383
384 int i = unitsCombo.getSelectedIndex();
385 NavigatableComponent.setSystemOfMeasurement(unitsValues[i]);
386
387 return false;
388 }
389
390 public static void setProjection() {
391 setProjection(PROP_PROJECTION.get(), PROP_SUB_PROJECTION.get());
392 }
393
394 public static void setProjection(String id, Collection<String> pref) {
395 ProjectionChoice pc = projectionChoicesById.get(id);
396
397 if (pc == null) {
398 JOptionPane.showMessageDialog(
399 Main.parent,
400 tr("The projection {0} could not be activated. Using Mercator", id),
401 tr("Error"),
402 JOptionPane.ERROR_MESSAGE
403 );
404 pref = null;
405 pc = mercator;
406 }
407 id = pc.getId();
408 PROP_PROJECTION.put(id);
409 PROP_SUB_PROJECTION.put(pref);
410 Main.pref.putCollection("projection.sub."+id, pref);
411 pc.setPreferences(pref);
412 Projection proj = pc.getProjection();
413 Main.setProjection(proj);
414 }
415
416 /**
417 * Handles all the work related to update the projection-specific
418 * preferences
419 * @param pc the choice class representing user selection
420 */
421 private void selectedProjectionChanged(final ProjectionChoice pc) {
422 // Don't try to update if we're still starting up
423 int size = projPanel.getComponentCount();
424 if(size < 1)
425 return;
426
427 final ActionListener listener = new ActionListener() {
428 @Override
429 public void actionPerformed(ActionEvent e) {
430 updateMeta(pc);
431 }
432 };
433
434 // Replace old panel with new one
435 projSubPrefPanelWrapper.removeAll();
436 projSubPrefPanel = pc.getPreferencePanel(listener);
437 projSubPrefPanelWrapper.add(projSubPrefPanel, projSubPrefPanelGBC);
438 projPanel.revalidate();
439 projSubPrefPanel.repaint();
440 updateMeta(pc);
441 }
442
443 /**
444 * Sets up projection combobox with default values and action listener
445 * @return the choice class for user selection
446 */
447 private ProjectionChoice setupProjectionCombo() {
448 ProjectionChoice pc = null;
449 for (int i = 0; i < projectionCombo.getItemCount(); ++i) {
450 ProjectionChoice pc1 = projectionCombo.getItemAt(i);
451 pc1.setPreferences(getSubprojectionPreference(pc1));
452 if (pc1.getId().equals(PROP_PROJECTION.get())) {
453 projectionCombo.setSelectedIndex(i);
454 selectedProjectionChanged(pc1);
455 pc = pc1;
456 }
457 }
458 // If the ProjectionChoice from the preferences is not available, it
459 // should have been set to Mercator at JOSM start.
460 if (pc == null)
461 throw new RuntimeException("Couldn't find the current projection in the list of available projections!");
462
463 projectionCombo.addActionListener(new ActionListener() {
464 @Override
465 public void actionPerformed(ActionEvent e) {
466 ProjectionChoice pc = (ProjectionChoice) projectionCombo.getSelectedItem();
467 selectedProjectionChanged(pc);
468 }
469 });
470 return pc;
471 }
472
473 private Collection<String> getSubprojectionPreference(ProjectionChoice pc) {
474 return Main.pref.getCollection("projection.sub."+pc.getId(), null);
475 }
476
477 @Override
478 public boolean isExpert() {
479 return false;
480 }
481
482 @Override
483 public TabPreferenceSetting getTabPreferenceSetting(final PreferenceTabbedPane gui) {
484 return gui.getMapPreference();
485 }
486
487 /**
488 * Selects the given projection.
489 * @param projection The projection to select.
490 * @since 5604
491 */
492 public void selectProjection(ProjectionChoice projection) {
493 if (projectionCombo != null && projection != null) {
494 projectionCombo.setSelectedItem(projection);
495 }
496 }
497}
Note: See TracBrowser for help on using the repository browser.