001 // License: GPL. Copyright 2007 by Immanuel Scholz and others 002 package org.openstreetmap.josm.data.osm.visitor.paint; 003 004 import java.awt.Graphics2D; 005 import java.awt.RenderingHints; 006 import java.util.ArrayList; 007 import java.util.Collection; 008 import java.util.Collections; 009 import java.util.List; 010 011 import org.openstreetmap.josm.Main; 012 import org.openstreetmap.josm.data.Bounds; 013 import org.openstreetmap.josm.data.osm.BBox; 014 import org.openstreetmap.josm.data.osm.DataSet; 015 import org.openstreetmap.josm.data.osm.Node; 016 import org.openstreetmap.josm.data.osm.OsmPrimitive; 017 import org.openstreetmap.josm.data.osm.Relation; 018 import org.openstreetmap.josm.data.osm.Way; 019 import org.openstreetmap.josm.data.osm.WaySegment; 020 import org.openstreetmap.josm.gui.NavigatableComponent; 021 import org.openstreetmap.josm.gui.mappaint.AreaElemStyle; 022 import org.openstreetmap.josm.gui.mappaint.ElemStyle; 023 import org.openstreetmap.josm.gui.mappaint.ElemStyles; 024 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles; 025 import org.openstreetmap.josm.gui.mappaint.NodeElemStyle; 026 import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList; 027 028 /** 029 * <p>A map renderer which renders a map according to style rules in a set of style sheets.</p> 030 * 031 */ 032 public class StyledMapRenderer extends AbstractMapRenderer{ 033 034 private ElemStyles styles; 035 private double circum; 036 private MapPainter painter; 037 private MapPaintSettings paintSettings; 038 039 private static int FLAG_NORMAL = 0; 040 private static int FLAG_DISABLED = 1; 041 private static int FLAG_MEMBER_OF_SELECTED = 2; 042 private static int FLAG_SELECTED = 4; 043 044 private static class StyleRecord implements Comparable<StyleRecord> { 045 final ElemStyle style; 046 final OsmPrimitive osm; 047 final int flags; 048 049 public StyleRecord(ElemStyle style, OsmPrimitive osm, int flags) { 050 this.style = style; 051 this.osm = osm; 052 this.flags = flags; 053 } 054 055 @Override 056 public int compareTo(StyleRecord other) { 057 if ((this.flags & FLAG_DISABLED) != 0 && (other.flags & FLAG_DISABLED) == 0) 058 return -1; 059 if ((this.flags & FLAG_DISABLED) == 0 && (other.flags & FLAG_DISABLED) != 0) 060 return 1; 061 062 int d0 = Float.compare(this.style.major_z_index, other.style.major_z_index); 063 if (d0 != 0) 064 return d0; 065 066 // selected on top of member of selected on top of unselected 067 // FLAG_DISABLED bit is the same at this point 068 if (this.flags > other.flags) 069 return 1; 070 if (this.flags < other.flags) 071 return -1; 072 073 int dz = Float.compare(this.style.z_index, other.style.z_index); 074 if (dz != 0) 075 return dz; 076 077 // simple node on top of icons and shapes 078 if (this.style == NodeElemStyle.SIMPLE_NODE_ELEMSTYLE && other.style != NodeElemStyle.SIMPLE_NODE_ELEMSTYLE) 079 return 1; 080 if (this.style != NodeElemStyle.SIMPLE_NODE_ELEMSTYLE && other.style == NodeElemStyle.SIMPLE_NODE_ELEMSTYLE) 081 return -1; 082 083 // newer primitives to the front 084 long id = this.osm.getUniqueId() - other.osm.getUniqueId(); 085 if (id > 0) 086 return 1; 087 if (id < 0) 088 return -1; 089 090 return Float.compare(this.style.object_z_index, other.style.object_z_index); 091 } 092 } 093 094 private class StyleCollector { 095 private final boolean drawArea; 096 private final boolean drawMultipolygon; 097 private final boolean drawRestriction; 098 099 private final List<StyleRecord> styleElems; 100 101 public StyleCollector(boolean drawArea, boolean drawMultipolygon, boolean drawRestriction) { 102 this.drawArea = drawArea; 103 this.drawMultipolygon = drawMultipolygon; 104 this.drawRestriction = drawRestriction; 105 styleElems = new ArrayList<StyleRecord>(); 106 } 107 108 public void add(Node osm, int flags) { 109 StyleList sl = styles.get(osm, circum, nc); 110 for (ElemStyle s : sl) { 111 styleElems.add(new StyleRecord(s, osm, flags)); 112 } 113 } 114 115 public void add(Way osm, int flags) { 116 StyleList sl = styles.get(osm, circum, nc); 117 for (ElemStyle s : sl) { 118 if (!(drawArea && (flags & FLAG_DISABLED) == 0) && s instanceof AreaElemStyle) { 119 continue; 120 } 121 styleElems.add(new StyleRecord(s, osm, flags)); 122 } 123 } 124 125 public void add(Relation osm, int flags) { 126 StyleList sl = styles.get(osm, circum, nc); 127 for (ElemStyle s : sl) { 128 if (drawMultipolygon && drawArea && s instanceof AreaElemStyle && (flags & FLAG_DISABLED) == 0) { 129 styleElems.add(new StyleRecord(s, osm, flags)); 130 } else if (drawRestriction && s instanceof NodeElemStyle) { 131 styleElems.add(new StyleRecord(s, osm, flags)); 132 } 133 } 134 } 135 136 public void drawAll() { 137 Collections.sort(styleElems); 138 for (StyleRecord r : styleElems) { 139 r.style.paintPrimitive( 140 r.osm, 141 paintSettings, 142 painter, 143 (r.flags & FLAG_SELECTED) != 0, 144 (r.flags & FLAG_MEMBER_OF_SELECTED) != 0 145 ); 146 } 147 } 148 } 149 150 /** 151 * {@inheritDoc} 152 */ 153 public StyledMapRenderer(Graphics2D g, NavigatableComponent nc, boolean isInactiveMode) { 154 super(g, nc, isInactiveMode); 155 } 156 157 private void collectNodeStyles(DataSet data, StyleCollector sc, BBox bbox) { 158 for (final Node n: data.searchNodes(bbox)) { 159 if (n.isDrawable()) { 160 if (n.isDisabled()) { 161 sc.add(n, FLAG_DISABLED); 162 } else if (data.isSelected(n)) { 163 sc.add(n, FLAG_SELECTED); 164 } else if (n.isMemberOfSelected()) { 165 sc.add(n, FLAG_MEMBER_OF_SELECTED); 166 } else { 167 sc.add(n, FLAG_NORMAL); 168 } 169 } 170 } 171 } 172 173 private void collectWayStyles(DataSet data, StyleCollector sc, BBox bbox) { 174 for (final Way w : data.searchWays(bbox)) { 175 if (w.isDrawable()) { 176 if (w.isDisabled()) { 177 sc.add(w, FLAG_DISABLED); 178 } else if (data.isSelected(w)) { 179 sc.add(w, FLAG_SELECTED); 180 } else if (w.isMemberOfSelected()) { 181 sc.add(w, FLAG_MEMBER_OF_SELECTED); 182 } else { 183 sc.add(w, FLAG_NORMAL); 184 } 185 } 186 } 187 } 188 189 private void collectRelationStyles(DataSet data, StyleCollector sc, BBox bbox) { 190 for (Relation r: data.searchRelations(bbox)) { 191 if (r.isDrawable()) { 192 if (r.isDisabled()) { 193 sc.add(r, FLAG_DISABLED); 194 } else if (data.isSelected(r)) { 195 sc.add(r, FLAG_SELECTED); 196 } else { 197 sc.add(r, FLAG_NORMAL); 198 } 199 } 200 } 201 } 202 203 @Override 204 public void render(final DataSet data, boolean renderVirtualNodes, Bounds bounds) { 205 //long start = System.currentTimeMillis(); 206 BBox bbox = new BBox(bounds); 207 208 styles = MapPaintStyles.getStyles(); 209 210 this.paintSettings = MapPaintSettings.INSTANCE; 211 212 circum = nc.getDist100Pixel(); 213 boolean drawArea = circum <= Main.pref.getInteger("mappaint.fillareas", 10000000); 214 boolean drawMultipolygon = drawArea && Main.pref.getBoolean("mappaint.multipolygon", true); 215 styles.setDrawMultipolygon(drawMultipolygon); 216 boolean drawRestriction = Main.pref.getBoolean("mappaint.restriction", true); 217 boolean leftHandTraffic = Main.pref.getBoolean("mappaint.lefthandtraffic", false); 218 219 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 220 Main.pref.getBoolean("mappaint.use-antialiasing", true) ? 221 RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF); 222 223 Collection<WaySegment> hws = data.getHighlightedWaySegments(); 224 225 this.painter = new MapPainter(paintSettings, g, isInactiveMode, nc, renderVirtualNodes, circum, leftHandTraffic, hws); 226 227 StyleCollector sc = new StyleCollector(drawArea, drawMultipolygon, drawRestriction); 228 collectNodeStyles(data, sc, bbox); 229 collectWayStyles(data, sc, bbox); 230 collectRelationStyles(data, sc, bbox); 231 //long phase1 = System.currentTimeMillis(); 232 sc.drawAll(); 233 sc = null; 234 painter.drawVirtualNodes(data.searchWays(bbox), data.getHighlightedVirtualNodes()); 235 236 //long now = System.currentTimeMillis(); 237 //System.err.println(String.format("PAINTING TOOK %d [PHASE1 took %d] (at scale %s)", now - start, phase1 - start, circum)); 238 } 239 }