All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.geotools.styling.visitor.RescaleStyleVisitor Maven / Gradle / Ivy

/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 *
 *    (C) 2007-2015, Open Source Geospatial Foundation (OSGeo)
 *
 *    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.geotools.styling.visitor;

import static org.geotools.styling.TextSymbolizer.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.measure.Unit;
import javax.measure.quantity.Length;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.styling.AnchorPoint;
import org.geotools.styling.Displacement;
import org.geotools.styling.Font;
import org.geotools.styling.Graphic;
import org.geotools.styling.LabelPlacement;
import org.geotools.styling.LinePlacement;
import org.geotools.styling.LineSymbolizer;
import org.geotools.styling.PointPlacement;
import org.geotools.styling.PointSymbolizer;
import org.geotools.styling.PolygonSymbolizer;
import org.geotools.styling.RasterSymbolizer;
import org.geotools.styling.Stroke;
import org.geotools.styling.Symbol;
import org.geotools.styling.Symbolizer;
import org.geotools.styling.TextSymbolizer;
import org.geotools.util.Converters;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.Literal;
import org.opengis.style.GraphicalSymbol;

/**
 * This is a style visitor that will produce a copy of the provided style rescaled by a provided
 * factor.
 *
 * 

The provided scale will be use to modify all line widths, font sizes and so forth. We may need * to go the extra distance and play with the min/max scale on rules, and if there is any DPI * specific madness going on we are going to cry. * *

According to the specification we are supposed to use environmental variables to make our * styles render in a resolution independent manner. The current GeoTools environment variable * visitor only does processing for mapscale but does not have a dpi substitution. On the * plus side this visitor accepts a general Expression and you are free to use an environmental * variable expression in order to make sure a normal base style is tweaked in all the right spots. * *

* * @author Jody Garnett (Refractions Research) */ public class RescaleStyleVisitor extends DuplicatingStyleVisitor { /** This is the scale used as a multiplication factory for everything that has a size. */ protected Expression scale; protected Unit defaultUnit; public RescaleStyleVisitor(double scale) { this(CommonFactoryFinder.getFilterFactory2(null), scale); } public RescaleStyleVisitor(Expression scale) { this(CommonFactoryFinder.getFilterFactory2(null), scale); } public RescaleStyleVisitor(FilterFactory2 filterFactory, double scale) { this(filterFactory, filterFactory.literal(scale)); } public RescaleStyleVisitor(FilterFactory2 filterFactory, Expression scale) { super(CommonFactoryFinder.getStyleFactory(null), filterFactory); this.scale = scale; } /** * Used to rescale the provided expr. * *

We do optimize the case where the provided expression is a literal; no sense doing a * calculation each time if we don't have to. * * @return expr multiplied by the provided scale */ protected Expression rescale(Expression expr) { if (expr == null) { return null; } if (expr == Expression.NIL) { return Expression.NIL; } Measure m = new Measure(expr, defaultUnit); return RescalingMode.KeepUnits.rescaleToExpression(scale, m); } /** Rescale a list of expressions, can handle null. */ protected List rescale(List expressions) { if (expressions == null || expressions.isEmpty()) { return expressions; } List rescaled = new ArrayList<>(expressions.size()); for (Expression expression : expressions) { rescaled.add(rescale(expression)); } return rescaled; } /** Rescale using listMultiply, if there is only one entry. */ protected List rescaleDashArray(List expressions) { if (expressions == null || expressions.isEmpty()) { return expressions; } Expression rescaleToExpression = rescale(ff.literal(1)); // How to test, if it is a measure with a unit or not? String data = ((Literal) rescaleToExpression).getValue().toString(); boolean evaluate = rescaleToExpression instanceof Literal && Character.isDigit(data.charAt(data.length() - 1)); List rescaled = new ArrayList<>(expressions.size()); for (Expression expression : expressions) { Expression rescale = ff.function("listMultiply", rescaleToExpression, expression); if (expression instanceof Literal && evaluate) { rescaled.add(ff.literal(rescale.evaluate(null))); } else { rescaled.add(rescale); } } return rescaled; } /** * Increase stroke width. * *

Based on feedback we may need to change the dash array as well. * *

*/ public void visit(org.geotools.styling.Stroke stroke) { Stroke copy = sf.getDefaultStroke(); copy.setColor(copy(stroke.getColor())); copy.setDashArray(rescaleDashArray(stroke.dashArray())); copy.setDashOffset(rescale(stroke.getDashOffset())); copy.setGraphicFill(copy(stroke.getGraphicFill())); copy.setGraphicStroke(copy(stroke.getGraphicStroke())); copy.setLineCap(copy(stroke.getLineCap())); copy.setLineJoin(copy(stroke.getLineJoin())); copy.setOpacity(copy(stroke.getOpacity())); copy.setWidth(rescale(stroke.getWidth())); pages.push(copy); } /** Make graphics (such as used with PointSymbolizer) bigger */ public void visit(Graphic gr) { Graphic copy = null; Displacement displacementCopy = null; if (gr.getDisplacement() != null) { gr.getDisplacement().accept(this); displacementCopy = (Displacement) pages.pop(); } AnchorPoint anchorCopy = null; if (gr.getAnchorPoint() != null) { gr.getAnchorPoint().accept(this); anchorCopy = (AnchorPoint) pages.pop(); } List symbols = gr.graphicalSymbols(); List symbolsCopy = new ArrayList<>(symbols.size()); for (GraphicalSymbol symbol : symbols) { if (symbol instanceof Symbol) { symbolsCopy.add(copy((Symbol) symbol)); } else { throw new RuntimeException("Don't know how to rescale " + symbol); } } Expression opacityCopy = copy(gr.getOpacity()); Expression rotationCopy = copy(gr.getRotation()); Expression sizeCopy = rescaleGraphicSize(gr); copy = sf.createDefaultGraphic(); copy.setDisplacement(displacementCopy); copy.setAnchorPoint(anchorCopy); copy.graphicalSymbols().clear(); copy.graphicalSymbols().addAll(symbolsCopy); copy.setOpacity(opacityCopy); copy.setRotation(rotationCopy); copy.setSize(sizeCopy); pages.push(copy); } protected Expression rescaleGraphicSize(Graphic gr) { return rescale(gr.getSize()); } @Override public void visit(TextSymbolizer text) { this.defaultUnit = text.getUnitOfMeasure(); try { super.visit(text); TextSymbolizer copy = (TextSymbolizer) pages.peek(); // rescales fonts for (Font font : copy.fonts()) { if (font != null) { font.setSize(rescale(font.getSize())); } } // rescales label placement LabelPlacement placement = copy.getLabelPlacement(); if (placement instanceof PointPlacement) { // rescales point label placement PointPlacement pointPlacement = (PointPlacement) placement; Displacement disp = pointPlacement.getDisplacement(); if (disp != null) { disp.setDisplacementX(rescale(disp.getDisplacementX())); disp.setDisplacementY(rescale(disp.getDisplacementY())); pointPlacement.setDisplacement(disp); } } else if (placement instanceof LinePlacement) { // rescales line label placement LinePlacement linePlacement = (LinePlacement) placement; linePlacement.setGap(rescale(linePlacement.getGap())); linePlacement.setInitialGap(rescale(linePlacement.getInitialGap())); linePlacement.setPerpendicularOffset( rescale(linePlacement.getPerpendicularOffset())); } copy.setLabelPlacement(placement); // rescale the halo if (copy.getHalo() != null) { copy.getHalo().setRadius(rescale(copy.getHalo().getRadius())); } // deal with the format options specified in pixels Map options = copy.getOptions(); rescaleOption(options, SPACE_AROUND_KEY, DEFAULT_SPACE_AROUND); rescaleOption(options, MAX_DISPLACEMENT_KEY, DEFAULT_MAX_DISPLACEMENT); rescaleOption(options, MIN_GROUP_DISTANCE_KEY, DEFAULT_MIN_GROUP_DISTANCE); rescaleOption(options, LABEL_REPEAT_KEY, DEFAULT_LABEL_REPEAT); rescaleOption(options, AUTO_WRAP_KEY, DEFAULT_AUTO_WRAP); rescaleArrayOption(options, GRAPHIC_MARGIN_KEY, 0); } finally { this.defaultUnit = null; } } @Override public void visit(Symbolizer sym) { this.defaultUnit = sym.getUnitOfMeasure(); try { super.visit(sym); } finally { this.defaultUnit = null; } } @Override public void visit(PointSymbolizer sym) { this.defaultUnit = sym.getUnitOfMeasure(); try { super.visit(sym); } finally { this.defaultUnit = null; } } @Override public void visit(LineSymbolizer sym) { this.defaultUnit = sym.getUnitOfMeasure(); try { super.visit(sym); LineSymbolizer copy = (LineSymbolizer) pages.peek(); copy.setPerpendicularOffset(rescale(copy.getPerpendicularOffset())); } finally { this.defaultUnit = null; } } @Override public void visit(PolygonSymbolizer sym) { this.defaultUnit = sym.getUnitOfMeasure(); try { super.visit(sym); PolygonSymbolizer copy = (PolygonSymbolizer) pages.peek(); rescaleArrayOption(copy.getOptions(), PolygonSymbolizer.GRAPHIC_MARGIN_KEY, 0); } finally { this.defaultUnit = null; } } @Override public void visit(RasterSymbolizer sym) { this.defaultUnit = sym.getUnitOfMeasure(); try { super.visit(sym); } finally { this.defaultUnit = null; } } /** Rescales the specified vendor option */ protected void rescaleOption(Map options, String key, double defaultValue) { double scaleFactor = (double) scale.evaluate(null, Double.class); if (options.get(key) != null) { double rescaled = Converters.convert(options.get(key), Double.class) * scaleFactor; options.put(key, String.valueOf(rescaled)); } else if (defaultValue != 0) { options.put(key, String.valueOf(defaultValue * scaleFactor)); } }; /** Rescales the specified vendor option */ protected void rescaleOption(Map options, String key, int defaultValue) { double scaleFactor = (double) scale.evaluate(null, Double.class); if (options.get(key) != null) { int rescaled = (int) Math.round( Converters.convert(options.get(key), Double.class) * scaleFactor); options.put(key, String.valueOf(rescaled)); } else if (defaultValue != 0) { options.put(key, String.valueOf((int) Math.round(defaultValue * scaleFactor))); } }; /** Rescales the specified vendor option */ protected void rescaleArrayOption(Map options, String key, int defaultValue) { double scaleFactor = (double) scale.evaluate(null, Double.class); if (options.get(key) != null) { String strValue = options.get(key); String[] splitted = strValue.split("\\s+"); StringBuilder sb = new StringBuilder(); for (String value : splitted) { double rescaled = (int) Math.round(Double.parseDouble(value) * scaleFactor); sb.append((int) rescaled).append(" "); } sb.setLength(sb.length() - 1); options.put(key, sb.toString()); } else if (defaultValue != 0) { options.put(key, String.valueOf((int) Math.round(defaultValue * scaleFactor))); } }; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy