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

Last change on this file since 9778 was 9778, checked in by bastiK, 9 years ago

see #12507 - fix mouse wheel scrolling at some places

  • Property svn:eol-style set to native
File size: 19.8 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.preferences.PreferenceSetting;
33import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
34import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
35import org.openstreetmap.josm.gui.preferences.SubPreferenceSetting;
36import org.openstreetmap.josm.gui.preferences.TabPreferenceSetting;
37import org.openstreetmap.josm.gui.widgets.JosmComboBox;
38import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
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 * Netherlands RD projection
198 *
199 * @author vholten
200 */
201 registerProjectionChoice(tr("Rijksdriehoekscoördinaten (Netherlands)"), "core:dutchrd", 28992); // NL
202
203 /**
204 * PUWG 1992 and 2000 are the official cordinate systems in Poland.
205 *
206 * They use the same math as UTM only with different constants.
207 *
208 * @author steelman
209 */
210 registerProjectionChoice(new PuwgProjectionChoice()); // PL
211
212 /**
213 * SWEREF99 13 30 projection. Based on data from spatialreference.org.
214 * http://spatialreference.org/ref/epsg/3008/
215 *
216 * @author Hanno Hecker
217 */
218 registerProjectionChoice(tr("SWEREF99 13 30 / EPSG:3008 (Sweden)"), "core:sweref99", 3008); // SE
219
220 /************************
221 * Projection by Code.
222 */
223 registerProjectionChoice(new CodeProjectionChoice());
224
225 /************************
226 * Custom projection.
227 */
228 registerProjectionChoice(new CustomProjectionChoice());
229 }
230
231 public static void registerProjectionChoice(ProjectionChoice c) {
232 projectionChoices.add(c);
233 projectionChoicesById.put(c.getId(), c);
234 }
235
236 public static ProjectionChoice registerProjectionChoice(String name, String id, Integer epsg, String cacheDir) {
237 ProjectionChoice pc = new SingleProjectionChoice(name, id, "EPSG:"+epsg, cacheDir);
238 registerProjectionChoice(pc);
239 return pc;
240 }
241
242 private static ProjectionChoice registerProjectionChoice(String name, String id, Integer epsg) {
243 ProjectionChoice pc = new SingleProjectionChoice(name, id, "EPSG:"+epsg);
244 registerProjectionChoice(pc);
245 return pc;
246 }
247
248 public static List<ProjectionChoice> getProjectionChoices() {
249 return Collections.unmodifiableList(projectionChoices);
250 }
251
252 private static final StringProperty PROP_PROJECTION = new StringProperty("projection", mercator.getId());
253 private static final StringProperty PROP_COORDINATES = new StringProperty("coordinates", null);
254 private static final CollectionProperty PROP_SUB_PROJECTION = new CollectionProperty("projection.sub", null);
255 public static final StringProperty PROP_SYSTEM_OF_MEASUREMENT = new StringProperty("system_of_measurement", "Metric");
256 private static final String[] unitsValues = (new ArrayList<>(SystemOfMeasurement.ALL_SYSTEMS.keySet())).toArray(new String[0]);
257 private static final String[] unitsValuesTr = new String[unitsValues.length];
258 static {
259 for (int i = 0; i < unitsValues.length; ++i) {
260 unitsValuesTr[i] = tr(unitsValues[i]);
261 }
262 }
263
264 /**
265 * Combobox with all projections available
266 */
267 private final JosmComboBox<ProjectionChoice> projectionCombo = new JosmComboBox<>(projectionChoices.toArray(new ProjectionChoice[0]));
268
269 /**
270 * Combobox with all coordinate display possibilities
271 */
272 private final JosmComboBox<CoordinateFormat> coordinatesCombo = new JosmComboBox<>(CoordinateFormat.values());
273
274 private final JosmComboBox<String> unitsCombo = new JosmComboBox<>(unitsValuesTr);
275
276 /**
277 * This variable holds the JPanel with the projection's preferences. If the
278 * selected projection does not implement this, it will be set to an empty
279 * Panel.
280 */
281 private JPanel projSubPrefPanel;
282 private final JPanel projSubPrefPanelWrapper = new JPanel(new GridBagLayout());
283
284 private JLabel projectionCodeLabel;
285 private Component projectionCodeGlue;
286 private final JLabel projectionCode = new JLabel();
287 private JLabel projectionNameLabel;
288 private Component projectionNameGlue;
289 private final JLabel projectionName = new JLabel();
290 private final JLabel bounds = new JLabel();
291
292 /**
293 * This is the panel holding all projection preferences
294 */
295 private final VerticallyScrollablePanel projPanel = new VerticallyScrollablePanel(new GridBagLayout());
296
297 /**
298 * The GridBagConstraints for the Panel containing the ProjectionSubPrefs.
299 * This is required twice in the code, creating it here keeps both occurrences
300 * in sync
301 */
302 private static final GBC projSubPrefPanelGBC = GBC.std().fill(GBC.BOTH).weight(1.0, 1.0);
303
304 @Override
305 public void addGui(PreferenceTabbedPane gui) {
306 ProjectionChoice pc = setupProjectionCombo();
307
308 for (int i = 0; i < coordinatesCombo.getItemCount(); ++i) {
309 if (coordinatesCombo.getItemAt(i).name().equals(PROP_COORDINATES.get())) {
310 coordinatesCombo.setSelectedIndex(i);
311 break;
312 }
313 }
314
315 for (int i = 0; i < unitsValues.length; ++i) {
316 if (unitsValues[i].equals(PROP_SYSTEM_OF_MEASUREMENT.get())) {
317 unitsCombo.setSelectedIndex(i);
318 break;
319 }
320 }
321
322 projPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
323 projPanel.add(new JLabel(tr("Projection method")), GBC.std().insets(5, 5, 0, 5));
324 projPanel.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
325 projPanel.add(projectionCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0, 5, 5, 5));
326 projPanel.add(projectionCodeLabel = new JLabel(tr("Projection code")), GBC.std().insets(25, 5, 0, 5));
327 projPanel.add(projectionCodeGlue = GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
328 projPanel.add(projectionCode, GBC.eop().fill(GBC.HORIZONTAL).insets(0, 5, 5, 5));
329 projPanel.add(projectionNameLabel = new JLabel(tr("Projection name")), GBC.std().insets(25, 5, 0, 5));
330 projPanel.add(projectionNameGlue = GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
331 projPanel.add(projectionName, GBC.eop().fill(GBC.HORIZONTAL).insets(0, 5, 5, 5));
332 projPanel.add(new JLabel(tr("Bounds")), GBC.std().insets(25, 5, 0, 5));
333 projPanel.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
334 projPanel.add(bounds, GBC.eop().fill(GBC.HORIZONTAL).insets(0, 5, 5, 5));
335 projPanel.add(projSubPrefPanelWrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 5, 5, 5));
336
337 projectionCodeLabel.setLabelFor(projectionCode);
338 projectionNameLabel.setLabelFor(projectionName);
339
340 projPanel.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0, 5, 0, 10));
341 projPanel.add(new JLabel(tr("Display coordinates as")), GBC.std().insets(5, 5, 0, 5));
342 projPanel.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
343 projPanel.add(coordinatesCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0, 5, 5, 5));
344 projPanel.add(new JLabel(tr("System of measurement")), GBC.std().insets(5, 5, 0, 5));
345 projPanel.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
346 projPanel.add(unitsCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0, 5, 5, 5));
347 projPanel.add(GBC.glue(1, 1), GBC.std().fill(GBC.HORIZONTAL).weight(1.0, 1.0));
348
349 gui.getMapPreference().addSubTab(this, tr("Map Projection"), projPanel.getVerticalScrollPane());
350
351 selectedProjectionChanged(pc);
352 }
353
354 private void updateMeta(ProjectionChoice pc) {
355 pc.setPreferences(pc.getPreferences(projSubPrefPanel));
356 Projection proj = pc.getProjection();
357 projectionCode.setText(proj.toCode());
358 projectionName.setText(proj.toString());
359 Bounds b = proj.getWorldBoundsLatLon();
360 CoordinateFormat cf = CoordinateFormat.getDefaultFormat();
361 bounds.setText(b.getMin().lonToString(cf) + ", " + b.getMin().latToString(cf) + " : " +
362 b.getMax().lonToString(cf) + ", " + b.getMax().latToString(cf));
363 boolean showCode = true;
364 boolean showName = false;
365 if (pc instanceof SubPrefsOptions) {
366 showCode = ((SubPrefsOptions) pc).showProjectionCode();
367 showName = ((SubPrefsOptions) pc).showProjectionName();
368 }
369 projectionCodeLabel.setVisible(showCode);
370 projectionCodeGlue.setVisible(showCode);
371 projectionCode.setVisible(showCode);
372 projectionNameLabel.setVisible(showName);
373 projectionNameGlue.setVisible(showName);
374 projectionName.setVisible(showName);
375 }
376
377 @Override
378 public boolean ok() {
379 ProjectionChoice pc = (ProjectionChoice) projectionCombo.getSelectedItem();
380
381 String id = pc.getId();
382 Collection<String> prefs = pc.getPreferences(projSubPrefPanel);
383
384 setProjection(id, prefs);
385
386 if (PROP_COORDINATES.put(((CoordinateFormat) coordinatesCombo.getSelectedItem()).name())) {
387 CoordinateFormat.setCoordinateFormat((CoordinateFormat) coordinatesCombo.getSelectedItem());
388 }
389
390 int i = unitsCombo.getSelectedIndex();
391 SystemOfMeasurement.setSystemOfMeasurement(unitsValues[i]);
392
393 return false;
394 }
395
396 public static void setProjection() {
397 setProjection(PROP_PROJECTION.get(), PROP_SUB_PROJECTION.get());
398 }
399
400 public static void setProjection(String id, Collection<String> pref) {
401 ProjectionChoice pc = projectionChoicesById.get(id);
402
403 if (pc == null) {
404 JOptionPane.showMessageDialog(
405 Main.parent,
406 tr("The projection {0} could not be activated. Using Mercator", id),
407 tr("Error"),
408 JOptionPane.ERROR_MESSAGE
409 );
410 pref = null;
411 pc = mercator;
412 }
413 id = pc.getId();
414 PROP_PROJECTION.put(id);
415 PROP_SUB_PROJECTION.put(pref);
416 Main.pref.putCollection("projection.sub."+id, pref);
417 pc.setPreferences(pref);
418 Projection proj = pc.getProjection();
419 Main.setProjection(proj);
420 }
421
422 /**
423 * Handles all the work related to update the projection-specific
424 * preferences
425 * @param pc the choice class representing user selection
426 */
427 private void selectedProjectionChanged(final ProjectionChoice pc) {
428 // Don't try to update if we're still starting up
429 int size = projPanel.getComponentCount();
430 if (size < 1)
431 return;
432
433 final ActionListener listener = new ActionListener() {
434 @Override
435 public void actionPerformed(ActionEvent e) {
436 updateMeta(pc);
437 }
438 };
439
440 // Replace old panel with new one
441 projSubPrefPanelWrapper.removeAll();
442 projSubPrefPanel = pc.getPreferencePanel(listener);
443 projSubPrefPanelWrapper.add(projSubPrefPanel, projSubPrefPanelGBC);
444 projPanel.revalidate();
445 projSubPrefPanel.repaint();
446 updateMeta(pc);
447 }
448
449 /**
450 * Sets up projection combobox with default values and action listener
451 * @return the choice class for user selection
452 */
453 private ProjectionChoice setupProjectionCombo() {
454 ProjectionChoice pc = null;
455 for (int i = 0; i < projectionCombo.getItemCount(); ++i) {
456 ProjectionChoice pc1 = projectionCombo.getItemAt(i);
457 pc1.setPreferences(getSubprojectionPreference(pc1));
458 if (pc1.getId().equals(PROP_PROJECTION.get())) {
459 projectionCombo.setSelectedIndex(i);
460 selectedProjectionChanged(pc1);
461 pc = pc1;
462 }
463 }
464 // If the ProjectionChoice from the preferences is not available, it
465 // should have been set to Mercator at JOSM start.
466 if (pc == null)
467 throw new RuntimeException("Couldn't find the current projection in the list of available projections!");
468
469 projectionCombo.addActionListener(new ActionListener() {
470 @Override
471 public void actionPerformed(ActionEvent e) {
472 ProjectionChoice pc = (ProjectionChoice) projectionCombo.getSelectedItem();
473 selectedProjectionChanged(pc);
474 }
475 });
476 return pc;
477 }
478
479 private static Collection<String> getSubprojectionPreference(ProjectionChoice pc) {
480 return Main.pref.getCollection("projection.sub."+pc.getId(), null);
481 }
482
483 @Override
484 public boolean isExpert() {
485 return false;
486 }
487
488 @Override
489 public TabPreferenceSetting getTabPreferenceSetting(final PreferenceTabbedPane gui) {
490 return gui.getMapPreference();
491 }
492
493 /**
494 * Selects the given projection.
495 * @param projection The projection to select.
496 * @since 5604
497 */
498 public void selectProjection(ProjectionChoice projection) {
499 if (projectionCombo != null && projection != null) {
500 projectionCombo.setSelectedItem(projection);
501 }
502 }
503}
Note: See TracBrowser for help on using the repository browser.