Skip to content

Commit

Permalink
[GEOS-9024] Expose equal area classification method in SLDService
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Nov 20, 2018
1 parent 4a461ba commit 08af18a
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 195 deletions.
4 changes: 2 additions & 2 deletions doc/en/user/source/extensions/sldservice/index.rst
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ Classify Vector Data


The service can be used to create a set of SLD rules for the given vector The service can be used to create a set of SLD rules for the given vector
layer, specyfing the **attribute** used for classification, the **classification layer, specyfing the **attribute** used for classification, the **classification
type** (equalInterval, uniqueInterval, quantile, jenks) and one of the type** (equalInterval, uniqueInterval, quantile, jenks, equalArea) and one of the
**predefined color ranges** (red, blue, gray, jet, random, custom), together **predefined color ranges** (red, blue, gray, jet, random, custom), together
with some other optional parameters. with some other optional parameters.


Expand All @@ -136,7 +136,7 @@ The parameters usable to customize the ColorMap are:
- -
* - method * - method
- Classification method - Classification method
- equalInterval, uniqueInterval, quantile, jenks - equalInterval, uniqueInterval, quantile, jenks, equalArea
- equalInterval - equalInterval
* - open * - open
- open or closed ranges - open or closed ranges
Expand Down
8 changes: 8 additions & 0 deletions src/extension/sldService/pom.xml
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@
<artifactId>javax.servlet-api</artifactId> <artifactId>javax.servlet-api</artifactId>
</dependency> </dependency>


<!-- This library is used by json-lib to write XML -->
<dependency>
<groupId>xom</groupId>
<artifactId>xom</artifactId>
<version>1.1</version>
</dependency>


<!-- test dependencies --> <!-- test dependencies -->
<dependency> <dependency>
<groupId>org.geoserver</groupId> <groupId>org.geoserver</groupId>
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import java.awt.Color; import java.awt.*;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
Expand Down Expand Up @@ -95,12 +95,6 @@ public ClassifierController(@Qualifier("catalog") Catalog catalog) {
super(catalog); super(catalog);
} }


/*
* (non-Javadoc)
*
* @see org.geoserver.rest.RestBaseController#configurePersister(org.geoserver.config.util.XStreamPersister,
* org.geoserver.rest.converters.XStreamMessageConverter)
*/
@Override @Override
public void configurePersister(XStreamPersister persister, XStreamMessageConverter converter) { public void configurePersister(XStreamPersister persister, XStreamMessageConverter converter) {
XStream xstream = persister.getXStream(); XStream xstream = persister.getXStream();
Expand All @@ -109,26 +103,6 @@ public void configurePersister(XStreamPersister persister, XStreamMessageConvert
xstream.allowTypes(new Class[] {RulesList.class, JSONObject.class}); xstream.allowTypes(new Class[] {RulesList.class, JSONObject.class});
} }


/**
* final String layerName = (String) attributes.get("layer"); final String property =
* form.getFirstValue("attribute"); final String method = form.getFirstValue("method",
* "equalInterval"); final String intervals = form.getFirstValue("intervals", "2"); final String
* intervalsForUnique = form.getFirstValue("intervals", "-1"); final String open =
* form.getFirstValue("open", "false"); final String colorRamp = form.getFirstValue("ramp",
* "red"); final boolean reverse = Boolean.parseBoolean(form.getFirstValue("reverse")); final
* boolean normalize = Boolean.parseBoolean(form.getFirstValue("normalize"));
*
* @param layerName
* @param property
* @param method
* @param intervals
* @param intervalsForUnique
* @param open
* @param colorRamp
* @param reverse
* @param normalize
* @return
*/
@GetMapping( @GetMapping(
path = "/{layerName}/classify", path = "/{layerName}/classify",
produces = { produces = {
Expand Down Expand Up @@ -469,6 +443,19 @@ private List<Rule> generateClassifiedSLD(
Integer.parseInt(intervals), Integer.parseInt(intervals),
Boolean.parseBoolean(open), Boolean.parseBoolean(open),
normalize); normalize);
} else if ("equalArea".equals(method)) {
rules =
builder.equalAreaClassification(
ftCollection,
property,
propertyType,
Integer.parseInt(intervals),
Boolean.parseBoolean(open),
normalize);
} else {
throw new RestException(
"Unknown classification method " + method,
HttpStatus.BAD_REQUEST);
} }
} else { } else {
RangedClassifier groups = RangedClassifier groups =
Expand Down Expand Up @@ -719,8 +706,8 @@ private void writeKey(HierarchicalStreamWriter writer, final JSONObject child, S
} }


/** /**
* @see com.thoughtworks.xstream.converters.Converter#unmarshal(com.thoughtworks.xstream.io. * @see
* HierarchicalStreamReader,com.thoughtworks.xstream.converters.UnmarshallingContext) * com.thoughtworks.xstream.converters.Converter#unmarshal(com.thoughtworks.xstream.io.HierarchicalStreamReader,com.thoughtworks.xstream.converters.UnmarshallingContext)
*/ */
public Object unmarshal(HierarchicalStreamReader arg0, UnmarshallingContext arg1) { public Object unmarshal(HierarchicalStreamReader arg0, UnmarshallingContext arg1) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.geotools.styling.StyleFactory; import org.geotools.styling.StyleFactory;
import org.geotools.styling.Symbolizer; import org.geotools.styling.Symbolizer;
import org.geotools.util.factory.GeoTools; import org.geotools.util.factory.GeoTools;
import org.opengis.feature.type.FeatureType;
import org.opengis.filter.Filter; import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2; import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.Expression; import org.opengis.filter.expression.Expression;
Expand Down Expand Up @@ -79,27 +78,18 @@ public void setIncludeStrokeForPoints(boolean includeStrokeForPoints) {
this.includeStrokeForPoints = includeStrokeForPoints; this.includeStrokeForPoints = includeStrokeForPoints;
} }


/** private List<Rule> getRules(
* Generate a List of rules using quantile classification Sets up only filter not symbolizer
*
* @param features
* @param property
* @param classNumber
*/
public List<Rule> quantileClassification(
FeatureCollection features, FeatureCollection features,
String property, String property,
Class<?> propertyType, Class<?> propertyType,
int classNumber, int classNumber,
boolean open, boolean open,
boolean normalize) { boolean normalize,

String functionName) {
FeatureType fType;
Classifier groups = null;
try { try {
final Function classify = final Function classify =
ff.function("Quantile", ff.property(property), ff.literal(classNumber)); ff.function(functionName, ff.property(property), ff.literal(classNumber));
groups = (Classifier) classify.evaluate(features); Classifier groups = (Classifier) classify.evaluate(features);
if (groups instanceof RangedClassifier) if (groups instanceof RangedClassifier)
if (open) if (open)
return openRangedRules( return openRangedRules(
Expand All @@ -114,12 +104,26 @@ else if (groups instanceof ExplicitClassifier)
if (LOGGER.isLoggable(Level.INFO)) if (LOGGER.isLoggable(Level.INFO))
LOGGER.log( LOGGER.log(
Level.INFO, Level.INFO,
"Failed to build Quantile Classification" + e.getLocalizedMessage(), "Failed to build "
+ functionName
+ " Classification"
+ e.getLocalizedMessage(),
e); e);
} }
return null; return null;
} }


/** Generate a List of rules using quantile classification Sets up only filter not symbolizer */
public List<Rule> quantileClassification(
FeatureCollection features,
String property,
Class<?> propertyType,
int classNumber,
boolean open,
boolean normalize) {
return getRules(features, property, propertyType, classNumber, open, normalize, "Quantile");
}

/** /**
* Generate a List of rules using equal interval classification Sets up only filter not * Generate a List of rules using equal interval classification Sets up only filter not
* symbolizer * symbolizer
Expand All @@ -132,34 +136,11 @@ public List<Rule> equalIntervalClassification(
FeatureCollection features, FeatureCollection features,
String property, String property,
Class<?> propertyType, Class<?> propertyType,
int classNumber, int intervals,
boolean open, boolean open,
boolean normalize) { boolean normalize) {
Classifier groups = null; return getRules(
try { features, property, propertyType, intervals, open, normalize, "EqualInterval");

final Function classify =
ff.function("EqualInterval", ff.property(property), ff.literal(classNumber));
groups = (Classifier) classify.evaluate(features);
// System.out.println(groups.getSize());
if (groups instanceof RangedClassifier)
if (open)
return openRangedRules(
(RangedClassifier) groups, property, propertyType, normalize);
else
return closedRangedRules(
(RangedClassifier) groups, property, propertyType, normalize);
else if (groups instanceof ExplicitClassifier)
return this.explicitRules((ExplicitClassifier) groups, property, propertyType);

} catch (Exception e) {
if (LOGGER.isLoggable(Level.INFO))
LOGGER.log(
Level.INFO,
"Failed to build EqualInterval Classification" + e.getLocalizedMessage(),
e);
}
return null;
} }


/** /**
Expand All @@ -176,35 +157,55 @@ public List<Rule> uniqueIntervalClassification(
int intervals, int intervals,
boolean normalize) boolean normalize)
throws IllegalArgumentException { throws IllegalArgumentException {
Classifier groups = null; List<Rule> rules =
int classNumber = features.size(); getRules(
try { features,
final Function classify = property,
ff.function("UniqueInterval", ff.property(property), ff.literal(classNumber)); propertyType,
groups = (Classifier) classify.evaluate(features); features.size(),
false,
normalize,
"UniqueInterval");
if (intervals > 0 && rules.size() > intervals) {
throw new IllegalArgumentException("Intervals: " + rules.size());
}
return rules;
}


if (groups instanceof RangedClassifier) /**
return this.closedRangedRules( * Generate a List of rules using Jenks Natural Breaks classification Sets up only filter not
(RangedClassifier) groups, property, propertyType, normalize); * symbolizer
else if (groups instanceof ExplicitClassifier) { *
ExplicitClassifier explicitGroups = (ExplicitClassifier) groups; * @param features
if (intervals > 0 && explicitGroups.getSize() > intervals) { * @param property
throw new IllegalArgumentException("Intervals: " + explicitGroups.getSize()); * @param classNumber
} */
return this.explicitRules(explicitGroups, property, propertyType); public List<Rule> jenksClassification(
} FeatureCollection features,
String property,
Class<?> propertyType,
int classNumber,
boolean open,
boolean normalize) {
return getRules(features, property, propertyType, classNumber, open, normalize, "Jenks");
}


} catch (Exception e) { /**
if (LOGGER.isLoggable(Level.INFO)) * Generate a List of rules using Equal Area classification. Sets up only filter not symbolizer.
LOGGER.log( *
Level.INFO, * @param features
"Failed to build UniqueInterval Classification" + e.getLocalizedMessage(), * @param property
e); * @param classNumber
if (e instanceof IllegalArgumentException) { */
throw (IllegalArgumentException) e; public List<Rule> equalAreaClassification(
} FeatureCollection features,
} String property,
return null; Class<?> propertyType,
int classNumber,
boolean open,
boolean normalize) {
return getRules(
features, property, propertyType, classNumber, open, normalize, "EqualArea");
} }


/** /**
Expand Down Expand Up @@ -462,7 +463,7 @@ public List<Rule> closedRangedRules(
: ff.greater(att, ff.literal(groups.getMin(i))), : ff.greater(att, ff.literal(groups.getMin(i))),
ff.lessOrEqual(att, ff.literal(groups.getMax(i)))); ff.lessOrEqual(att, ff.literal(groups.getMax(i))));
r.setTitle( r.setTitle(
" > " " >= "
+ ff.literal(groups.getMin(i)) + ff.literal(groups.getMin(i))
+ " AND <= " + " AND <= "
+ ff.literal(groups.getMax(i))); + ff.literal(groups.getMax(i)));
Expand Down Expand Up @@ -525,45 +526,4 @@ public List<Rule> explicitRules(
} }
return null; return null;
} }

/**
* Generate a List of rules using Jenks Natural Breaks classification Sets up only filter not
* symbolizer
*
* @param features
* @param property
* @param classNumber
*/
public List<Rule> jenksClassification(
FeatureCollection features,
String property,
Class<?> propertyType,
int classNumber,
boolean open,
boolean normalize) {
Classifier groups = null;
try {
final Function classify =
ff.function("Jenks", ff.property(property), ff.literal(classNumber));
groups = (Classifier) classify.evaluate(features);
// System.out.println(groups.getSize());
if (groups instanceof RangedClassifier)
if (open)
return openRangedRules(
(RangedClassifier) groups, property, propertyType, normalize);
else
return closedRangedRules(
(RangedClassifier) groups, property, propertyType, normalize);
else if (groups instanceof ExplicitClassifier)
return this.explicitRules((ExplicitClassifier) groups, property, propertyType);

} catch (Exception e) {
if (LOGGER.isLoggable(Level.INFO))
LOGGER.log(
Level.INFO,
"Failed to build Jenks classification" + e.getLocalizedMessage(),
e);
}
return null;
}
} }

0 comments on commit 08af18a

Please sign in to comment.