source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/RenderingHelper.java@ 15731

Last change on this file since 15731 was 15731, checked in by simon04, 4 years ago

see #10435, fix #18095 - MapCSS: add settings of type string/double

File size: 7.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Dimension;
8import java.awt.Graphics2D;
9import java.awt.Point;
10import java.awt.RenderingHints;
11import java.awt.image.BufferedImage;
12import java.io.IOException;
13import java.util.Collection;
14import java.util.HashMap;
15import java.util.Map;
16import java.util.Optional;
17
18import org.openstreetmap.josm.data.Bounds;
19import org.openstreetmap.josm.data.ProjectionBounds;
20import org.openstreetmap.josm.data.osm.DataSet;
21import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
22import org.openstreetmap.josm.data.projection.Projection;
23import org.openstreetmap.josm.data.projection.ProjectionRegistry;
24import org.openstreetmap.josm.gui.NavigatableComponent;
25import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
26import org.openstreetmap.josm.io.IllegalDataException;
27import org.openstreetmap.josm.tools.CheckParameterUtil;
28import org.openstreetmap.josm.tools.Logging;
29
30/**
31 * Class to render osm data to a file.
32 * @since 12963
33 */
34public class RenderingHelper {
35
36 private final DataSet ds;
37 private final Bounds bounds;
38 private final ProjectionBounds projBounds;
39 private final double scale;
40 private final Collection<StyleData> styles;
41 private Color backgroundColor;
42 private boolean fillBackground = true;
43
44 /**
45 * Data class to save style settings along with the corresponding style URL.
46 */
47 public static class StyleData {
48 public String styleUrl;
49 public Map<String, String> settings = new HashMap<>();
50 }
51
52 /**
53 * Construct a new {@code RenderingHelper}.
54 * @param ds the dataset to render
55 * @param bounds the bounds of the are to render
56 * @param scale the scale to render at (east/north units per pixel)
57 * @param styles the styles to use for rendering
58 */
59 public RenderingHelper(DataSet ds, Bounds bounds, double scale, Collection<StyleData> styles) {
60 CheckParameterUtil.ensureParameterNotNull(ds, "ds");
61 CheckParameterUtil.ensureParameterNotNull(bounds, "bounds");
62 CheckParameterUtil.ensureParameterNotNull(styles, "styles");
63 this.ds = ds;
64 this.bounds = bounds;
65 this.scale = scale;
66 this.styles = styles;
67 Projection proj = ProjectionRegistry.getProjection();
68 projBounds = new ProjectionBounds();
69 projBounds.extend(proj.latlon2eastNorth(bounds.getMin()));
70 projBounds.extend(proj.latlon2eastNorth(bounds.getMax()));
71 }
72
73 /**
74 * Set the background color to use for rendering.
75 *
76 * @param backgroundColor the background color to use, {@code} means
77 * to determine the background color automatically from the style
78 * @see #setFillBackground(boolean)
79 * @since 12966
80 */
81 public void setBackgroundColor(Color backgroundColor) {
82 this.backgroundColor = backgroundColor;
83 }
84
85 /**
86 * Decide if background should be filled or left transparent.
87 * @param fillBackground true, if background should be filled
88 * @see #setBackgroundColor(java.awt.Color)
89 * @since 12966
90 */
91 public void setFillBackground(boolean fillBackground) {
92 this.fillBackground = fillBackground;
93 }
94
95 Dimension getImageSize() {
96 double widthEn = projBounds.maxEast - projBounds.minEast;
97 double heightEn = projBounds.maxNorth - projBounds.minNorth;
98 int widthPx = (int) Math.round(widthEn / scale);
99 int heightPx = (int) Math.round(heightEn / scale);
100 return new Dimension(widthPx, heightPx);
101 }
102
103 /**
104 * Invoke the renderer.
105 *
106 * @return the rendered image
107 * @throws IOException in case of an IOException
108 * @throws IllegalDataException when illegal data is encountered (style has errors, etc.)
109 */
110 public BufferedImage render() throws IOException, IllegalDataException {
111 // load the styles
112 ElemStyles elemStyles = new ElemStyles();
113 MapCSSStyleSource.STYLE_SOURCE_LOCK.writeLock().lock();
114 try {
115 for (StyleData sd : styles) {
116 MapCSSStyleSource source = new MapCSSStyleSource(sd.styleUrl, "cliRenderingStyle", "cli rendering style '" + sd.styleUrl + "'");
117 source.loadStyleSource();
118 elemStyles.add(source);
119 if (!source.getErrors().isEmpty()) {
120 throw new IllegalDataException("Failed to load style file. Errors: " + source.getErrors());
121 }
122 for (String key : sd.settings.keySet()) {
123 StyleSetting.PropertyStyleSetting<?> match = source.settings.stream()
124 .filter(s -> s instanceof StyleSetting.PropertyStyleSetting)
125 .map(s -> (StyleSetting.PropertyStyleSetting<?>) s)
126 .filter(bs -> bs.getKey().endsWith(":" + key))
127 .findFirst().orElse(null);
128 if (match == null) {
129 Logging.warn(tr("Style setting not found: ''{0}''", key));
130 } else {
131 String value = sd.settings.get(key);
132 Logging.trace("setting applied: ''{0}:{1}''", key, value);
133 match.setStringValue(value);
134 }
135 }
136 if (!sd.settings.isEmpty()) {
137 source.loadStyleSource(); // reload to apply settings
138 }
139 }
140 } finally {
141 MapCSSStyleSource.STYLE_SOURCE_LOCK.writeLock().unlock();
142 }
143
144 Dimension imgDimPx = getImageSize();
145 NavigatableComponent nc = new NavigatableComponent() {
146 {
147 setBounds(0, 0, imgDimPx.width, imgDimPx.height);
148 updateLocationState();
149 }
150
151 @Override
152 protected boolean isVisibleOnScreen() {
153 return true;
154 }
155
156 @Override
157 public Point getLocationOnScreen() {
158 return new Point(0, 0);
159 }
160 };
161 nc.zoomTo(projBounds.getCenter(), scale);
162
163 // render the data
164 BufferedImage image = new BufferedImage(imgDimPx.width, imgDimPx.height, BufferedImage.TYPE_INT_ARGB);
165 Graphics2D g = image.createGraphics();
166
167 // Force all render hints to be defaults - do not use platform values
168 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
169 g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
170 g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
171 g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
172 g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
173 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
174 g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
175 g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
176 g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
177
178 if (fillBackground) {
179 g.setColor(Optional.ofNullable(backgroundColor).orElse(elemStyles.getBackgroundColor()));
180 g.fillRect(0, 0, imgDimPx.width, imgDimPx.height);
181 }
182 StyledMapRenderer smr = new StyledMapRenderer(g, nc, false);
183 smr.setStyles(elemStyles);
184 smr.render(ds, false, bounds);
185 return image;
186 }
187
188}
Note: See TracBrowser for help on using the repository browser.