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