org.geomajas.plugin.print.component.impl.VectorLayerComponentImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.geomajas.plugin.print.component.impl.VectorLayerComponentImpl.java

Source

/*
 * This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
 *
 * Copyright 2008-2014 Geosparc nv, http://www.geosparc.com/, Belgium.
 *
 * The program is available in open source according to the GNU Affero
 * General Public License. All contributions in this program are covered
 * by the Geomajas Contributors License Agreement. For full licensing
 * details, see LICENSE.txt in the project root.
 */
package org.geomajas.plugin.print.component.impl;

import java.awt.Color;
import java.awt.Font;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.geomajas.configuration.FeatureStyleInfo;
import org.geomajas.configuration.LabelStyleInfo;
import org.geomajas.configuration.NamedStyleInfo;
import org.geomajas.configuration.SymbolInfo;
import org.geomajas.configuration.VectorLayerInfo;
import org.geomajas.configuration.client.ClientMapInfo;
import org.geomajas.layer.VectorLayerService;
import org.geomajas.layer.feature.InternalFeature;
import org.geomajas.plugin.print.component.LayoutConstraint;
import org.geomajas.plugin.print.component.PdfContext;
import org.geomajas.plugin.print.component.PrintComponentVisitor;
import org.geomajas.plugin.print.component.dto.VectorLayerComponentInfo;
import org.geomajas.plugin.print.component.service.PrintConfigurationService;
import org.geomajas.service.FilterService;
import org.geomajas.service.GeoService;
import org.opengis.filter.Filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.lowagie.text.Rectangle;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateFilter;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;

/**
 * Internal implementation of {@link org.geomajas.plugin.print.component.BaseLayerComponent}.
 * 
 * @author Jan De Moerloose
 */
@Component()
@Scope(value = "prototype")
public class VectorLayerComponentImpl extends BaseLayerComponentImpl<VectorLayerComponentInfo> {

    /** Style for this layer. */
    private NamedStyleInfo styleInfo;

    /** CQL filter */
    private String filter;

    /** Array of selected feature id's for this layer. */
    private String[] selectedFeatureIds = new String[0];

    /** True if labels are visible. */
    private boolean labelsVisible;

    /** The calculated bounds. */
    @XStreamOmitField
    protected Envelope bbox;

    /** List of the features. */
    @XStreamOmitField
    protected List<InternalFeature> features = new ArrayList<InternalFeature>();

    /** A sorted set of selected feature ids. */
    @XStreamOmitField
    protected Set<String> selectedFeatures = new TreeSet<String>();

    @XStreamOmitField
    private final Logger log = LoggerFactory.getLogger(VectorLayerComponentImpl.class);

    // length of the connector line for symbols
    private static final float SYMBOL_CONNECT_LENGTH = 15;

    @Autowired
    @XStreamOmitField
    private GeoService geoService;

    @Autowired
    @XStreamOmitField
    private FilterService filterService;

    @Autowired
    @XStreamOmitField
    private VectorLayerService layerService;

    @Autowired
    @XStreamOmitField
    private PrintConfigurationService configurationService;

    public VectorLayerComponentImpl() {

        // stretch to map
        getConstraint().setAlignmentX(LayoutConstraint.JUSTIFIED);
        getConstraint().setAlignmentY(LayoutConstraint.JUSTIFIED);
    }

    /**
     * Call back visitor.
     * 
     * @param visitor visitor
     */
    public void accept(PrintComponentVisitor visitor) {
        // nothing to do
    }

    @Override
    public void render(PdfContext context) {
        if (isVisible()) {
            bbox = createBbox();
            Collections.addAll(selectedFeatures, getSelectedFeatureIds());
            // Fetch features
            ClientMapInfo map = configurationService.getMapInfo(getMap().getMapId(), getMap().getApplicationId());
            try {

                // If MapCRS does not equal LayerCRS then instantiate
                // the CrsTransform which will be used for transforming the bbox
                VectorLayerInfo layerInfo = configurationService.getVectorLayerInfo(getLayerId());
                String mapCrs = map.getCrs();
                String layerCrs = layerInfo.getCrs();
                Envelope layerBbox = bbox;
                if (!mapCrs.equals(layerCrs)) {
                    layerBbox = geoService.transform(layerBbox, mapCrs, layerCrs);
                }

                String geomName = layerInfo.getFeatureInfo().getGeometryType().getName();

                // If the transform is null then just use the bbox for the filter
                // Else if the transform is not null then transform the bbox for the filter
                Filter filter = filterService.createBboxFilter(layerCrs, layerBbox, geomName);

                if (getFilter() != null) {
                    filter = filterService.createAndFilter(filterService.parseFilter(getFilter()), filter);
                }

                features = layerService.getFeatures(getLayerId(), geoService.getCrs2(map.getCrs()), filter,
                        styleInfo, VectorLayerService.FEATURE_INCLUDE_ALL);

            } catch (Exception e) { // NOSONAR
                log.error("Error getting features", e);
            }

            // order features, selected last
            List<InternalFeature> orderedFeatures = new ArrayList<InternalFeature>();
            for (InternalFeature feature : features) {
                if (selectedFeatures.contains(feature.getId())) {
                    orderedFeatures.add(feature);
                } else {
                    orderedFeatures.add(0, feature);
                }
            }

            for (InternalFeature f : orderedFeatures) {
                drawFeature(context, map, f);
            }
            if (isLabelsVisible()) {
                for (InternalFeature f : orderedFeatures) {
                    drawLabel(context, f);
                }
            }
        }
    }

    private void drawLabel(PdfContext context, InternalFeature f) {
        LabelStyleInfo labelType = styleInfo.getLabelStyle();
        String label = f.getLabel();
        if (label != null) {
            Font font = new Font("Helvetica", Font.ITALIC, 10);
            Color fontColor = Color.black;
            if (labelType.getFontStyle() != null) {
                fontColor = context.getColor(labelType.getFontStyle().getColor(),
                        labelType.getFontStyle().getOpacity());
            }
            Rectangle rect = calculateLabelRect(context, f, label, font);
            Color bgColor = Color.white;
            if (labelType.getBackgroundStyle() != null) {
                bgColor = context.getColor(labelType.getBackgroundStyle().getFillColor(),
                        labelType.getBackgroundStyle().getFillOpacity());
            }
            context.fillRoundRectangle(rect, bgColor, 3);
            Color borderColor = Color.black;
            if (labelType.getBackgroundStyle() != null) {
                borderColor = context.getColor(labelType.getBackgroundStyle().getStrokeColor(),
                        labelType.getBackgroundStyle().getStrokeOpacity());
            }
            float linewidth = 0.5f;
            if (labelType.getBackgroundStyle() != null) {
                linewidth = labelType.getBackgroundStyle().getStrokeWidth();
            }
            context.strokeRoundRectangle(rect, borderColor, linewidth, 3);
            context.drawText(label, font, rect, fontColor);
            if (f.getGeometry() instanceof Point) {
                context.drawLine(0.5f * (rect.getLeft() + rect.getRight()), rect.getBottom(),
                        0.5f * (rect.getLeft() + rect.getRight()), rect.getBottom() - SYMBOL_CONNECT_LENGTH,
                        borderColor, linewidth);
            }
        }
    }

    private float getSymbolHeight(InternalFeature f) {
        SymbolInfo info = f.getStyleInfo().getSymbol();
        if (info.getCircle() != null) {
            return 2 * info.getCircle().getR();
        } else {
            return info.getRect().getH();
        }
    }

    private Rectangle calculateLabelRect(PdfContext context, InternalFeature f, String label, Font font) {
        Rectangle textSize = context.getTextSize(label, font);
        float margin = 0.25f * font.getSize();
        Rectangle rect = new Rectangle(textSize.getWidth() + 2 * margin, textSize.getHeight() + 2 * margin);
        Coordinate labelPosition = geoService.calcDefaultLabelPosition(f);
        // SPRINT-53 Labels should be rendered in Screen Space
        new MapToUserFilter().filter(labelPosition);

        context.moveRectangleTo(rect, (float) labelPosition.x - rect.getWidth() / 2f,
                (float) labelPosition.y - rect.getHeight() / 2f);
        if (f.getGeometry() instanceof Point) {
            float shiftHeight = 0.5f * (rect.getHeight() + getSymbolHeight(f));
            // move up 15 pixels to make the symbol visible
            context.moveRectangleTo(rect, rect.getLeft(), rect.getBottom() + shiftHeight + SYMBOL_CONNECT_LENGTH);
        }
        if (rect.getLeft() < 0) {
            context.moveRectangleTo(rect, 10, rect.getBottom());
        }
        if (rect.getBottom() < 0) {
            context.moveRectangleTo(rect, rect.getLeft(), 10);
        }
        if (rect.getTop() > getBounds().getHeight()) {
            context.moveRectangleTo(rect, rect.getLeft(), getBounds().getHeight() - rect.getHeight() - 10);
        }
        if (rect.getRight() > getBounds().getWidth()) {
            context.moveRectangleTo(rect, getBounds().getWidth() - rect.getWidth() - 10, rect.getBottom());
        }
        return rect;
    }

    private void drawFeature(PdfContext context, ClientMapInfo map, InternalFeature f) {
        FeatureStyleInfo style = f.getStyleInfo();

        // Color, transparency, dash
        Color fillColor = context.getColor(style.getFillColor(), style.getFillOpacity());
        Color strokeColor = context.getColor(style.getStrokeColor(), style.getStrokeOpacity());
        float[] dashArray = context.getDashArray(style.getDashArray());

        // check if the feature is selected
        if (selectedFeatures.contains(f.getId())) {
            if (f.getGeometry() instanceof MultiPolygon || f.getGeometry() instanceof Polygon) {
                style = mergeStyle(style, map.getPolygonSelectStyle());
                fillColor = context.getColor(style.getFillColor(), style.getFillOpacity());
                strokeColor = context.getColor(style.getStrokeColor(), style.getStrokeOpacity());
            } else if (f.getGeometry() instanceof MultiLineString || f.getGeometry() instanceof LineString) {
                style = mergeStyle(style, map.getLineSelectStyle());
                strokeColor = context.getColor(style.getStrokeColor(), style.getStrokeOpacity());
            } else if (f.getGeometry() instanceof MultiPoint || f.getGeometry() instanceof Point) {
                style = mergeStyle(style, map.getPointSelectStyle());
                strokeColor = context.getColor(style.getStrokeColor(), style.getStrokeOpacity());
            }
        }

        float lineWidth = style.getStrokeWidth();

        SymbolInfo symbol = null;
        if (f.getGeometry() instanceof MultiPoint || f.getGeometry() instanceof Point) {
            symbol = style.getSymbol();
        }
        // clone geometry
        Geometry geometry = (Geometry) f.getGeometry().clone();
        // transform to user space
        geometry.apply(new MapToUserFilter());
        // notify geometry change !!!
        geometry.geometryChanged();
        // now draw
        context.drawGeometry(geometry, symbol, fillColor, strokeColor, lineWidth, dashArray, getSize());
    }

    private FeatureStyleInfo mergeStyle(FeatureStyleInfo base, FeatureStyleInfo extension) {
        FeatureStyleInfo merged = new FeatureStyleInfo();
        merged.setDashArray(extension.getDashArray() != null ? extension.getDashArray() : base.getDashArray());
        merged.setFillColor(extension.getFillColor() != null ? extension.getFillColor() : base.getFillColor());
        merged.setFillOpacity(
                extension.getFillOpacity() != -1 ? extension.getFillOpacity() : base.getFillOpacity());
        merged.setStrokeColor(
                extension.getStrokeColor() != null ? extension.getStrokeColor() : base.getStrokeColor());
        merged.setStrokeOpacity(
                extension.getStrokeOpacity() != -1 ? extension.getStrokeOpacity() : base.getStrokeOpacity());
        merged.setSymbol(extension.getSymbol() != null ? extension.getSymbol() : base.getSymbol());
        merged.setStrokeWidth(
                extension.getStrokeWidth() != -1 ? extension.getStrokeWidth() : base.getStrokeWidth());
        return merged;
    }

    /**
     * ???
     */
    private final class MapToUserFilter implements CoordinateFilter {

        public void filter(Coordinate coordinate) {
            coordinate.x = (coordinate.x - bbox.getMinX()) * getMap().getPpUnit();
            coordinate.y = (coordinate.y - bbox.getMinY()) * getMap().getPpUnit();
        }
    }

    /**
     * Set style for layer.
     *
     * @param styleInfo style info
     */
    public void setStyleInfo(NamedStyleInfo styleInfo) {
        this.styleInfo = styleInfo;
    }

    /**
     * Get selected feature ids.
     *
     * @return selected feature ids.
     */
    public String[] getSelectedFeatureIds() {
        return selectedFeatureIds;
    }

    /**
     * Set selected feature ids.
     *
     * @param selectedFeatureIds selected feature ids
     */
    public void setSelectedFeatureIds(String[] selectedFeatureIds) {
        this.selectedFeatureIds = selectedFeatureIds;
    }

    /**
     * Get layer filter.
     *
     * @return layer filter
     */
    public String getFilter() {
        return filter;
    }

    /**
     * Set the layer filter.
     *
     * @param filter layer filter
     */
    public void setFilter(String filter) {
        this.filter = filter;
    }

    /**
     * Are labels visible?
     *
     * @return true when labels are visible
     */
    public boolean isLabelsVisible() {
        return labelsVisible;
    }

    /**
     * Set the label visibility.
     *
     * @param labelsVisible labels visible?
     */
    public void setLabelsVisible(boolean labelsVisible) {
        this.labelsVisible = labelsVisible;
    }

    @Override
    public void fromDto(VectorLayerComponentInfo vectorLayerInfo) {
        super.fromDto(vectorLayerInfo);
        setLabelsVisible(vectorLayerInfo.isLabelsVisible());
        setSelectedFeatureIds(vectorLayerInfo.getSelectedFeatureIds());
        setStyleInfo(vectorLayerInfo.getStyleInfo());
        setFilter(vectorLayerInfo.getFilter());
    }

}