View Javadoc
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   * @author peck7 on 11/09/2018.
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          // nothing to draw
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                 // nothing to draw
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 }