Ticket #18679: 18679.patch
| File 18679.patch, 16.9 KB (added by , 6 years ago) |
|---|
-
src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java
commit fcd8204971c64c1d217ae7a946e254d50b1b5332 Author: Simon Legner <Simon.Legner@gmail.com> Date: 2020-02-05 22:06:43 +0100 fix #18679 diff --git a/src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java b/src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java index c5e1fd9f6..2c16604eb 100644a b 7 7 import java.util.ArrayList; 8 8 import java.util.Arrays; 9 9 import java.util.Collection; 10 import java.util.Comparator; 11 import java.util.Iterator; 10 import java.util.Collections; 12 11 import java.util.List; 13 12 import java.util.Map; 14 13 import java.util.NavigableSet; 15 14 import java.util.Objects; 16 import java.util.Set;17 15 import java.util.TreeMap; 18 16 import java.util.TreeSet; 19 17 import java.util.function.Consumer; 20 18 import java.util.regex.Matcher; 21 19 import java.util.regex.Pattern; 22 20 import java.util.stream.IntStream; 23 import java.util.stream.Stream;24 21 25 22 import org.openstreetmap.josm.actions.mapmode.MapMode; 26 23 import org.openstreetmap.josm.data.osm.BBox; … … 93 90 /** 94 91 * The buttons currently displayed in map view. 95 92 */ 96 private final Map< String, AutoFilterButton> buttons = new TreeMap<>();93 private final Map<Integer, AutoFilterButton> buttons = new TreeMap<>(); 97 94 98 95 /** 99 96 * The list of registered auto filter rules. … … private synchronized void updateButtons() { 145 142 if (enabledRule != null && map != null 146 143 && enabledRule.getMinZoomLevel() <= Selector.GeneralSelector.scale2level(map.mapView.getDist100Pixel())) { 147 144 // Retrieve the values from current rule visible on screen 148 NavigableSet< String> values = getNumericValues(enabledRule.getKey(), enabledRule.getValueComparator());145 NavigableSet<Integer> values = getNumericValues(enabledRule.getKey()); 149 146 // Make sure current auto filter button remains visible even if no data is found, to allow user to disable it 150 147 if (currentAutoFilter != null) { 151 148 values.add(currentAutoFilter.getFilter().value); … … private synchronized void updateButtons() { 157 154 } 158 155 } 159 156 160 staticclass CompiledFilter extends Filter implements MatchSupplier {157 class CompiledFilter extends Filter implements MatchSupplier { 161 158 final String key; 162 final Stringvalue;159 final int value; 163 160 164 CompiledFilter(String key, Stringvalue) {161 CompiledFilter(String key, int value) { 165 162 this.key = key; 166 163 this.value = value; 167 164 this.enable = true; … … private synchronized void updateButtons() { 174 171 return new SearchCompiler.Match() { 175 172 @Override 176 173 public boolean match(OsmPrimitive osm) { 177 return getTagValuesForPrimitive(key, osm).anyMatch(v alue::equals);174 return getTagValuesForPrimitive(key, osm).anyMatch(v -> v == value); 178 175 } 179 176 }; 180 177 } 181 178 } 182 179 183 private synchronized void addNewButtons(NavigableSet< String> values) {180 private synchronized void addNewButtons(NavigableSet<Integer> values) { 184 181 if (values.isEmpty()) { 185 182 return; 186 183 } 187 184 int i = 0; 188 185 int maxWidth = 16; 189 186 final AutoFilterButton keyButton = AutoFilterButton.forOsmKey(enabledRule.getKey()); 190 addButton(keyButton, Integer. toString(Integer.MIN_VALUE), i++);191 for (final Stringvalue : values.descendingSet()) {187 addButton(keyButton, Integer.MIN_VALUE, i++); 188 for (final Integer value : values.descendingSet()) { 192 189 CompiledFilter filter = new CompiledFilter(enabledRule.getKey(), value); 193 190 String label = enabledRule.getValueFormatter().apply(value); 194 191 AutoFilter autoFilter = new AutoFilter(label, filter.text, filter); … … private synchronized void addNewButtons(NavigableSet<String> values) { 205 202 MainApplication.getMap().mapView.validate(); 206 203 } 207 204 208 private void addButton(AutoFilterButton button, Stringvalue, int i) {205 private void addButton(AutoFilterButton button, int value, int i) { 209 206 MapView mapView = MainApplication.getMap().mapView; 210 207 buttons.put(value, button); 211 208 mapView.add(button).setLocation(3, 60 + 22*i); 212 209 } 213 210 214 211 private void removeAllButtons() { 215 for (Iterator<String> it = buttons.keySet().iterator(); it.hasNext();) { 216 MainApplication.getMap().mapView.remove(buttons.get(it.next())); 217 it.remove(); 218 } 212 buttons.values().forEach(MainApplication.getMap().mapView::remove); 213 buttons.clear(); 219 214 } 220 215 221 private static NavigableSet<String> getNumericValues(String key, Comparator<String> comparator) { 222 NavigableSet<String> values = new TreeSet<>(comparator); 223 for (String s : getTagValues(key)) { 224 try { 225 Integer.parseInt(s); 226 values.add(s); 227 } catch (NumberFormatException e) { 228 Logging.trace(e); 229 } 230 } 231 return values; 232 } 233 234 private static Set<String> getTagValues(String key) { 216 private NavigableSet<Integer> getNumericValues(String key) { 235 217 DataSet ds = MainApplication.getLayerManager().getActiveDataSet(); 236 Set<String> values = new TreeSet<>(); 237 if (ds != null) { 238 BBox bbox = MainApplication.getMap().mapView.getState().getViewArea().getLatLonBoundsBox().toBBox(); 239 Consumer<OsmPrimitive> consumer = o -> getTagValuesForPrimitive(key, o).forEach(values::add); 240 ds.searchNodes(bbox).forEach(consumer); 241 ds.searchWays(bbox).forEach(consumer); 242 ds.searchRelations(bbox).forEach(consumer); 218 if (ds == null) { 219 return Collections.emptyNavigableSet(); 243 220 } 221 BBox bbox = MainApplication.getMap().mapView.getState().getViewArea().getLatLonBoundsBox().toBBox(); 222 NavigableSet<Integer> values = new TreeSet<>(); 223 Consumer<OsmPrimitive> consumer = o -> getTagValuesForPrimitive(key, o).forEach(values::add); 224 ds.searchNodes(bbox).forEach(consumer); 225 ds.searchWays(bbox).forEach(consumer); 226 ds.searchRelations(bbox).forEach(consumer); 244 227 return values; 245 228 } 246 229 247 static Stream<String>getTagValuesForPrimitive(String key, OsmPrimitive osm) {230 private IntStream getTagValuesForPrimitive(String key, OsmPrimitive osm) { 248 231 String value = osm.get(key); 249 232 if (value != null) { 250 233 Pattern p = Pattern.compile("(-?[0-9]+)-(-?[0-9]+)"); 251 return OsmUtils.splitMultipleValues(value).flatMap (v -> {234 return OsmUtils.splitMultipleValues(value).flatMapToInt(v -> { 252 235 Matcher m = p.matcher(v); 253 236 if (m.matches()) { 254 237 int a = Integer.parseInt(m.group(1)); 255 238 int b = Integer.parseInt(m.group(2)); 256 return IntStream.rangeClosed(Math.min(a, b), Math.max(a, b)) 257 .mapToObj(Integer::toString); 239 return IntStream.rangeClosed(Math.min(a, b), Math.max(a, b)); 258 240 } else { 259 return Stream.of(v); 241 try { 242 return IntStream.of(enabledRule.getValueExtractor().applyAsInt(v)); 243 } catch (NumberFormatException e) { 244 Logging.trace(e); 245 return IntStream.empty(); 246 } 260 247 } 261 248 }); 262 } else if (PROP_AUTO_FILTER_DEFAULTS.get() && "layer".equals(key)) {263 // assume sensible defaults, see #17496264 if (osm.hasTag("bridge") || osm.hasTag("power", "line") || osm.hasTag("location", "overhead")) {265 return Stream.of("1");266 } else if (osm.isKeyTrue("tunnel") || osm.hasTag("tunnel", "culvert") || osm.hasTag("location", "underground")) {267 return Stream.of("-1");268 } else if (osm.hasTag("tunnel", "building_passage") || osm.hasKey("highway", "railway", "waterway")) {269 return Stream.of("0");270 }271 249 } 272 return Stream.empty();250 return enabledRule.getExtractDefaultValues().apply(osm); 273 251 } 274 252 275 253 @Override -
src/org/openstreetmap/josm/gui/autofilter/AutoFilterRule.java
diff --git a/src/org/openstreetmap/josm/gui/autofilter/AutoFilterRule.java b/src/org/openstreetmap/josm/gui/autofilter/AutoFilterRule.java index 858e772e4..c45d3b571 100644
a b 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.gui.autofilter; 3 3 4 import java.util.Comparator;5 4 import java.util.Objects; 6 5 import java.util.function.Function; 7 import java.util.function.UnaryOperator; 6 import java.util.function.IntFunction; 7 import java.util.function.ToIntFunction; 8 import java.util.stream.IntStream; 9 10 import org.openstreetmap.josm.data.osm.OsmPrimitive; 8 11 9 12 /** 10 13 * An auto filter rule determines how auto filter can be built from visible map data. … … 19 22 20 23 private final int minZoomLevel; 21 24 22 private UnaryOperator<String> valueFormatter = s -> s; 25 private ToIntFunction<String> valueExtractor = Integer::parseInt; 26 27 private IntFunction<String> valueFormatter = Integer::toString; 23 28 24 private Comparator<String> valueComparator = Comparator.comparingInt(s -> Integer.parseInt(valueFormatter.apply(s)));29 private Function<OsmPrimitive, IntStream> extractDefaultValues = p -> IntStream.empty(); 25 30 26 31 /** 27 32 * Constructs a new {@code AutoFilterRule}. … … public int getMinZoomLevel() { 53 58 * Returns the OSM value formatter that defines the associated button label. 54 59 * @return the OSM value formatter that defines the associated button label (identity by default) 55 60 */ 56 public Function<String,String> getValueFormatter() {61 public IntFunction<String> getValueFormatter() { 57 62 return valueFormatter; 58 63 } 59 64 … … public int getMinZoomLevel() { 63 68 * @return {@code this} 64 69 * @throws NullPointerException if {@code valueFormatter} is null 65 70 */ 66 public AutoFilterRule setValueFormatter( UnaryOperator<String> valueFormatter) {71 public AutoFilterRule setValueFormatter(IntFunction<String> valueFormatter) { 67 72 this.valueFormatter = Objects.requireNonNull(valueFormatter); 68 73 return this; 69 74 } 70 75 71 /** 72 * Returns the OSM value comparator used to order the buttons. 73 * @return the OSM value comparator 74 */ 75 public Comparator<String> getValueComparator() { 76 return valueComparator; 76 public ToIntFunction<String> getValueExtractor() { 77 return valueExtractor; 77 78 } 78 79 79 /** 80 * Sets the OSM value comparator used to order the buttons. 81 * @param valueComparator the OSM value comparator 82 * @return {@code this} 83 * @throws NullPointerException if {@code valueComparator} is null 84 */ 85 public AutoFilterRule setValueComparator(Comparator<String> valueComparator) { 86 this.valueComparator = valueComparator; 80 public AutoFilterRule setValueExtractor(ToIntFunction<String> valueExtractor) { 81 this.valueExtractor = valueExtractor; 82 return this; 83 } 84 85 public Function<OsmPrimitive, IntStream> getExtractDefaultValues() { 86 return extractDefaultValues; 87 } 88 89 public AutoFilterRule setExtractDefaultValues(Function<OsmPrimitive, IntStream> extractDefaultValues) { 90 this.extractDefaultValues = extractDefaultValues; 87 91 return this; 88 92 } 89 93 … … public AutoFilterRule setValueComparator(Comparator<String> valueComparator) { 92 96 * @return the default list of auto filter rules 93 97 */ 94 98 public static AutoFilterRule[] defaultRules() { 95 return new AutoFilterRule[] {99 return new AutoFilterRule[]{ 96 100 new AutoFilterRule("level", 17), 97 new AutoFilterRule("layer", 16), 101 new AutoFilterRule("layer", 16) 102 .setExtractDefaultValues(AutoFilterRule::defaultLayer), 98 103 new AutoFilterRule("maxspeed", 16) 99 .setValueFormatter(s -> s.replace(" mph", "")),104 .setValueExtractor(s -> Integer.parseInt(s.replace(" mph", ""))), 100 105 new AutoFilterRule("voltage", 5) 101 .setValueFormatter(s -> s.replaceAll("000$", "k") + 'V') 102 .setValueComparator(Comparator.comparingInt(Integer::parseInt)) 106 .setValueFormatter(s -> s % 1000 == 0 ? (s / 1000) + "kV" : s + "V"), 107 new AutoFilterRule("building:levels", 17), 108 new AutoFilterRule("gauge", 5), 109 new AutoFilterRule("frequency", 5), 110 new AutoFilterRule("incline", 13) 111 .setValueExtractor(s -> Integer.parseInt(s.replaceAll("%$", ""))) 112 .setValueFormatter(v -> v + "\u2009%"), 113 new AutoFilterRule("lanes", 13), 114 new AutoFilterRule("admin_level", 11) 103 115 }; 104 116 } 105 117 118 private static IntStream defaultLayer(OsmPrimitive osm) { 119 // assume sensible defaults, see #17496 120 if (osm.hasTag("bridge") || osm.hasTag("power", "line") || osm.hasTag("location", "overhead")) { 121 return IntStream.of(1); 122 } else if (osm.isKeyTrue("tunnel") || osm.hasTag("tunnel", "culvert") || osm.hasTag("location", "underground")) { 123 return IntStream.of(-1); 124 } else if (osm.hasTag("tunnel", "building_passage") || osm.hasKey("highway", "railway", "waterway")) { 125 return IntStream.of(0); 126 } else { 127 return IntStream.empty(); 128 } 129 } 130 106 131 @Override 107 132 public String toString() { 108 133 return key + '[' + minZoomLevel + ']'; -
test/unit/org/openstreetmap/josm/gui/autofilter/AutoFilterManagerTest.java
diff --git a/test/unit/org/openstreetmap/josm/gui/autofilter/AutoFilterManagerTest.java b/test/unit/org/openstreetmap/josm/gui/autofilter/AutoFilterManagerTest.java index dc9fbf9aa..2b26dfe0b 100644
a b 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.gui.autofilter; 3 3 4 import static org.junit.Assert.assertEquals;5 import static org.junit.Assert.assertNull;6 7 import java.util.Arrays;8 import java.util.TreeSet;9 import java.util.stream.Collectors;10 import java.util.stream.Stream;11 12 import org.junit.Rule;13 import org.junit.Test;14 import org.openstreetmap.josm.data.osm.OsmUtils;15 import org.openstreetmap.josm.testutils.JOSMTestRules;16 17 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;18 19 4 /** 20 5 * Unit tests of {@link AutoFilterManager} class. 21 6 */ 22 7 public class AutoFilterManagerTest { 23 24 /**25 * Setup tests26 */27 @Rule28 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")29 public JOSMTestRules test = new JOSMTestRules();30 31 /**32 * Unit test of {@link AutoFilterManager#getTagValuesForPrimitive}.33 */34 @Test35 public void testTagValuesForPrimitive() {36 final TreeSet<String> values = Stream.of(37 OsmUtils.createPrimitive("way level=-4--5"),38 OsmUtils.createPrimitive("way level=-2"),39 OsmUtils.createPrimitive("node level=0"),40 OsmUtils.createPrimitive("way level=1"),41 OsmUtils.createPrimitive("way level=2;3"),42 OsmUtils.createPrimitive("way level=6-9"),43 OsmUtils.createPrimitive("way level=10;12-13"))44 .flatMap(o -> AutoFilterManager.getTagValuesForPrimitive("level", o))45 .collect(Collectors.toCollection(TreeSet::new));46 assertEquals(new TreeSet<>(Arrays.asList("-5", "-4", "-2", "0", "1", "2", "3", "6", "7", "8", "9", "10", "12", "13")), values);47 48 }49 50 /**51 * Unit test of {@link AutoFilterManager#getTagValuesForPrimitive} provides sensible defaults, see #17496.52 */53 @Test54 public void testTagValuesForPrimitivesDefaults() {55 assertNull(getLayer("way foo=bar"));56 assertEquals("1", getLayer("way bridge=yes"));57 assertEquals("1", getLayer("way power=line"));58 assertEquals("-1", getLayer("way tunnel=yes"));59 assertEquals("0", getLayer("way tunnel=building_passage"));60 assertEquals("0", getLayer("way highway=residential"));61 assertEquals("0", getLayer("way railway=rail"));62 assertEquals("0", getLayer("way waterway=canal"));63 }64 65 private String getLayer(final String assertion) {66 return AutoFilterManager.getTagValuesForPrimitive("layer", OsmUtils.createPrimitive(assertion))67 .findFirst()68 .orElse(null);69 }70 8 }
