diff --git a/src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java b/src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java
index ba5514dc03..1c40d8a82c 100644
|
a
|
b
|
import java.util.ArrayList;
|
| 8 | 8 | import java.util.Arrays; |
| 9 | 9 | import java.util.Collection; |
| 10 | 10 | import java.util.Collections; |
| | 11 | import java.util.HashMap; |
| 11 | 12 | import java.util.List; |
| 12 | 13 | import java.util.Map; |
| 13 | 14 | import java.util.NavigableSet; |
| 14 | 15 | import java.util.Objects; |
| 15 | | import java.util.TreeMap; |
| | 16 | import java.util.OptionalInt; |
| 16 | 17 | import java.util.TreeSet; |
| 17 | 18 | import java.util.stream.Collectors; |
| | 19 | import java.util.stream.IntStream; |
| 18 | 20 | |
| 19 | 21 | import org.openstreetmap.josm.actions.mapmode.MapMode; |
| 20 | 22 | import org.openstreetmap.josm.data.osm.BBox; |
| … |
… |
implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
|
| 87 | 89 | /** |
| 88 | 90 | * The buttons currently displayed in map view. |
| 89 | 91 | */ |
| 90 | | private final Map<Integer, AutoFilterButton> buttons = new TreeMap<>(); |
| | 92 | private final Map<OptionalInt, AutoFilterButton> buttons = new HashMap<>(); |
| 91 | 93 | |
| 92 | 94 | /** |
| 93 | 95 | * The list of registered auto filter rules. |
| … |
… |
implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
|
| 143 | 145 | NavigableSet<Integer> values = getNumericValues(); |
| 144 | 146 | // Make sure current auto filter buttons remain visible even if no data is found, to allow user to disable them |
| 145 | 147 | for (var currentAutoFilter : currentAutoFilters) { |
| 146 | | values.add(currentAutoFilter.getFilter().value); |
| | 148 | if (currentAutoFilter.getFilter().value != null) { |
| | 149 | values.add(currentAutoFilter.getFilter().value); |
| | 150 | } |
| 147 | 151 | } |
| 148 | | if (!values.equals(buttons.keySet())) { |
| | 152 | if (!values.equals(buttons.keySet().stream() |
| | 153 | .filter(it -> it.isPresent() && it.getAsInt() != Integer.MIN_VALUE) |
| | 154 | .map(OptionalInt::getAsInt).collect(Collectors.toSet()))) { |
| 149 | 155 | removeAllButtons(); |
| 150 | 156 | addNewButtons(values); |
| 151 | 157 | } |
| … |
… |
implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
|
| 154 | 160 | |
| 155 | 161 | static class CompiledFilter extends Filter implements MatchSupplier { |
| 156 | 162 | final AutoFilterRule rule; |
| 157 | | final int value; |
| | 163 | final Integer value; |
| 158 | 164 | |
| 159 | | CompiledFilter(AutoFilterRule rule, int value, boolean hiding) { |
| | 165 | CompiledFilter(AutoFilterRule rule, Integer value, boolean hiding) { |
| 160 | 166 | this.rule = rule; |
| 161 | 167 | this.value = value; |
| 162 | 168 | this.hiding = hiding; |
| … |
… |
implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
|
| 182 | 188 | if (!super.equals(obj) || getClass() != obj.getClass()) |
| 183 | 189 | return false; |
| 184 | 190 | CompiledFilter other = (CompiledFilter) obj; |
| 185 | | return Objects.equals(rule, other.rule) && value == other.value; |
| | 191 | return Objects.equals(rule, other.rule) && Objects.equals(value, other.value); |
| 186 | 192 | } |
| 187 | 193 | } |
| 188 | 194 | |
| … |
… |
implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
|
| 242 | 248 | |
| 243 | 249 | static class Match extends SearchCompiler.Match { |
| 244 | 250 | final AutoFilterRule rule; |
| 245 | | final int value; |
| | 251 | final Integer value; |
| 246 | 252 | |
| 247 | | Match(AutoFilterRule rule, int value) { |
| | 253 | Match(AutoFilterRule rule, Integer value) { |
| 248 | 254 | this.rule = rule; |
| 249 | 255 | this.value = value; |
| 250 | 256 | } |
| 251 | 257 | |
| 252 | 258 | @Override |
| 253 | 259 | public boolean match(OsmPrimitive osm) { |
| 254 | | return rule.getTagValuesForPrimitive(osm, false).anyMatch(v -> v == value); |
| | 260 | IntStream values = rule.getTagValuesForPrimitive(osm, false); |
| | 261 | if (value != null) { |
| | 262 | return values.anyMatch(v -> v == value); |
| | 263 | } else { |
| | 264 | return values.findAny().isEmpty(); |
| | 265 | } |
| 255 | 266 | } |
| 256 | 267 | |
| 257 | 268 | @Override |
| … |
… |
implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
|
| 259 | 270 | if (this == o) return true; |
| 260 | 271 | if (o == null || getClass() != o.getClass()) return false; |
| 261 | 272 | Match match = (Match) o; |
| 262 | | return value == match.value && |
| | 273 | return Objects.equals(value, match.value) && |
| 263 | 274 | Objects.equals(rule, match.rule); |
| 264 | 275 | } |
| 265 | 276 | |
| … |
… |
implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
|
| 277 | 288 | int maxWidth = 16; |
| 278 | 289 | final AutoFilterButton keyButton = AutoFilterButton.forOsmKey(enabledRule.getKey()); |
| 279 | 290 | addButton(keyButton, Integer.MIN_VALUE, i++); |
| 280 | | for (final Integer value : values.descendingSet()) { |
| | 291 | var valueList = new ArrayList<>(values.descendingSet()); |
| | 292 | if (enabledRule.getNoValueFilter()) { |
| | 293 | valueList.add(null); |
| | 294 | } |
| | 295 | for (final Integer value : valueList) { |
| 281 | 296 | CompiledFilter filter = new CompiledFilter(enabledRule, value, PROP_AUTO_FILTER_HIDING.get()); |
| 282 | 297 | String label = enabledRule.formatValue(value); |
| 283 | 298 | AutoFilter autoFilter = new AutoFilter(label, filter.text, filter); |
| … |
… |
implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
|
| 294 | 309 | MainApplication.getMap().mapView.validate(); |
| 295 | 310 | } |
| 296 | 311 | |
| 297 | | private void addButton(AutoFilterButton button, int value, int i) { |
| | 312 | private void addButton(AutoFilterButton button, Integer value, int i) { |
| 298 | 313 | MapView mapView = MainApplication.getMap().mapView; |
| 299 | | buttons.put(value, button); |
| | 314 | buttons.put(value == null ? OptionalInt.empty() : OptionalInt.of(value), button); |
| 300 | 315 | mapView.add(button).setLocation(3, 60 + 22*i); |
| 301 | 316 | } |
| 302 | 317 | |
diff --git a/src/org/openstreetmap/josm/gui/autofilter/AutoFilterRule.java b/src/org/openstreetmap/josm/gui/autofilter/AutoFilterRule.java
index e78c42d156..ec1f98d145 100644
|
a
|
b
|
public class AutoFilterRule {
|
| 42 | 42 | |
| 43 | 43 | private IntFunction<String> valueFormatter = Integer::toString; |
| 44 | 44 | |
| | 45 | private boolean noValueFilter = false; |
| | 46 | |
| 45 | 47 | /** The union of {@link #key} and the keys provided by {@link #setExtraKeys(List)}. */ |
| 46 | 48 | private List<String> allKeys; |
| 47 | 49 | |
| … |
… |
public class AutoFilterRule {
|
| 72 | 74 | return minZoomLevel; |
| 73 | 75 | } |
| 74 | 76 | |
| | 77 | /** |
| | 78 | * Returns true if there should be a filter button for OSM primitives which have no value for the key. |
| | 79 | */ |
| | 80 | public boolean getNoValueFilter() { |
| | 81 | return noValueFilter; |
| | 82 | } |
| | 83 | |
| 75 | 84 | /** |
| 76 | 85 | * Formats the numeric value |
| 77 | 86 | * @param value the numeric value to format |
| 78 | 87 | * @return the formatted value |
| 79 | 88 | */ |
| 80 | | public String formatValue(int value) { |
| 81 | | return valueFormatter.apply(value); |
| | 89 | public String formatValue(Integer value) { |
| | 90 | return value == null ? "∅" : valueFormatter.apply(value); |
| 82 | 91 | } |
| 83 | 92 | |
| 84 | 93 | /** |
| … |
… |
public class AutoFilterRule {
|
| 115 | 124 | return this; |
| 116 | 125 | } |
| 117 | 126 | |
| | 127 | /** |
| | 128 | * Adds a filter button for OSM primitives which have no value for the key. |
| | 129 | */ |
| | 130 | public AutoFilterRule enableNoValueFilter() { |
| | 131 | this.noValueFilter = true; |
| | 132 | return this; |
| | 133 | } |
| | 134 | |
| 118 | 135 | /** |
| 119 | 136 | * Sets extra OSM keys on which the rule applies in addition to the primary key ({@link #getKey()}). |
| 120 | 137 | * This allows a filter to look at the values of more than one key at the same time. |
| … |
… |
public class AutoFilterRule {
|
| 195 | 212 | .setDefaultValueSupplier(AutoFilterRule::defaultLayer), |
| 196 | 213 | new AutoFilterRule("level", 17) |
| 197 | 214 | .setExtraKeys(List.of("repeat_on")) |
| | 215 | .enableNoValueFilter() |
| 198 | 216 | // #17109, support values like 0.5 or 1.5 - level values are multiplied by 2 when parsing, values are divided by 2 for formatting |
| 199 | 217 | .setValueExtractor(s -> (int) (Double.parseDouble(s) * 2.)) |
| 200 | 218 | .setValueFormatter(v -> DecimalFormat.getInstance(Locale.ROOT).format(v / 2.)), |