/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
* ... and it just works.
*
****************************************************/
/*
* SimpleWebFeatureService.java
*
* Created on 17. November 2006, 10:32
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package de.cismet.cismap.commons.featureservice;
//import org.deegree2.model.feature.Feature;
//import org.deegree2.model.feature.FeatureCollection;
//import org.deegree2.model.feature.GMLFeatureCollectionDocument;
import org.apache.log4j.Logger;
import org.jdom.DataConversionException;
import org.jdom.Element;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.util.HashMap;
import java.util.List;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import de.cismet.cismap.commons.Crs;
import de.cismet.cismap.commons.LayerInfoProvider;
import de.cismet.cismap.commons.features.WFSFeature;
import de.cismet.cismap.commons.featureservice.factory.FeatureFactory;
import de.cismet.cismap.commons.featureservice.factory.WFSFeatureFactory;
import de.cismet.cismap.commons.interaction.CismapBroker;
import de.cismet.cismap.commons.interaction.DefaultQueryButtonAction;
import de.cismet.cismap.commons.interaction.DefaultXMLQueryButtonAction;
import de.cismet.cismap.commons.preferences.CapabilityLink;
import de.cismet.cismap.commons.wfs.WFSFacade;
import de.cismet.cismap.commons.wfs.capabilities.FeatureType;
import de.cismet.cismap.commons.wfs.capabilities.WFSCapabilities;
import de.cismet.cismap.commons.wfs.capabilities.WFSCapabilitiesFactory;
import de.cismet.cismap.commons.wms.capabilities.Layer;
/**
* This class provides access to a Web Feature service. Requests will be send to a WFS instance. The response will be
* parsed and transformed to an internal features representation. These internal features will be send to all registered
* listeners
*
* @author Sebastian Puhl
* @version $Revision$, $Date$
*/
public final class WebFeatureService extends AbstractFeatureService<WFSFeature, String> implements LayerInfoProvider {
//~ Static fields/initializers ---------------------------------------------
private static final transient Logger LOG = Logger.getLogger(WebFeatureService.class);
public static final String WFS_FEATURELAYER_TYPE = "WebFeatureServiceLayer"; // NOI18N
public static final HashMap<Integer, Icon> layerIcons = new HashMap<Integer, Icon>();
static {
layerIcons.put(
LAYER_ENABLED_VISIBLE,
new ImageIcon(
AbstractFeatureService.class.getResource(
"/de/cismet/cismap/commons/gui/layerwidget/res/layerWfs.png"))); // NOI18N
layerIcons.put(
LAYER_ENABLED_INVISIBLE,
new ImageIcon(
AbstractFeatureService.class.getResource(
"/de/cismet/cismap/commons/gui/layerwidget/res/layerWfsInvisible.png"))); // NOI18N
layerIcons.put(
LAYER_DISABLED_VISIBLE,
new ImageIcon(
AbstractFeatureService.class.getResource(
"/de/cismet/cismap/commons/gui/layerwidget/res/disabled/layerWfs.png"))); // NOI18N
layerIcons.put(
LAYER_DISABLED_INVISIBLE,
new ImageIcon(
AbstractFeatureService.class.getResource(
"/de/cismet/cismap/commons/gui/layerwidget/res/disabled/layerWfsInvisible.png"))); // NOI18N
}
//~ Instance fields --------------------------------------------------------
/** the request which will be send to the WFS. */
private Crs crs;
private String wfsQueryString;
private Element wfsQueryElement;
/** the hostname of the WFS server. */
private String hostname;
/** the version of the wfs. */
private FeatureType feature;
private String backupVersion = "";
private boolean reverseAxisOrder;
//~ Instance initializers --------------------------------------------------
{
queryButtons.clear();
queryButtons.add(new DefaultXMLQueryButtonAction("PropertyIsEqualTo", "="));
queryButtons.add(new DefaultXMLQueryButtonAction("PropertyIsNotEqualTo", "<>"));
queryButtons.add(new DefaultXMLQueryButtonAction("PropertyIsLike", "Like"));
queryButtons.add(new DefaultXMLQueryButtonAction("PropertyIsGreaterThan", ">"));
queryButtons.add(new DefaultXMLQueryButtonAction("PropertyIsGreaterThanOrEqualTo", ">="));
queryButtons.add(new DefaultXMLQueryButtonAction("And", "And"));
queryButtons.add(new DefaultXMLQueryButtonAction("PropertyIsLessThan", "<"));
queryButtons.add(new DefaultXMLQueryButtonAction("PropertyIsLessThanOrEqualTo", "<="));
queryButtons.add(new DefaultXMLQueryButtonAction("Or", "Or"));
queryButtons.add(new DefaultQueryButtonAction("_", 1));
queryButtons.add(new DefaultQueryButtonAction("%", 1));
queryButtons.add(new DefaultXMLQueryButtonAction("Not", "Not"));
queryButtons.add(new DefaultXMLQueryButtonAction("PropertyIsNull", "Null"));
queryButtons.add(new DefaultXMLQueryButtonAction("Literal", "Lit"));
}
//~ Constructors -----------------------------------------------------------
/**
* Creates a new WebFeatureService object.
*
* @param e DOCUMENT ME!
*
* @throws Exception DOCUMENT ME!
*/
public WebFeatureService(final Element e) throws Exception {
super(e);
if (e.getAttribute("reverseAxisOrder") != null) {
try {
this.reverseAxisOrder = e.getAttribute("reverseAxisOrder").getBooleanValue(); // NOI18N
} catch (DataConversionException ex) {
LOG.error("Invalid attribute value", ex);
}
}
}
/**
* Create a new <b>uninitialised</b> AbstractFeatureService except for the attributes provided.
*
* @param name the name of this FeatureService
* @param host hostname of the WFS server
* @param query the request which will be send to the WFS
* @param attributes featureServiceAttributes vector with all FeatureServiceAttributes of the FeatureService
* @param feature version DOCUMENT ME!
*
* @throws Exception if something went wrong
*/
public WebFeatureService(final String name,
final String host,
final Element query,
final List<FeatureServiceAttribute> attributes,
final FeatureType feature) throws Exception {
this(name, host, query, attributes, feature, false);
}
/**
* Create a new <b>uninitialised</b> AbstractFeatureService except for the attributes provided.
*
* @param name the name of this FeatureService
* @param host hostname of the WFS server
* @param query the request which will be send to the WFS
* @param attributes featureServiceAttributes vector with all FeatureServiceAttributes of the
* FeatureService
* @param feature version DOCUMENT ME!
* @param reverseAxisOrder if true, the axis order of the crs will be changed
*
* @throws Exception if something went wrong
*/
public WebFeatureService(final String name,
final String host,
final Element query,
final List<FeatureServiceAttribute> attributes,
final FeatureType feature,
final boolean reverseAxisOrder) throws Exception {
super(name, attributes);
crs = CismapBroker.getInstance().getSrs();
setFeature(feature);
setQueryElement(query);
setHostname(host);
// defaults for new services
this.setTranslucency(0.2f);
this.setMaxFeatureCount(2900);
this.reverseAxisOrder = reverseAxisOrder;
}
/**
* Protected Constructor that clones (shallow) the delivered WebFeatureService. Attributes, layer properties and
* feature factories are not cloned deeply. The WebFeatureService to be cloned should be initilaised.
*
* @param wfs FeatureService that should be cloned
*/
protected WebFeatureService(final WebFeatureService wfs) {
super(wfs);
this.setCrs(wfs.getCrs());
this.setFeature(wfs.getFeature());
this.setHostname(wfs.getHostname());
this.setQueryElement(wfs.getQueryElement());
// overwrite with customised query if applicable
this.setQuery(wfs.getQuery());
this.maxFeatureCount = Integer.MAX_VALUE;
this.backupVersion = wfs.backupVersion;
this.setInitialisationError(wfs.getInitialisationError());
this.errorObject = wfs.errorObject;
this.reverseAxisOrder = wfs.reverseAxisOrder;
}
//~ Methods ----------------------------------------------------------------
@Override
protected void initConcreteInstance() throws Exception {
this.layerProperties.setQueryType(LayerProperties.QUERYTYPE_XML);
}
@Override
public void setMaxFeatureCount(final int maxFeatureCount) {
super.setMaxFeatureCount(maxFeatureCount);
if (this.wfsQueryElement != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("setting max features of WFS query to " + (maxFeatureCount + 100)); // NOI18N
}
WFSFacade.setMaxFeatureCount(this.wfsQueryElement, maxFeatureCount + 100, getVersion());
this.wfsQueryString = FeatureServiceUtilities.elementToString(this.wfsQueryElement);
}
}
@Override
public Element toElement() {
final Element parentElement = super.toElement();
final CapabilityLink capLink = new CapabilityLink(
CapabilityLink.OGC,
hostname,
reverseAxisOrder,
getVersion(),
false);
if (reverseAxisOrder) {
parentElement.setAttribute("reverseAxisOrder", "true");
}
try {
parentElement.addContent(capLink.getElement());
} catch (Exception e) {
LOG.warn("error in parentElement.addContent(capLink.getElement());", e);
}
try {
parentElement.addContent(getQueryElement().detach());
} catch (Exception e) {
LOG.warn("error in parentElement.addContent(getQueryElement().detach());", e);
}
return parentElement;
}
@Override
public void initFromElement(final Element element) throws Exception {
initFromElement(element, getInitialisationError());
}
/**
* DOCUMENT ME!
*
* @param element DOCUMENT ME!
* @param loadCapDoc DOCUMENT ME!
*
* @throws Exception DOCUMENT ME!
*/
public void initFromElement(Element element, final boolean loadCapDoc) throws Exception {
if (element == null) {
element = this.getInitElement();
}
if (!initializedFromElement) {
// without the initializedFromElement check, the layer adjustments from the user will be overridden
// in some cases
super.initFromElement(element);
}
final CapabilityLink cp = new CapabilityLink(element);
final Element query = element.getChild(FeatureServiceUtilities.GET_FEATURE, FeatureServiceUtilities.WFS);
String capLink = cp.getLink();
if ((cp.getVersion() != null) && !cp.getVersion().equals("")) {
capLink += "?VERSION=" + cp.getVersion();
}
if (loadCapDoc && (feature == null)) {
try {
final WFSCapabilitiesFactory fac = new WFSCapabilitiesFactory();
final WFSCapabilities cap = fac.createCapabilities(capLink);
if ((cap != null) && !cap.getVersion().equals(cp.getVersion())) {
LOG.warn("Cannot retrieve the wfs capabilities for version " + cp.getVersion() + " but for version "
+ cap.getVersion());
}
feature = WFSFacade.extractRequestedFeatureType(FeatureServiceUtilities.elementToString(query), cap);
this.setErrorObject(null);
setInitialisationError(false);
} catch (Exception ex) {
this.setErrorObject(ex.toString());
this.backupVersion = cp.getVersion();
this.setQueryElement(query);
this.setHostname(cp.getLink());
// if (getInitialisationError()) {
// throw ex;
// }
setInitialisationError(true);
}
} else {
this.setErrorObject(null);
this.backupVersion = cp.getVersion();
this.setQueryElement(query);
this.setHostname(cp.getLink());
setInitialisationError(true);
}
// query string will be set, when the query element will be set
this.setQueryElement(query);
this.setHostname(cp.getLink());
}
/**
* This method creates an one-to-one hard copy of the SimpleWebFeatureService.
*
* @return the copy of the SimpleWebFeatureService
*/
@Override
public Object clone() {
return new WebFeatureService(this);
}
/**
* DOCUMENT ME!
*/
public void removeAllListeners() {
listeners.clear();
}
/**
* Delivers the host-string of the FeatureService.
*
* @return hostname as string
*/
public String getHostname() {
return hostname;
}
/**
* Setter for the host-string of the FeatureService.
*
* @param hostname hostname to set
*/
protected void setHostname(final String hostname) {
this.hostname = hostname;
if (this.getFeatureFactory() != null) {
((WFSFeatureFactory)this.getFeatureFactory()).setHostname(hostname);
}
}
@Override
public String getQuery() {
return wfsQueryString;
}
@Override
public void setQuery(final String wfsQueryString) {
if (LOG.isDebugEnabled()) {
LOG.debug("setting the string representation of the WFS query (will not be saved)"); // NOI18N
}
this.wfsQueryString = wfsQueryString;
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public Element getQueryElement() {
return wfsQueryElement;
}
/**
* Sets a new wfsQuery Element and overwites the string query.
*
* @param wfsQuery DOCUMENT ME!
*/
public void setQueryElement(final Element wfsQuery) {
if (LOG.isDebugEnabled()) {
LOG.debug("setting the XML Element representation of the WFS query (will be saved)"); // NOI18N
}
this.wfsQueryElement = wfsQuery;
// overwrite string representation of query
if (this.wfsQueryElement != null) {
// +1 reich nicht aus, daher +100
WFSFacade.setMaxFeatureCount(this.wfsQueryElement, maxFeatureCount + 100, getVersion());
this.wfsQueryString = FeatureServiceUtilities.elementToString(wfsQuery);
}
}
@Override
protected String getFeatureLayerType() {
return WFS_FEATURELAYER_TYPE;
}
@Override
public Icon getLayerIcon(final int type) {
return layerIcons.get(type);
}
@Override
protected LayerProperties createLayerProperties() {
final DefaultLayerProperties defaultLayerProperties = new DefaultLayerProperties();
// very slow: defaultLayerProperties.setPrimaryAnnotationExpression("if (app:flurstn!=\"0\") {return app:flurstz
// + \" / \" + app:flurstn;} else {return app:flurstz;}", LayerProperties.EXPRESSIONTYPE_GROOVY);
defaultLayerProperties.setPrimaryAnnotationExpression(
"app:flurstz",
LayerProperties.EXPRESSIONTYPE_PROPERTYNAME); // NOI18N
defaultLayerProperties.getStyle().setMultiplier(1d);
defaultLayerProperties.getStyle().setFont(new Font("sansserif", Font.PLAIN, 12)); // NOI18N
defaultLayerProperties.setIdExpression("app:gid", LayerProperties.EXPRESSIONTYPE_PROPERTYNAME); // NOI18N
defaultLayerProperties.setQueryType(LayerProperties.QUERYTYPE_XML);
defaultLayerProperties.setFeatureService(this);
return defaultLayerProperties;
}
@Override
protected FeatureFactory createFeatureFactory() throws Exception {
return new WFSFeatureFactory(this.getLayerProperties(),
this.getHostname(),
this.feature,
getCrs(),
parseSLD(getSLDDefiniton()),
reverseAxisOrder);
}
/**
* Sets the Layer properties but does not refresh the cached features.
*
* @param layerProperties DOCUMENT ME!
*/
public void setLayerPropertiesWithoutUpdate(final LayerProperties layerProperties) {
this.layerProperties = layerProperties;
this.featureFactory.setLayerProperties(layerProperties);
}
/**
* DOCUMENT ME!
*
* @return the version of the referenced wfs
*/
public String getVersion() {
if (feature != null) {
return feature.getWFSCapabilities().getVersion();
} else {
LOG.warn("Version is not set. Use backup version " + backupVersion);
return backupVersion;
}
}
@Override
public String getLayerURI() {
return wfsQueryString;
}
@Override
public String getServerURI() {
return hostname;
}
@Override
public boolean isLayerQuerySelected() {
return false;
}
@Override
public void setLayerQuerySelected(final boolean selected) {
}
@Override
public boolean isQueryable() {
return false;
}
@Override
public Layer getLayerInformation() {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* DOCUMENT ME!
*
* @return the feature
*/
public FeatureType getFeature() {
return feature;
}
/**
* DOCUMENT ME!
*
* @param feature the feature to set
*/
public void setFeature(final FeatureType feature) {
this.feature = feature;
}
/**
* DOCUMENT ME!
*
* @return the crs
*/
public Crs getCrs() {
if (crs == null) {
return CismapBroker.getInstance().getSrs();
}
return crs;
}
/**
* DOCUMENT ME!
*
* @param crs the crs to set
*/
public void setCrs(final Crs crs) {
this.crs = crs;
if (featureFactory != null) {
((WFSFeatureFactory)featureFactory).setCrs(crs);
}
// try {
// featureFactory = createFeatureFactory();
// this.featureFactory.setMaxFeatureCount(this.getMaxFeatureCount());
// this.featureFactory.setLayerProperties(layerProperties);
// } catch (Exception e) {
// LOG.error("Error while creating a new feature factory.", e);
// }
}
@Override
public String decoratePropertyName(final String name) {
return "<PropertyName>" + name + "</PropertyName>";
}
@Override
public String decoratePropertyValue(final String column, final String value) {
return "<Literal>" + value + "</Literal>";
}
}