1 package fr.ifremer.dali.ui.swing.util.map.layer;
2
3 import org.apache.commons.logging.Log;
4 import org.apache.commons.logging.LogFactory;
5 import org.geotools.geometry.jts.LiteShape;
6 import org.geotools.geometry.jts.ReferencedEnvelope;
7 import org.geotools.legend.Drawer;
8 import org.geotools.map.DirectLayer;
9 import org.geotools.map.Layer;
10 import org.geotools.map.MapContent;
11 import org.geotools.map.MapViewport;
12 import org.geotools.styling.*;
13 import org.opengis.feature.simple.SimpleFeature;
14
15 import java.awt.Color;
16 import java.awt.Graphics2D;
17 import java.awt.Paint;
18 import java.awt.Stroke;
19 import java.awt.geom.AffineTransform;
20 import java.awt.geom.Rectangle2D;
21 import java.awt.image.BufferedImage;
22 import java.util.LinkedHashSet;
23 import java.util.List;
24 import java.util.Set;
25 import java.util.stream.Collectors;
26
27
28
29
30 public class LegendLayer extends DirectLayer {
31
32 private static final Log LOG = LogFactory.getLog(LegendLayer.class);
33
34 private static final int GLYPH_LEFT_GAP = 5;
35 private static final int TEXT_LEFT_GAP = 20;
36 private static final int TEXT_RIGHT_GAP = 5;
37 private static final int TEXT_TOP_GAP = 2;
38 private static final int TEXT_BOTTOM_GAP = 2;
39 private static final int LEGEND_MARGIN = 10;
40
41 private Drawer drawer = Drawer.create();
42 private AffineTransform defaultTransform = new AffineTransform();
43 private LiteShape defaultShape = new LiteShape(null, defaultTransform, false);
44
45 public LegendLayer() {
46 setTitle("legendLayer");
47 }
48
49 @Override
50 public void draw(Graphics2D graphics, MapContent map, MapViewport viewport) {
51
52 List<DataFeatureLayer> dataFeatureLayers = map.layers().stream()
53 .filter(layer -> layer instanceof DataFeatureLayer)
54 .map(layer -> (DataFeatureLayer) layer)
55 .collect(Collectors.toList());
56
57
58 if (dataFeatureLayers.isEmpty()) return;
59
60 Set<NamedBufferedImage> items = new LinkedHashSet<>();
61
62 Stroke oldStroke = graphics.getStroke();
63 Paint oldPaint = graphics.getPaint();
64 Color oldBackground = graphics.getBackground();
65
66 try {
67 try {
68
69 for (Layer layer : map.layers()) {
70 if (layer instanceof DataFeatureLayer) {
71 DataFeatureLayer dataFeatureLayer = (DataFeatureLayer) layer;
72
73 Style style = dataFeatureLayer.getStyle();
74 if (style != null) {
75
76 List<Symbolizer> symbolizers = style.featureTypeStyles().stream()
77 .flatMap(featureTypeStyle -> featureTypeStyle.rules().stream())
78 .flatMap(rule -> rule.symbolizers().stream())
79 .filter(symbolizer -> symbolizer.getDescription() != null && symbolizer.getDescription().getTitle() != null)
80 .collect(Collectors.toList());
81 for (Symbolizer symbolizer : symbolizers) {
82 if (symbolizer instanceof PointSymbolizer) {
83
84 NamedBufferedImage item = createItem(graphics, symbolizer);
85 drawFeature(
86 item,
87 symbolizer,
88 drawer.feature(drawer.point(GLYPH_LEFT_GAP * 2, item.getHeight() / 2)));
89 items.add(item);
90
91 } else if (symbolizer instanceof LineSymbolizer) {
92
93 NamedBufferedImage item = createItem(graphics, symbolizer);
94 drawFeature(
95 item,
96 symbolizer,
97 drawer.feature(drawer.line(new int[]{
98 GLYPH_LEFT_GAP, item.getHeight() / 2, GLYPH_LEFT_GAP + 10, item.getHeight() / 2
99 })));
100 items.add(item);
101
102 } else if (symbolizer instanceof PolygonSymbolizer) {
103
104 NamedBufferedImage item = createItem(graphics, symbolizer);
105 int glyphTopGap = (item.getHeight() - 10) / 2;
106 drawFeature(
107 item,
108 symbolizer,
109 drawer.feature(drawer.polygon(new int[]{
110 GLYPH_LEFT_GAP, glyphTopGap,
111 GLYPH_LEFT_GAP + 10, glyphTopGap,
112 GLYPH_LEFT_GAP + 10, glyphTopGap + 10,
113 GLYPH_LEFT_GAP, glyphTopGap + 10,
114 GLYPH_LEFT_GAP, glyphTopGap
115 })));
116 items.add(item);
117
118 }
119 }
120 }
121
122 }
123 }
124
125
126 if (items.isEmpty()) return;
127
128 int legendHeight = items.stream().mapToInt(NamedBufferedImage::getHeight).sum();
129 int legendWidth = items.stream().mapToInt(NamedBufferedImage::getWidth).max().getAsInt();
130 BufferedImage legendImage = new BufferedImage(legendWidth, legendHeight, BufferedImage.TYPE_INT_RGB);
131 Graphics2D g2d = legendImage.createGraphics();
132 g2d.setPaint(Color.WHITE);
133 g2d.fillRect(0, 0, legendWidth, legendHeight);
134
135 int y = 0;
136 for (BufferedImage item : items) {
137 legendImage.getGraphics().drawImage(item, 0, y, null);
138 y += item.getHeight();
139 }
140
141 graphics.drawImage(legendImage, viewport.getScreenArea().width - legendWidth - LEGEND_MARGIN, LEGEND_MARGIN, null);
142
143 } catch (Exception e) {
144 LOG.error(e.getMessage(), e);
145 }
146
147 } finally {
148
149 graphics.setStroke(oldStroke);
150 graphics.setPaint(oldPaint);
151 graphics.setBackground(oldBackground);
152
153 }
154 }
155
156 private NamedBufferedImage createItem(Graphics2D graphics, Symbolizer symbolizer) {
157 String text = symbolizer.getDescription().getTitle().toString();
158 Rectangle2D textRect = graphics.getFontMetrics().getStringBounds(text, graphics);
159 int width = (int) (textRect.getWidth() + 0.5) + TEXT_LEFT_GAP + TEXT_RIGHT_GAP;
160 int height = (int) (textRect.getHeight() + 0.5) + TEXT_TOP_GAP + TEXT_BOTTOM_GAP;
161 NamedBufferedImage item = new NamedBufferedImage(symbolizer.getName(), width, height);
162 Graphics2D g2d = item.createGraphics();
163 g2d.setPaint(Color.WHITE);
164 g2d.fillRect(0, 0, width, height);
165 g2d.setPaint(Color.BLACK);
166 g2d.drawString(text, TEXT_LEFT_GAP, (float) -textRect.getY() + TEXT_TOP_GAP);
167 return item;
168 }
169
170 private void drawFeature(BufferedImage item, Symbolizer symbolizer, SimpleFeature simpleFeature) {
171 drawer.drawFeature(item, simpleFeature, defaultTransform, false, symbolizer, null, defaultShape);
172 }
173
174 @Override
175 public ReferencedEnvelope getBounds() {
176 return null;
177 }
178
179 class NamedBufferedImage extends BufferedImage {
180
181 private final String name;
182
183 NamedBufferedImage(String name, int width, int height) {
184 super(width, height, BufferedImage.TYPE_INT_ARGB);
185 this.name = name;
186 }
187
188 @Override
189 public int hashCode() {
190 return name.hashCode();
191 }
192
193 @Override
194 public boolean equals(Object obj) {
195 return obj instanceof NamedBufferedImage && hashCode() == obj.hashCode();
196 }
197 }
198 }