/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2013-2014, Geomatys
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotoolkit.display2d.ext.cellular;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.measure.quantity.Length;
import javax.measure.Unit;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import org.apache.sis.measure.Units;
import org.apache.sis.feature.FeatureExt;
import org.apache.sis.feature.builder.AttributeRole;
import org.apache.sis.feature.builder.FeatureTypeBuilder;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.storage.coverage.CoverageReference;
import org.geotoolkit.coverage.GridSampleDimension;
import org.geotoolkit.coverage.grid.GeneralGridGeometry;
import org.geotoolkit.coverage.grid.GridCoverage2D;
import org.geotoolkit.coverage.io.GridCoverageReadParam;
import org.geotoolkit.coverage.io.GridCoverageReader;
import org.geotoolkit.display2d.GO2Utilities;
import org.geotoolkit.map.CoverageMapLayer;
import org.geotoolkit.se.xml.v110.RuleType;
import org.geotoolkit.se.xml.v110.SymbolizerType;
import org.geotoolkit.sld.xml.StyleXmlIO;
import org.opengis.feature.AttributeType;
import org.opengis.feature.FeatureType;
import org.opengis.feature.PropertyType;
import org.opengis.filter.expression.Expression;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.style.ExtensionSymbolizer;
import org.opengis.style.Rule;
import org.opengis.style.StyleVisitor;
import org.opengis.util.FactoryException;
/**
*
* @author Johann Sorel (Geomatys)
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "CellSymbolizerType")
@XmlRootElement(name="CellSymbolizer",namespace="http://geotoolkit.org")
public class CellSymbolizer extends SymbolizerType implements ExtensionSymbolizer{
public static final String PROPERY_GEOM_CENTER = "geom_center";
public static final String PROPERY_GEOM_CONTOUR = "geom_contour";
public static final String PROPERY_SUFFIX_COUNT = "_count";
public static final String PROPERY_SUFFIX_MIN = "_min";
public static final String PROPERY_SUFFIX_MEAN = "_mean";
public static final String PROPERY_SUFFIX_MAX = "_max";
public static final String PROPERY_SUFFIX_RANGE = "_range";
public static final String PROPERY_SUFFIX_RMS = "_rms";
public static final String PROPERY_SUFFIX_SUM = "_sum";
private static final Logger LOGGER = Logging.getLogger("org.geotoolkit.display2d.ext.cellular");
public static final String NAME = "Cell";
@XmlElement(name = "CellSize",namespace="http://geotoolkit.org")
private int cellSize;
@XmlElement(name = "Rule", type = RuleType.class)
private RuleType ruleType;
@XmlTransient
private Rule rule;
public CellSymbolizer() {
}
public CellSymbolizer(int cellSize, Rule rule){
this.cellSize = cellSize;
this.rule = rule;
final StyleXmlIO util = new StyleXmlIO();
if(rule!=null){
this.ruleType = (RuleType) util.getTransformerXMLv110().visit(rule,null);
}
}
@Override
public Unit<Length> getUnitOfMeasure() {
return Units.POINT;
}
@Override
public String getGeometryPropertyName() {
return null;
}
@Override
public Expression getGeometry() {
return null;
}
@Override
public String getExtensionName() {
return NAME;
}
public void setCellSize(int cellSize) {
this.cellSize = cellSize;
}
public int getCellSize() {
return cellSize;
}
public Rule getRule() {
if(rule!=null){
return rule;
}
if(ruleType!=null){
final StyleXmlIO util = new StyleXmlIO();
try {
rule = util.getTransformer110().visitRule(ruleType);
} catch (FactoryException ex) {
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
}
}
return rule;
}
public RuleType getRuleType() {
return ruleType;
}
public void setRuleType(RuleType jaxrule) {
this.ruleType = jaxrule;
this.rule = null;
}
@Override
public Map<String, Expression> getParameters() {
final Map<String,Expression> config = new HashMap<>();
final Set<String> props = GO2Utilities.propertiesNames(Collections.singleton(getRule()));
int i=0;
for(String s : props){
s = cellToBasePropertyName(s);
if(s!=null){
config.put(""+i, GO2Utilities.FILTER_FACTORY.property(s));
}
i++;
}
return config;
}
@Override
public Object accept(StyleVisitor sv, Object o) {
return sv.visit(this, o);
}
public static String cellToBasePropertyName(String s){
if(s.endsWith(CellSymbolizer.PROPERY_SUFFIX_COUNT)){
return s.substring(0, s.length()-CellSymbolizer.PROPERY_SUFFIX_COUNT.length());
}else if(s.endsWith(CellSymbolizer.PROPERY_SUFFIX_MAX)){
return s.substring(0, s.length()-CellSymbolizer.PROPERY_SUFFIX_MAX.length());
}else if(s.endsWith(CellSymbolizer.PROPERY_SUFFIX_MEAN)){
return s.substring(0, s.length()-CellSymbolizer.PROPERY_SUFFIX_MEAN.length());
}else if(s.endsWith(CellSymbolizer.PROPERY_SUFFIX_MIN)){
return s.substring(0, s.length()-CellSymbolizer.PROPERY_SUFFIX_MIN.length());
}else if(s.endsWith(CellSymbolizer.PROPERY_SUFFIX_RANGE)){
return s.substring(0, s.length()-CellSymbolizer.PROPERY_SUFFIX_RANGE.length());
}else if(s.endsWith(CellSymbolizer.PROPERY_SUFFIX_RMS)){
return s.substring(0, s.length()-CellSymbolizer.PROPERY_SUFFIX_RMS.length());
}else if(s.endsWith(CellSymbolizer.PROPERY_SUFFIX_SUM)){
return s.substring(0, s.length()-CellSymbolizer.PROPERY_SUFFIX_SUM.length());
}else if(CellSymbolizer.PROPERY_GEOM_CENTER.equals(s) || CellSymbolizer.PROPERY_GEOM_CONTOUR.equals(s)){
return null;
}else{
return s;
}
}
public static FeatureType buildCellType(CoverageMapLayer layer) throws DataStoreException{
return buildCellType(layer.getCoverageReference());
}
public static FeatureType buildCellType(CoverageReference ref) throws DataStoreException{
final GridCoverageReader reader = ref.acquireReader();
final FeatureType sft = buildCellType(reader, ref.getImageIndex());
ref.recycle(reader);
return sft;
}
public static FeatureType buildCellType(GridCoverageReader reader, int imageIndex) throws DataStoreException{
final List<GridSampleDimension> lst = reader.getSampleDimensions(imageIndex);
final GeneralGridGeometry gg = reader.getGridGeometry(imageIndex);
final CoordinateReferenceSystem crs = gg.getCoordinateReferenceSystem();
if(lst!=null){
final String[] names = new String[lst.size()];
for(int i=0;i<names.length;i++){
names[i] = lst.get(i).getDescription().toString();
}
return buildCellType(lst.size(), names, crs);
}else{
//we need to find the number of bands by some other way
final GridCoverageReadParam param = new GridCoverageReadParam();
param.setResolution(gg.getEnvelope().getSpan(0),gg.getEnvelope().getSpan(1));
final GridCoverage2D cov = (GridCoverage2D) reader.read(0, param);
final int nbBands = cov.getRenderedImage().getSampleModel().getNumBands();
return buildCellType(nbBands, null, crs);
}
}
public static FeatureType buildCellType(GridCoverage2D coverage){
final int nbBand = coverage.getNumSampleDimensions();
final GridSampleDimension[] dims = coverage.getSampleDimensions();
final String[] names = new String[dims.length];
for(int i=0;i<names.length;i++){
names[i] = dims[i].getDescription().toString();
}
return buildCellType(nbBand, names, coverage.getCoordinateReferenceSystem2D());
}
public static FeatureType buildCellType(int nbBand, String[] bandnames, CoordinateReferenceSystem crs){
final FeatureTypeBuilder ftb = new FeatureTypeBuilder();
ftb.setName("cell");
ftb.addAttribute(Point.class).setName(PROPERY_GEOM_CENTER).setCRS(crs).addRole(AttributeRole.DEFAULT_GEOMETRY);
ftb.addAttribute(Polygon.class).setName(PROPERY_GEOM_CONTOUR).setCRS(crs);
for(int b=0,n=nbBand;b<n;b++){
final String name = "band_"+b;
final String bandName = (bandnames!=null) ? bandnames[b] : "";
ftb.addAttribute(Double.class).setDescription(bandName).setName(name+PROPERY_SUFFIX_COUNT);
ftb.addAttribute(Double.class).setDescription(bandName).setName(name+PROPERY_SUFFIX_MIN);
ftb.addAttribute(Double.class).setDescription(bandName).setName(name+PROPERY_SUFFIX_MEAN);
ftb.addAttribute(Double.class).setDescription(bandName).setName(name+PROPERY_SUFFIX_MAX);
ftb.addAttribute(Double.class).setDescription(bandName).setName(name+PROPERY_SUFFIX_RANGE);
ftb.addAttribute(Double.class).setDescription(bandName).setName(name+PROPERY_SUFFIX_RMS);
ftb.addAttribute(Double.class).setDescription(bandName).setName(name+PROPERY_SUFFIX_SUM);
}
return ftb.build();
}
public static FeatureType buildCellType(final FeatureType basetype, CoordinateReferenceSystem crs){
crs = (crs==null)? FeatureExt.getCRS(basetype) : crs;
final FeatureTypeBuilder ftb = new FeatureTypeBuilder();
ftb.setName("cell");
ftb.addAttribute(Point.class).setName(PROPERY_GEOM_CENTER).setCRS(crs).addRole(AttributeRole.DEFAULT_GEOMETRY);
ftb.addAttribute(Polygon.class).setName(PROPERY_GEOM_CONTOUR).setCRS(crs);
//loop on all properties, extract numeric fields only
for(PropertyType desc : basetype.getProperties(true)){
if(desc instanceof AttributeType){
final AttributeType att = (AttributeType) desc;
final Class binding = att.getValueClass();
if(Number.class.isAssignableFrom(binding) || String.class.isAssignableFrom(binding)){
final String name = att.getName().toString();
ftb.addAttribute(double.class).setDescription(name).setName(name+PROPERY_SUFFIX_COUNT);
ftb.addAttribute(double.class).setDescription(name).setName(name+PROPERY_SUFFIX_MIN);
ftb.addAttribute(double.class).setDescription(name).setName(name+PROPERY_SUFFIX_MEAN);
ftb.addAttribute(double.class).setDescription(name).setName(name+PROPERY_SUFFIX_MAX);
ftb.addAttribute(double.class).setDescription(name).setName(name+PROPERY_SUFFIX_RANGE);
ftb.addAttribute(double.class).setDescription(name).setName(name+PROPERY_SUFFIX_RMS);
ftb.addAttribute(double.class).setDescription(name).setName(name+PROPERY_SUFFIX_SUM);
}
}
}
return ftb.build();
}
@Override
public String toString() {
return "CellSymbolizer";
}
}