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

Last change on this file since 3473 was 3473, checked in by bastiK, 14 years ago

see #5327 (patch by sbrunner) - Swissgrild uses approximate formulas => better use exact formulas

  • Property svn:eol-style set to native
File size: 11.8 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.gui.preferences;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.GridBagLayout;
7import java.awt.event.ActionEvent;
8import java.awt.event.ActionListener;
9import java.util.ArrayList;
10import java.util.Collection;
11import java.util.concurrent.CopyOnWriteArrayList;
12
13import javax.swing.BorderFactory;
14import javax.swing.JComboBox;
15import javax.swing.JLabel;
16import javax.swing.JOptionPane;
17import javax.swing.JPanel;
18import javax.swing.JScrollPane;
19import javax.swing.JSeparator;
20
21import org.openstreetmap.josm.Main;
22import org.openstreetmap.josm.data.Bounds;
23import org.openstreetmap.josm.data.coor.CoordinateFormat;
24import org.openstreetmap.josm.data.preferences.CollectionProperty;
25import org.openstreetmap.josm.data.preferences.ParametrizedCollectionProperty;
26import org.openstreetmap.josm.data.preferences.StringProperty;
27import org.openstreetmap.josm.data.projection.Mercator;
28import org.openstreetmap.josm.data.projection.Projection;
29import org.openstreetmap.josm.data.projection.ProjectionSubPrefs;
30import org.openstreetmap.josm.gui.NavigatableComponent;
31import org.openstreetmap.josm.tools.GBC;
32
33public class ProjectionPreference implements PreferenceSetting {
34
35 public static class Factory implements PreferenceSettingFactory {
36 public PreferenceSetting createPreferenceSetting() {
37 return new ProjectionPreference();
38 }
39 }
40
41 public interface ProjectionChangedListener {
42 void projectionChanged();
43 }
44
45 private static final StringProperty PROP_PROJECTION = new StringProperty("projection", Mercator.class.getName());
46 private static final StringProperty PROP_COORDINATES = new StringProperty("coordinates", null);
47 private static final CollectionProperty PROP_SUB_PROJECTION = new CollectionProperty("projection.sub", null);
48 private static final ParametrizedCollectionProperty PROP_PROJECTION_SUBPROJECTION = new ParametrizedCollectionProperty(null) {
49 @Override
50 protected String getKey(String... params) {
51 String name = params[0];
52 String sname = name.substring(name.lastIndexOf(".")+1);
53 return "projection.sub."+sname;
54 }
55 };
56 private static final StringProperty PROP_SYSTEM_OF_MEASUREMENT = new StringProperty("system_of_measurement", "Metric");
57 private static final String[] unitsValues = (new ArrayList<String>(NavigatableComponent.SYSTEMS_OF_MEASUREMENT.keySet())).toArray(new String[0]);
58 private static final String[] unitsValuesTr = new String[unitsValues.length];
59 static {
60 for (int i=0; i<unitsValues.length; ++i) {
61 unitsValuesTr[i] = tr(unitsValues[i]);
62 }
63 }
64
65 //TODO This is not nice place for a listener code but probably only Dataset will want to listen for projection changes so it's acceptable
66 private static CopyOnWriteArrayList<ProjectionChangedListener> listeners = new CopyOnWriteArrayList<ProjectionChangedListener>();
67
68 public static void addProjectionChangedListener(ProjectionChangedListener listener) {
69 listeners.addIfAbsent(listener);
70 }
71
72 public static void removeProjectionChangedListener(ProjectionChangedListener listener) {
73 listeners.remove(listener);
74 }
75
76 private static void fireProjectionChanged() {
77 for (ProjectionChangedListener listener: listeners) {
78 listener.projectionChanged();
79 }
80 }
81
82
83 /**
84 * Combobox with all projections available
85 */
86 private JComboBox projectionCombo = new JComboBox(Projection.allProjections);
87
88 /**
89 * Combobox with all coordinate display possibilities
90 */
91 private JComboBox coordinatesCombo = new JComboBox(CoordinateFormat.values());
92
93 private JComboBox unitsCombo = new JComboBox(unitsValuesTr);
94
95 /**
96 * This variable holds the JPanel with the projection's preferences. If the
97 * selected projection does not implement this, it will be set to an empty
98 * Panel.
99 */
100 private JPanel projSubPrefPanel;
101 private JPanel projSubPrefPanelWrapper = new JPanel(new GridBagLayout());
102
103 private JLabel projectionCode = new JLabel();
104 private JLabel bounds = new JLabel();
105
106 /**
107 * This is the panel holding all projection preferences
108 */
109 private JPanel projPanel = new JPanel(new GridBagLayout());
110
111 /**
112 * The GridBagConstraints for the Panel containing the ProjectionSubPrefs.
113 * This is required twice in the code, creating it here keeps both occurrences
114 * in sync
115 */
116 static private GBC projSubPrefPanelGBC = GBC.std().fill(GBC.BOTH).weight(1.0, 1.0);
117
118 public void addGui(PreferenceTabbedPane gui) {
119 setupProjectionCombo();
120
121 for (int i = 0; i < coordinatesCombo.getItemCount(); ++i) {
122 if (((CoordinateFormat)coordinatesCombo.getItemAt(i)).name().equals(PROP_COORDINATES.get())) {
123 coordinatesCombo.setSelectedIndex(i);
124 break;
125 }
126 }
127
128 for (int i = 0; i < unitsValues.length; ++i) {
129 if (unitsValues[i].equals(PROP_SYSTEM_OF_MEASUREMENT.get())) {
130 unitsCombo.setSelectedIndex(i);
131 break;
132 }
133 }
134
135 projPanel.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 ));
136 projPanel.setLayout(new GridBagLayout());
137 projPanel.add(new JLabel(tr("Projection method")), GBC.std().insets(5,5,0,5));
138 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
139 projPanel.add(projectionCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
140 projPanel.add(new JLabel(tr("Projection code")), GBC.std().insets(25,5,0,5));
141 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
142 projPanel.add(projectionCode, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
143 projPanel.add(new JLabel(tr("Bounds")), GBC.std().insets(25,5,0,5));
144 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
145 projPanel.add(bounds, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
146 projSubPrefPanelWrapper.add(projSubPrefPanel, projSubPrefPanelGBC);
147 projPanel.add(projSubPrefPanelWrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(20,5,5,5));
148
149 projPanel.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0,5,0,10));
150 projPanel.add(new JLabel(tr("Display coordinates as")), GBC.std().insets(5,5,0,5));
151 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
152 projPanel.add(coordinatesCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
153 projPanel.add(new JLabel(tr("System of measurement")), GBC.std().insets(5,5,0,5));
154 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL));
155 projPanel.add(unitsCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5));
156 projPanel.add(GBC.glue(1,1), GBC.std().fill(GBC.HORIZONTAL).weight(1.0, 1.0));
157
158 JScrollPane scrollpane = new JScrollPane(projPanel);
159 gui.mapcontent.addTab(tr("Map Projection"), scrollpane);
160
161 updateMeta(Main.proj);
162 }
163
164 private void updateMeta(Projection proj)
165 {
166 projectionCode.setText(proj.toCode());
167 Bounds b = proj.getWorldBoundsLatLon();
168 CoordinateFormat cf = CoordinateFormat.getDefaultFormat();
169 bounds.setText(b.getMin().latToString(cf)+"; "+b.getMin().lonToString(cf)+" : "+b.getMax().latToString(cf)+"; "+b.getMax().lonToString(cf));
170 }
171
172 public boolean ok() {
173 Projection proj = (Projection) projectionCombo.getSelectedItem();
174
175 String projname = proj.getClass().getName();
176 Collection<String> prefs = null;
177 if(proj instanceof ProjectionSubPrefs) {
178 prefs = ((ProjectionSubPrefs) proj).getPreferences(projSubPrefPanel);
179 }
180
181 PROP_PROJECTION.put(projname);
182 setProjection(projname, prefs);
183
184 if(PROP_COORDINATES.put(((CoordinateFormat)coordinatesCombo.getSelectedItem()).name())) {
185 CoordinateFormat.setCoordinateFormat((CoordinateFormat)coordinatesCombo.getSelectedItem());
186 }
187
188 int i = unitsCombo.getSelectedIndex();
189 PROP_SYSTEM_OF_MEASUREMENT.put(unitsValues[i]);
190
191 return false;
192 }
193
194 static public void setProjection()
195 {
196 setProjection(PROP_PROJECTION.get(), PROP_SUB_PROJECTION.get());
197 }
198
199 static public void setProjection(String name, Collection<String> coll)
200 {
201 Bounds b = (Main.map != null && Main.map.mapView != null) ? Main.map.mapView.getRealBounds() : null;
202 Projection oldProj = Main.proj;
203
204 try {
205 Main.proj = (Projection)Class.forName(name).newInstance();
206 } catch (final Exception e) {
207 JOptionPane.showMessageDialog(
208 Main.parent,
209 tr("The projection {0} could not be activated. Using Mercator", name),
210 tr("Error"),
211 JOptionPane.ERROR_MESSAGE
212 );
213 coll = null;
214 Main.proj = new Mercator();
215 name = Main.proj.getClass().getName();
216 }
217 PROP_SUB_PROJECTION.put(coll);
218 PROP_PROJECTION_SUBPROJECTION.put(coll, name);
219 if(Main.proj instanceof ProjectionSubPrefs) {
220 ((ProjectionSubPrefs) Main.proj).setPreferences(coll);
221 }
222 fireProjectionChanged(); // This should be probably called from the if bellow, but hashCode condition doesn't look sure enough
223 if(b != null && (!Main.proj.getClass().getName().equals(oldProj.getClass().getName()) || Main.proj.hashCode() != oldProj.hashCode()))
224 {
225 Main.map.mapView.zoomTo(b);
226 /* TODO - remove layers with fixed projection */
227 }
228 }
229
230 private class SBPanel extends JPanel
231 {
232 private Projection p;
233 public SBPanel(Projection pr)
234 {
235 super();
236 p = pr;
237 }
238 @Override
239 public void paint(java.awt.Graphics g)
240 {
241 super.paint(g);
242 ((ProjectionSubPrefs) p).setPreferences(((ProjectionSubPrefs) p).getPreferences(this));
243 updateMeta(p);
244 }
245 }
246
247 /**
248 * Handles all the work related to update the projection-specific
249 * preferences
250 * @param proj
251 */
252 private void selectedProjectionChanged(Projection proj) {
253 if(!(proj instanceof ProjectionSubPrefs)) {
254 projSubPrefPanel = new JPanel();
255 } else {
256 ProjectionSubPrefs projPref = (ProjectionSubPrefs) proj;
257 projSubPrefPanel = new SBPanel(proj);
258 projPref.setupPreferencePanel(projSubPrefPanel);
259 }
260
261 // Don't try to update if we're still starting up
262 int size = projPanel.getComponentCount();
263 if(size < 1)
264 return;
265
266 // Replace old panel with new one
267 projSubPrefPanelWrapper.removeAll();
268 projSubPrefPanelWrapper.add(projSubPrefPanel, projSubPrefPanelGBC);
269 projPanel.revalidate();
270 projSubPrefPanel.repaint();
271 updateMeta(proj);
272 }
273
274 /**
275 * Sets up projection combobox with default values and action listener
276 */
277 private void setupProjectionCombo() {
278 for (int i = 0; i < projectionCombo.getItemCount(); ++i) {
279 Projection proj = (Projection)projectionCombo.getItemAt(i);
280 String name = proj.getClass().getName();
281 if(proj instanceof ProjectionSubPrefs) {
282 ((ProjectionSubPrefs) proj).setPreferences(PROP_PROJECTION_SUBPROJECTION.get(name));
283 }
284 if (name.equals(PROP_PROJECTION.get())) {
285 projectionCombo.setSelectedIndex(i);
286 selectedProjectionChanged(proj);
287 break;
288 }
289 }
290
291 projectionCombo.addActionListener(new ActionListener() {
292 public void actionPerformed(ActionEvent e) {
293 JComboBox cb = (JComboBox)e.getSource();
294 Projection proj = (Projection)cb.getSelectedItem();
295 selectedProjectionChanged(proj);
296 }
297 });
298 }
299}
Note: See TracBrowser for help on using the repository browser.