/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2008 - 2015, Geomatys
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotoolkit.display2d.style.labeling;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Point;
import java.awt.BasicStroke;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.geotoolkit.display2d.canvas.RenderingContext2D;
import org.geotoolkit.display2d.style.j2d.TextStroke;
import static org.apache.sis.util.ArgumentChecks.*;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.display2d.GO2Utilities;
import org.opengis.referencing.operation.TransformException;
/**
* Default implementation of label renderer.
*
* @author Johann Sorel (Geomatys)
* @module
*/
public class DefaultLabelRenderer implements LabelRenderer{
private final List<LabelLayer> layers = new ArrayList<>();
protected RenderingContext2D context = null;
public DefaultLabelRenderer() {
}
@Override
public LabelLayer createLabelLayer() {
return new DefaultLabelLayer(false, true);
}
/**
* {@inheritDoc }
*/
@Override
public void setRenderingContext(final RenderingContext2D context){
ensureNonNull("context", context);
this.context = context;
}
/**
* {@inheritDoc }
*/
@Override
public RenderingContext2D getRenderingContext() {
return context;
}
/**
* {@inheritDoc }
*/
@Override
public void append(final LabelLayer layer) {
layers.add(layer);
}
/**
* {@inheritDoc }
*/
@Override
public void portrayLabels(){
final Graphics2D g2 = context.getGraphics();
//enable antialiasing for labels
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for(final LabelLayer layer : layers){
for(LabelDescriptor label : layer.labels()){
if(label instanceof PointLabelDescriptor){
portray(g2, (PointLabelDescriptor)label);
}else if(label instanceof LinearLabelDescriptor){
portray(g2, (LinearLabelDescriptor)label);
}
}
}
this.layers.clear();
}
private void portray(final Graphics2D g2, final PointLabelDescriptor label){
context.switchToDisplayCRS();
final FontMetrics metric = context.getFontMetrics(label.getTextFont());
final int textHeight = metric.getHeight();
final int textWidth = metric.stringWidth(label.getText());
final Geometry[] geoms;
try {
//we don't use the display geometry because it is clipped to view area
//it will move the real geometry centroid, we rendering tiles we don' want the
//point to change from tile to tile
geoms = label.getGeometry().getDisplayGeometryJTS();
} catch (TransformException ex) {
Logging.getLogger("org.geotoolkit.display2d.style.labeling").log(Level.WARNING, null, ex);
return;
}
for(Geometry geom : geoms){
//get most appropriate point
final Point pt = GO2Utilities.getBestPoint(geom);
if(pt==null) continue;
final Coordinate point = pt.getCoordinate();
float refX = (float)point.x;
float refY = (float)point.y;
//adjust displacement---------------------------------------------------
//displacement is oriented above and to the right
refX = refX + label.getDisplacementX();
refY = refY - label.getDisplacementY();
//rotation--------------------------------------------------------------
final float rotate = (float) Math.toRadians(label.getRotation());
g2.rotate(rotate, refX, refY);
//adjust anchor---------------------------------------------------------
refX = refX - (label.getAnchorX()*textWidth);
//text is draw above reference point so use +
refY = refY + (label.getAnchorY()*textHeight);
g2.setFont(label.getTextFont());
//paint halo------------------------------------------------------------
final float haloWidth = label.getHaloWidth();
if(label.getHaloWidth() > 0){
final float haloWidth2 = haloWidth+haloWidth;
final Rectangle2D bounds = metric.getStringBounds(label.getText(), g2);
final Shape shape = new RoundRectangle2D.Double(
bounds.getMinX() + refX - haloWidth,
bounds.getMinY() + refY - haloWidth,
bounds.getWidth() + haloWidth2,
bounds.getHeight()+ haloWidth2,
2+haloWidth2,
2+haloWidth2);
g2.setPaint(label.getHaloPaint());
g2.fill(shape);
}
//paint text------------------------------------------------------------
g2.setPaint(label.getTextPaint());
g2.drawString(label.getText(), refX, refY);
}
}
private void portray(final Graphics2D g2, final LinearLabelDescriptor label){
context.switchToDisplayCRS();
final TextStroke stroke = new TextStroke(label.getText(), label.getTextFont(), label.isRepeated(),
label.getOffSet(), label.getInitialGap(), label.getGap(),context.getCanvasDisplayBounds());
final Shape[] geoms;
try {
geoms = label.getGeometry().getDisplayShape();
} catch (TransformException ex) {
Logging.getLogger("org.geotoolkit.display2d.style.labeling").log(Level.WARNING, null, ex);
return;
}
for(Shape geom : geoms){
final Shape shape = stroke.createStrokedShape(geom);
//paint halo
if(label.getHaloWidth() > 0){
g2.setStroke(new BasicStroke(label.getHaloWidth(),BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND) );
g2.setPaint(label.getHaloPaint());
g2.draw(shape);
}
//paint text
g2.setStroke(new BasicStroke(0));
g2.setPaint(label.getTextPaint());
g2.fill(shape);
}
}
@Override
public void portrayImmidiately(final LabelLayer layer) {
final Graphics2D g2 = context.getGraphics();
//enable antialiasing for labels
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for(LabelDescriptor label : layer.labels()){
if(label instanceof PointLabelDescriptor){
portray(g2, (PointLabelDescriptor)label);
}else if(label instanceof LinearLabelDescriptor){
portray(g2, (LinearLabelDescriptor)label);
}
}
}
}