001 // License: GPL. Copyright 2007 by Immanuel Scholz and others 002 package org.openstreetmap.josm.gui.preferences.projection; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.awt.Component; 007 import java.awt.GridBagLayout; 008 import java.awt.event.ActionEvent; 009 import java.awt.event.ActionListener; 010 import java.util.ArrayList; 011 import java.util.Collection; 012 import java.util.Collections; 013 import java.util.HashMap; 014 import java.util.List; 015 import java.util.Map; 016 017 import javax.swing.BorderFactory; 018 import javax.swing.JLabel; 019 import javax.swing.JOptionPane; 020 import javax.swing.JPanel; 021 import javax.swing.JScrollPane; 022 import javax.swing.JSeparator; 023 024 import org.openstreetmap.josm.Main; 025 import org.openstreetmap.josm.data.Bounds; 026 import org.openstreetmap.josm.data.coor.CoordinateFormat; 027 import org.openstreetmap.josm.data.preferences.CollectionProperty; 028 import org.openstreetmap.josm.data.preferences.StringProperty; 029 import org.openstreetmap.josm.data.projection.BelgianLambert1972; 030 import org.openstreetmap.josm.data.projection.BelgianLambert2008; 031 import org.openstreetmap.josm.data.projection.Epsg3008; 032 import org.openstreetmap.josm.data.projection.Epsg4326; 033 import org.openstreetmap.josm.data.projection.Lambert93; 034 import org.openstreetmap.josm.data.projection.LambertEST; 035 import org.openstreetmap.josm.data.projection.Mercator; 036 import org.openstreetmap.josm.data.projection.Projection; 037 import org.openstreetmap.josm.data.projection.TransverseMercatorLV; 038 import org.openstreetmap.josm.gui.NavigatableComponent; 039 import org.openstreetmap.josm.gui.preferences.PreferenceSetting; 040 import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory; 041 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 042 import org.openstreetmap.josm.gui.preferences.SubPreferenceSetting; 043 import org.openstreetmap.josm.gui.preferences.TabPreferenceSetting; 044 import org.openstreetmap.josm.gui.widgets.JosmComboBox; 045 import org.openstreetmap.josm.tools.GBC; 046 047 public class ProjectionPreference implements SubPreferenceSetting { 048 049 public static class Factory implements PreferenceSettingFactory { 050 public PreferenceSetting createPreferenceSetting() { 051 return new ProjectionPreference(); 052 } 053 } 054 055 private static List<ProjectionChoice> projectionChoices = new ArrayList<ProjectionChoice>(); 056 private static Map<String, ProjectionChoice> projectionChoicesById = new HashMap<String, ProjectionChoice>(); 057 private static Map<String, String> aliasNormalizer = new HashMap<String, String>(); 058 059 public static ProjectionChoice mercator = new SingleProjectionChoice("core:mercator", new Mercator()); 060 static { 061 // global projections 062 registerProjectionChoice("core:wgs84", new Epsg4326()); 063 registerProjectionChoice(mercator); 064 registerProjectionChoice(new UTMProjectionChoice()); 065 // regional - alphabetical order by country code 066 registerProjectionChoice("core:belambert1972", new BelgianLambert1972()); // BE 067 registerProjectionChoice("core:belambert2008", new BelgianLambert2008()); // BE 068 registerProjectionChoice(new SwissGridProjectionChoice()); // CH 069 registerProjectionChoice(new GaussKruegerProjectionChoice()); // DE 070 registerProjectionChoice("core:lambertest", new LambertEST()); // EE 071 registerProjectionChoice(new LambertProjectionChoice()); // FR 072 registerProjectionChoice("core:lambert93", new Lambert93()); // FR 073 registerProjectionChoice(new LambertCC9ZonesProjectionChoice()); // FR 074 registerProjectionChoice(new UTM_France_DOM_ProjectionChoice()); // FR 075 registerProjectionChoice("core:tmerclv", new TransverseMercatorLV()); // LV 076 registerProjectionChoice(new PuwgProjectionChoice()); // PL 077 registerProjectionChoice("core:sweref99", new Epsg3008()); // SE 078 registerProjectionChoice(new CustomProjectionChoice()); 079 } 080 081 public static void registerProjectionChoice(ProjectionChoice c) { 082 projectionChoices.add(c); 083 projectionChoicesById.put(c.getId(), c); 084 aliasNormalizer.put(c.getId(), c.getId()); 085 if (c instanceof Alias) { 086 String alias = ((Alias) c).getAlias(); 087 projectionChoicesById.put(alias, c); 088 aliasNormalizer.put(alias, c.getId()); 089 } 090 } 091 092 public static void registerProjectionChoice(String id, Projection projection) { 093 registerProjectionChoice(new SingleProjectionChoice(id, projection)); 094 } 095 096 public static List<ProjectionChoice> getProjectionChoices() { 097 return Collections.unmodifiableList(projectionChoices); 098 } 099 100 private static final StringProperty PROP_PROJECTION = new StringProperty("projection", mercator.getId()); 101 private static final StringProperty PROP_COORDINATES = new StringProperty("coordinates", null); 102 private static final CollectionProperty PROP_SUB_PROJECTION = new CollectionProperty("projection.sub", null); 103 public static final StringProperty PROP_SYSTEM_OF_MEASUREMENT = new StringProperty("system_of_measurement", "Metric"); 104 private static final String[] unitsValues = (new ArrayList<String>(NavigatableComponent.SYSTEMS_OF_MEASUREMENT.keySet())).toArray(new String[0]); 105 private static final String[] unitsValuesTr = new String[unitsValues.length]; 106 static { 107 for (int i=0; i<unitsValues.length; ++i) { 108 unitsValuesTr[i] = tr(unitsValues[i]); 109 } 110 } 111 112 /** 113 * Combobox with all projections available 114 */ 115 private JosmComboBox projectionCombo = new JosmComboBox(projectionChoices.toArray()); 116 117 /** 118 * Combobox with all coordinate display possibilities 119 */ 120 private JosmComboBox coordinatesCombo = new JosmComboBox(CoordinateFormat.values()); 121 122 private JosmComboBox unitsCombo = new JosmComboBox(unitsValuesTr); 123 124 /** 125 * This variable holds the JPanel with the projection's preferences. If the 126 * selected projection does not implement this, it will be set to an empty 127 * Panel. 128 */ 129 private JPanel projSubPrefPanel; 130 private JPanel projSubPrefPanelWrapper = new JPanel(new GridBagLayout()); 131 132 private JLabel projectionCodeLabel; 133 private Component projectionCodeGlue; 134 private JLabel projectionCode = new JLabel(); 135 private JLabel bounds = new JLabel(); 136 137 /** 138 * This is the panel holding all projection preferences 139 */ 140 private JPanel projPanel = new JPanel(new GridBagLayout()); 141 142 /** 143 * The GridBagConstraints for the Panel containing the ProjectionSubPrefs. 144 * This is required twice in the code, creating it here keeps both occurrences 145 * in sync 146 */ 147 static private GBC projSubPrefPanelGBC = GBC.std().fill(GBC.BOTH).weight(1.0, 1.0); 148 149 public void addGui(PreferenceTabbedPane gui) { 150 ProjectionChoice pc = setupProjectionCombo(); 151 152 for (int i = 0; i < coordinatesCombo.getItemCount(); ++i) { 153 if (((CoordinateFormat)coordinatesCombo.getItemAt(i)).name().equals(PROP_COORDINATES.get())) { 154 coordinatesCombo.setSelectedIndex(i); 155 break; 156 } 157 } 158 159 for (int i = 0; i < unitsValues.length; ++i) { 160 if (unitsValues[i].equals(PROP_SYSTEM_OF_MEASUREMENT.get())) { 161 unitsCombo.setSelectedIndex(i); 162 break; 163 } 164 } 165 166 projPanel.setBorder(BorderFactory.createEmptyBorder( 0, 0, 0, 0 )); 167 projPanel.setLayout(new GridBagLayout()); 168 projPanel.add(new JLabel(tr("Projection method")), GBC.std().insets(5,5,0,5)); 169 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL)); 170 projPanel.add(projectionCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5)); 171 projPanel.add(projectionCodeLabel = new JLabel(tr("Projection code")), GBC.std().insets(25,5,0,5)); 172 projPanel.add(projectionCodeGlue = GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL)); 173 projPanel.add(projectionCode, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5)); 174 projPanel.add(new JLabel(tr("Bounds")), GBC.std().insets(25,5,0,5)); 175 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL)); 176 projPanel.add(bounds, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5)); 177 projPanel.add(projSubPrefPanelWrapper, GBC.eol().fill(GBC.HORIZONTAL).insets(20,5,5,5)); 178 179 projPanel.add(new JSeparator(), GBC.eol().fill(GBC.HORIZONTAL).insets(0,5,0,10)); 180 projPanel.add(new JLabel(tr("Display coordinates as")), GBC.std().insets(5,5,0,5)); 181 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL)); 182 projPanel.add(coordinatesCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5)); 183 projPanel.add(new JLabel(tr("System of measurement")), GBC.std().insets(5,5,0,5)); 184 projPanel.add(GBC.glue(5,0), GBC.std().fill(GBC.HORIZONTAL)); 185 projPanel.add(unitsCombo, GBC.eop().fill(GBC.HORIZONTAL).insets(0,5,5,5)); 186 projPanel.add(GBC.glue(1,1), GBC.std().fill(GBC.HORIZONTAL).weight(1.0, 1.0)); 187 188 JScrollPane scrollpane = new JScrollPane(projPanel); 189 gui.getMapPreference().mapcontent.addTab(tr("Map Projection"), scrollpane); 190 191 selectedProjectionChanged(pc); 192 } 193 194 private void updateMeta(ProjectionChoice pc) { 195 pc.setPreferences(pc.getPreferences(projSubPrefPanel)); 196 Projection proj = pc.getProjection(); 197 projectionCode.setText(proj.toCode()); 198 Bounds b = proj.getWorldBoundsLatLon(); 199 CoordinateFormat cf = CoordinateFormat.getDefaultFormat(); 200 bounds.setText(b.getMin().lonToString(cf)+", "+b.getMin().latToString(cf)+" : "+b.getMax().lonToString(cf)+", "+b.getMax().latToString(cf)); 201 boolean showCode = true; 202 if (pc instanceof SubPrefsOptions) { 203 showCode = ((SubPrefsOptions) pc).showProjectionCode(); 204 } 205 projectionCodeLabel.setVisible(showCode); 206 projectionCodeGlue.setVisible(showCode); 207 projectionCode.setVisible(showCode); 208 } 209 210 @Override 211 public boolean ok() { 212 ProjectionChoice pc = (ProjectionChoice) projectionCombo.getSelectedItem(); 213 214 String id = pc.getId(); 215 Collection<String> prefs = pc.getPreferences(projSubPrefPanel); 216 217 setProjection(id, prefs); 218 219 if(PROP_COORDINATES.put(((CoordinateFormat)coordinatesCombo.getSelectedItem()).name())) { 220 CoordinateFormat.setCoordinateFormat((CoordinateFormat)coordinatesCombo.getSelectedItem()); 221 } 222 223 int i = unitsCombo.getSelectedIndex(); 224 PROP_SYSTEM_OF_MEASUREMENT.put(unitsValues[i]); 225 226 return false; 227 } 228 229 static public void setProjection() { 230 setProjection(PROP_PROJECTION.get(), PROP_SUB_PROJECTION.get()); 231 } 232 233 static public void setProjection(String id, Collection<String> pref) { 234 ProjectionChoice pc = projectionChoicesById.get(id); 235 236 if (pc == null) { 237 JOptionPane.showMessageDialog( 238 Main.parent, 239 tr("The projection {0} could not be activated. Using Mercator", id), 240 tr("Error"), 241 JOptionPane.ERROR_MESSAGE 242 ); 243 pref = null; 244 pc = mercator; 245 } 246 id = pc.getId(); 247 PROP_PROJECTION.put(id); 248 PROP_SUB_PROJECTION.put(pref); 249 Main.pref.putCollection("projection.sub."+id, pref); 250 pc.setPreferences(pref); 251 Projection proj = pc.getProjection(); 252 Main.setProjection(proj); 253 } 254 255 /** 256 * Handles all the work related to update the projection-specific 257 * preferences 258 * @param proj 259 */ 260 private void selectedProjectionChanged(final ProjectionChoice pc) { 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 final ActionListener listener = new ActionListener() { 267 @Override 268 public void actionPerformed(ActionEvent e) { 269 updateMeta(pc); 270 } 271 }; 272 273 // Replace old panel with new one 274 projSubPrefPanelWrapper.removeAll(); 275 projSubPrefPanel = pc.getPreferencePanel(listener); 276 projSubPrefPanelWrapper.add(projSubPrefPanel, projSubPrefPanelGBC); 277 projPanel.revalidate(); 278 projSubPrefPanel.repaint(); 279 updateMeta(pc); 280 } 281 282 /** 283 * Sets up projection combobox with default values and action listener 284 */ 285 private ProjectionChoice setupProjectionCombo() { 286 ProjectionChoice pc = null; 287 for (int i = 0; i < projectionCombo.getItemCount(); ++i) { 288 ProjectionChoice pc1 = (ProjectionChoice) projectionCombo.getItemAt(i); 289 pc1.setPreferences(getSubprojectionPreference(pc1)); 290 if (pc1.getId().equals(aliasNormalizer.get(PROP_PROJECTION.get()))) { 291 projectionCombo.setSelectedIndex(i); 292 selectedProjectionChanged(pc1); 293 pc = pc1; 294 } 295 } 296 // If the ProjectionChoice from the preferences is not available, it 297 // should have been set to Mercator at JOSM start. 298 if (pc == null) 299 throw new RuntimeException("Couldn't find the current projection in the list of available projections!"); 300 301 projectionCombo.addActionListener(new ActionListener() { 302 public void actionPerformed(ActionEvent e) { 303 ProjectionChoice pc = (ProjectionChoice) projectionCombo.getSelectedItem(); 304 selectedProjectionChanged(pc); 305 } 306 }); 307 return pc; 308 } 309 310 private Collection<String> getSubprojectionPreference(ProjectionChoice pc) { 311 Collection<String> c1 = Main.pref.getCollection("projection.sub."+pc.getId(), null); 312 if (c1 != null) 313 return c1; 314 if (pc instanceof Alias) { 315 String alias = ((Alias) pc).getAlias(); 316 String sname = alias.substring(alias.lastIndexOf(".")+1); 317 return Main.pref.getCollection("projection.sub."+sname, null); 318 } 319 return null; 320 } 321 322 @Override 323 public boolean isExpert() { 324 return false; 325 } 326 327 @Override 328 public TabPreferenceSetting getTabPreferenceSetting(final PreferenceTabbedPane gui) { 329 return gui.getMapPreference(); 330 } 331 }