001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.osm.visitor.paint; 003 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.List; 007import java.util.concurrent.ForkJoinTask; 008import java.util.concurrent.RecursiveTask; 009 010import org.openstreetmap.josm.data.osm.Node; 011import org.openstreetmap.josm.data.osm.OsmPrimitive; 012import org.openstreetmap.josm.data.osm.Relation; 013import org.openstreetmap.josm.data.osm.Way; 014import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor; 015import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer.StyleRecord; 016import org.openstreetmap.josm.gui.NavigatableComponent; 017import org.openstreetmap.josm.gui.mappaint.ElemStyles; 018import org.openstreetmap.josm.gui.mappaint.MapPaintStyles; 019import org.openstreetmap.josm.gui.mappaint.StyleElementList; 020import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource; 021import org.openstreetmap.josm.gui.mappaint.styleelement.AreaElement; 022import org.openstreetmap.josm.gui.mappaint.styleelement.AreaIconElement; 023import org.openstreetmap.josm.gui.mappaint.styleelement.NodeElement; 024import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement; 025import org.openstreetmap.josm.gui.mappaint.styleelement.TextElement; 026import org.openstreetmap.josm.spi.preferences.Config; 027import org.openstreetmap.josm.tools.JosmRuntimeException; 028import org.openstreetmap.josm.tools.bugreport.BugReport; 029 030/** 031 * Helper to compute style list. 032 * @since 11914 (extracted from StyledMapRenderer) 033 */ 034public class ComputeStyleListWorker extends RecursiveTask<List<StyleRecord>> implements OsmPrimitiveVisitor { 035 private final transient List<? extends OsmPrimitive> input; 036 private final transient List<StyleRecord> output; 037 038 private final transient ElemStyles styles; 039 private final int directExecutionTaskSize; 040 private final double circum; 041 private final NavigatableComponent nc; 042 043 private final boolean drawArea; 044 private final boolean drawMultipolygon; 045 private final boolean drawRestriction; 046 047 /** 048 * Constructs a new {@code ComputeStyleListWorker}. 049 * @param circum distance on the map in meters that 100 screen pixels represent 050 * @param nc navigatable component 051 * @param input the primitives to process 052 * @param output the list of styles to which styles will be added 053 * @param directExecutionTaskSize the threshold deciding whether to subdivide the tasks 054 */ 055 ComputeStyleListWorker(double circum, NavigatableComponent nc, 056 final List<? extends OsmPrimitive> input, List<StyleRecord> output, int directExecutionTaskSize) { 057 this(circum, nc, input, output, directExecutionTaskSize, MapPaintStyles.getStyles()); 058 } 059 060 /** 061 * Constructs a new {@code ComputeStyleListWorker}. 062 * @param circum distance on the map in meters that 100 screen pixels represent 063 * @param nc navigatable component 064 * @param input the primitives to process 065 * @param output the list of styles to which styles will be added 066 * @param directExecutionTaskSize the threshold deciding whether to subdivide the tasks 067 * @param styles the {@link ElemStyles} instance used to generate primitive {@link StyleElement}s. 068 * @since 12964 069 */ 070 ComputeStyleListWorker(double circum, NavigatableComponent nc, 071 final List<? extends OsmPrimitive> input, List<StyleRecord> output, int directExecutionTaskSize, 072 ElemStyles styles) { 073 this.circum = circum; 074 this.nc = nc; 075 this.input = input; 076 this.output = output; 077 this.directExecutionTaskSize = directExecutionTaskSize; 078 this.styles = styles; 079 this.drawArea = circum <= Config.getPref().getInt("mappaint.fillareas", 10_000_000); 080 this.drawMultipolygon = drawArea && Config.getPref().getBoolean("mappaint.multipolygon", true); 081 this.drawRestriction = Config.getPref().getBoolean("mappaint.restriction", true); 082 this.styles.setDrawMultipolygon(drawMultipolygon); 083 } 084 085 @Override 086 protected List<StyleRecord> compute() { 087 if (input.size() <= directExecutionTaskSize) { 088 return computeDirectly(); 089 } else { 090 final Collection<ForkJoinTask<List<StyleRecord>>> tasks = new ArrayList<>(); 091 for (int fromIndex = 0; fromIndex < input.size(); fromIndex += directExecutionTaskSize) { 092 final int toIndex = Math.min(fromIndex + directExecutionTaskSize, input.size()); 093 tasks.add(new ComputeStyleListWorker(circum, nc, input.subList(fromIndex, toIndex), 094 new ArrayList<>(directExecutionTaskSize), directExecutionTaskSize, styles).fork()); 095 } 096 for (ForkJoinTask<List<StyleRecord>> task : tasks) { 097 output.addAll(task.join()); 098 } 099 return output; 100 } 101 } 102 103 /** 104 * Compute directly (without using fork/join) the style list. Only called for small input. 105 * @return list of computed style records 106 */ 107 public List<StyleRecord> computeDirectly() { 108 MapCSSStyleSource.STYLE_SOURCE_LOCK.readLock().lock(); 109 try { 110 for (final OsmPrimitive osm : input) { 111 acceptDrawable(osm); 112 } 113 return output; 114 } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException e) { 115 throw BugReport.intercept(e).put("input-size", input.size()).put("output-size", output.size()); 116 } finally { 117 MapCSSStyleSource.STYLE_SOURCE_LOCK.readLock().unlock(); 118 } 119 } 120 121 private void acceptDrawable(final OsmPrimitive osm) { 122 try { 123 if (osm.isDrawable()) { 124 osm.accept(this); 125 } 126 } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException e) { 127 throw BugReport.intercept(e).put("osm", osm); 128 } 129 } 130 131 @Override 132 public void visit(Node n) { 133 add(n, StyledMapRenderer.computeFlags(n, false)); 134 } 135 136 @Override 137 public void visit(Way w) { 138 add(w, StyledMapRenderer.computeFlags(w, true)); 139 } 140 141 @Override 142 public void visit(Relation r) { 143 add(r, StyledMapRenderer.computeFlags(r, true)); 144 } 145 146 /** 147 * Add new style records for the given node. 148 * @param osm node 149 * @param flags flags 150 */ 151 public void add(Node osm, int flags) { 152 StyleElementList sl = styles.get(osm, circum, nc); 153 for (StyleElement s : sl) { 154 output.add(new StyleRecord(s, osm, flags)); 155 } 156 } 157 158 /** 159 * Add new style records for the given way. 160 * @param osm way 161 * @param flags flags 162 */ 163 public void add(Way osm, int flags) { 164 StyleElementList sl = styles.get(osm, circum, nc); 165 for (StyleElement s : sl) { 166 if ((drawArea && (flags & StyledMapRenderer.FLAG_DISABLED) == 0) || !(s instanceof AreaElement)) { 167 output.add(new StyleRecord(s, osm, flags)); 168 } 169 } 170 } 171 172 /** 173 * Add new style records for the given relation. 174 * @param osm relation 175 * @param flags flags 176 */ 177 public void add(Relation osm, int flags) { 178 StyleElementList sl = styles.get(osm, circum, nc); 179 for (StyleElement s : sl) { 180 if (drawAreaElement(flags, s) || 181 (drawMultipolygon && drawArea && s instanceof TextElement) || 182 (drawRestriction && s instanceof NodeElement)) { 183 output.add(new StyleRecord(s, osm, flags)); 184 } 185 } 186 } 187 188 private boolean drawAreaElement(int flags, StyleElement s) { 189 return drawMultipolygon && drawArea && (s instanceof AreaElement || s instanceof AreaIconElement) 190 && (flags & StyledMapRenderer.FLAG_DISABLED) == 0; 191 } 192}