kopia lustrzana https://github.com/JOSM/MapWithAI
Allow users to add third-party sources
Signed-off-by: Taylor Smock <taylor.smock@kaart.com>pull/1/head
rodzic
a3fb91b2a8
commit
f20e862287
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue