org.mapfish.print.map.style.json.MapfishJsonStyleVersion2 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of print-lib Show documentation
Show all versions of print-lib Show documentation
Library for generating PDFs and images from online webmapping services
package org.mapfish.print.map.style.json;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL;
import org.geotools.styling.Rule;
import org.geotools.styling.Style;
import org.geotools.styling.StyleBuilder;
import org.geotools.styling.Symbolizer;
import org.json.JSONException;
import org.json.JSONObject;
import org.mapfish.print.ExceptionUtils;
import org.mapfish.print.config.Configuration;
import org.mapfish.print.wrapper.json.PJsonArray;
import org.mapfish.print.wrapper.json.PJsonObject;
import org.opengis.filter.Filter;
import org.springframework.http.client.ClientHttpRequestFactory;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Support a more flexible json styling than that which is supported by version 1.
*/
public final class MapfishJsonStyleVersion2 {
static final String JSON_SYMB = "symbolizers";
private static final String JSON_TYPE = "type";
private static final Pattern VALUE_EXPR_PATTERN = Pattern.compile("\\$\\{([\\w\\d_-]+)\\}");
private static final String JSON_MIN_SCALE = "minScale";
private static final String JSON_MAX_SCALE = "maxScale";
private static final String JSON_FILTER_INCLUDE = "*";
private final PJsonObject json;
private final StyleBuilder styleBuilder;
private final JsonStyleParserHelper parserHelper;
MapfishJsonStyleVersion2(
@Nonnull final PJsonObject json,
@Nonnull final StyleBuilder styleBuilder,
@Nullable final Configuration configuration,
@Nonnull final ClientHttpRequestFactory requestFactory) {
this.json = json;
this.styleBuilder = styleBuilder;
this.parserHelper = new JsonStyleParserHelper(configuration, requestFactory, styleBuilder,
false);
}
@VisibleForTesting
static Map resolveAllValues(final Map values) {
Map toResolve = Maps.newHashMap(values);
Map resolved = Maps.newHashMapWithExpectedSize(values.size());
while (!toResolve.isEmpty()) {
Iterator> entries = toResolve.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry next = entries.next();
String value = next.getValue();
final String resolve = resolve(values, value);
if (resolve == null) {
resolved.put(next.getKey(), value);
entries.remove();
} else {
next.setValue(resolve);
}
}
}
return resolved;
}
private static String resolve(final Map values, final String value) {
int lastEnd = 0;
final Matcher matcher = VALUE_EXPR_PATTERN.matcher(value);
boolean changed = false;
StringBuilder updatedValue = new StringBuilder();
while (matcher.find()) {
final String valName = matcher.group(1);
String replacement;
if (values.containsKey(valName)) {
changed = true;
replacement = values.get(valName);
} else {
replacement = matcher.group(0);
}
updatedValue.append(value, lastEnd, matcher.start());
updatedValue.append(replacement);
lastEnd = matcher.end();
}
if (changed) {
updatedValue.append(value.substring(lastEnd));
return updatedValue.toString();
} else {
return null;
}
}
Style parseStyle() {
List rules = Lists.newArrayList();
final Iterator keys = this.json.keys();
while (keys.hasNext()) {
final String next = keys.next().trim();
if (isRule(next)) {
rules.add(createRule(next));
}
}
if (rules.isEmpty()) {
throw new IllegalArgumentException(String.format(
"No rules found in style. Rules are json objects that have the key %s or have the " +
"form: [ecql]", JSON_FILTER_INCLUDE));
}
return this.parserHelper.createStyle(rules);
}
private Rule createRule(final String jsonKey) {
final PJsonObject ruleJson = this.json.getJSONObject(jsonKey);
Filter filter = Filter.INCLUDE;
if (!jsonKey.equals(JSON_FILTER_INCLUDE)) {
try {
filter = ECQL.toFilter(jsonKey, this.styleBuilder.getFilterFactory());
} catch (CQLException e) {
throw new RuntimeException("Error compiling rule filter: " + jsonKey, e);
}
}
final PJsonArray symbolizerJsonArray = ruleJson.getJSONArray(JSON_SYMB);
final Symbolizer[] symbolizers = new Symbolizer[symbolizerJsonArray.size()];
for (int i = 0; i < symbolizerJsonArray.size(); i++) {
final PJsonObject symbolizerJson = symbolizerJsonArray.getJSONObject(i);
updateSymbolizerProperties(ruleJson, symbolizerJson);
SymbolizerType type = SymbolizerType.valueOf(symbolizerJson.getString(JSON_TYPE).toUpperCase());
symbolizers[i] = type.parseJson(this.parserHelper, symbolizerJson);
if (symbolizers[i] == null) {
throw new RuntimeException(
"Error creating symbolizer " + symbolizerJson.getString(JSON_TYPE) + " in rule " +
jsonKey);
}
}
Map ruleValues = buildValuesMap(ruleJson, new PJsonObject(new JSONObject(), "empty"));
double minScale = getScaleDenominator(ruleValues, JSON_MIN_SCALE, Double.MIN_VALUE);
double maxScale = getScaleDenominator(ruleValues, JSON_MAX_SCALE, Double.MAX_VALUE);
final Rule rule = this.styleBuilder.createRule(symbolizers, minScale, maxScale);
rule.setFilter(filter);
return rule;
}
private void updateSymbolizerProperties(final PJsonObject ruleJson, final PJsonObject symbolizerJson) {
Map values = buildValuesMap(ruleJson, symbolizerJson);
for (Map.Entry entry: values.entrySet()) {
try {
symbolizerJson.getInternalObj().put(entry.getKey(), entry.getValue());
} catch (JSONException e) {
throw ExceptionUtils.getRuntimeException(e);
}
}
}
private double getScaleDenominator(
final Map ruleValues, final String keyName, final double defaultValue) {
String scaleString = ruleValues.get(keyName);
if (scaleString != null) {
return Double.parseDouble(scaleString);
}
return defaultValue;
}
private Map buildValuesMap(
final PJsonObject ruleJson,
final PJsonObject symbolizerJson) {
Map values = Maps.newHashMap();
Iterator keys = this.json.keys();
while (keys.hasNext()) {
String key = keys.next().trim();
if (!isRule(key)) {
values.put(key, this.json.getString(key));
}
}
keys = ruleJson.keys();
while (keys.hasNext()) {
String key = keys.next().trim();
if (!key.equals(JSON_SYMB)) {
values.put(key, ruleJson.getString(key));
}
}
keys = symbolizerJson.keys();
while (keys.hasNext()) {
String key = keys.next().trim();
if (!key.equals(JSON_SYMB)) {
values.put(key, symbolizerJson.getString(key));
}
}
return resolveAllValues(values);
}
private boolean isRule(final String jsonKey) {
return this.json.optJSONObject(jsonKey) != null;
}
enum SymbolizerType {
POINT {
@Override
protected Symbolizer parseJson(
final JsonStyleParserHelper parser,
final PJsonObject symbolizerJson) {
return parser.createPointSymbolizer(symbolizerJson);
}
}, LINE {
@Override
protected Symbolizer parseJson(
final JsonStyleParserHelper parser,
final PJsonObject symbolizerJson) {
return parser.createLineSymbolizer(symbolizerJson);
}
}, POLYGON {
@Override
protected Symbolizer parseJson(
final JsonStyleParserHelper parser,
final PJsonObject symbolizerJson) {
return parser.createPolygonSymbolizer(symbolizerJson);
}
}, TEXT {
@Override
protected Symbolizer parseJson(
final JsonStyleParserHelper parser,
final PJsonObject symbolizerJson) {
return parser.createTextSymbolizer(symbolizerJson);
}
};
protected abstract Symbolizer parseJson(
JsonStyleParserHelper parser,
PJsonObject symbolizerJson);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy