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

Last change on this file since 3873 was 3873, checked in by framm, 13 years ago

rather ugly hack to allow using plugin-defined projection classes.
this ought to be replaced by something that properly engages the plugin's
class loader.

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