| 1 | // License: GPL. For details, see LICENSE file.
|
|---|
| 2 | package org.openstreetmap.josm.data.projection;
|
|---|
| 3 |
|
|---|
| 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
|---|
| 5 |
|
|---|
| 6 | import java.awt.BorderLayout;
|
|---|
| 7 | import java.awt.GridBagLayout;
|
|---|
| 8 | import java.awt.Insets;
|
|---|
| 9 | import java.awt.event.ActionEvent;
|
|---|
| 10 | import java.awt.event.ActionListener;
|
|---|
| 11 | import java.util.ArrayList;
|
|---|
| 12 | import java.util.Arrays;
|
|---|
| 13 | import java.util.Collection;
|
|---|
| 14 | import java.util.Collections;
|
|---|
| 15 | import java.util.LinkedList;
|
|---|
| 16 | import java.util.List;
|
|---|
| 17 | import java.util.Map;
|
|---|
| 18 |
|
|---|
| 19 | import javax.swing.JButton;
|
|---|
| 20 | import javax.swing.JComponent;
|
|---|
| 21 | import javax.swing.JLabel;
|
|---|
| 22 | import javax.swing.JPanel;
|
|---|
| 23 | import javax.swing.JTextField;
|
|---|
| 24 | import javax.swing.plaf.basic.BasicComboBoxEditor;
|
|---|
| 25 |
|
|---|
| 26 | import org.openstreetmap.josm.Main;
|
|---|
| 27 | import org.openstreetmap.josm.gui.ExtendedDialog;
|
|---|
| 28 | import org.openstreetmap.josm.gui.preferences.projection.SubPrefsOptions;
|
|---|
| 29 | import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionListItem;
|
|---|
| 30 | import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
|
|---|
| 31 | import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
|
|---|
| 32 | import org.openstreetmap.josm.gui.widgets.HtmlPanel;
|
|---|
| 33 | import org.openstreetmap.josm.tools.GBC;
|
|---|
| 34 | import org.openstreetmap.josm.tools.ImageProvider;
|
|---|
| 35 | import org.openstreetmap.josm.tools.Utils;
|
|---|
| 36 |
|
|---|
| 37 | public class CustomProjectionPrefGui extends CustomProjection implements ProjectionSubPrefs, SubPrefsOptions {
|
|---|
| 38 |
|
|---|
| 39 | @Override
|
|---|
| 40 | public void setupPreferencePanel(JPanel p, ActionListener listener) {
|
|---|
| 41 | JPanel inner = new PreferencePanel(listener);
|
|---|
| 42 | p.setLayout(new GridBagLayout());
|
|---|
| 43 | p.add(inner, GBC.std().fill(GBC.HORIZONTAL));
|
|---|
| 44 | }
|
|---|
| 45 |
|
|---|
| 46 | private class PreferencePanel extends JPanel {
|
|---|
| 47 |
|
|---|
| 48 | public JTextField input;
|
|---|
| 49 | private HistoryComboBox cbInput;
|
|---|
| 50 |
|
|---|
| 51 | public PreferencePanel(ActionListener listener) {
|
|---|
| 52 | build(listener);
|
|---|
| 53 | }
|
|---|
| 54 |
|
|---|
| 55 | private void build(final ActionListener listener) {
|
|---|
| 56 | input = new JTextField(30);
|
|---|
| 57 | cbInput = new HistoryComboBox();
|
|---|
| 58 | cbInput.setPrototypeDisplayValue(new AutoCompletionListItem("xxxx"));
|
|---|
| 59 | cbInput.setEditor(new BasicComboBoxEditor() {
|
|---|
| 60 | @Override
|
|---|
| 61 | protected JTextField createEditorComponent() {
|
|---|
| 62 | return input;
|
|---|
| 63 | }
|
|---|
| 64 | });
|
|---|
| 65 | Collection<String> samples = Arrays.asList(
|
|---|
| 66 | "+proj=lonlat +ellps=WGS84 +datum=WGS84 +bounds=-180,-90,180,90",
|
|---|
| 67 | "+proj=tmerc +lat_0=0 +lon_0=9 +k_0=1 +x_0=3500000 +y_0=0 +ellps=bessel +nadgrids=BETA2007.gsb");
|
|---|
| 68 | List<String> inputHistory = new LinkedList<String>(Main.pref.getCollection("projection.custom.value.history", samples));
|
|---|
| 69 | Collections.reverse(inputHistory);
|
|---|
| 70 | cbInput.setPossibleItems(inputHistory);
|
|---|
| 71 | cbInput.setText(pref == null ? "" : pref);
|
|---|
| 72 |
|
|---|
| 73 | final HtmlPanel errorsPanel = new HtmlPanel();
|
|---|
| 74 | errorsPanel.setVisible(false);
|
|---|
| 75 | final JLabel valStatus = new JLabel();
|
|---|
| 76 | valStatus.setVisible(false);
|
|---|
| 77 |
|
|---|
| 78 | final AbstractTextComponentValidator val = new AbstractTextComponentValidator(input, false, false, false) {
|
|---|
| 79 |
|
|---|
| 80 | private String error;
|
|---|
| 81 |
|
|---|
| 82 | @Override
|
|---|
| 83 | public void validate() {
|
|---|
| 84 | if (!isValid()) {
|
|---|
| 85 | feedbackInvalid(tr("Invalid projection configuration: {0}",error));
|
|---|
| 86 | } else {
|
|---|
| 87 | feedbackValid(tr("Projection configuration is valid."));
|
|---|
| 88 | }
|
|---|
| 89 | listener.actionPerformed(null);
|
|---|
| 90 | }
|
|---|
| 91 |
|
|---|
| 92 | @Override
|
|---|
| 93 | public boolean isValid() {
|
|---|
| 94 | try {
|
|---|
| 95 | CustomProjection test = new CustomProjection();
|
|---|
| 96 | test.update(input.getText());
|
|---|
| 97 | } catch (ProjectionConfigurationException ex) {
|
|---|
| 98 | error = ex.getMessage();
|
|---|
| 99 | valStatus.setIcon(ImageProvider.get("data", "error.png"));
|
|---|
| 100 | valStatus.setVisible(true);
|
|---|
| 101 | errorsPanel.setText(error);
|
|---|
| 102 | errorsPanel.setVisible(true);
|
|---|
| 103 | return false;
|
|---|
| 104 | }
|
|---|
| 105 | errorsPanel.setVisible(false);
|
|---|
| 106 | valStatus.setIcon(ImageProvider.get("misc", "green_check.png"));
|
|---|
| 107 | valStatus.setVisible(true);
|
|---|
| 108 | return true;
|
|---|
| 109 | }
|
|---|
| 110 |
|
|---|
| 111 | };
|
|---|
| 112 |
|
|---|
| 113 | JButton btnCheck = new JButton(tr("Validate"));
|
|---|
| 114 | btnCheck.addActionListener(new ActionListener() {
|
|---|
| 115 | @Override
|
|---|
| 116 | public void actionPerformed(ActionEvent e) {
|
|---|
| 117 | val.validate();
|
|---|
| 118 | }
|
|---|
| 119 | });
|
|---|
| 120 | btnCheck.setLayout(new BorderLayout());
|
|---|
| 121 | btnCheck.setMargin(new Insets(-1,0,-1,0));
|
|---|
| 122 |
|
|---|
| 123 | JButton btnInfo = new JButton(tr("Parameter information..."));
|
|---|
| 124 | btnInfo.addActionListener(new ActionListener() {
|
|---|
| 125 | @Override
|
|---|
| 126 | public void actionPerformed(ActionEvent e) {
|
|---|
| 127 | ParameterInfoDialog dlg = new ParameterInfoDialog();
|
|---|
| 128 | dlg.showDialog();
|
|---|
| 129 | dlg.toFront();
|
|---|
| 130 | }
|
|---|
| 131 | });
|
|---|
| 132 |
|
|---|
| 133 | this.setLayout(new GridBagLayout());
|
|---|
| 134 | JPanel p2 = new JPanel(new GridBagLayout());
|
|---|
| 135 | p2.add(cbInput, GBC.std().fill(GBC.HORIZONTAL).insets(0, 20, 5, 5));
|
|---|
| 136 | p2.add(btnCheck, GBC.eol().insets(0, 20, 0, 5));
|
|---|
| 137 | this.add(p2, GBC.eol().fill(GBC.HORIZONTAL));
|
|---|
| 138 | p2 = new JPanel(new GridBagLayout());
|
|---|
| 139 | p2.add(valStatus, GBC.std().anchor(GBC.WEST).weight(0.0001, 0));
|
|---|
| 140 | p2.add(errorsPanel, GBC.eol().fill(GBC.HORIZONTAL));
|
|---|
| 141 | this.add(p2, GBC.eol().fill(GBC.HORIZONTAL));
|
|---|
| 142 | p2 = new JPanel(new GridBagLayout());
|
|---|
| 143 | p2.add(btnInfo, GBC.std().insets(0, 20, 0, 0));
|
|---|
| 144 | p2.add(GBC.glue(1, 0), GBC.eol().fill(GBC.HORIZONTAL));
|
|---|
| 145 | this.add(p2, GBC.eol().fill(GBC.HORIZONTAL));
|
|---|
| 146 | }
|
|---|
| 147 |
|
|---|
| 148 | public void rememberHistory() {
|
|---|
| 149 | cbInput.addCurrentItemToHistory();
|
|---|
| 150 | Main.pref.putCollection("projection.custom.value.history", cbInput.getHistory());
|
|---|
| 151 | }
|
|---|
| 152 | }
|
|---|
| 153 |
|
|---|
| 154 | public class ParameterInfoDialog extends ExtendedDialog {
|
|---|
| 155 |
|
|---|
| 156 | public ParameterInfoDialog() {
|
|---|
| 157 | super(null, tr("Parameter information"), new String[] { tr("Close") }, false);
|
|---|
| 158 | setContent(build());
|
|---|
| 159 | }
|
|---|
| 160 |
|
|---|
| 161 | private JComponent build() {
|
|---|
| 162 | StringBuilder s = new StringBuilder();
|
|---|
| 163 | s.append("<b>+proj=...</b> - <i>"+tr("Projection name")+"</i><br>");
|
|---|
| 164 | s.append(" "+tr("Supported values:")+" ");
|
|---|
| 165 | s.append(listKeys(Projections.projs)+"<br>");
|
|---|
| 166 | s.append("<b>+lat_0=..., +lat_1=..., +lat_2=...</b> - <i>"+tr("Projection parameters")+"</i><br>");
|
|---|
| 167 | s.append("<b>+x_0=..., +y_0=...</b> - <i>"+tr("False easting and false northing")+"</i><br>");
|
|---|
| 168 | s.append("<b>+lon_0=...</b> - <i>"+tr("Central meridian")+"</i><br>");
|
|---|
| 169 | s.append("<b>+k_0=...</b> - <i>"+tr("Scaling factor")+"</i><br>");
|
|---|
| 170 | s.append("<b>+ellps=...</b> - <i>"+tr("Ellipsoid name")+"</i><br>");
|
|---|
| 171 | s.append(" "+tr("Supported values:")+" ");
|
|---|
| 172 | s.append(listKeys(Projections.ellipsoids)+"<br>");
|
|---|
| 173 | s.append("<b>+a=..., +b=..., +rf=..., +f=..., +es=...</b> - <i>"+tr("Ellipsoid parameters")+"</i><br>");
|
|---|
| 174 | s.append("<b>+datum=...</b> - <i>"+tr("Datum name")+"</i><br>");
|
|---|
| 175 | s.append(" "+tr("Supported values:")+" ");
|
|---|
| 176 | s.append(listKeys(Projections.datums)+"<br>");
|
|---|
| 177 | s.append("<b>+towgs84=...</b> - <i>"+tr("3 or 7 term datum transform parameters")+"</i><br>");
|
|---|
| 178 | s.append("<b>+nadgrids=...</b> - <i>"+tr("NTv2 grid file")+"</i><br>");
|
|---|
| 179 | s.append(" "+tr("Build-in:")+" ");
|
|---|
| 180 | s.append(listKeys(Projections.nadgrids)+"<br>");
|
|---|
| 181 | s.append("<b>+bounds=</b>minlon,minlat,maxlon,maxlat - <i>"+tr("Projection bounds (in degrees)")+"</i><br>");
|
|---|
| 182 |
|
|---|
| 183 | HtmlPanel info = new HtmlPanel(s.toString());
|
|---|
| 184 | return info;
|
|---|
| 185 | }
|
|---|
| 186 |
|
|---|
| 187 | private String listKeys(Map<String, ?> map) {
|
|---|
| 188 | List<String> keys = new ArrayList<String>(map.keySet());
|
|---|
| 189 | Collections.sort(keys);
|
|---|
| 190 | return Utils.join(", ", keys);
|
|---|
| 191 | }
|
|---|
| 192 | }
|
|---|
| 193 |
|
|---|
| 194 | @Override
|
|---|
| 195 | public Collection<String> getPreferences(JPanel p) {
|
|---|
| 196 | PreferencePanel prefPanel = (PreferencePanel) p.getComponent(0);
|
|---|
| 197 | String pref = prefPanel.input.getText();
|
|---|
| 198 | prefPanel.rememberHistory();
|
|---|
| 199 | return Collections.singleton(pref);
|
|---|
| 200 | }
|
|---|
| 201 |
|
|---|
| 202 | @Override
|
|---|
| 203 | public void setPreferences(Collection<String> args) {
|
|---|
| 204 | try {
|
|---|
| 205 | if (args == null || args.isEmpty()) throw new ProjectionConfigurationException();
|
|---|
| 206 | update(args.iterator().next());
|
|---|
| 207 | } catch (ProjectionConfigurationException ex) {
|
|---|
| 208 | System.err.println("Error: Parsing of custom projection failed, falling back to Mercator. Error message is: "+ex.getMessage());
|
|---|
| 209 | try {
|
|---|
| 210 | update(null);
|
|---|
| 211 | } catch (ProjectionConfigurationException ex1) {
|
|---|
| 212 | throw new RuntimeException(ex1);
|
|---|
| 213 | }
|
|---|
| 214 | }
|
|---|
| 215 | }
|
|---|
| 216 |
|
|---|
| 217 | @Override
|
|---|
| 218 | public String[] allCodes() {
|
|---|
| 219 | return new String[0];
|
|---|
| 220 | }
|
|---|
| 221 |
|
|---|
| 222 | @Override
|
|---|
| 223 | public Collection<String> getPreferencesFromCode(String code) {
|
|---|
| 224 | return null;
|
|---|
| 225 | }
|
|---|
| 226 |
|
|---|
| 227 | @Override
|
|---|
| 228 | public boolean showProjectionCode() {
|
|---|
| 229 | return false;
|
|---|
| 230 | }
|
|---|
| 231 |
|
|---|
| 232 | }
|
|---|