Allow users to add third-party sources

Signed-off-by: Taylor Smock <taylor.smock@kaart.com>
pull/1/head
Taylor Smock 2020-04-08 08:20:45 -06:00
rodzic a3fb91b2a8
commit f20e862287
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 625F6A74A3E4311A
5 zmienionych plików z 283 dodań i 20 usunięć

Wyświetl plik

@ -31,6 +31,7 @@ import org.openstreetmap.josm.data.StructUtils.StructEntry;
import org.openstreetmap.josm.data.imagery.ImageryInfo;
import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds;
import org.openstreetmap.josm.data.imagery.Shape;
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo.MapWithAIPreferenceEntry;
import org.openstreetmap.josm.spi.preferences.Config;
import org.openstreetmap.josm.tools.CheckParameterUtil;
import org.openstreetmap.josm.tools.ImageProvider;
@ -126,8 +127,6 @@ public class MapWithAIInfo extends TileSourceInfo implements Comparable<MapWithA
* activated
*/
private String eulaAcceptanceRequired;
/** Type of the MapWithAI service */
private MapWithAIType mapwithaiType = MapWithAIType.THIRD_PARTY;
/** data boundaries, displayed in preferences and used for automatic download */
private ImageryInfo.ImageryBounds bounds;
/**
@ -241,7 +240,7 @@ public class MapWithAIInfo extends TileSourceInfo implements Comparable<MapWithA
public MapWithAIPreferenceEntry(MapWithAIInfo i) {
name = i.name;
id = i.id;
type = i.mapwithaiType.getTypeString();
type = i.type.getTypeString();
url = i.url;
eula = i.eulaAcceptanceRequired;
attribution_text = i.attributionText;
@ -303,6 +302,9 @@ public class MapWithAIInfo extends TileSourceInfo implements Comparable<MapWithA
this.name = name;
this.url = baseUrl;
this.id = id;
if (type == null) {
type = MapWithAIType.THIRD_PARTY;
}
}
/**
@ -730,4 +732,8 @@ public class MapWithAIInfo extends TileSourceInfo implements Comparable<MapWithA
return sb.toString();
}
public void setSourceType(MapWithAIType type) {
this.type = type;
}
}

Wyświetl plik

@ -13,6 +13,10 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObjectBuilder;
import javax.swing.AbstractButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
@ -24,7 +28,9 @@ import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.JTextComponent;
import org.apache.commons.lang3.tuple.Pair;
import org.openstreetmap.josm.actions.ExpertToggleAction;
import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
import org.openstreetmap.josm.data.imagery.TMSCachedTileLoaderJob;
import org.openstreetmap.josm.gui.preferences.imagery.AddImageryPanel.ContentValidationListener;
import org.openstreetmap.josm.gui.preferences.imagery.HeadersTable;
@ -40,6 +46,8 @@ import org.openstreetmap.josm.tools.Logging;
*/
public class AddMapWithAIPanel extends JPanel {
private static final long serialVersionUID = -2838267045934203122L;
private final transient JPanel layerPanel = new JPanel(new GridBagLayout());
protected final JosmTextArea rawUrl = new JosmTextArea(3, 40).transferFocusOnTab();
protected final JosmTextField name = new JosmTextField();
@ -47,13 +55,25 @@ public class AddMapWithAIPanel extends JPanel {
private final JCheckBox validGeoreference = new JCheckBox(tr("Is layer properly georeferenced?"));
private HeadersTable headersTable;
private MapWithAIParametersPanel parametersTable;
private JSpinner minimumCacheExpiry;
private JComboBox<String> minimumCacheExpiryUnit;
private TimeUnit currentUnit;
protected AddMapWithAIPanel() {
private MapWithAIInfo info;
protected AddMapWithAIPanel(LayoutManager layout) {
super(layout);
registerValidableComponent(name);
}
/**
* default constructor
*/
public AddMapWithAIPanel() {
this(new GridBagLayout());
headersTable = new HeadersTable();
parametersTable = new MapWithAIParametersPanel();
minimumCacheExpiry = new JSpinner(new SpinnerNumberModel(
(Number) TimeUnit.MILLISECONDS.toSeconds(TMSCachedTileLoaderJob.MINIMUM_EXPIRES.get()), 0L,
Long.valueOf(Integer.MAX_VALUE), 1));
@ -86,10 +106,34 @@ public class AddMapWithAIPanel extends JPanel {
minimumCacheExpiry.setValue(newValue);
}
});
add(new JLabel(tr("{0} Make sure OSM has the permission to use this service", "1.")), GBC.eol());
add(new JLabel(tr("{0} Enter Service URL", "2.")), GBC.eol());
add(rawUrl, GBC.eop().fill(GBC.HORIZONTAL));
rawUrl.setLineWrap(true);
rawUrl.setAlignmentY(TOP_ALIGNMENT);
add(layerPanel, GBC.eol().fill());
addCommonSettings();
add(new JLabel(tr("{0} Enter name for this source", "3.")), GBC.eol());
add(name, GBC.eol().fill(GBC.HORIZONTAL));
registerValidableComponent(rawUrl);
}
public AddMapWithAIPanel(MapWithAIInfo info) {
this();
this.info = info;
rawUrl.setText(info.getUrl());
name.setText(info.getName());
if (info.getParameters() != null) {
parametersTable.setParameters(info.getParameters());
}
parametersTable.addListener(e -> notifyListeners());
}
protected void addCommonSettings() {
add(new JLabel(tr("Set parameters")), GBC.eop());
add(parametersTable, GBC.eol().fill());
if (ExpertToggleAction.isExpert()) {
add(new JLabel(tr("Minimum cache expiry: ")));
add(minimumCacheExpiry);
@ -104,13 +148,12 @@ public class AddMapWithAIPanel extends JPanel {
return headersTable.getHeaders();
}
protected boolean getCommonIsValidGeoreference() {
return validGeoreference.isSelected();
protected Map<String, Pair<String, Boolean>> getCommonParameters() {
return parametersTable.getParameters();
}
protected AddMapWithAIPanel(LayoutManager layout) {
super(layout);
registerValidableComponent(name);
protected boolean getCommonIsValidGeoreference() {
return validGeoreference.isSelected();
}
protected final void registerValidableComponent(AbstractButton component) {
@ -136,16 +179,33 @@ public class AddMapWithAIPanel extends JPanel {
});
}
protected MapWithAIInfo getInfo() {
// TODO
return new MapWithAIInfo();
private JsonArray convertToJsonParameterArray(Map<String, Pair<String, Boolean>> parameters) {
JsonArrayBuilder builder = Json.createArrayBuilder();
for (Map.Entry<String, Pair<String, Boolean>> entry : parameters.entrySet()) {
JsonObjectBuilder entryBuilder = Json.createObjectBuilder();
entryBuilder.add("parameter", entry.getKey());
entryBuilder.add("description", entry.getValue().getKey());
entryBuilder.add("enabled", entry.getValue().getValue());
builder.add(entryBuilder.build());
}
return builder.build();
}
protected MapWithAIInfo getSourceInfo() {
MapWithAIInfo ret = info == null ? new MapWithAIInfo() : info;
ret.setName(getImageryName());
ret.setUrl(getImageryRawUrl());
ret.setCustomHttpHeaders(getCommonHeaders());
ret.setSourceType(MapWithAIType.THIRD_PARTY);
ret.setParameters(convertToJsonParameterArray(getCommonParameters()));
return ret;
}
protected static String sanitize(String s) {
return s.replaceAll("[\r\n]+", "").trim();
}
protected static String sanitize(String s, MapWithAIType type) {
protected static String sanitize(String s, ImageryType type) {
String ret = s;
String imageryType = type.getTypeString() + ':';
if (ret.startsWith(imageryType)) {
@ -155,16 +215,16 @@ public class AddMapWithAIPanel extends JPanel {
return sanitize(ret);
}
protected final String getInfoName() {
protected final String getImageryName() {
return sanitize(name.getText());
}
protected final String getInfoRawUrl() {
protected final String getImageryRawUrl() {
return sanitize(rawUrl.getText());
}
protected boolean isInfoValid() {
return true; // TODO check validity
protected boolean isSourceValid() {
return !getImageryName().isEmpty() && !getImageryRawUrl().isEmpty();
}
/**
@ -181,7 +241,7 @@ public class AddMapWithAIPanel extends JPanel {
private void notifyListeners() {
for (ContentValidationListener l : listeners) {
l.contentChanged(isInfoValid());
l.contentChanged(isSourceValid());
}
}
}

Wyświetl plik

@ -0,0 +1,158 @@
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.mapwithai.gui.preferences.mapwithai;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.GridBagLayout;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import org.apache.commons.lang3.tuple.Pair;
import org.openstreetmap.josm.tools.GBC;
public class MapWithAIParametersPanel extends JPanel {
private final class ParametersTableModel extends AbstractTableModel {
@Override
public String getColumnName(int column) {
switch (column) {
case 0:
return tr("Parameter name");
case 1:
return tr("Parameter value");
case 2:
return tr("Enabled");
default:
return "";
}
}
@Override
public Class<?> getColumnClass(int column) {
switch (column) {
case 2:
return Boolean.class;
default:
return String.class;
}
}
@Override
public int getRowCount() {
return headers.size() + 1;
}
@Override
public int getColumnCount() {
return 3;
}
@Override
public Object getValueAt(int row, int col) {
if (row < headers.size()) {
return headers.get(row)[col];
}
if (String.class.equals(getColumnClass(col))) {
return "";
}
return null;
}
@Override
public boolean isCellEditable(int row, int column) {
return true;
}
@Override
public void setValueAt(Object value, int row, int col) {
if (row < headers.size()) {
Object[] headerRow = headers.get(row);
headerRow[col] = value;
if ("".equals(headerRow[0]) && "".equals(headerRow[1])) {
headers.remove(row);
fireTableRowsDeleted(row, row);
}
} else if (row == headers.size()) {
Object[] entry = { "", "", null };
entry[col] = value;
headers.add(entry);
fireTableRowsInserted(row + 1, row + 1);
}
fireTableCellUpdated(row, col);
}
}
private List<Object[]> headers;
private ParametersTableModel model;
/**
* Creates empty table
*/
public MapWithAIParametersPanel() {
this(new ConcurrentHashMap<>());
}
/**
* Create table prefilled with headers
*
* @param headers contents of table
*/
public MapWithAIParametersPanel(Map<String, Pair<String, Boolean>> headers) {
super(new GridBagLayout());
this.headers = getHeadersAsVector(headers);
this.model = new ParametersTableModel();
JTable table = new JTable(model);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setAutoCreateRowSorter(true);
table.setRowSelectionAllowed(false);
table.setColumnSelectionAllowed(false);
add(new JScrollPane(table), GBC.eol().fill());
}
private static List<Object[]> getHeadersAsVector(Map<String, Pair<String, Boolean>> headers) {
return headers.entrySet().stream().sorted((e1, e2) -> e1.getKey().compareTo(e2.getKey()))
.map(e -> new Object[] { e.getKey(), e.getValue().getLeft(), e.getValue().getRight() })
.collect(Collectors.toList());
}
/**
* @return headers provided by user
*/
public Map<String, Pair<String, Boolean>> getParameters() {
return headers.stream().distinct()
.collect(Collectors.toMap(x -> (String) x[0], x -> Pair.of((String) x[1], (Boolean) x[2])));
}
public void setParameters(JsonArray parameters) {
int i = 0;
for (JsonObject obj : parameters.stream().filter(JsonObject.class::isInstance).map(JsonObject.class::cast)
.collect(Collectors.toList())) {
model.setValueAt(obj.getString("parameter"), i, 1);
model.setValueAt(obj.getString("description", ""), i, 0);
model.setValueAt(obj.getBoolean("enabled", false), i, 2);
}
model.fireTableDataChanged();
}
public void addListener(TableModelListener l) {
model.addTableModelListener(l);
}
public TableModel getModel() {
return model;
}
}

Wyświetl plik

@ -275,6 +275,9 @@ public class MapWithAIProvidersPanel extends JPanel {
RemoveEntryAction remove = new RemoveEntryAction();
activeTable.getSelectionModel().addListSelectionListener(remove);
EditEntryAction edit = new EditEntryAction();
activeTable.getSelectionModel().addListSelectionListener(edit);
add(new JLabel(tr("Available default entries:")), GBC.std().insets(5, 5, 0, 0));
add(new JLabel(tr("Boundaries of selected MapWithAI entries:")), GBC.eol().insets(5, 5, 0, 0));
@ -341,6 +344,7 @@ public class MapWithAIProvidersPanel extends JPanel {
activeToolbar.setBorderPainted(false);
activeToolbar.setOpaque(false);
activeToolbar.add(new NewEntryAction(MapWithAIInfo.MapWithAIType.THIRD_PARTY));
activeToolbar.add(edit);
activeToolbar.add(remove);
add(activeToolbar, GBC.eol().anchor(GBC.NORTH).insets(0, 0, 5, 5));
}
@ -470,7 +474,7 @@ public class MapWithAIProvidersPanel extends JPanel {
if (addDialog.getValue() == 1) {
try {
activeModel.addRow(p.getInfo());
activeModel.addRow(p.getSourceInfo());
} catch (IllegalArgumentException ex) {
if (ex.getMessage() == null || ex.getMessage().isEmpty()) {
throw ex;
@ -483,6 +487,41 @@ public class MapWithAIProvidersPanel extends JPanel {
}
}
private class EditEntryAction extends AbstractAction implements ListSelectionListener {
private static final long serialVersionUID = -1682304557691078801L;
/**
* Constructs a new {@code EditEntryAction}.
*/
EditEntryAction() {
putValue(NAME, tr("Edit"));
putValue(SHORT_DESCRIPTION, tr("Edit entry"));
new ImageProvider("dialogs", "edit").getResource().attachImageIcon(this, true);
updateEnabledState();
}
protected final void updateEnabledState() {
setEnabled(activeTable.getSelectedRowCount() > 0);
}
@Override
public void valueChanged(ListSelectionEvent e) {
updateEnabledState();
}
@Override
public void actionPerformed(ActionEvent e) {
if (activeTable.getSelectedRow() != -1) {
final AddMapWithAIPanel p = new AddMapWithAIPanel(activeModel.getRow(activeTable.getSelectedRow()));
final AddMapWithAIDialog addDialog = new AddMapWithAIDialog(gui, p);
addDialog.showDialog();
if (addDialog.getValue() == 1) {
p.getSourceInfo();
}
}
}
}
private class RemoveEntryAction extends AbstractAction implements ListSelectionListener {
private static final long serialVersionUID = 2666450386256004180L;

Wyświetl plik

@ -136,8 +136,8 @@ public class MapWithAISourceReader implements Closeable {
bounds.forEach(bound::extend);
bounds.forEach(b -> b.getShapes().forEach(bound::addShape));
info.setBounds(bound);
return info;
}
return info;
}
return new MapWithAIInfo(name);
}