package org.mapfish.print.map.style.json;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
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;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.filter.text.ecql.ECQL;
import org.geotools.referencing.CRS;
import org.geotools.styling.AbstractStyleVisitor;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.LineSymbolizer;
import org.geotools.styling.Mark;
import org.geotools.styling.PointSymbolizer;
import org.geotools.styling.PolygonSymbolizer;
import org.geotools.styling.Rule;
import org.geotools.styling.SLDTransformer;
import org.geotools.styling.Style;
import org.geotools.styling.Symbolizer;
import org.geotools.styling.TextSymbolizer;
import org.junit.Test;
import org.mapfish.print.Constants;
import org.mapfish.print.TestHttpClientFactory;
import org.mapfish.print.attribute.map.CenterScaleMapBounds;
import org.mapfish.print.attribute.map.MapfishMapContext;
import org.mapfish.print.config.Configuration;
import org.mapfish.print.wrapper.json.PJsonObject;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.And;
import org.opengis.filter.Filter;
import org.opengis.filter.PropertyIsEqualTo;
import org.opengis.filter.PropertyIsLessThan;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.PropertyName;
import java.awt.Dimension;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mapfish.print.AbstractMapfishSpringTest.parseJSONObjectFromFile;
import static org.mapfish.print.map.style.json.JsonStyleParserHelperTest.valueOf;
public class MapfishStyleParserPluginTest {
static final String REQUEST_DATA_STYLE_JSON_V1_STYLE_JSON = "requestData-style-json-v1-style.json";
private static final double DELTA = 0.000001;
final MapfishStyleParserPlugin mapfishStyleParserPlugin = new MapfishStyleParserPlugin();
final TestHttpClientFactory httpClient = new TestHttpClientFactory();
final SLDTransformer transformer = new SLDTransformer();
MapfishStyleParserPlugin parser = new MapfishStyleParserPlugin();
@Test
public void testVersion1StyleParser() throws Throwable {
PJsonObject layerJson = parseJSONObjectFromFile(MapfishStyleParserPluginTest.class,
"bug_cant_transform_to_xml.json");
Optional<Style> style = parser.parseStyle(
null, new TestHttpClientFactory(), layerJson.getString("style"));
assertTrue(style.isPresent());
transformer.transform(style.get()); // assert it can be converted to SLD
}
@Test
public void testVersion1() throws Throwable {
PJsonObject layerJson = parseJSONObjectFromFile(MapfishStyleParserPluginTest.class,
REQUEST_DATA_STYLE_JSON_V1_STYLE_JSON);
Optional<Style> style = parser.parseStyle(
null, new TestHttpClientFactory(), layerJson.getString("style"));
assertTrue(style.isPresent());
transformer.transform(style.get()); // assert it can be converted to SLD
final List<Rule> rules = Lists.newArrayList();
style.get().accept(new AbstractStyleVisitor() {
@Override
public void visit(Rule rule) {
rules.add(rule);
}
});
assertEquals(4, rules.size());
PointSymbolizer point = null;
LineSymbolizer line = null;
PolygonSymbolizer polygon = null;
TextSymbolizer text = null;
for (Rule rule : rules) {
Filter geomSelectFunction = null;
if (!(rule.getSymbolizers()[0] instanceof TextSymbolizer)) {
assertTrue(rule.getFilter() instanceof And);
And andFilter = (And) rule.getFilter();
assertEquals(2, andFilter.getChildren().size());
PropertyIsEqualTo filter = (PropertyIsEqualTo) andFilter.getChildren().get(0);
PropertyName propertyName = (PropertyName) filter.getExpression1();
assertEquals("_gx_style", propertyName.getPropertyName());
Literal valueExpression = (Literal) filter.getExpression2();
assertEquals("1", valueExpression.getValue());
geomSelectFunction = andFilter.getChildren().get(1);
}
final List<Symbolizer> symbolizers = rule.symbolizers();
assertEquals(1, symbolizers.size());
for (Symbolizer symbolizer : symbolizers) {
if (symbolizer instanceof PointSymbolizer) {
assertEquals("1_Point", rule.getName());
assertNull(point);
point = (PointSymbolizer) symbolizer;
assertFilter(geomSelectFunction, Point.class, MultiPoint.class, GeometryCollection.class);
} else if (symbolizer instanceof LineSymbolizer) {
assertEquals("1_LineString", rule.getName());
assertNull(line);
line = (LineSymbolizer) symbolizer;
assertFilter(geomSelectFunction, LineString.class, LinearRing.class, MultiLineString.class, GeometryCollection.class);
} else if (symbolizer instanceof PolygonSymbolizer) {
assertEquals("1_Polygon", rule.getName());
assertNull(polygon);
polygon = (PolygonSymbolizer) symbolizer;
assertFilter(geomSelectFunction, Polygon.class, MultiPolygon.class, GeometryCollection.class);
} else if (symbolizer instanceof TextSymbolizer) {
assertEquals("1_Text", rule.getName());
assertNull(text);
text = (TextSymbolizer) symbolizer;
} else {
fail(symbolizer + " was unexpected");
}
}
}
assertNotNull(point);
assertNotNull(line);
assertNotNull(polygon);
assertNotNull(text);
}
private void assertFilter(Filter geomSelectFunction, Class<? extends Geometry>... geomClasses) {
List<Class<? extends Geometry>> allowed = Arrays.asList(geomClasses);
final ArrayList<Class<? extends Geometry>> allGeomTypes = Lists.newArrayList(Point.class, MultiPoint.class, LineString.class,
LinearRing.class, MultiLineString.class,
Polygon.class, MultiPolygon.class, GeometryCollection.class);
for (Class<? extends Geometry> geomType : allGeomTypes) {
final SimpleFeature feature = createFeature(geomType, MapfishJsonStyleVersion1.DEFAULT_GEOM_ATT_NAME);
assertEquals(allowed.contains(geomType), geomSelectFunction.evaluate(feature));
}
}
private SimpleFeature createFeature(final Class<? extends Geometry> geomClass, String geomAttName) {
final SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.add(geomAttName, geomClass);
builder.setName(geomClass.getName() + "Feature");
GeometryFactory factory = new GeometryFactory();
Geometry geom = createGeometry(geomClass, factory);
return SimpleFeatureBuilder.build(builder.buildFeatureType(), new Object[]{geom}, "testFeature");
}
private <T extends Geometry> T createGeometry(Class<T> geomClass, GeometryFactory factory) {
Geometry geom;
if (geomClass.equals(Point.class)) {
geom = factory.createPoint(new Coordinate(2, 3));
} else if (geomClass.equals(MultiPoint.class)) {
geom = factory.createMultiPoint(new Point[]{createGeometry(Point.class, factory)});
} else if (geomClass.equals(LineString.class)) {
geom = factory.createLineString(new Coordinate[]{new Coordinate(2, 3), new Coordinate(1, 3)});
} else if (geomClass.equals(LinearRing.class)) {
geom = factory.createLinearRing(new Coordinate[]{new Coordinate(2, 3), new Coordinate(2, 2), new Coordinate(1, 2), new Coordinate(2, 3)});
} else if (geomClass.equals(MultiLineString.class)) {
geom = factory.createMultiLineString(new LineString[]{createGeometry(LineString.class, factory)});
} else if (geomClass.equals(Polygon.class)) {
geom = factory.createPolygon(createGeometry(LinearRing.class, factory));
} else if (geomClass.equals(MultiPolygon.class)) {
geom = factory.createMultiPolygon(new Polygon[]{createGeometry(Polygon.class, factory)});
} else if (geomClass.equals(GeometryCollection.class)) {
geom = factory.createGeometryCollection(new Geometry[]{
createGeometry(Point.class, factory),
createGeometry(LineString.class, factory),
createGeometry(MultiPolygon.class, factory)
});
} else {
throw new IllegalArgumentException(geomClass + " not known");
}
return geomClass.cast(geom);
}
@Test
public void testV2ParseSymbolizersWithDefaultsAndValues() throws Throwable {
final Style style = parseStyle("v2-style-symbolizers-default-values.json");
final List<FeatureTypeStyle> featureTypeStyles = style.featureTypeStyles();
assertEquals(1, featureTypeStyles.size());
final List<Rule> rules = featureTypeStyles.get(0).rules();
assertEquals(1, rules.size());
final Rule rule = rules.get(0);
assertEquals(1000000, rule.getMaxScaleDenominator(), DELTA);
assertEquals(100, rule.getMinScaleDenominator(), DELTA);
final Filter filter = rule.getFilter();
assertTrue(filter instanceof PropertyIsLessThan);
assertEquals("att < 3", ECQL.toCQL(filter));
assertEquals(2, rule.symbolizers().size());
PointSymbolizer symbolizer = (PointSymbolizer) rule.symbolizers().get(0);
assertEquals(1, symbolizer.getGraphic().graphicalSymbols().size());
Mark mark = (Mark) symbolizer.getGraphic().graphicalSymbols().get(0);
assertEquals("circle", valueOf(mark.getWellKnownName()));
assertEquals(30, (Double) valueOf(symbolizer.getGraphic().getRotation()), DELTA);
assertEquals(0.4, (Double) valueOf(symbolizer.getGraphic().getOpacity()), DELTA);
assertEquals("#00FF00", valueOf(mark.getStroke().getColor()));
LineSymbolizer lineSymbolizer = (LineSymbolizer) rule.symbolizers().get(1);
assertNull(lineSymbolizer.getStroke().getDashArray());
}
@Test
public void testV2ParseDefaultSymbolizers() throws Throwable {
final Style style = parseStyle("v2-style-default-symbolizers.json");
final List<FeatureTypeStyle> featureTypeStyles = style.featureTypeStyles();
assertEquals(1, featureTypeStyles.size());
final List<Rule> rules = featureTypeStyles.get(0).rules();
assertEquals(1, rules.size());
final Rule rule = rules.get(0);
assertEquals(Filter.INCLUDE, rule.getFilter());
assertEquals(4, rule.symbolizers().size());
PointSymbolizer pointSymbolizer = (PointSymbolizer) rule.symbolizers().get(0);
LineSymbolizer lineSymbolizer = (LineSymbolizer) rule.symbolizers().get(1);
PolygonSymbolizer polygonSymbolizer = (PolygonSymbolizer) rule.symbolizers().get(2);
TextSymbolizer textSymbolizer = (TextSymbolizer) rule.symbolizers().get(3);
assertNotNull(pointSymbolizer);
assertNotNull(lineSymbolizer);
assertNotNull(polygonSymbolizer);
assertNotNull(textSymbolizer);
}
private Style parseStyle(String styleJsonFileName) throws Throwable {
Configuration config = new Configuration();
config.setConfigurationFile(getFile(styleJsonFileName));
final String styleJson = getSpec(styleJsonFileName);
final CenterScaleMapBounds bounds = new CenterScaleMapBounds(
CRS.decode("CRS:84"), 0, 0,300000);
MapfishMapContext context = new MapfishMapContext(
bounds, new Dimension(500, 500), 0, 72,
true, true);
final Optional<Style> styleOptional = mapfishStyleParserPlugin.parseStyle(
config, httpClient, styleJson);
assertTrue(styleOptional.isPresent());
return styleOptional.get();
}
private String getSpec(String name) throws IOException, URISyntaxException {
return Files.toString(getFile(name), Constants.DEFAULT_CHARSET);
}
private File getFile(String name) throws URISyntaxException {
return new File(MapfishJsonStyleVersion2Test.class.getResource(name).toURI());
}
}