/*******************************************************************************
* Copyright (c) 2010 Stefan A. Tzeggai.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v2.1
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan A. Tzeggai - initial API and implementation
******************************************************************************/
package org.geopublishing.atlasStyler.classification;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.awt.Color;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
import javax.xml.transform.TransformerException;
import org.geopublishing.atlasStyler.AtlasStylerVector;
import org.geotools.data.DataUtilities;
import org.geotools.data.FeatureSource;
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.Symbolizer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import de.schmitzm.geotools.styling.StyledFS;
import de.schmitzm.geotools.styling.StylingUtil;
import de.schmitzm.geotools.testing.GTTestingUtil;
import de.schmitzm.geotools.testing.GTTestingUtil.TestDatasetsVector;
import de.schmitzm.lang.LangUtil;
import de.schmitzm.testing.TestingClass;
public class FeatureClassificationTest extends TestingClass {
/**
* Uses TestingUtil.TestDatasetsVector.countryShp.getFeatureSource() and
* disposes afterwards
**/
private FeatureSource<SimpleFeatureType, SimpleFeature> featureSource_polygon;
/**
* Uses TestingUtil.TestDatasetsVector.polygonSnow.getFeatureSource() and
* disposes afterwards
**/
private FeatureSource<SimpleFeatureType, SimpleFeature> featureSource_snowPolygon;
@Before
public void setup() throws IOException {
featureSource_polygon = GTTestingUtil.TestDatasetsVector.countryShp
.getFeatureSource();
featureSource_snowPolygon = TestDatasetsVector.polygonSnow
.getFeatureSource();
}
@After
public void after() {
featureSource_polygon.getDataStore().dispose();
featureSource_snowPolygon.getDataStore().dispose();
}
private void extractColors(Style snowStyleOriginal, List<Color> beforeColors) {
for (Rule r : snowStyleOriginal.featureTypeStyles().get(0).rules()) {
for (final Symbolizer s : r.getSymbolizers()) {
final Color c = StylingUtil.getSymbolizerColor(s);
if (c != null) {
beforeColors.add(c);
break;
}
}
}
}
/**
* Utility method to easily check the breaks
*/
private boolean testBreaks(TreeSet<Double> classLimits, Double... expected) {
Double[] actual = classLimits.toArray(new Double[] {});
for (int i = 0; i < expected.length; i++) {
if (actual[i] > expected[i] + 0.00000001
|| actual[i] < expected[i] - 0.00000001) {
String msg = (i + 1) + "th class limit not as expected:"
+ expected[i] + " vs. " + actual[i];
log.error(msg);
log.error("expected = "
+ LangUtil.stringConcatWithSep(" , ", expected));
log.error("actual = "
+ LangUtil.stringConcatWithSep(" , ", actual));
return false;
}
}
return true;
}
@Test
public void testChangeOfValueFieldNameAndReusingTheObject()
throws IOException, InterruptedException, CQLException {
final FeatureClassification clfcn = new FeatureClassification(
new StyledFS(featureSource_polygon), "SQMI_CNTRY");
clfcn.setRecalcAutomatically(false);
clfcn.setMethod(CLASSIFICATION_METHOD.QUANTILES);
clfcn.setNumClasses(3);
clfcn.calculateClassLimitsBlocking();
assertTrue(testBreaks(clfcn.getClassLimits(), 0.644,
3692.8633333333373, 86534.47100000014, 6506534.0));
/**
* Change ValueFieldName to POP,
*/
clfcn.setValue_field_name("POP_CNTRY");
clfcn.setMethod(CLASSIFICATION_METHOD.QUANTILES);
clfcn.setNumClasses(10);
clfcn.calculateClassLimitsBlocking();
assertTrue(testBreaks(clfcn.getClassLimits(), -99999.0, 6782.0,
62920.0, 260627.0, 1085777.0, 3084641.0, 5245515.0, 9951515.0,
1.782752E7, 4.309962E7, 1.281008318E9));
/**
* Normalize POP with SQKM and add a filter.
*/
clfcn.getStyledFeatures().setFilter(
ECQL.toFilter("SQKM_CNTRY > 0 and POP_CNTRY > 0"));
clfcn.setNormalizer_field_name("SQKM_CNTRY");
clfcn.setNumClasses(4);
clfcn.calculateClassLimitsBlocking();
assertTrue(testBreaks(clfcn.getClassLimits(), 0.025861767213758966,
22.20006623432529, 69.62649327083018, 157.5165469904705,
30126.841370755155));
/**
* Finally reset NORMALIZER to null. Only POP is classified.
*/
clfcn.setNormalizer_field_name(null);
clfcn.setMethod(CLASSIFICATION_METHOD.EI);
clfcn.calculateClassLimitsBlocking();
assertTrue(testBreaks(clfcn.getClassLimits(), 56.0, 3.202521215E8,
6.40504187E8, 9.607562525E8, 1.281008318E9));
}
@Test
public void testNoDataValueviaAMD() throws IOException,
InterruptedException {
FeatureClassification clfcn = new FeatureClassification(new StyledFS(
featureSource_polygon), "SQKM_CNTRY");
clfcn.getStyledFeatures().getAttributeMetaDataMap().get("SQKM_CNTRY")
.addNodataValue(0.0);
clfcn.setRecalcAutomatically(false);
clfcn.setMethod(CLASSIFICATION_METHOD.EI);
clfcn.setNumClasses(4);
clfcn.calculateClassLimitsBlocking();
assertTrue(testBreaks(clfcn.getClassLimits(), 1.668, 4212986.250999999,
8425970.833999999, 1.2638955416999998E7, 1.685194E7));
}
@Test
public void testQEqualInterval() throws IOException, InterruptedException {
FeatureClassification clfcn = new FeatureClassification(new StyledFS(
featureSource_polygon), "POP_CNTRY");
clfcn.setRecalcAutomatically(false);
clfcn.setMethod(CLASSIFICATION_METHOD.EI);
clfcn.setNumClasses(4);
clfcn.calculateClassLimitsBlocking();
assertTrue(testBreaks(clfcn.getClassLimits(), -99999.0, 3.2017708025E8,
6.404541595E8, 9.6073123875E8, 1.281008318E9));
assertEquals("class widthds are not equal",
3.2017708025E8 - (-99999.0), 6.404541595E8 - 3.2017708025E8,
0.00000001);
}
@Test
public void testQEqualIntervalNormalizedFilteredAndBlockingClalculations()
throws IOException, InterruptedException {
// Filter exclude = ff.equals(ff.property("LANDLOCKED"),
// ff.literal("N"));
FeatureClassification clfcn = new FeatureClassification(new StyledFS(
featureSource_polygon), "POP_CNTRY", null);
clfcn.setRecalcAutomatically(false);
clfcn.setMethod(CLASSIFICATION_METHOD.EI);
clfcn.setNumClasses(4);
clfcn.calculateClassLimitsBlocking();
assertTrue(testBreaks(clfcn.getClassLimits(), -99999.0, 3.2017708025E8,
6.404541595E8, 9.6073123875E8, 1.281008318E9));
}
@Test
public void testQuantile() throws IOException, InterruptedException {
final FeatureClassification clfcn = new FeatureClassification(
new StyledFS(featureSource_polygon), "POP_CNTRY");
clfcn.setRecalcAutomatically(false);
clfcn.setMethod(CLASSIFICATION_METHOD.QUANTILES);
clfcn.setNumClasses(10);
clfcn.calculateClassLimitsBlocking();
assertTrue(testBreaks(clfcn.getClassLimits(), -99999.0, 6782.0,
62920.0, 260627.0, 1085777.0, 3084641.0, 5245515.0, 9951515.0,
1.782752E7, 4.309962E7, 1.281008318E9));
}
@Test
public void testQuantileNormalized() throws IOException,
InterruptedException {
final FeatureClassification clfcn = new FeatureClassification(
new StyledFS(featureSource_polygon), "POP_CNTRY", "SQKM_CNTRY");
clfcn.setRecalcAutomatically(false);
clfcn.setMethod(CLASSIFICATION_METHOD.QUANTILES);
clfcn.setNumClasses(5);
clfcn.calculateClassLimitsBlocking();
assertTrue(testBreaks(clfcn.getClassLimits(), -219.66456884905597,
11.128059688187763, 39.738285449459624, 87.66258335206413,
220.55727207025177, 30126.841370755155));
}
@Test
public void testQuantileNormalizedFiltered() throws IOException,
InterruptedException, CQLException {
StyledFS styledFeaturesFiltered = new StyledFS(featureSource_polygon);
styledFeaturesFiltered.setFilter(ECQL
.toFilter("POP_CNTRY > 0 and POP_CNTRY < 500000"));
final FeatureClassification clfcn = new FeatureClassification(
styledFeaturesFiltered);
clfcn.setRecalcAutomatically(false);
clfcn.setValue_field_name("POP_CNTRY");
clfcn.setNormalizer_field_name("SQKM_CNTRY");
clfcn.setNumClasses(4);
clfcn.setMethod(CLASSIFICATION_METHOD.QUANTILES);
clfcn.calculateClassLimitsBlocking();
assertTrue(testBreaks(clfcn.getClassLimits(), 0.025861767213758966,
28.29196367998521, 109.31626340104614, 259.54752934208244,
30126.841370755155));
}
@Test
public void testQuantilesClassificationImportWithManualColors()
throws IOException, TransformerException {
URL sldUrl = DataUtilities.changeUrlExt(
TestDatasetsVector.polygonSnow.getUrl(), "sld");
Style snowStyleOriginal = StylingUtil.loadSLD(sldUrl)[0];
Style snowStyle = StylingUtil.copy(snowStyleOriginal);
assertFalse(StylingUtil.isStyleDifferent(snowStyleOriginal, snowStyle));
List<Color> beforeColors = new ArrayList<Color>();
List<Color> afterColors = new ArrayList<Color>();
extractColors(snowStyleOriginal, beforeColors);
AtlasStylerVector atlasStyler = new AtlasStylerVector(
featureSource_snowPolygon, snowStyle);
Style afterImport = atlasStyler.getStyle();
extractColors(afterImport, afterColors);
assertEquals("The actual color values must be the same", beforeColors,
afterColors);
StylingUtil.validates(afterImport);
}
}