diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/.MapWithAIURLPreferenceTable.java.swp b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/.MapWithAIURLPreferenceTable.java.swp deleted file mode 100644 index 9c6eda9..0000000 Binary files a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/.MapWithAIURLPreferenceTable.java.swp and /dev/null differ diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIPreferences.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIPreferences.java index b17eff9..d52bdfe 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIPreferences.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIPreferences.java @@ -71,7 +71,8 @@ public class MapWithAIPreferences implements SubPreferenceSetting { .forEach( entry -> list .add(new DataUrl(entry.get(SOURCE), entry.get("url"), - Boolean.valueOf(entry.get("enabled"))))); + Boolean.valueOf(entry.getOrDefault("enabled", "false")), + entry.getOrDefault("parameters", "[]")))); } @Override @@ -94,7 +95,9 @@ public class MapWithAIPreferences implements SubPreferenceSetting { "If checked, automatically merge address nodes onto added buildings, if and only if one address is withing the building boundary")); switchLayerCheckBox.setSelected(MapWithAIPreferenceHelper.isSwitchLayers()); + switchLayerCheckBox.setToolTipText(switchLayer.getToolTipText()); mergeBuildingAddressCheckBox.setSelected(MapWithAIPreferenceHelper.isMergeBuildingAddress()); + mergeBuildingAddressCheckBox.setToolTipText(mergeBuildingWithAddress.getToolTipText()); final JPanel pane = new JPanel(new GridBagLayout()); pane.setAlignmentY(Component.TOP_ALIGNMENT); @@ -107,15 +110,45 @@ public class MapWithAIPreferences implements SubPreferenceSetting { pane.add(mapWithAIApiUrl, first); final JScrollPane scroll1 = new JScrollPane(mapwithaiUrlPreferenceTable); + scroll1.setToolTipText(mapWithAIApiUrl.getToolTipText()); pane.add(scroll1, GBC.eol().fill(GridBagConstraints.BOTH)); scroll1.setPreferredSize(new Dimension(width, height)); + pane.add(new JLabel(), first); + JPanel replaceAddEditDeleteScroll1 = new JPanel(new GridBagLayout()); + pane.add(replaceAddEditDeleteScroll1, second); + final JButton addScroll1 = new JButton(tr("Add")); + replaceAddEditDeleteScroll1.add(addScroll1, buttonInsets); + addScroll1.addActionListener(e -> { + mapwithaiurlTableDisplayData.add(DataUrl.emptyData()); + mapwithaiUrlPreferenceTable.fireDataChanged(); + }); + final JButton editScroll1 = new JButton(tr("Edit Parameters")); + replaceAddEditDeleteScroll1.add(editScroll1, buttonInsets); + editScroll1.addActionListener(e -> { + final List toEdit = mapwithaiUrlPreferenceTable.getSelectedItems(); + if (toEdit.size() == MAX_SELECTED_TO_EDIT) { + mapwithaiUrlPreferenceTable.editPreference(gui); + } + }); + final JButton deleteScroll1 = new JButton(tr("Delete")); + replaceAddEditDeleteScroll1.add(deleteScroll1, buttonInsets); + deleteScroll1.addActionListener(e -> { + List toRemove = mapwithaiUrlPreferenceTable.getSelectedItems(); + if (!toRemove.isEmpty()) { + mapwithaiurlTableDisplayData.removeAll(toRemove); + } + mapwithaiUrlPreferenceTable.fireDataChanged(); + }); + pane.add(switchLayer, first); pane.add(switchLayerCheckBox, second); + switchLayerCheckBox.setToolTipText(switchLayer.getToolTipText()); pane.add(maximumAddition, first); pane.add(maximumAdditionSpinner, second); + maximumAdditionSpinner.setToolTipText(maximumAddition.getToolTipText()); pane.add(mergeBuildingWithAddress, first); pane.add(mergeBuildingAddressCheckBox, second); @@ -129,11 +162,11 @@ public class MapWithAIPreferences implements SubPreferenceSetting { scroll2.setPreferredSize(new Dimension(width, height)); pane.add(new JLabel(), first); - JPanel replaceAddEditDelete = new JPanel(new GridBagLayout()); - pane.add(replaceAddEditDelete, second); - final JButton add = new JButton(tr("Add")); - replaceAddEditDelete.add(add, buttonInsets); - add.addActionListener(e -> { + JPanel replaceAddEditDeleteScroll2 = new JPanel(new GridBagLayout()); + pane.add(replaceAddEditDeleteScroll2, second); + final JButton addScroll2 = new JButton(tr("Add")); + replaceAddEditDeleteScroll2.add(addScroll2, buttonInsets); + addScroll2.addActionListener(e -> { final PrefEntry pe = replacementPreferenceTable.addPreference(gui); if (pe != null && pe.getValue() instanceof StringSetting) { replacementTableDisplayData.add(pe); @@ -142,18 +175,18 @@ public class MapWithAIPreferences implements SubPreferenceSetting { } }); - final JButton edit = new JButton(tr("Edit")); - replaceAddEditDelete.add(edit, buttonInsets); - edit.addActionListener(e -> { + final JButton editScroll2 = new JButton(tr("Edit")); + replaceAddEditDeleteScroll2.add(editScroll2, buttonInsets); + editScroll2.addActionListener(e -> { final List toEdit = replacementPreferenceTable.getSelectedItems(); if (toEdit.size() == MAX_SELECTED_TO_EDIT) { replacementPreferenceTable.editPreference(gui); } }); - final JButton delete = new JButton(tr("Delete")); - replaceAddEditDelete.add(delete, buttonInsets); - delete.addActionListener(e -> { + final JButton deleteScroll2 = new JButton(tr("Delete")); + replaceAddEditDeleteScroll2.add(deleteScroll2, buttonInsets); + deleteScroll2.addActionListener(e -> { final List toRemove = replacementPreferenceTable.getSelectedItems(); if (!toRemove.isEmpty()) { replacementTableDisplayData.removeAll(toRemove); @@ -161,7 +194,7 @@ public class MapWithAIPreferences implements SubPreferenceSetting { replacementPreferenceTable.fireDataChanged(); }); - Arrays.asList(replaceAddEditDelete, scroll2, expertHorizontalGlue, replacementTags) + Arrays.asList(replaceAddEditDeleteScroll2, scroll2, expertHorizontalGlue, replacementTags) .forEach(ExpertToggleAction::addVisibilitySwitcher); getTabPreferenceSetting(gui).addSubTab(this, MapWithAIPlugin.NAME, pane); @@ -169,6 +202,13 @@ public class MapWithAIPreferences implements SubPreferenceSetting { @Override public boolean ok() { + ArrayList tData = new ArrayList<>( + mapwithaiurlTableDisplayData.stream().distinct() + .filter(data -> !data.getMap().getOrDefault("url", "http://example.com") + .equalsIgnoreCase(DataUrl.emptyData().getMap().get("url"))) + .collect(Collectors.toList())); + mapwithaiurlTableDisplayData.clear(); + mapwithaiurlTableDisplayData.addAll(tData); MapWithAIPreferenceHelper.setMapWithAIURLs(convertUrlPrefToMap(mapwithaiurlTableDisplayData)); MapWithAIPreferenceHelper.setSwitchLayers(switchLayerCheckBox.isSelected(), true); final Object value = maximumAdditionSpinner.getValue(); @@ -180,7 +220,10 @@ public class MapWithAIPreferences implements SubPreferenceSetting { } private static List> convertUrlPrefToMap(List displayData) { - return displayData.stream().map(DataUrl::getMap).collect(Collectors.toList()); + return displayData.stream().map(DataUrl::getMap) + .filter(map -> !map.getOrDefault("url", "").isEmpty() + && !DataUrl.emptyData().getMap().get("url").equals(map.get("url"))) + .collect(Collectors.toList()); } private static Map convertReplacementPrefToMap(List displayData) { diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIURLPreferenceTable.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIURLPreferenceTable.java index f3ba895..6a73c25 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIURLPreferenceTable.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/MapWithAIURLPreferenceTable.java @@ -11,8 +11,15 @@ import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; +import java.util.TreeMap; +import java.util.stream.Collectors; +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObject; import javax.swing.ButtonGroup; import javax.swing.DefaultCellEditor; import javax.swing.JComponent; @@ -82,12 +89,13 @@ public class MapWithAIURLPreferenceTable extends JTable { /** * The list of currently selected rows - * @return newly created list of PrefEntry + * + * @return newly created list of DataUrl */ - public List getSelectedItems() { - List entries = new ArrayList<>(); + public List getSelectedItems() { + List entries = new ArrayList<>(); for (int row : getSelectedRows()) { - PrefEntry p = (PrefEntry) model.getValueAt(row, -1); + DataUrl p = displayData.get(row); entries.add(p); } return entries; @@ -99,6 +107,7 @@ public class MapWithAIURLPreferenceTable extends JTable { * @return true if editing was actually performed during this call */ public boolean editPreference(final JComponent gui) { + final int column = 3; if (getSelectedRowCount() != 1) { JOptionPane.showMessageDialog( gui, @@ -108,8 +117,18 @@ public class MapWithAIURLPreferenceTable extends JTable { ); return false; } - final Object e = model.getValueAt(getSelectedRow(), 1); + final Object e = model.getValueAt(getSelectedRow(), column); Setting stg = e instanceof PrefEntry ? ((PrefEntry) e).getValue() : null; + if (e instanceof JsonArray) { + JsonArray array = (JsonArray) e; + List> map = array.stream().filter(JsonObject.class::isInstance) + .map(JsonObject.class::cast).map(obj -> { + Map tMap = new TreeMap<>(); + obj.forEach((str, val) -> tMap.put(str, val.toString())); + return tMap; + }).collect(Collectors.toList()); + stg = new MapListSetting(map); + } boolean ok = false; if (stg instanceof StringSetting || e instanceof String || e instanceof Boolean) { editCellAt(getSelectedRow(), getSelectedColumn()); @@ -122,11 +141,41 @@ public class MapWithAIURLPreferenceTable extends JTable { } else if (stg instanceof ListListSetting) { ok = doEditListList(gui, (PrefEntry) e, (ListListSetting) stg); } else if (stg instanceof MapListSetting) { - ok = doEditMapList(gui, (PrefEntry) e, (MapListSetting) stg); + PrefEntry pref = e instanceof PrefEntry ? (PrefEntry) e : new PrefEntry("Parameters", stg, stg, false); + ok = doEditMapList(gui, pref, (MapListSetting) stg); + if (!e.equals(pref) && e instanceof JsonArray) { + JsonArrayBuilder array = Json.createArrayBuilder(); + ((MapListSetting) pref.getValue()).getValue() + .forEach(entry -> array.add(Json.createObjectBuilder(objectify(entry)).build())); + model.setValueAt(array.build(), getSelectedRow(), column); + fireDataChanged(); + } } return ok; } + private static Map objectify(Map map) { + Map returnMap = new TreeMap<>(); + for (Entry entry : map.entrySet()) { + Object obj = null; + if (entry.getValue().equalsIgnoreCase("true") || entry.getValue().equalsIgnoreCase("false") + || "enabled".equals(entry.getKey())) { + obj = Boolean.parseBoolean(entry.getValue()); + } else if (entry.getValue().matches("[0-9.-]+")) { + try { + obj = Double.parseDouble(entry.getValue()); + obj = Integer.parseInt(entry.getValue()); + } catch (NumberFormatException e) { + // do nothing + } + } else { + obj = entry.getValue().replaceAll("(^(\\\\*\\\"+)+|(\\\\*\\\"+)+$)", ""); + } + returnMap.put(entry.getKey(), obj); + } + return returnMap; + } + private static boolean doEditList(final JComponent gui, final PrefEntry e, ListSetting lSetting) { ListEditor lEditor = new ListEditor(gui, e, lSetting); lEditor.showDialog(); diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/GetDataRunnable.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/GetDataRunnable.java index 39c65c0..7315cb6 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/GetDataRunnable.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/GetDataRunnable.java @@ -36,6 +36,7 @@ import org.openstreetmap.josm.gui.progress.ProgressMonitor; import org.openstreetmap.josm.gui.progress.ProgressMonitor.CancelListener; import org.openstreetmap.josm.io.IllegalDataException; import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; +import org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation.DataUrl; import org.openstreetmap.josm.plugins.mapwithai.commands.MergeDuplicateWays; import org.openstreetmap.josm.tools.Geometry; import org.openstreetmap.josm.tools.HttpClient; @@ -316,6 +317,8 @@ public class GetDataRunnable extends RecursiveTask implements CancelLis urlMaps.forEach(map -> map.put("url", map.get("url").concat("&crop_bbox={crop_bbox}"))); } + urlMaps.parallelStream().filter(map -> map.containsKey("parameters")).forEach(DataUrl::addUrlParameters); + dataSet.setUploadPolicy(UploadPolicy.DISCOURAGED); clients = new ArrayList<>(); diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIPreferenceHelper.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIPreferenceHelper.java index 914305e..39967b1 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIPreferenceHelper.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIPreferenceHelper.java @@ -9,15 +9,13 @@ import java.util.Map; import java.util.TreeMap; import java.util.stream.Collectors; -import org.openstreetmap.josm.actions.ExpertToggleAction; import org.openstreetmap.josm.data.osm.Tag; import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; import org.openstreetmap.josm.spi.preferences.Config; public final class MapWithAIPreferenceHelper { public static final String DEFAULT_MAPWITHAI_API = "https://www.facebook.com/maps/ml_roads?conflate_with_osm=true&theme=ml_road_vector&collaborator=josm&token=ASb3N5o9HbX8QWn8G_NtHIRQaYv3nuG2r7_f3vnGld3KhZNCxg57IsaQyssIaEw5rfRNsPpMwg4TsnrSJtIJms5m&hash=ASawRla3rBcwEjY4HIY&bbox={bbox}"; - protected static final String DEFAULT_MAPWITHAI_API_BUILDING = DEFAULT_MAPWITHAI_API - .concat("&result_type=road_building_vector_xml"); + protected static final String DEFAULT_MAPWITHAI_API_PARAMETERS = "[{\"parameter\": \"result_type=road_building_vector_xml\", \"description\": \"buildings\", \"enabled\": false}]"; private static final int DEFAULT_MAXIMUM_ADDITION = 5; private static final String AUTOSWITCHLAYERS = MapWithAIPlugin.NAME.concat(".autoswitchlayers"); private static final String MERGEBUILDINGADDRESSES = MapWithAIPlugin.NAME.concat(".mergebuildingaddresses"); @@ -27,6 +25,7 @@ public final class MapWithAIPreferenceHelper { private static final String URL_STRING = "url"; private static final String SOURCE_STRING = "source"; private static final String ENABLED_STRING = "enabled"; + private static final String PARAMETERS_STRING = "parameters"; private MapWithAIPreferenceHelper() { // Hide the constructor @@ -63,36 +62,22 @@ public final class MapWithAIPreferenceHelper { List> returnMap = Config.getPref().getListOfMaps(API_MAP_CONFIG, new ArrayList<>()).stream() .map(map -> new TreeMap<>(map)).collect(Collectors.toList()); if (returnMap.isEmpty()) { - List defaultAPIs = ExpertToggleAction.isExpert() - ? Arrays.asList(DEFAULT_MAPWITHAI_API, DEFAULT_MAPWITHAI_API_BUILDING) - : Collections.singletonList(DEFAULT_MAPWITHAI_API); - List defaultList = Config.getPref().getList(API_CONFIG).isEmpty() - ? defaultAPIs - : Config.getPref().getList(API_CONFIG); - returnMap.addAll(defaultList.stream().map(string -> { - TreeMap map = new TreeMap<>(); - map.put(URL_STRING, string); - return map; - }).collect(Collectors.toList())); - } else if (ExpertToggleAction.isExpert() && returnMap.parallelStream() - .noneMatch(map -> DEFAULT_MAPWITHAI_API_BUILDING.equals(map.get(URL_STRING)))) { - Map building = returnMap.parallelStream() - .filter(map -> DEFAULT_MAPWITHAI_API_BUILDING.equals(map.get(URL_STRING))).findFirst() - .orElse(new TreeMap<>()); - returnMap.add(building); - building.putIfAbsent(URL_STRING, DEFAULT_MAPWITHAI_API_BUILDING); - building.putIfAbsent(ENABLED_STRING, Boolean.TRUE.toString()); - + List defaultAPIs = Collections.singletonList(DEFAULT_MAPWITHAI_API); + List defaultList = Config.getPref().getList(API_CONFIG).isEmpty() + ? defaultAPIs + : Config.getPref().getList(API_CONFIG); + returnMap.addAll(defaultList.stream().map(string -> { + TreeMap map = new TreeMap<>(); + map.put(URL_STRING, string); + return map; + }).collect(Collectors.toList())); } returnMap.parallelStream().forEach(map -> { String url = map.get(URL_STRING); - if (DEFAULT_MAPWITHAI_API.equals(url) || DEFAULT_MAPWITHAI_API_BUILDING.equals(url)) { + if (DEFAULT_MAPWITHAI_API.equals(url)) { map.putIfAbsent(SOURCE_STRING, MapWithAIPlugin.NAME); - } - if (DEFAULT_MAPWITHAI_API_BUILDING.equals(url)) { - map.putIfAbsent(ENABLED_STRING, Boolean.toString(ExpertToggleAction.isExpert())); - } else if (DEFAULT_MAPWITHAI_API.equals(url)) { - map.putIfAbsent(ENABLED_STRING, Boolean.toString(!ExpertToggleAction.isExpert())); + map.putIfAbsent(ENABLED_STRING, "true"); + map.putIfAbsent(PARAMETERS_STRING, DEFAULT_MAPWITHAI_API_PARAMETERS); } else { map.putIfAbsent(SOURCE_STRING, url); map.putIfAbsent(ENABLED_STRING, Boolean.FALSE.toString()); @@ -177,13 +162,11 @@ public final class MapWithAIPreferenceHelper { List> setUrls = urls; if (urls.isEmpty()) { TreeMap defaultAPIMap = new TreeMap<>(); - TreeMap defaultBuildingAPIMap = new TreeMap<>(); defaultAPIMap.put(URL_STRING, DEFAULT_MAPWITHAI_API); defaultAPIMap.put(ENABLED_STRING, Boolean.TRUE.toString()); defaultAPIMap.put(SOURCE_STRING, MapWithAIPlugin.NAME); - defaultBuildingAPIMap.put(URL_STRING, DEFAULT_MAPWITHAI_API_BUILDING); - defaultBuildingAPIMap.put(ENABLED_STRING, Boolean.TRUE.toString()); - defaultBuildingAPIMap.put(SOURCE_STRING, MapWithAIPlugin.NAME); + defaultAPIMap.put(PARAMETERS_STRING, DEFAULT_MAPWITHAI_API_PARAMETERS); + setUrls.add(defaultAPIMap); } return Config.getPref().putListOfMaps(API_MAP_CONFIG, setUrls); } diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/DataUrl.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/DataUrl.java index 01c8259..d035bf4 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/DataUrl.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/commands/conflation/DataUrl.java @@ -1,20 +1,46 @@ package org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation; +import static org.openstreetmap.josm.tools.I18n.tr; + +import java.io.StringReader; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonReader; +import javax.json.JsonValue; + public class DataUrl { private List dataList; public DataUrl(String source, String url, Boolean enabled) { - this(source, url, enabled, Collections.emptyMap()); + this(source, url, enabled, "[]"); } - public DataUrl(String source, String url, Boolean enabled, Map> parameters) { - setDataList(Arrays.asList(source, url, enabled, parameters)); + public DataUrl(String source, String url, Boolean enabled, String jsonString) { + setDataList(Arrays.asList(source, url, enabled, readJsonStringArraySimple(jsonString))); + } + + public static DataUrl emptyData() { + return new DataUrl(tr("Default Source"), "http://example.com", false); + } + + /** + * Naively read a json string into an array + * + * @param jsonString A json array (e.g. [0, {"this": "that"}]) + * @return A JsonArray + */ + public static JsonArray readJsonStringArraySimple(String jsonString) { + JsonArray returnArray = JsonValue.EMPTY_JSON_ARRAY; + try (JsonReader parser = Json.createReader(new StringReader(jsonString))) { + returnArray = parser.readArray(); + } + return returnArray; } /** @@ -42,6 +68,22 @@ public class DataUrl { map.put("source", dataList.get(0).toString()); map.put("url", dataList.get(1).toString()); map.put("enabled", dataList.get(2).toString()); + map.put("parameters", dataList.size() > 3 ? dataList.get(3).toString() : "[]"); + return map; + } + + public static Map addUrlParameters(Map map) { + if (map.containsKey("parameters") && map.containsKey("url")) { + JsonArray array = readJsonStringArraySimple(map.get("parameters")); + for (Object val : array.toArray()) { + if (val instanceof JsonObject) { + JsonObject obj = (JsonObject) val; + if (obj.getBoolean("enabled", false)) { + map.put("url", map.get("url").concat("&").concat(obj.getString("parameter"))); + } + } + } + } return map; } } diff --git a/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtilsTest.java b/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtilsTest.java index 0bdf2d7..284abef 100644 --- a/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtilsTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtilsTest.java @@ -48,7 +48,7 @@ public class MapWithAIDataUtilsTest { public void setUp() { MapWithAIPreferenceHelper.setMapWithAIURLs(MapWithAIPreferenceHelper.getMapWithAIURLs().stream().map(map -> { map.put("url", getDefaultMapWithAIAPIForTest( - map.getOrDefault("url", MapWithAIPreferenceHelper.DEFAULT_MAPWITHAI_API_BUILDING))); + map.getOrDefault("url", MapWithAIPreferenceHelper.DEFAULT_MAPWITHAI_API))); return map; }).collect(Collectors.toList())); }