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/src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java
+++ b/src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java
@@ -8,13 +8,15 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.NavigableSet;
 import java.util.Objects;
-import java.util.TreeMap;
+import java.util.OptionalInt;
 import java.util.TreeSet;
 import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 import org.openstreetmap.josm.actions.mapmode.MapMode;
 import org.openstreetmap.josm.data.osm.BBox;
@@ -87,7 +89,7 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
     /**
      * The buttons currently displayed in map view.
      */
-    private final Map<Integer, AutoFilterButton> buttons = new TreeMap<>();
+    private final Map<OptionalInt, AutoFilterButton> buttons = new HashMap<>();
 
     /**
      * The list of registered auto filter rules.
@@ -143,9 +145,13 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
             NavigableSet<Integer> values = getNumericValues();
             // Make sure current auto filter buttons remain visible even if no data is found, to allow user to disable them
             for (var currentAutoFilter : currentAutoFilters) {
-                values.add(currentAutoFilter.getFilter().value);
+                if (currentAutoFilter.getFilter().value != null) {
+                    values.add(currentAutoFilter.getFilter().value);
+                }
             }
-            if (!values.equals(buttons.keySet())) {
+            if (!values.equals(buttons.keySet().stream()
+                    .filter(it -> it.isPresent() && it.getAsInt() != Integer.MIN_VALUE)
+                    .map(OptionalInt::getAsInt).collect(Collectors.toSet()))) {
                 removeAllButtons();
                 addNewButtons(values);
             }
@@ -154,9 +160,9 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
 
     static class CompiledFilter extends Filter implements MatchSupplier {
         final AutoFilterRule rule;
-        final int value;
+        final Integer value;
 
-        CompiledFilter(AutoFilterRule rule, int value, boolean hiding) {
+        CompiledFilter(AutoFilterRule rule, Integer value, boolean hiding) {
             this.rule = rule;
             this.value = value;
             this.hiding = hiding;
@@ -182,7 +188,7 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
             if (!super.equals(obj) || getClass() != obj.getClass())
                 return false;
             CompiledFilter other = (CompiledFilter) obj;
-            return Objects.equals(rule, other.rule) && value == other.value;
+            return Objects.equals(rule, other.rule) && Objects.equals(value, other.value);
         }
     }
 
@@ -242,16 +248,21 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
 
     static class Match extends SearchCompiler.Match {
         final AutoFilterRule rule;
-        final int value;
+        final Integer value;
 
-        Match(AutoFilterRule rule, int value) {
+        Match(AutoFilterRule rule, Integer value) {
             this.rule = rule;
             this.value = value;
         }
 
         @Override
         public boolean match(OsmPrimitive osm) {
-            return rule.getTagValuesForPrimitive(osm, false).anyMatch(v -> v == value);
+            IntStream values = rule.getTagValuesForPrimitive(osm, false);
+            if (value != null) {
+                return values.anyMatch(v -> v == value);
+            } else {
+                return values.findAny().isEmpty();
+            }
         }
 
         @Override
@@ -259,7 +270,7 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             Match match = (Match) o;
-            return value == match.value &&
+            return Objects.equals(value, match.value) &&
                     Objects.equals(rule, match.rule);
         }
 
@@ -277,7 +288,11 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
         int maxWidth = 16;
         final AutoFilterButton keyButton = AutoFilterButton.forOsmKey(enabledRule.getKey());
         addButton(keyButton, Integer.MIN_VALUE, i++);
-        for (final Integer value : values.descendingSet()) {
+        var valueList = new ArrayList<>(values.descendingSet());
+        if (enabledRule.getNoValueFilter()) {
+            valueList.add(null);
+        }
+        for (final Integer value : valueList) {
             CompiledFilter filter = new CompiledFilter(enabledRule, value, PROP_AUTO_FILTER_HIDING.get());
             String label = enabledRule.formatValue(value);
             AutoFilter autoFilter = new AutoFilter(label, filter.text, filter);
@@ -294,9 +309,9 @@ implements ZoomChangeListener, MapModeChangeListener, DataSetListener, Preferenc
         MainApplication.getMap().mapView.validate();
     }
 
-    private void addButton(AutoFilterButton button, int value, int i) {
+    private void addButton(AutoFilterButton button, Integer value, int i) {
         MapView mapView = MainApplication.getMap().mapView;
-        buttons.put(value, button);
+        buttons.put(value == null ? OptionalInt.empty() : OptionalInt.of(value), button);
         mapView.add(button).setLocation(3, 60 + 22*i);
     }
 
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/src/org/openstreetmap/josm/gui/autofilter/AutoFilterRule.java
+++ b/src/org/openstreetmap/josm/gui/autofilter/AutoFilterRule.java
@@ -42,6 +42,8 @@ public class AutoFilterRule {
 
     private IntFunction<String> valueFormatter = Integer::toString;
 
+    private boolean noValueFilter = false;
+
     /** The union of {@link #key} and the keys provided by {@link #setExtraKeys(List)}. */
     private List<String> allKeys;
 
@@ -72,13 +74,20 @@ public class AutoFilterRule {
         return minZoomLevel;
     }
 
+    /**
+     * Returns true if there should be a filter button for OSM primitives which have no value for the key.
+     */
+    public boolean getNoValueFilter() {
+        return noValueFilter;
+    }
+
     /**
      * Formats the numeric value
      * @param value the numeric value to format
      * @return the formatted value
      */
-    public String formatValue(int value) {
-        return valueFormatter.apply(value);
+    public String formatValue(Integer value) {
+        return value == null ? "∅" : valueFormatter.apply(value);
     }
 
     /**
@@ -115,6 +124,14 @@ public class AutoFilterRule {
         return this;
     }
 
+    /**
+     * Adds a filter button for OSM primitives which have no value for the key.
+     */
+    public AutoFilterRule enableNoValueFilter() {
+        this.noValueFilter = true;
+        return this;
+    }
+
     /**
      * Sets extra OSM keys on which the rule applies in addition to the primary key ({@link #getKey()}).
      * This allows a filter to look at the values of more than one key at the same time.
@@ -195,6 +212,7 @@ public class AutoFilterRule {
                     .setDefaultValueSupplier(AutoFilterRule::defaultLayer),
             new AutoFilterRule("level", 17)
                 .setExtraKeys(List.of("repeat_on"))
+                .enableNoValueFilter()
                 // #17109, support values like 0.5 or 1.5 - level values are multiplied by 2 when parsing, values are divided by 2 for formatting
                 .setValueExtractor(s -> (int) (Double.parseDouble(s) * 2.))
                 .setValueFormatter(v -> DecimalFormat.getInstance(Locale.ROOT).format(v / 2.)),
