/*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2007 by: Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de 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; either version 2.1 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: Andreas Poth lat/lon GmbH Aennchenstr. 19 53177 Bonn Germany E-Mail: poth@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.igeo.style.classification; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.ANCHORPOINT; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.DISPLACEMENT; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.FONTCOLOR; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.FONTFAMILY; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.FONTSIZE; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.FONTSTYLE; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.FONTTRANSPARENCY; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.FONTWEIGHT; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.HALOCOLOR; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.HALORADIUS; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.LINEWIDTH; import static org.deegree.igeo.style.model.classification.Column.COLUMNTYPE.ROTATION; import java.net.MalformedURLException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Map; import javax.vecmath.Point2d; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; import org.deegree.framework.util.Pair; import org.deegree.graphics.sld.CssParameter; import org.deegree.graphics.sld.ExternalGraphic; import org.deegree.graphics.sld.Fill; import org.deegree.graphics.sld.Font; import org.deegree.graphics.sld.Graphic; import org.deegree.graphics.sld.Halo; import org.deegree.graphics.sld.LineSymbolizer; import org.deegree.graphics.sld.Mark; import org.deegree.graphics.sld.ParameterValueType; import org.deegree.graphics.sld.PointPlacement; import org.deegree.graphics.sld.PointSymbolizer; import org.deegree.graphics.sld.PolygonSymbolizer; import org.deegree.graphics.sld.Rule; import org.deegree.graphics.sld.Stroke; import org.deegree.graphics.sld.Symbolizer; import org.deegree.graphics.sld.TextSymbolizer; import org.deegree.igeo.settings.GraphicOptions; import org.deegree.igeo.settings.Settings; import org.deegree.igeo.style.model.DashArray; import org.deegree.igeo.style.model.FillPattern; import org.deegree.igeo.style.model.GraphicSymbol; import org.deegree.igeo.style.model.SldProperty; import org.deegree.igeo.style.model.SldValues; import org.deegree.igeo.style.model.WellKnownMark; import org.deegree.igeo.style.model.classification.ClassificationTableRow; import org.deegree.igeo.style.model.classification.IllegalClassificationException; import org.deegree.igeo.style.model.classification.Intervallable; import org.deegree.igeo.style.model.classification.Intervallables.DateIntervallable; import org.deegree.igeo.style.model.classification.Intervallables.DoubleIntervallable; import org.deegree.igeo.style.model.classification.Intervallables.StringIntervallable; import org.deegree.igeo.style.model.classification.ThematicGroupingInformation; import org.deegree.igeo.style.model.classification.ThematicGroupingInformation.GROUPINGTYPE; import org.deegree.igeo.style.model.classification.ValueRange; import org.deegree.igeo.style.perform.UnitsValue; import org.deegree.igeo.style.utils.SldCreatorUtils; import org.deegree.igeo.views.swing.style.component.classification.AbstractClassificationPanel.SYMBOLIZERTYPE; import org.deegree.model.feature.schema.FeatureType; import org.deegree.model.feature.schema.PropertyType; import org.deegree.model.filterencoding.ComplexFilter; import org.deegree.model.filterencoding.Expression; import org.deegree.model.filterencoding.ExpressionDefines; import org.deegree.model.filterencoding.FilterEvaluationException; import org.deegree.model.filterencoding.Literal; import org.deegree.model.filterencoding.LogicalOperation; import org.deegree.model.filterencoding.Operation; import org.deegree.model.filterencoding.OperationDefines; import org.deegree.model.filterencoding.PropertyIsCOMPOperation; import org.deegree.model.filterencoding.PropertyName; import org.deegree.ogcbase.PropertyPath; import org.deegree.ogcbase.PropertyPathFactory; /** * <code>ClassificationFromSld</code> * * @author <a href="mailto:buesching@lat-lon.de">Lyn Buesching</a> * @author last edited by: $Author$ * * @version $Revision$, $Date$ * */ public class ClassificationFromSld { private static final ILogger LOG = LoggerFactory.getLogger( ClassificationFromSld.class ); private static final String IllegalPTMsg = "PropertyType is not supported!"; private static final String IllegalPNMsg = "PropertyNames are not all the same!"; private static final String IllegalMiscMsg = "Operation differs - must be all PROPERTYISEQUALTO, or not!"; private static enum ISUNIQUE { UNKNOWN, TRUE, FALSE }; /** * creates an unique values classification of the given rules, which property names has type VARCHAR * * @param rules * @param propertyType * @return * @throws IllegalClassificationException * @throws FilterEvaluationException */ public static ThematicGroupingInformation<String> createStringClassification( List<Rule> rules, List<Intervallable<String>> values, int propertyType, Settings settings ) throws IllegalClassificationException, FilterEvaluationException { List<ClassificationTableRow<String>> classification = new ArrayList<ClassificationTableRow<String>>(); for ( Rule rule : rules ) { ValueRange<String> vr = null; if ( rule.getFilter() instanceof ComplexFilter ) { Operation filterOperation = cleanFilter( (ComplexFilter) rule.getFilter() ); if ( filterOperation.getOperatorId() == OperationDefines.PROPERTYISEQUALTO ) { if ( filterOperation instanceof PropertyIsCOMPOperation ) { PropertyIsCOMPOperation compOp = (PropertyIsCOMPOperation) filterOperation; Expression e1 = compOp.getFirstExpression(); Expression e2 = compOp.getSecondExpression(); Literal literal = null; if ( e2.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e2; } else if ( e1.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e1; } if ( literal != null ) { vr = getStringValueRange( values, literal ); } } else { throw new IllegalClassificationException( IllegalMiscMsg ); } } else { throw new IllegalClassificationException( "For Strings, only UNIQUE/QUALITY classification is supported!" ); } } if ( vr != null ) { ClassificationTableRow<String> row = new ClassificationTableRow<String>( vr ); setRowStyles( row, rule, settings ); classification.add( row ); } else { throw new IllegalClassificationException( "Could not create list of value ranges" ); } } return new ThematicGroupingInformation<String>( getUniqueClassificationType( classification, values ), classification ); } private static <V extends Comparable<V>> GROUPINGTYPE getUniqueClassificationType( List<ClassificationTableRow<V>> classification, List<Intervallable<V>> data ) { boolean containsDoubleValues = false; List<Intervallable<V>> values = new ArrayList<Intervallable<V>>( classification.size() ); for ( ClassificationTableRow<V> row : classification ) { Intervallable<V> min = row.getValue().getMin(); if ( values.contains( min ) ) { containsDoubleValues = true; break; } else { values.add( min ); } } if ( containsDoubleValues ) { return GROUPINGTYPE.UNIQUE; } else { return GROUPINGTYPE.QUALITY; } } public static boolean isTypeCorrect( List<Rule> rules, SYMBOLIZERTYPE type ) { for ( Rule rule : rules ) { if ( rule.getSymbolizers() != null && rule.getSymbolizers().length > 0 ) { Symbolizer s = rule.getSymbolizers()[0]; if ( SYMBOLIZERTYPE.POINT.equals( type ) && !( s instanceof PointSymbolizer ) ) { return false; } else if ( SYMBOLIZERTYPE.POLYGON.equals( type ) && !( s instanceof PolygonSymbolizer ) ) { return false; } else if ( SYMBOLIZERTYPE.LINE.equals( type ) && !( s instanceof LineSymbolizer ) ) { return false; } else if ( SYMBOLIZERTYPE.LABEL.equals( type ) && !( s instanceof TextSymbolizer ) ) { return false; } } else { return false; } } return true; } /** * creates a classification of the given rules, which property names has type DOUBLE or INTEGER * * @param rules * @param values * @param propertyType * @param decimalPattern * @param settings * @return * @throws IllegalClassificationException * @throws FilterEvaluationException */ public static ThematicGroupingInformation<Double> createDoubleClassification( List<Rule> rules, List<Intervallable<Double>> values, int propertyType, String decimalPattern, Settings settings ) throws IllegalClassificationException, FilterEvaluationException { List<ClassificationTableRow<Double>> classification = new ArrayList<ClassificationTableRow<Double>>(); ISUNIQUE isUnique = ISUNIQUE.UNKNOWN; boolean foundFirst = false; boolean foundLast = false; for ( Rule rule : rules ) { ValueRange<Double> vr = null; if ( rule.getFilter() instanceof ComplexFilter ) { Operation filterOperation = cleanFilter( (ComplexFilter) rule.getFilter() ); switch ( filterOperation.getOperatorId() ) { case OperationDefines.AND: if ( isUnique == ISUNIQUE.UNKNOWN ) { isUnique = ISUNIQUE.FALSE; } if ( isUnique == ISUNIQUE.FALSE ) { if ( filterOperation instanceof LogicalOperation ) { LogicalOperation lo = (LogicalOperation) filterOperation; List<Operation> arguments = lo.getArguments(); if ( arguments.size() > 1 ) { Intervallable<Double> int1; // first argument switch ( arguments.get( 0 ).getOperatorId() ) { case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: case OperationDefines.PROPERTYISLESSTHAN: int1 = createDoubleIntervallable( arguments.get( 0 ), decimalPattern ); break; default: throw new IllegalClassificationException( "First operation of the AND operation is not supported (must be one of PropertyIsLessThan, PropertyIsGreaterThanOrEqualTo), but is " + ExpressionDefines.getNameById( arguments.get( 0 ).getOperatorId() ) ); } Intervallable<Double> int2; // second argument switch ( arguments.get( 1 ).getOperatorId() ) { case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: case OperationDefines.PROPERTYISLESSTHAN: int2 = createDoubleIntervallable( arguments.get( 1 ), decimalPattern ); break; default: throw new IllegalClassificationException( "Second operation of the AND operation is not supported (must be one of PropertyIsLessThan, PropertyIsGreaterThanOrEqualTo), but is " + ExpressionDefines.getNameById( arguments.get( 1 ).getOperatorId() ) ); } if ( int1 != null && int2 != null ) { vr = new ValueRange<Double>( int1, int2, 0 ); updateCountDouble( values, vr ); } } } } else { throw new IllegalClassificationException( IllegalMiscMsg ); } break; case OperationDefines.PROPERTYISEQUALTO: if ( isUnique == ISUNIQUE.UNKNOWN ) { isUnique = ISUNIQUE.TRUE; } if ( isUnique == ISUNIQUE.TRUE ) { if ( filterOperation instanceof PropertyIsCOMPOperation ) { PropertyIsCOMPOperation compOp = (PropertyIsCOMPOperation) filterOperation; Expression e1 = compOp.getFirstExpression(); Expression e2 = compOp.getSecondExpression(); Literal literal = null; if ( e2.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e2; } else if ( e1.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e1; } if ( literal != null ) { vr = getDoubleValueRange( values, literal, literal, decimalPattern ); } } } else { throw new IllegalClassificationException( IllegalMiscMsg ); } break; case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: if ( isUnique == ISUNIQUE.UNKNOWN ) { isUnique = ISUNIQUE.FALSE; } if ( isUnique == ISUNIQUE.FALSE && !foundLast ) { foundLast = true; if ( filterOperation instanceof PropertyIsCOMPOperation ) { PropertyIsCOMPOperation compOp = (PropertyIsCOMPOperation) filterOperation; Expression e1 = compOp.getFirstExpression(); Expression e2 = compOp.getSecondExpression(); Literal literal = null; if ( e2.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e2; } else if ( e1.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e1; } if ( literal != null ) { vr = getDoubleValueRange( values, literal, null, decimalPattern ); } } } else { throw new IllegalClassificationException( IllegalMiscMsg ); } break; case OperationDefines.PROPERTYISLESSTHAN: if ( isUnique == ISUNIQUE.UNKNOWN ) { isUnique = ISUNIQUE.FALSE; } if ( isUnique == ISUNIQUE.FALSE && !foundFirst ) { foundFirst = true; if ( filterOperation instanceof PropertyIsCOMPOperation ) { PropertyIsCOMPOperation compOp = (PropertyIsCOMPOperation) filterOperation; Expression e1 = compOp.getFirstExpression(); Expression e2 = compOp.getSecondExpression(); Literal literal = null; if ( e2.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e2; } else if ( e1.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e1; } if ( literal != null ) { vr = getDoubleValueRange( values, null, literal, decimalPattern ); } } } else { throw new IllegalClassificationException( IllegalMiscMsg ); } break; default: throw new IllegalClassificationException( " First operation is not supported (must be one of PropertyIsLessThan, PropertyIsGreaterThanOrEqualTo, And), but is " + ExpressionDefines.getNameById( filterOperation.getOperatorId() ) ); } } if ( vr != null ) { ClassificationTableRow<Double> row = new ClassificationTableRow<Double>( vr ); setRowStyles( row, rule, settings ); classification.add( row ); } else { throw new IllegalClassificationException( "Could not create list of value ranges" ); } } GROUPINGTYPE gt = GROUPINGTYPE.MANUAL; if ( isUnique == ISUNIQUE.TRUE ) { gt = getUniqueClassificationType( classification, values ); } else { ClassificationCalculator<Double> classCalculator = new ClassificationCalculator<Double>(); if ( isClassificationEqual( classification, classCalculator.calculateEqualInterval( values, classification.size() ) ) ) { gt = GROUPINGTYPE.EQUAL; } else if ( isClassificationEqual( classification, classCalculator.calculateQuantileClassification( values, classification.size() ) ) ) { gt = GROUPINGTYPE.QUANTILE; } } return new ThematicGroupingInformation<Double>( gt, classification ); } /** * creates a classification of the given rules, which property names has type DATE * * @param rules * @param propertyType * @param datePattern * @return * @throws IllegalClassificationException * @throws FilterEvaluationException */ public static ThematicGroupingInformation<Date> createDateClassification( List<Rule> rules, List<Intervallable<Date>> values, int propertyType, String datePattern, Settings settings ) throws IllegalClassificationException, FilterEvaluationException { List<ClassificationTableRow<Date>> classification = new ArrayList<ClassificationTableRow<Date>>(); ISUNIQUE isUnique = ISUNIQUE.UNKNOWN; boolean foundFirst = false; boolean foundLast = false; for ( Rule rule : rules ) { ValueRange<Date> vr = null; if ( rule.getFilter() instanceof ComplexFilter ) { Operation filterOperation = cleanFilter( (ComplexFilter) rule.getFilter() ); switch ( filterOperation.getOperatorId() ) { case OperationDefines.AND: if ( isUnique == ISUNIQUE.UNKNOWN ) { isUnique = ISUNIQUE.FALSE; } if ( isUnique == ISUNIQUE.FALSE ) { if ( filterOperation instanceof LogicalOperation ) { LogicalOperation lo = (LogicalOperation) filterOperation; List<Operation> arguments = lo.getArguments(); if ( arguments.size() > 1 ) { Intervallable<Date> int1; // first argument switch ( arguments.get( 0 ).getOperatorId() ) { case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: case OperationDefines.PROPERTYISLESSTHAN: int1 = createDateIntervallable( arguments.get( 0 ), datePattern ); break; default: throw new IllegalClassificationException( "First operation of the AND operation is not supported (must be one of PropertyIsLessThan, PropertyIsGreaterThanOrEqualTo), but is " + ExpressionDefines.getNameById( arguments.get( 0 ).getOperatorId() ) ); } Intervallable<Date> int2; // second argument switch ( arguments.get( 1 ).getOperatorId() ) { case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: case OperationDefines.PROPERTYISLESSTHAN: int2 = createDateIntervallable( arguments.get( 1 ), datePattern ); break; default: throw new IllegalClassificationException( "Second operation of the AND operation is not supported (must be one of PropertyIsLessThan, PropertyIsGreaterThanOrEqualTo), but is " + ExpressionDefines.getNameById( arguments.get( 1 ).getOperatorId() ) ); } if ( int1 != null && int2 != null ) { vr = new ValueRange<Date>( int1, int2, 0 ); updateCountDate( values, vr ); } } } } else { throw new IllegalClassificationException( IllegalMiscMsg ); } break; case OperationDefines.PROPERTYISEQUALTO: if ( isUnique == ISUNIQUE.UNKNOWN ) { isUnique = ISUNIQUE.TRUE; } if ( isUnique == ISUNIQUE.TRUE ) { if ( filterOperation instanceof PropertyIsCOMPOperation ) { PropertyIsCOMPOperation compOp = (PropertyIsCOMPOperation) filterOperation; Expression e1 = compOp.getFirstExpression(); Expression e2 = compOp.getSecondExpression(); Literal literal = null; if ( e2.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e2; } else if ( e1.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e1; } if ( literal != null ) { vr = getDateValueRange( values, literal, literal, datePattern ); } } } else { throw new IllegalClassificationException( IllegalMiscMsg ); } break; case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: if ( isUnique == ISUNIQUE.UNKNOWN ) { isUnique = ISUNIQUE.FALSE; } if ( isUnique == ISUNIQUE.FALSE && !foundLast ) { foundLast = true; if ( filterOperation instanceof PropertyIsCOMPOperation ) { PropertyIsCOMPOperation compOp = (PropertyIsCOMPOperation) filterOperation; Expression e1 = compOp.getFirstExpression(); Expression e2 = compOp.getSecondExpression(); Literal literal = null; if ( e2.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e2; } else if ( e1.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e1; } if ( literal != null ) { vr = getDateValueRange( values, literal, null, datePattern ); } } } else { throw new IllegalClassificationException( IllegalMiscMsg ); } break; case OperationDefines.PROPERTYISLESSTHAN: if ( isUnique == ISUNIQUE.UNKNOWN ) { isUnique = ISUNIQUE.FALSE; } if ( isUnique == ISUNIQUE.FALSE && !foundFirst ) { foundFirst = true; if ( filterOperation instanceof PropertyIsCOMPOperation ) { PropertyIsCOMPOperation compOp = (PropertyIsCOMPOperation) filterOperation; Expression e1 = compOp.getFirstExpression(); Expression e2 = compOp.getSecondExpression(); Literal literal = null; if ( e2.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e2; } else if ( e1.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e1; } if ( literal != null ) { vr = getDateValueRange( values, null, literal, datePattern ); } } } else { throw new IllegalClassificationException( IllegalMiscMsg ); } break; default: throw new IllegalClassificationException( " First operation is not supported (must be one of PropertyIsLessThan, PropertyIsGreaterThanOrEqualTo, And), but is " + ExpressionDefines.getNameById( filterOperation.getOperatorId() ) ); } } if ( vr != null ) { ClassificationTableRow<Date> row = new ClassificationTableRow<Date>( vr ); setRowStyles( row, rule, settings ); classification.add( row ); } else { throw new IllegalClassificationException( "Could not create list of value ranges" ); } } GROUPINGTYPE gt = GROUPINGTYPE.MANUAL; if ( isUnique == ISUNIQUE.TRUE ) { gt = getUniqueClassificationType( classification, values ); } else { ClassificationCalculator<Date> classCalculator = new ClassificationCalculator<Date>(); if ( isClassificationEqual( classification, classCalculator.calculateEqualInterval( values, classification.size() ) ) ) { gt = GROUPINGTYPE.EQUAL; } else if ( isClassificationEqual( classification, classCalculator.calculateQuantileClassification( values, classification.size() ) ) ) { gt = GROUPINGTYPE.QUANTILE; } } return new ThematicGroupingInformation<Date>( gt, classification ); } private static <V extends Comparable<V>> boolean isClassificationEqual( List<ClassificationTableRow<V>> classification, List<ValueRange<V>> origClassification ) { for ( ValueRange<V> origClass : origClassification ) { boolean isInOrig = false; for ( ClassificationTableRow<V> classificationTableRow : classification ) { if ( origClass.equals( classificationTableRow.getValue() ) ) { isInOrig = true; } } if ( !isInOrig ) { return false; } } return true; } private static void setRowStyles( ClassificationTableRow<?> row, Rule rule, Settings settings ) throws FilterEvaluationException { row.setLabel( rule.getTitle() ); if ( rule.getSymbolizers()[0] instanceof PolygonSymbolizer ) { PolygonSymbolizer ps = (PolygonSymbolizer) rule.getSymbolizers()[0]; Fill fill = ps.getFill(); Stroke s = ps.getStroke(); row.setFillColor( fill.getFill( null ) ); if ( fill.getGraphicFill() != null && fill.getGraphicFill().getGraphic() != null && fill.getGraphicFill().getGraphic().getMarksAndExtGraphics() != null ) { Object[] marksAndExtGrapics = fill.getGraphicFill().getGraphic().getMarksAndExtGraphics(); if ( marksAndExtGrapics.length > 0 && marksAndExtGrapics[0] instanceof ExternalGraphic ) { ExternalGraphic eg = (ExternalGraphic) marksAndExtGrapics[0]; GraphicSymbol gs; if ( SldValues.isFillPattern( eg.getOnlineResource() ) ) { gs = new FillPattern( eg.getOnlineResource().getFile(), eg.getOnlineResource(), fill.getFill( null ) ); } else { gs = new GraphicSymbol( eg.getOnlineResource().getFile(), eg.getOnlineResource() ); } gs.setSize( getSize( fill.getGraphicFill().getGraphic() ) ); row.setFillColor( gs ); } } row.setFillTransparency( SldValues.getOpacityInPercent( fill.getOpacity( null ) ) ); row.setLineColor( s.getStroke( null ) ); row.setLineTransparency( SldValues.getOpacityInPercent( s.getOpacity( null ) ) ); row.setValue( LINEWIDTH, getStrokeWidth( s ) ); setDashArray( s, row, settings ); } else if ( rule.getSymbolizers()[0] instanceof PointSymbolizer ) { Graphic g = ( (PointSymbolizer) rule.getSymbolizers()[0] ).getGraphic(); Object[] marksAndExtGrapics = g.getMarksAndExtGraphics(); if ( marksAndExtGrapics != null && marksAndExtGrapics.length > 0 ) { if ( marksAndExtGrapics[0] instanceof Mark ) { Mark m = (Mark) marksAndExtGrapics[0]; WellKnownMark wkm = SldValues.getDefaultWKM(); for ( WellKnownMark mark : SldValues.getWellKnownMarks() ) { if ( mark.getSldName().equals( m.getWellKnownName() ) ) { wkm = mark; } } row.setSymbol( wkm ); Fill fill = m.getFill(); if ( fill != null ) { row.setFillColor( fill.getFill( null ) ); row.setFillTransparency( SldValues.getOpacityInPercent( fill.getOpacity( null ) ) ); } Stroke stroke = m.getStroke(); if ( stroke != null ) { row.setLineColor( stroke.getStroke( null ) ); } } else if ( marksAndExtGrapics[0] instanceof ExternalGraphic ) { ExternalGraphic eg = (ExternalGraphic) marksAndExtGrapics[0]; GraphicSymbol gs = getGraphicSymbol( settings.getGraphicOptions(), eg ); try { GraphicSymbol gsWithSameName = null; GraphicSymbol gsWithSameUrl = null; Map<String, GraphicSymbol> symbols = settings.getGraphicOptions().getSymbolDefinitions(); for ( String symbolName : symbols.keySet() ) { GraphicSymbol graphicSymbol = symbols.get( symbolName ); if ( ( ( eg.getTitle() != null && eg.getTitle().equals( graphicSymbol.getName() ) ) || eg.getTitle() == null ) && graphicSymbol.getUrl().equals( eg.getOnlineResource() ) ) { gs = graphicSymbol; break; } else if ( gsWithSameName != null && eg.getTitle() != null && eg.getTitle().equals( graphicSymbol.getName() ) ) { gsWithSameName = graphicSymbol; } else if ( gsWithSameUrl != null && graphicSymbol.getUrl().equals( eg.getOnlineResource() ) ) { gsWithSameUrl = graphicSymbol; } } if ( gs == null ) { gs = gsWithSameUrl; } if ( gs == null ) { gs = gsWithSameName; } } catch ( MalformedURLException e ) { LOG.logDebug( "Could not find the symbol with URL " + eg.getOnlineResource() ); } if ( gs == null ) { gs = new GraphicSymbol( eg.getOnlineResource().getFile(), eg.getOnlineResource() ); } row.setSymbol( gs ); } } ParameterValueType rotation = g.getRotation(); if ( rotation != null ) { PropertyName pointRotationPropName = null; if ( rotation != null ) { Object[] o = rotation.getComponents(); for ( int i = 0; i < o.length; i++ ) { if ( o[i] instanceof PropertyName ) { pointRotationPropName = (PropertyName) o[i]; } } } if ( pointRotationPropName != null ) { row.setValue( ROTATION, pointRotationPropName ); } else { row.setValue( ROTATION, g.getRotation( null ) ); } } row.setSize( getSize( g ) ); } else if ( rule.getSymbolizers()[0] instanceof LineSymbolizer ) { Stroke stroke = ( (LineSymbolizer) rule.getSymbolizers()[0] ).getStroke(); row.setLineColor( stroke.getStroke( null ) ); row.setLineTransparency( SldValues.getOpacityInPercent( stroke.getOpacity( null ) ) ); row.setValue( LINEWIDTH, getStrokeWidth( stroke ) ); setLineCap( stroke, row ); setDashArray( stroke, row, settings ); } else if ( rule.getSymbolizers()[0] instanceof TextSymbolizer ) { TextSymbolizer ts = (TextSymbolizer) rule.getSymbolizers()[0]; Font font = ts.getFont(); if ( font != null ) { // font-color CssParameter fontColorParam = (CssParameter) font.getCssParameters().get( "font-color" ); if ( fontColorParam != null && fontColorParam.getValueAsPropertyName() != null ) { row.setValue( FONTCOLOR, fontColorParam.getValueAsPropertyName() ); } else { row.setValue( FONTCOLOR, font.getColor( null ) ); } // font-family CssParameter fontFamilyParam = (CssParameter) font.getCssParameters().get( "font-family" ); if ( fontFamilyParam != null && fontFamilyParam.getValueAsPropertyName() != null ) { row.setValue( FONTFAMILY, fontFamilyParam.getValueAsPropertyName() ); } else { row.setValue( FONTFAMILY, font.getFamily( null ) ); } // font-size CssParameter fontSizeParam = (CssParameter) font.getCssParameters().get( "font-size" ); if ( fontSizeParam != null ) { ParameterValueType pvt = (ParameterValueType) fontSizeParam.getValue(); PropertyName propertyNameFromPvt = SldCreatorUtils.getPropertyNameFromPvt( pvt ); if ( propertyNameFromPvt != null ) { row.setValue( FONTSIZE, propertyNameFromPvt ); } else { double defaultValue; try { defaultValue = font.getSize( null ); } catch ( Exception e ) { defaultValue = SldValues.getDefaultFontSize(); } row.setValue( FONTSIZE, UnitsValue.readFromParameterValueType( pvt, defaultValue ).getValue() ); } } // font-style CssParameter fontStyleParam = (CssParameter) font.getCssParameters().get( "font-style" ); if ( fontStyleParam != null && fontStyleParam.getValueAsPropertyName() != null ) { row.setValue( FONTSTYLE, fontStyleParam.getValueAsPropertyName() ); } else { row.setValue( FONTSTYLE, SldValues.getFontStyle( font.getStyle( null ) ) ); } // font-weight CssParameter fontWeightParam = (CssParameter) font.getCssParameters().get( "font-weight" ); if ( fontWeightParam != null && fontWeightParam.getValueAsPropertyName() != null ) { row.setValue( FONTWEIGHT, fontWeightParam.getValueAsPropertyName() ); } else { row.setValue( FONTWEIGHT, SldValues.getFontWeight( font.getWeight( null ) ) ); } } if ( ts.getLabelPlacement() != null && ts.getLabelPlacement().getPointPlacement() != null ) { PointPlacement pp = ts.getLabelPlacement().getPointPlacement(); Point2d anchorAsPoint = new Point2d( pp.getAnchorPoint( null ) ); ParameterValueType[] anchorPoint = pp.getAnchorPoint(); if ( anchorPoint != null && anchorPoint.length > 1 && anchorPoint[0] != null && anchorPoint[1] != null ) { row.setValue( ANCHORPOINT, new Pair<PropertyName, PropertyName>( anchorPoint[0].getValueAsPropertyName(), anchorPoint[1].getValueAsPropertyName() ) ); } else if ( anchorAsPoint != null ) { row.setValue( ANCHORPOINT, anchorAsPoint ); } Point2d displacementAsPoint = null; try { displacementAsPoint = new Point2d( pp.getDisplacement( null ) ); } catch ( Exception e ) { // displacement is not a simple point LOG.logWarning( "displacement is not a simple point", e ); } ParameterValueType[] displacement = pp.getDisplacement(); if ( displacementAsPoint != null ) { row.setValue( DISPLACEMENT, displacementAsPoint ); } else if ( displacement != null && displacement.length > 1 ) { PropertyName pn1 = SldCreatorUtils.getPropertyNameFromPvt( displacement[0] ); PropertyName pn2 = SldCreatorUtils.getPropertyNameFromPvt( displacement[1] ); if ( pn1 != null && pn2 != null ) { row.setValue( DISPLACEMENT, new Pair<PropertyName, PropertyName>( pn1, pn2 ) ); } } ParameterValueType rotation = pp.getRotation(); if ( rotation != null ) { PropertyName pointRotationPropName = pp.getRotationPropertyName(); if ( pointRotationPropName != null ) { row.setValue( ROTATION, pointRotationPropName ); } else { row.setValue( ROTATION, pp.getRotation( null ) ); } } } // halo Halo halo = ts.getHalo(); if ( halo != null ) { ParameterValueType haloRadius = halo.getRadius(); if ( haloRadius != null ) { PropertyName haloRadiusPropName = haloRadius.getValueAsPropertyName(); if ( haloRadiusPropName != null ) { row.setValue( HALORADIUS, haloRadiusPropName ); } else { row.setValue( HALORADIUS, halo.getRadius( null ) ); } } if ( halo.getFill() != null && halo.getFill().getCssParameters().get( "fill" ) != null ) { CssParameter haloFillColor = (CssParameter) halo.getFill().getCssParameters().get( "fill" ); if ( haloFillColor != null && haloFillColor.getValueAsPropertyName() != null ) { row.setValue( HALOCOLOR, haloFillColor.getValueAsPropertyName() ); } else { row.setValue( HALOCOLOR, halo.getFill().getFill( null ) ); } } } // fill Fill fill = ts.getFill(); if ( fill != null ) { CssParameter fillOpacityParam = (CssParameter) fill.getCssParameters().get( "fill-opacity" ); if ( fillOpacityParam != null && fillOpacityParam.getValueAsPropertyName() != null ) { row.setValue( FONTTRANSPARENCY, fillOpacityParam.getValueAsPropertyName() ); } else { row.setValue( FONTTRANSPARENCY, SldValues.getOpacityInPercent( fill.getOpacity( null ) ) ); } } } } private static GraphicSymbol getGraphicSymbol( GraphicOptions graphicOptions, ExternalGraphic eg ) { String name = eg.getTitle(); if ( name == null ) name = eg.getOnlineResource().getFile(); try { GraphicSymbol symbol = graphicOptions.getSymbolDefinition( name, eg.getOnlineResource() ); if ( symbol != null ) { return symbol; } graphicOptions.addSymbolDefinition( name, eg.getOnlineResource().toExternalForm() ); return graphicOptions.getSymboldefinition( name ); } catch ( MalformedURLException e ) { LOG.logInfo( "Could not add symbol to settings: " + e.getMessage() ); return new GraphicSymbol( name, eg.getOnlineResource() ); } } private static Object getStrokeWidth( Stroke stroke ) { CssParameter strokeWidthParam = (CssParameter) stroke.getCssParameters().get( "stroke-width" ); if ( strokeWidthParam != null && strokeWidthParam.getValue() != null ) { ParameterValueType pvt = (ParameterValueType) strokeWidthParam.getValue(); PropertyName propertyNameFromPvt = SldCreatorUtils.getPropertyNameFromPvt( pvt ); if ( propertyNameFromPvt != null ) { return propertyNameFromPvt; } else { double defaultValue; try { defaultValue = stroke.getWidth( null ); } catch ( Exception e ) { defaultValue = SldValues.getDefaultLineWidth(); } return UnitsValue.readFromParameterValueType( pvt, defaultValue ).getValue(); } } return SldValues.getDefaultLineWidth(); } private static double getSize( Graphic g ) { ParameterValueType pvt = g.getSize(); double defaultValue; try { defaultValue = g.getSize( null ); } catch ( Exception e ) { defaultValue = SldValues.getDefaultLineWidth(); } UnitsValue sizeFromParameterValueType = UnitsValue.readFromParameterValueType( pvt, defaultValue ); return sizeFromParameterValueType.getValue(); } private static void setDashArray( Stroke stroke, ClassificationTableRow<?> row, Settings settings ) throws FilterEvaluationException { float[] daOrig = stroke.getDashArray( null ); DashArray daToSelect = null; for ( DashArray da : SldValues.getDashArrays() ) { if ( Arrays.equals( daOrig, da.getDashArray() ) ) { daToSelect = da; } } Map<String, DashArray> dashArrays = settings.getGraphicOptions().getDashArrays(); for ( DashArray da : dashArrays.values() ) { if ( Arrays.equals( daOrig, da.getDashArray() ) ) { daToSelect = da; } } if ( daToSelect == null && daOrig != null ) { StringBuffer sb = new StringBuffer( daOrig.length * 3 ); for ( int i = 0; i < daOrig.length; i++ ) { sb.append( daOrig[i] ); if ( i != daOrig.length - 1 ) { sb.append( ", " ); } } DashArray dashArray = new DashArray( sb.toString(), daOrig ); settings.getGraphicOptions().addDashArray( sb.toString(), dashArray ); daToSelect = dashArray; } if ( daToSelect == null ) { daToSelect = SldValues.getDefaultLineStyle(); } row.setLineStyle( daToSelect ); } private static void setLineCap( Stroke stroke, ClassificationTableRow<?> row ) throws FilterEvaluationException { SldProperty lineCapToSelect = SldValues.getDefaultLineCapAsProperty(); for ( SldProperty lineCap : SldValues.getLineCaps() ) { if ( lineCap.getTypeCode() == stroke.getLineCap( null ) ) { lineCapToSelect = lineCap; } } row.setLineCap( lineCapToSelect ); } private static Intervallable<Double> createDoubleIntervallable( Operation op, String decimalPattern ) { Intervallable<Double> intervallable = null; Literal literal = null; if ( op instanceof PropertyIsCOMPOperation ) { PropertyIsCOMPOperation compOp = (PropertyIsCOMPOperation) op; Expression e1 = compOp.getFirstExpression(); Expression e2 = compOp.getSecondExpression(); if ( e2.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e2; } else if ( e1.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e1; } } if ( literal != null ) { intervallable = new DoubleIntervallable( Double.parseDouble( literal.getValue() ), decimalPattern ); } return intervallable; } private static Intervallable<Date> createDateIntervallable( Operation op, String datePattern ) throws IllegalClassificationException { Intervallable<Date> intervallable = null; Literal literal = null; if ( op instanceof PropertyIsCOMPOperation ) { PropertyIsCOMPOperation compOp = (PropertyIsCOMPOperation) op; Expression e1 = compOp.getFirstExpression(); Expression e2 = compOp.getSecondExpression(); if ( e2.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e2; } else if ( e1.getExpressionId() == ExpressionDefines.LITERAL ) { literal = (Literal) e1; } } if ( literal != null ) { try { SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-mm-dd" ); intervallable = new DateIntervallable( sdf.parse( literal.getValue() ), datePattern ); } catch ( ParseException e ) { throw new IllegalClassificationException( "Could not create a valid date of the given literal value " + literal.getValue(), e ); } } return intervallable; } private static ValueRange<Double> getDoubleValueRange( List<Intervallable<Double>> values, Literal literal1, Literal literal2, String decimalPattern ) throws IllegalClassificationException { ValueRange<Double> vr = null; String litValue1 = null; if ( literal1 != null ) { litValue1 = literal1.getValue(); } String litValue2 = null; if ( literal2 != null ) { litValue2 = literal2.getValue(); } try { Intervallable<Double> di1 = null; if ( litValue1 != null ) { di1 = new DoubleIntervallable( Double.parseDouble( litValue1 ), decimalPattern ); } Intervallable<Double> di2 = null; if ( litValue2 != null ) { di2 = new DoubleIntervallable( Double.parseDouble( litValue2 ), decimalPattern ); } vr = new ValueRange<Double>( di1, di2, 0 ); updateCountDouble( values, vr ); } catch ( Exception e ) { throw new IllegalClassificationException( "Could not create a valid value of the given literal values " + litValue1 + " and " + litValue2, e ); } return vr; } private static void updateCountDouble( List<Intervallable<Double>> values, ValueRange<Double> vr ) { int count = 0; for ( Intervallable<Double> value : values ) { if ( vr.isInThisValueRange( value ) ) { count++; } } vr.setCount( count ); } private static void updateCountDate( List<Intervallable<Date>> values, ValueRange<Date> vr ) { int count = 0; for ( Intervallable<Date> value : values ) { if ( vr.isInThisValueRange( value ) ) { count++; } } vr.setCount( count ); } private static ValueRange<String> getStringValueRange( List<Intervallable<String>> values, Literal literal ) throws IllegalClassificationException { String litValue = null; if ( literal != null ) { litValue = literal.getValue(); } int count = 0; for ( Intervallable<String> value : values ) { if ( value.getValue().equals( litValue ) ) { count++; } } return new ValueRange<String>( new StringIntervallable( litValue ), new StringIntervallable( litValue ), count ); } private static ValueRange<Date> getDateValueRange( List<Intervallable<Date>> values, Literal literal1, Literal literal2, String datePattern ) throws IllegalClassificationException { ValueRange<Date> vr = null; String litValue1 = null; if ( literal1 != null ) { litValue1 = literal1.getValue(); } String litValue2 = null; if ( literal2 != null ) { litValue2 = literal2.getValue(); } try { SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-mm-dd" ); Intervallable<Date> di1 = null; if ( litValue1 != null ) { di1 = new DateIntervallable( sdf.parse( litValue1 ), datePattern ); } Intervallable<Date> di2 = null; if ( litValue2 != null ) { di2 = new DateIntervallable( sdf.parse( litValue2 ), datePattern ); } vr = new ValueRange<Date>( di1, di2, 0 ); updateCountDate( values, vr ); } catch ( Exception e ) { throw new IllegalClassificationException( "Could not create a valid value of the given literal values " + litValue1 + " and " + litValue2, e ); } return vr; } /** * detect the property name of the rules (all of them must be same!) * * @param rules * @return * @throws IllegalClassificationException */ public static PropertyName detectPropertyName( List<Rule> rules ) throws IllegalClassificationException { PropertyName propName = null; for ( Rule rule : rules ) { if ( rule.getFilter() instanceof ComplexFilter ) { Operation filterOperation = cleanFilter( (ComplexFilter) rule.getFilter() ); switch ( filterOperation.getOperatorId() ) { case OperationDefines.AND: if ( filterOperation instanceof LogicalOperation ) { LogicalOperation lo = (LogicalOperation) filterOperation; List<Operation> arguments = lo.getArguments(); if ( arguments.size() > 2 ) { // first argument switch ( arguments.get( 0 ).getOperatorId() ) { case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: case OperationDefines.PROPERTYISLESSTHAN: PropertyName pn = evaluatePropertyName( arguments.get( 0 ), propName ); if ( pn == null ) { throw new IllegalClassificationException( IllegalPNMsg ); } else { propName = pn; } break; default: throw new IllegalClassificationException( IllegalPTMsg ); } // second argument switch ( arguments.get( 1 ).getOperatorId() ) { case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: case OperationDefines.PROPERTYISLESSTHAN: PropertyName pn = evaluatePropertyName( arguments.get( 1 ), propName ); if ( pn == null ) { throw new IllegalClassificationException( IllegalPNMsg ); } else { propName = pn; } break; default: throw new IllegalClassificationException( IllegalPTMsg ); } } } break; case OperationDefines.PROPERTYISEQUALTO: case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: case OperationDefines.PROPERTYISLESSTHAN: PropertyName pn = evaluatePropertyName( filterOperation, propName ); if ( pn == null ) { throw new IllegalClassificationException( IllegalPNMsg ); } else { propName = pn; } break; default: throw new IllegalClassificationException( IllegalPTMsg ); } } } return propName; } /** * @param op * the operation to evaluate * @param propName * the given propertyName * @return the unchanged propertyName or null, if the operation contains property names which are not equal to the * given propertyName */ private static PropertyName evaluatePropertyName( Operation op, PropertyName propName ) { if ( op instanceof PropertyIsCOMPOperation ) { PropertyIsCOMPOperation compOp = (PropertyIsCOMPOperation) op; if ( propName == null && compOp.getFirstExpression().getExpressionId() == ExpressionDefines.PROPERTYNAME ) { return (PropertyName) compOp.getFirstExpression(); } else if ( propName != null && compOp.getFirstExpression().getExpressionId() == ExpressionDefines.PROPERTYNAME && propName.equals( (PropertyName) compOp.getFirstExpression() ) ) { return propName; } } return null; } /** * detects the property name of the rules (they must be all the same)) * * @param propertyName * @param ft * @return * @throws IllegalClassificationException */ public static int detectPropertyType( PropertyName propertyName, FeatureType ft ) throws IllegalClassificationException { PropertyType[] propertyTypes = ft.getProperties(); for ( PropertyType pt : propertyTypes ) { PropertyPath typeAsPath = PropertyPathFactory.createPropertyPath( pt.getName() ); if ( equalsPropertyNameWithotNS( propertyName.getValue(), typeAsPath ) ) { return pt.getType(); } } throw new IllegalClassificationException( "Can not detect PropertyType of the propertyName " + propertyName.toString() + " in the featureTyp " + ft.getName() ); } /** * Compares two property pathes, does not consider the namespace of the qualified name of each property path step * * @param pp1 * @param pp2 * @return */ public static boolean equalsPropertyNameWithotNS( PropertyPath pp1, PropertyPath pp2 ) { if ( pp1.getSteps() != pp2.getSteps() ) { return false; } for ( int i = 0; i < pp1.getSteps(); i++ ) { if ( pp1.getStep( i ) == null || pp2.getStep( i ) == null ) { return false; } if ( pp1.getStep( i ).getPropertyName() == null || pp2.getStep( i ).getPropertyName() == null ) { return false; } if ( pp1.getStep( i ).getPropertyName().getLocalName() == null || pp2.getStep( i ).getPropertyName().getLocalName() == null ) { return false; } if ( !pp1.getStep( i ).getPropertyName().getLocalName().equals( pp2.getStep( i ).getPropertyName().getLocalName() ) ) { return false; } } return true; } // removes PropertyIsInstanceOf filter, defined if geomType is specified private static Operation cleanFilter( ComplexFilter filter ) { if ( filter.getOperation().getOperatorId() == OperationDefines.AND ) { LogicalOperation lo = (LogicalOperation) filter.getOperation(); Operation op1 = lo.getArguments().get( 0 ); Operation op2 = lo.getArguments().get( 1 ); if ( op2.getOperatorId() == OperationDefines.PROPERTYISINSTANCEOF ) { return op1; } else if ( op1.getOperatorId() == OperationDefines.PROPERTYISINSTANCEOF ) { return op2; } } return filter.getOperation(); } }