001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.preferences.projection; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.BorderLayout; 007import java.awt.GridBagLayout; 008import java.awt.Insets; 009import java.awt.event.ActionEvent; 010import java.awt.event.ActionListener; 011import java.util.Arrays; 012import java.util.Collection; 013import java.util.Collections; 014import java.util.LinkedList; 015import java.util.List; 016 017import javax.swing.JButton; 018import javax.swing.JComponent; 019import javax.swing.JLabel; 020import javax.swing.JPanel; 021import javax.swing.plaf.basic.BasicComboBoxEditor; 022 023import org.openstreetmap.josm.Main; 024import org.openstreetmap.josm.data.projection.CustomProjection; 025import org.openstreetmap.josm.data.projection.Projection; 026import org.openstreetmap.josm.data.projection.ProjectionConfigurationException; 027import org.openstreetmap.josm.data.projection.Projections; 028import org.openstreetmap.josm.gui.ExtendedDialog; 029import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionListItem; 030import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator; 031import org.openstreetmap.josm.gui.widgets.HistoryComboBox; 032import org.openstreetmap.josm.gui.widgets.HtmlPanel; 033import org.openstreetmap.josm.gui.widgets.JosmTextField; 034import org.openstreetmap.josm.tools.GBC; 035import org.openstreetmap.josm.tools.ImageProvider; 036 037public class CustomProjectionChoice extends AbstractProjectionChoice implements SubPrefsOptions { 038 039 private String pref; 040 041 /** 042 * Constructs a new {@code CustomProjectionChoice}. 043 */ 044 public CustomProjectionChoice() { 045 super(tr("Custom Projection"), /* NO-ICON */ "core:custom"); 046 } 047 048 private static class PreferencePanel extends JPanel { 049 050 public JosmTextField input; 051 private HistoryComboBox cbInput; 052 053 PreferencePanel(String initialText, ActionListener listener) { 054 build(initialText, listener); 055 } 056 057 private void build(String initialText, final ActionListener listener) { 058 input = new JosmTextField(30); 059 cbInput = new HistoryComboBox(); 060 cbInput.setPrototypeDisplayValue(new AutoCompletionListItem("xxxx")); 061 cbInput.setEditor(new BasicComboBoxEditor() { 062 @Override 063 protected JosmTextField 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<>(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")); 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")); 109 valStatus.setVisible(true); 110 return true; 111 } 112 }; 113 114 JButton btnCheck = new JButton(tr("Validate")); 115 btnCheck.addActionListener(new ActionListener() { 116 @Override 117 public void actionPerformed(ActionEvent e) { 118 val.validate(); 119 } 120 }); 121 btnCheck.setLayout(new BorderLayout()); 122 btnCheck.setMargin(new Insets(-1, 0, -1, 0)); 123 124 JButton btnInfo = new JButton(tr("Parameter information...")); 125 btnInfo.addActionListener(new ActionListener() { 126 @Override 127 public void actionPerformed(ActionEvent e) { 128 CustomProjectionChoice.ParameterInfoDialog dlg = new CustomProjectionChoice.ParameterInfoDialog(); 129 dlg.showDialog(); 130 dlg.toFront(); 131 } 132 }); 133 134 this.setLayout(new GridBagLayout()); 135 JPanel p2 = new JPanel(new GridBagLayout()); 136 p2.add(cbInput, GBC.std().fill(GBC.HORIZONTAL).insets(0, 20, 5, 5)); 137 p2.add(btnCheck, GBC.eol().insets(0, 20, 0, 5)); 138 this.add(p2, GBC.eol().fill(GBC.HORIZONTAL)); 139 p2 = new JPanel(new GridBagLayout()); 140 p2.add(valStatus, GBC.std().anchor(GBC.WEST).weight(0.0001, 0)); 141 p2.add(errorsPanel, GBC.eol().fill(GBC.HORIZONTAL)); 142 this.add(p2, GBC.eol().fill(GBC.HORIZONTAL)); 143 p2 = new JPanel(new GridBagLayout()); 144 p2.add(btnInfo, GBC.std().insets(0, 20, 0, 0)); 145 p2.add(GBC.glue(1, 0), GBC.eol().fill(GBC.HORIZONTAL)); 146 this.add(p2, GBC.eol().fill(GBC.HORIZONTAL)); 147 } 148 149 public void rememberHistory() { 150 cbInput.addCurrentItemToHistory(); 151 Main.pref.putCollection("projection.custom.value.history", cbInput.getHistory()); 152 } 153 } 154 155 public static class ParameterInfoDialog extends ExtendedDialog { 156 157 /** 158 * Constructs a new {@code ParameterInfoDialog}. 159 */ 160 public ParameterInfoDialog() { 161 super(null, tr("Parameter information"), new String[] {tr("Close")}, false); 162 setContent(build()); 163 } 164 165 private static JComponent build() { 166 StringBuilder s = new StringBuilder(1024); 167 s.append("<b>+proj=...</b> - <i>").append(tr("Projection name")) 168 .append("</i><br> ").append(tr("Supported values:")).append(' ') 169 .append(Projections.listProjs()) 170 .append("<br><b>+lat_0=..., +lat_1=..., +lat_2=...</b> - <i>").append(tr("Projection parameters")) 171 .append("</i><br><b>+x_0=..., +y_0=...</b> - <i>").append(tr("False easting and false northing")) 172 .append("</i><br><b>+lon_0=...</b> - <i>").append(tr("Central meridian")) 173 .append("</i><br><b>+k_0=...</b> - <i>").append(tr("Scaling factor")) 174 .append("</i><br><b>+ellps=...</b> - <i>").append(tr("Ellipsoid name")) 175 .append("</i><br> ").append(tr("Supported values:")).append(' ') 176 .append(Projections.listEllipsoids()) 177 .append("<br><b>+a=..., +b=..., +rf=..., +f=..., +es=...</b> - <i>").append(tr("Ellipsoid parameters")) 178 .append("</i><br><b>+datum=...</b> - <i>").append(tr("Datum name")) 179 .append("</i><br> ").append(tr("Supported values:")).append(' ') 180 .append(Projections.listDatums()) 181 .append("<br><b>+towgs84=...</b> - <i>").append(tr("3 or 7 term datum transform parameters")) 182 .append("</i><br><b>+nadgrids=...</b> - <i>").append(tr("NTv2 grid file")) 183 .append("</i><br> ").append(tr("Built-in:")).append(' ') 184 .append(Projections.listNadgrids()) 185 .append("<br><b>+bounds=</b>minlon,minlat,maxlon,maxlat - <i>").append(tr("Projection bounds (in degrees)")) 186 .append("</i><br><b>+wmssrs=</b>EPSG:123456 - <i>").append(tr("Sets the SRS=... parameter in the WMS request")) 187 .append("</i><br>"); 188 189 return new HtmlPanel(s.toString()); 190 } 191 } 192 193 @Override 194 public void setPreferences(Collection<String> args) { 195 if (args != null && !args.isEmpty()) { 196 pref = args.iterator().next(); 197 } 198 } 199 200 @Override 201 public Projection getProjection() { 202 return new CustomProjection(pref); 203 } 204 205 @Override 206 public String getCurrentCode() { 207 // not needed - getProjection() is overridden 208 throw new UnsupportedOperationException(); 209 } 210 211 @Override 212 public String getProjectionName() { 213 // not needed - getProjection() is overridden 214 throw new UnsupportedOperationException(); 215 } 216 217 @Override 218 public JPanel getPreferencePanel(ActionListener listener) { 219 return new PreferencePanel(pref, listener); 220 } 221 222 @Override 223 public Collection<String> getPreferences(JPanel panel) { 224 if (!(panel instanceof PreferencePanel)) { 225 throw new IllegalArgumentException("Unsupported panel: "+panel); 226 } 227 PreferencePanel prefPanel = (PreferencePanel) panel; 228 String pref = prefPanel.input.getText(); 229 prefPanel.rememberHistory(); 230 return Collections.singleton(pref); 231 } 232 233 @Override 234 public String[] allCodes() { 235 return new String[0]; 236 } 237 238 @Override 239 public Collection<String> getPreferencesFromCode(String code) { 240 return null; 241 } 242 243 @Override 244 public boolean showProjectionCode() { 245 return false; 246 } 247 248 @Override 249 public boolean showProjectionName() { 250 return false; 251 } 252}