1 |
alfonx |
244 |
/******************************************************************************* |
2 |
|
|
* Copyright (c) 2009 Martin O. J. Schmitz. |
3 |
|
|
* |
4 |
|
|
* This file is part of the SCHMITZM library - a collection of utility |
5 |
alfonx |
256 |
* classes based on Java 1.6, focusing (not only) on Java Swing |
6 |
alfonx |
244 |
* and the Geotools library. |
7 |
|
|
* |
8 |
|
|
* The SCHMITZM project is hosted at: |
9 |
|
|
* http://wald.intevation.org/projects/schmitzm/ |
10 |
|
|
* |
11 |
|
|
* This program is free software; you can redistribute it and/or |
12 |
|
|
* modify it under the terms of the GNU Lesser General Public License |
13 |
|
|
* as published by the Free Software Foundation; either version 3 |
14 |
|
|
* of the License, or (at your option) any later version. |
15 |
|
|
* |
16 |
|
|
* This program is distributed in the hope that it will be useful, |
17 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 |
|
|
* GNU General Public License for more details. |
20 |
|
|
* |
21 |
|
|
* You should have received a copy of the GNU Lesser General Public License (license.txt) |
22 |
|
|
* along with this program; if not, write to the Free Software |
23 |
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
24 |
|
|
* or try this link: http://www.gnu.org/licenses/lgpl.html |
25 |
|
|
* |
26 |
|
|
* Contributors: |
27 |
|
|
* Martin O. J. Schmitz - initial API and implementation |
28 |
alfonx |
862 |
* Stefan A. Tzeggai - additional utility classes |
29 |
alfonx |
244 |
******************************************************************************/ |
30 |
alfonx |
154 |
/** |
31 |
alfonx |
862 |
Copyright 2008 Stefan Alfons Tzeggai and parts from some Geotools code |
32 |
alfonx |
1223 |
|
33 |
alfonx |
154 |
atlas-framework - This file is part of the Atlas Framework |
34 |
|
|
|
35 |
|
|
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. |
36 |
|
|
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
37 |
|
|
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA |
38 |
|
|
|
39 |
|
|
Diese Bibliothek ist freie Software; Sie dürfen sie unter den Bedingungen der GNU Lesser General Public License, wie von der Free Software Foundation veröffentlicht, weiterverteilen und/oder modifizieren; entweder gemäß Version 2.1 der Lizenz oder (nach Ihrer Option) jeder späteren Version. |
40 |
|
|
Diese Bibliothek wird in der Hoffnung weiterverbreitet, daß sie nützlich sein wird, jedoch OHNE IRGENDEINE GARANTIE, auch ohne die implizierte Garantie der MARKTREIFE oder der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK. Mehr Details finden Sie in der GNU Lesser General Public License. |
41 |
|
|
Sie sollten eine Kopie der GNU Lesser General Public License zusammen mit dieser Bibliothek erhalten haben; falls nicht, schreiben Sie an die Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA. |
42 |
|
|
**/ |
43 |
|
|
package skrueger.geotools; |
44 |
|
|
|
45 |
|
|
import java.awt.Color; |
46 |
|
|
import java.awt.Dimension; |
47 |
|
|
import java.awt.Graphics2D; |
48 |
|
|
import java.awt.RenderingHints; |
49 |
|
|
import java.awt.image.BufferedImage; |
50 |
|
|
|
51 |
|
|
import javax.swing.tree.DefaultTreeCellRenderer; |
52 |
|
|
|
53 |
|
|
import org.apache.log4j.Logger; |
54 |
|
|
import org.geotools.factory.GeoTools; |
55 |
|
|
import org.geotools.factory.Hints; |
56 |
|
|
import org.geotools.feature.IllegalAttributeException; |
57 |
|
|
import org.geotools.geometry.jts.LiteShape2; |
58 |
|
|
import org.geotools.renderer.lite.StyledShapePainter; |
59 |
|
|
import org.geotools.renderer.style.SLDStyleFactory; |
60 |
|
|
import org.geotools.renderer.style.Style2D; |
61 |
|
|
import org.geotools.styling.LineSymbolizer; |
62 |
|
|
import org.geotools.styling.PointSymbolizer; |
63 |
|
|
import org.geotools.styling.PolygonSymbolizer; |
64 |
|
|
import org.geotools.styling.RasterSymbolizer; |
65 |
|
|
import org.geotools.styling.Rule; |
66 |
|
|
import org.geotools.styling.Style; |
67 |
|
|
import org.geotools.styling.Symbolizer; |
68 |
|
|
import org.geotools.styling.TextSymbolizer; |
69 |
|
|
import org.geotools.util.NumberRange; |
70 |
mojays |
325 |
import org.opengis.feature.simple.SimpleFeature; |
71 |
|
|
import org.opengis.feature.simple.SimpleFeatureType; |
72 |
alfonx |
332 |
import org.opengis.feature.type.AttributeDescriptor; |
73 |
alfonx |
154 |
|
74 |
|
|
import schmitzm.geotools.feature.FeatureUtil; |
75 |
|
|
|
76 |
|
|
import com.vividsolutions.jts.geom.Coordinate; |
77 |
|
|
import com.vividsolutions.jts.geom.GeometryFactory; |
78 |
|
|
import com.vividsolutions.jts.geom.LineString; |
79 |
|
|
import com.vividsolutions.jts.geom.LinearRing; |
80 |
|
|
import com.vividsolutions.jts.geom.Polygon; |
81 |
|
|
|
82 |
|
|
/** |
83 |
|
|
* Based on geoserver! |
84 |
|
|
* <hr> |
85 |
|
|
* <b>Changes by <a href="mailto:[email protected]">Martin Schmitz</a></b> |
86 |
|
|
* <br> |
87 |
|
|
* <ul> |
88 |
|
|
* <li>07.02.2008:<br> |
89 |
alfonx |
318 |
* Determining the default values of a {@link SimpleFeatureType} by |
90 |
alfonx |
1223 |
* {@link FeatureUtil#getDefaultAttributeValues(SimpleFeatureType)} instead of |
91 |
|
|
* using {@link AttributeDescriptor#createDefaultValue()} directly, because the |
92 |
|
|
* latter returns {@code null} even though the attribut is not nillable.</li> |
93 |
alfonx |
154 |
* </ul> |
94 |
|
|
* |
95 |
alfonx |
862 |
* @author Stefan Alfons Tzeggai |
96 |
alfonx |
154 |
*/ |
97 |
|
|
public class LegendIconFeatureRenderer extends DefaultTreeCellRenderer { |
98 |
alfonx |
1223 |
// private static final Dimension SIZE = new Dimension(30,20); |
99 |
alfonx |
154 |
|
100 |
|
|
Logger LOGGER = Logger.getLogger(LegendIconFeatureRenderer.class); |
101 |
|
|
|
102 |
|
|
/** |
103 |
|
|
* This is a static class |
104 |
|
|
*/ |
105 |
|
|
private LegendIconFeatureRenderer() { |
106 |
|
|
} |
107 |
|
|
|
108 |
|
|
final static LegendIconFeatureRenderer instance = new LegendIconFeatureRenderer(); |
109 |
|
|
|
110 |
|
|
public static LegendIconFeatureRenderer getInstance() { |
111 |
|
|
// In GT 2.4.5 it we have to create a new one all the time! |
112 |
alfonx |
1223 |
|
113 |
alfonx |
377 |
// TODO TEST, whether we still need to recreate the renderer here.... |
114 |
alfonx |
452 |
// TODO TEST, whether we still need to recreate the renderer here.... |
115 |
|
|
// TODO TEST, whether we still need to recreate the renderer here.... |
116 |
|
|
// TODO TEST, whether we still need to recreate the renderer here.... |
117 |
|
|
// TODO TEST, whether we still need to recreate the renderer here.... |
118 |
|
|
// TODO TEST, whether we still need to recreate the renderer here.... |
119 |
|
|
// TODO TEST, whether we still need to recreate the renderer here.... |
120 |
|
|
// TODO TEST, whether we still need to recreate the renderer here.... |
121 |
alfonx |
154 |
return new LegendIconFeatureRenderer(); |
122 |
alfonx |
1223 |
// return instance; |
123 |
alfonx |
154 |
} |
124 |
|
|
|
125 |
|
|
/** |
126 |
|
|
* used to create sample point shapes with LiteShape (not lines nor |
127 |
|
|
* polygons) |
128 |
|
|
*/ |
129 |
alfonx |
377 |
private static final GeometryFactory geomFac = FeatureUtil.GEOMETRY_FACTORY; |
130 |
alfonx |
154 |
|
131 |
|
|
/** padding percentage factor at both sides of the legend. */ |
132 |
|
|
private static final float hpaddingFactor = 0.11f; // was 0.15 |
133 |
|
|
|
134 |
|
|
/** top & bottom padding percentage factor for the legend */ |
135 |
|
|
private static final float vpaddingFactor = 0.08f; // was 0.15 |
136 |
|
|
|
137 |
|
|
/** |
138 |
|
|
* Just a holder to avoid creating many polygon shapes from inside |
139 |
|
|
* <code>getSampleShape()</code> |
140 |
|
|
*/ |
141 |
|
|
private LiteShape2 sampleRect; |
142 |
|
|
|
143 |
|
|
/** |
144 |
|
|
* Just a holder to avoid creating many line shapes from inside |
145 |
|
|
* <code>getSampleShape()</code> |
146 |
|
|
*/ |
147 |
|
|
private LiteShape2 sampleLine; |
148 |
|
|
|
149 |
|
|
/** |
150 |
|
|
* Just a holder to avoid creating many point shapes from inside |
151 |
|
|
* <code>getSampleShape()</code> |
152 |
|
|
*/ |
153 |
|
|
private LiteShape2 samplePoint; |
154 |
|
|
|
155 |
|
|
private Hints hints; |
156 |
|
|
|
157 |
|
|
/** |
158 |
|
|
* Returns a <code>java.awt.Shape</code> appropiate to render a legend |
159 |
|
|
* graphic given the symbolizer type and the legend dimensions. |
160 |
|
|
* |
161 |
|
|
* @param symbolizer |
162 |
|
|
* the Symbolizer for whose type a sample shape will be created |
163 |
|
|
* @param legendWidth |
164 |
|
|
* the requested width, in output units, of the legend graphic |
165 |
|
|
* @param legendHeight |
166 |
|
|
* the requested height, in output units, of the legend graphic |
167 |
|
|
* |
168 |
|
|
* @return an appropiate Line2D, Rectangle2D or LiteShape(Point) for the |
169 |
|
|
* symbolizer, wether it is a LineSymbolizer, a PolygonSymbolizer, |
170 |
|
|
* or a Point ot Text Symbolizer |
171 |
|
|
* |
172 |
|
|
* @throws IllegalArgumentException |
173 |
|
|
* if an unknown symbolizer impl was passed in. |
174 |
|
|
*/ |
175 |
|
|
private LiteShape2 getSampleShape(Symbolizer symbolizer, int legendWidth, |
176 |
|
|
int legendHeight) { |
177 |
|
|
LiteShape2 sampleShape; |
178 |
|
|
final float hpad = (legendWidth * hpaddingFactor); |
179 |
|
|
final float vpad = (legendHeight * vpaddingFactor); |
180 |
|
|
|
181 |
|
|
if (symbolizer instanceof LineSymbolizer) { |
182 |
|
|
if (this.sampleLine == null) { |
183 |
|
|
Coordinate[] coords = { |
184 |
|
|
new Coordinate(hpad, legendHeight - vpad), |
185 |
|
|
new Coordinate(legendWidth - hpad, vpad) }; |
186 |
|
|
LineString geom = geomFac.createLineString(coords); |
187 |
|
|
|
188 |
|
|
try { |
189 |
|
|
this.sampleLine = new LiteShape2(geom, null, null, false); |
190 |
|
|
} catch (Exception e) { |
191 |
|
|
this.sampleLine = null; |
192 |
|
|
} |
193 |
|
|
} |
194 |
|
|
|
195 |
|
|
sampleShape = this.sampleLine; |
196 |
|
|
} else if ((symbolizer instanceof PolygonSymbolizer) |
197 |
|
|
|| (symbolizer instanceof RasterSymbolizer)) { |
198 |
|
|
if (this.sampleRect == null) { |
199 |
|
|
final float w = legendWidth - (2 * hpad); |
200 |
|
|
final float h = legendHeight - (2 * vpad); |
201 |
|
|
|
202 |
|
|
Coordinate[] coords = { new Coordinate(hpad, vpad), |
203 |
|
|
new Coordinate(hpad, vpad + h), |
204 |
|
|
new Coordinate(hpad + w, vpad + h), |
205 |
|
|
new Coordinate(hpad + w, vpad), |
206 |
|
|
new Coordinate(hpad, vpad) }; |
207 |
|
|
LinearRing shell = geomFac.createLinearRing(coords); |
208 |
|
|
Polygon geom = geomFac.createPolygon(shell, null); |
209 |
|
|
|
210 |
|
|
try { |
211 |
|
|
this.sampleRect = new LiteShape2(geom, null, null, false); |
212 |
|
|
} catch (Exception e) { |
213 |
|
|
this.sampleRect = null; |
214 |
|
|
} |
215 |
|
|
} |
216 |
|
|
|
217 |
|
|
sampleShape = this.sampleRect; |
218 |
|
|
} else if (symbolizer instanceof PointSymbolizer |
219 |
|
|
|| symbolizer instanceof TextSymbolizer) { |
220 |
|
|
if (this.samplePoint == null) { |
221 |
|
|
Coordinate coord = new Coordinate(legendWidth / 2, |
222 |
|
|
legendHeight / 2); |
223 |
|
|
|
224 |
|
|
try { |
225 |
alfonx |
1223 |
this.samplePoint = new LiteShape2( |
226 |
|
|
geomFac.createPoint(coord), null, null, false); |
227 |
alfonx |
154 |
} catch (Exception e) { |
228 |
|
|
this.samplePoint = null; |
229 |
|
|
} |
230 |
|
|
} |
231 |
|
|
|
232 |
|
|
sampleShape = this.samplePoint; |
233 |
|
|
} else { |
234 |
|
|
throw new IllegalArgumentException("Unknown symbolizer: " |
235 |
|
|
+ symbolizer); |
236 |
|
|
} |
237 |
|
|
return sampleShape; |
238 |
|
|
} |
239 |
alfonx |
1223 |
|
240 |
alfonx |
154 |
/** |
241 |
|
|
* Creates a little BufferedImage that presents the Style/Symbols used by |
242 |
alfonx |
318 |
* the {@link MapLegend} to show a legend for the {@link SimpleFeatureType} |
243 |
alfonx |
154 |
* |
244 |
|
|
* @param rule |
245 |
|
|
* {@link Rule} that provides the text and the style to present |
246 |
|
|
* @param featureType |
247 |
alfonx |
1223 |
* Schema that describes the kind of the sample |
248 |
|
|
* {@link SimpleFeature} that will be rendered with the |
249 |
|
|
* {@link Style} |
250 |
alfonx |
154 |
* @param bg |
251 |
|
|
* Background {@link Color} or <code>null</code> |
252 |
|
|
* |
253 |
|
|
* @throws IllegalAttributeException |
254 |
|
|
*/ |
255 |
alfonx |
1223 |
public BufferedImage createImageForRule(Rule rule, |
256 |
|
|
SimpleFeatureType featureType, Dimension size, Color bg) { |
257 |
alfonx |
154 |
|
258 |
|
|
Symbolizer[] symbolizers = rule.getSymbolizers(); |
259 |
|
|
|
260 |
|
|
BufferedImage buffImage = new BufferedImage(size.width, size.height, |
261 |
|
|
BufferedImage.TYPE_INT_ARGB); |
262 |
|
|
Graphics2D graphics = buffImage.createGraphics(); |
263 |
|
|
|
264 |
|
|
if (bg != null) { |
265 |
|
|
// **************************************************************************** |
266 |
alfonx |
918 |
// The following lines define the background color for the rendered |
267 |
alfonx |
154 |
// images |
268 |
|
|
// **************************************************************************** |
269 |
|
|
graphics.setBackground(bg); |
270 |
|
|
graphics.setColor(bg); |
271 |
|
|
graphics.fillRect(0, 0, size.width, size.height); |
272 |
|
|
} |
273 |
|
|
|
274 |
|
|
// Enable anti-aliasing for the legend symbols |
275 |
|
|
graphics.setRenderingHints(getHints()); |
276 |
|
|
|
277 |
alfonx |
1223 |
final NumberRange<Integer> scaleRange = NumberRange.create( |
278 |
|
|
Integer.MAX_VALUE, Integer.MAX_VALUE); |
279 |
alfonx |
154 |
|
280 |
|
|
try { |
281 |
|
|
|
282 |
|
|
for (int sIdx = 0; sIdx < symbolizers.length; sIdx++) { |
283 |
|
|
Symbolizer symbolizer = symbolizers[sIdx]; |
284 |
|
|
|
285 |
|
|
if (symbolizer instanceof TextSymbolizer) { |
286 |
|
|
continue; |
287 |
|
|
} |
288 |
|
|
|
289 |
|
|
// if (symbolizer instanceof RasterSymbolizer) { |
290 |
|
|
// log.warn("createImageForRule method can't be used for |
291 |
|
|
// RasterSymbolizers.."); |
292 |
|
|
// } |
293 |
|
|
// else |
294 |
alfonx |
1223 |
final SimpleFeature sampleFeature = FeatureUtil |
295 |
|
|
.createSampleFeature(featureType); |
296 |
alfonx |
154 |
|
297 |
alfonx |
1223 |
if (sampleFeature == null) |
298 |
|
|
LOGGER.debug("sampleFeature " + sampleFeature); |
299 |
|
|
|
300 |
alfonx |
154 |
// The SLDStyleFactory has to be recreated, as it seams to cache |
301 |
|
|
// some stuff. |
302 |
|
|
SLDStyleFactory sldStyleFactory = new SLDStyleFactory(); |
303 |
|
|
Style2D style2d = sldStyleFactory.createStyle(sampleFeature, |
304 |
|
|
symbolizer, scaleRange); |
305 |
|
|
|
306 |
|
|
LiteShape2 shape = getSampleShape(symbolizer, size.width, |
307 |
|
|
size.height); |
308 |
|
|
|
309 |
|
|
if (style2d != null) { |
310 |
|
|
new StyledShapePainter(null).paint(graphics, shape, |
311 |
alfonx |
1223 |
style2d, Integer.MAX_VALUE); |
312 |
alfonx |
154 |
} |
313 |
|
|
} |
314 |
|
|
} catch (Exception e) { |
315 |
alfonx |
1223 |
LOGGER.error( |
316 |
|
|
"Error during createImageForRule, returning empty Image", e); |
317 |
alfonx |
154 |
} |
318 |
|
|
return buffImage; |
319 |
|
|
} |
320 |
|
|
|
321 |
|
|
/** |
322 |
|
|
* Define Java2D and other Hints |
323 |
|
|
* |
324 |
|
|
* @param hints |
325 |
alfonx |
1223 |
* @author <a href="mailto:[email protected]">Stefan Alfons Tzeggai</a> |
326 |
alfonx |
154 |
*/ |
327 |
|
|
public void setHints(Hints hints) { |
328 |
|
|
getHints().add(hints); |
329 |
|
|
} |
330 |
|
|
|
331 |
|
|
private Hints getHints() { |
332 |
|
|
if (hints == null) { |
333 |
|
|
hints = GeoTools.getDefaultHints(); |
334 |
|
|
hints.put(RenderingHints.KEY_ANTIALIASING, |
335 |
|
|
RenderingHints.VALUE_ANTIALIAS_ON); |
336 |
|
|
hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, |
337 |
|
|
RenderingHints.VALUE_TEXT_ANTIALIAS_GASP); |
338 |
|
|
hints.put(RenderingHints.KEY_ALPHA_INTERPOLATION, |
339 |
|
|
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); |
340 |
|
|
hints.put(RenderingHints.KEY_RENDERING, |
341 |
|
|
RenderingHints.VALUE_RENDER_QUALITY); |
342 |
|
|
hints.put(RenderingHints.KEY_COLOR_RENDERING, |
343 |
|
|
RenderingHints.VALUE_COLOR_RENDER_QUALITY); |
344 |
|
|
} |
345 |
|
|
return hints; |
346 |
|
|
} |
347 |
|
|
|
348 |
|
|
/** |
349 |
|
|
* Creates a little BufferedImage that presents the Style/Symbols used by |
350 |
alfonx |
318 |
* the {@link MapLegend} to show a legend for the {@link SimpleFeatureType} |
351 |
alfonx |
154 |
* |
352 |
|
|
* @param rule |
353 |
|
|
* {@link Rule} that provides the text and the style to present |
354 |
|
|
* @param featureType |
355 |
alfonx |
1223 |
* Schema that describes the kind of the sample |
356 |
|
|
* {@link SimpleFeature} that will be rendered with the |
357 |
|
|
* {@link Style} |
358 |
alfonx |
154 |
* |
359 |
|
|
* @throws IllegalAttributeException |
360 |
|
|
*/ |
361 |
|
|
public BufferedImage createImageForRule(final Rule rule, |
362 |
alfonx |
318 |
final SimpleFeatureType featureType, final Dimension size) { |
363 |
alfonx |
154 |
return createImageForRule(rule, featureType, size, null); |
364 |
|
|
} |
365 |
|
|
|
366 |
|
|
} |