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