001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.gui.preferences.projection; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.awt.BorderLayout; 007 import java.awt.GridBagLayout; 008 import java.awt.Insets; 009 import java.awt.event.ActionEvent; 010 import java.awt.event.ActionListener; 011 import java.util.ArrayList; 012 import java.util.Arrays; 013 import java.util.Collection; 014 import java.util.Collections; 015 import java.util.LinkedList; 016 import java.util.List; 017 import java.util.Map; 018 019 import javax.swing.JButton; 020 import javax.swing.JComponent; 021 import javax.swing.JLabel; 022 import javax.swing.JPanel; 023 import javax.swing.JTextField; 024 import javax.swing.plaf.basic.BasicComboBoxEditor; 025 026 import org.openstreetmap.josm.Main; 027 import org.openstreetmap.josm.data.projection.CustomProjection; 028 import org.openstreetmap.josm.data.projection.Projection; 029 import org.openstreetmap.josm.data.projection.ProjectionConfigurationException; 030 import org.openstreetmap.josm.data.projection.Projections; 031 import org.openstreetmap.josm.gui.ExtendedDialog; 032 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionListItem; 033 import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator; 034 import org.openstreetmap.josm.gui.widgets.HistoryComboBox; 035 import org.openstreetmap.josm.gui.widgets.HtmlPanel; 036 import org.openstreetmap.josm.tools.GBC; 037 import org.openstreetmap.josm.tools.ImageProvider; 038 import org.openstreetmap.josm.tools.Utils; 039 040 public class CustomProjectionChoice extends AbstractProjectionChoice implements Alias, SubPrefsOptions { 041 042 private String pref; 043 044 public CustomProjectionChoice() { 045 super("core:custom", tr("Custom Projection")); 046 } 047 048 private static class PreferencePanel extends JPanel { 049 050 public JTextField input; 051 private HistoryComboBox cbInput; 052 053 public PreferencePanel(String initialText, ActionListener listener) { 054 build(initialText, listener); 055 } 056 057 private void build(String initialText, final ActionListener listener) { 058 input = new JTextField(30); 059 cbInput = new HistoryComboBox(); 060 cbInput.setPrototypeDisplayValue(new AutoCompletionListItem("xxxx")); 061 cbInput.setEditor(new BasicComboBoxEditor() { 062 @Override 063 protected JTextField createEditorComponent() { 064 return input; 065 } 066 }); 067 Collection<String> samples = Arrays.asList( 068 "+proj=lonlat +ellps=WGS84 +datum=WGS84 +bounds=-180,-90,180,90", 069 "+proj=tmerc +lat_0=0 +lon_0=9 +k_0=1 +x_0=3500000 +y_0=0 +ellps=bessel +nadgrids=BETA2007.gsb"); 070 List<String> inputHistory = new LinkedList<String>(Main.pref.getCollection("projection.custom.value.history", samples)); 071 Collections.reverse(inputHistory); 072 cbInput.setPossibleItems(inputHistory); 073 cbInput.setText(initialText == null ? "" : initialText); 074 075 final HtmlPanel errorsPanel = new HtmlPanel(); 076 errorsPanel.setVisible(false); 077 final JLabel valStatus = new JLabel(); 078 valStatus.setVisible(false); 079 080 final AbstractTextComponentValidator val = new AbstractTextComponentValidator(input, false, false, false) { 081 082 private String error; 083 084 @Override 085 public void validate() { 086 if (!isValid()) { 087 feedbackInvalid(tr("Invalid projection configuration: {0}",error)); 088 } else { 089 feedbackValid(tr("Projection configuration is valid.")); 090 } 091 listener.actionPerformed(null); 092 } 093 094 @Override 095 public boolean isValid() { 096 try { 097 CustomProjection test = new CustomProjection(); 098 test.update(input.getText()); 099 } catch (ProjectionConfigurationException ex) { 100 error = ex.getMessage(); 101 valStatus.setIcon(ImageProvider.get("data", "error.png")); 102 valStatus.setVisible(true); 103 errorsPanel.setText(error); 104 errorsPanel.setVisible(true); 105 return false; 106 } 107 errorsPanel.setVisible(false); 108 valStatus.setIcon(ImageProvider.get("misc", "green_check.png")); 109 valStatus.setVisible(true); 110 return true; 111 } 112 113 }; 114 115 JButton btnCheck = new JButton(tr("Validate")); 116 btnCheck.addActionListener(new ActionListener() { 117 @Override 118 public void actionPerformed(ActionEvent e) { 119 val.validate(); 120 } 121 }); 122 btnCheck.setLayout(new BorderLayout()); 123 btnCheck.setMargin(new Insets(-1,0,-1,0)); 124 125 JButton btnInfo = new JButton(tr("Parameter information...")); 126 btnInfo.addActionListener(new ActionListener() { 127 @Override 128 public void actionPerformed(ActionEvent e) { 129 CustomProjectionChoice.ParameterInfoDialog dlg = new CustomProjectionChoice.ParameterInfoDialog(); 130 dlg.showDialog(); 131 dlg.toFront(); 132 } 133 }); 134 135 this.setLayout(new GridBagLayout()); 136 JPanel p2 = new JPanel(new GridBagLayout()); 137 p2.add(cbInput, GBC.std().fill(GBC.HORIZONTAL).insets(0, 20, 5, 5)); 138 p2.add(btnCheck, GBC.eol().insets(0, 20, 0, 5)); 139 this.add(p2, GBC.eol().fill(GBC.HORIZONTAL)); 140 p2 = new JPanel(new GridBagLayout()); 141 p2.add(valStatus, GBC.std().anchor(GBC.WEST).weight(0.0001, 0)); 142 p2.add(errorsPanel, GBC.eol().fill(GBC.HORIZONTAL)); 143 this.add(p2, GBC.eol().fill(GBC.HORIZONTAL)); 144 p2 = new JPanel(new GridBagLayout()); 145 p2.add(btnInfo, GBC.std().insets(0, 20, 0, 0)); 146 p2.add(GBC.glue(1, 0), GBC.eol().fill(GBC.HORIZONTAL)); 147 this.add(p2, GBC.eol().fill(GBC.HORIZONTAL)); 148 } 149 150 public void rememberHistory() { 151 cbInput.addCurrentItemToHistory(); 152 Main.pref.putCollection("projection.custom.value.history", cbInput.getHistory()); 153 } 154 } 155 156 public static class ParameterInfoDialog extends ExtendedDialog { 157 158 public ParameterInfoDialog() { 159 super(null, tr("Parameter information"), new String[] { tr("Close") }, false); 160 setContent(build()); 161 } 162 163 private JComponent build() { 164 StringBuilder s = new StringBuilder(); 165 s.append("<b>+proj=...</b> - <i>"+tr("Projection name")+"</i><br>"); 166 s.append(" "+tr("Supported values:")+" "); 167 s.append(listKeys(Projections.projs)+"<br>"); 168 s.append("<b>+lat_0=..., +lat_1=..., +lat_2=...</b> - <i>"+tr("Projection parameters")+"</i><br>"); 169 s.append("<b>+x_0=..., +y_0=...</b> - <i>"+tr("False easting and false northing")+"</i><br>"); 170 s.append("<b>+lon_0=...</b> - <i>"+tr("Central meridian")+"</i><br>"); 171 s.append("<b>+k_0=...</b> - <i>"+tr("Scaling factor")+"</i><br>"); 172 s.append("<b>+ellps=...</b> - <i>"+tr("Ellipsoid name")+"</i><br>"); 173 s.append(" "+tr("Supported values:")+" "); 174 s.append(listKeys(Projections.ellipsoids)+"<br>"); 175 s.append("<b>+a=..., +b=..., +rf=..., +f=..., +es=...</b> - <i>"+tr("Ellipsoid parameters")+"</i><br>"); 176 s.append("<b>+datum=...</b> - <i>"+tr("Datum name")+"</i><br>"); 177 s.append(" "+tr("Supported values:")+" "); 178 s.append(listKeys(Projections.datums)+"<br>"); 179 s.append("<b>+towgs84=...</b> - <i>"+tr("3 or 7 term datum transform parameters")+"</i><br>"); 180 s.append("<b>+nadgrids=...</b> - <i>"+tr("NTv2 grid file")+"</i><br>"); 181 s.append(" "+tr("Built-in:")+" "); 182 s.append(listKeys(Projections.nadgrids)+"<br>"); 183 s.append("<b>+bounds=</b>minlon,minlat,maxlon,maxlat - <i>"+tr("Projection bounds (in degrees)")+"</i><br>"); 184 185 HtmlPanel info = new HtmlPanel(s.toString()); 186 return info; 187 } 188 189 private String listKeys(Map<String, ?> map) { 190 List<String> keys = new ArrayList<String>(map.keySet()); 191 Collections.sort(keys); 192 return Utils.join(", ", keys); 193 } 194 } 195 196 @Override 197 public void setPreferences(Collection<String> args) { 198 if (args != null && !args.isEmpty()) { 199 pref = args.iterator().next(); 200 } 201 } 202 203 @Override 204 public Projection getProjection() { 205 return new CustomProjection(pref); 206 } 207 208 @Override 209 public JPanel getPreferencePanel(ActionListener listener) { 210 return new PreferencePanel(pref, listener); 211 } 212 213 @Override 214 public Collection<String> getPreferences(JPanel panel) { 215 PreferencePanel prefPanel = (PreferencePanel) panel; 216 String pref = prefPanel.input.getText(); 217 prefPanel.rememberHistory(); 218 return Collections.singleton(pref); 219 } 220 221 @Override 222 public String[] allCodes() { 223 return new String[0]; 224 } 225 226 @Override 227 public Collection<String> getPreferencesFromCode(String code) { 228 return null; 229 } 230 231 @Override 232 public String getAlias() { 233 return "org.openstreetmap.josm.data.projection.CustomProjectionPrefGui"; 234 } 235 236 @Override 237 public boolean showProjectionCode() { 238 return false; 239 } 240 }