/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * 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.geotools.data.wfs.v1_1_0; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Map; import java.util.logging.Logger; import javax.xml.namespace.QName; import net.opengis.wfs.GetFeatureType; import net.opengis.wfs.QueryType; import org.geotools.data.wfs.protocol.wfs.GetFeature; import org.geotools.data.wfs.protocol.wfs.WFSOperationType; import org.geotools.data.wfs.protocol.wfs.WFSProtocol; import org.geotools.filter.Capabilities; import org.geotools.filter.v1_0.OGCConfiguration; import org.geotools.gml2.GML; import org.geotools.gml2.bindings.GMLBoxTypeBinding; import org.geotools.gml2.bindings.GMLCoordinatesTypeBinding; import org.geotools.util.logging.Logging; import org.geotools.xml.Configuration; import org.opengis.filter.Filter; import org.opengis.filter.capability.FilterCapabilities; import org.opengis.filter.capability.SpatialCapabilities; import org.opengis.filter.capability.SpatialOperator; import org.opengis.filter.capability.SpatialOperators; import org.opengis.filter.spatial.Intersects; import org.picocontainer.MutablePicoContainer; import com.vividsolutions.jts.geom.CoordinateSequence; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.impl.PackedCoordinateSequence; @SuppressWarnings("nls") public class IonicStrategy extends DefaultWFSStrategy { private static final Logger LOGGER = Logging.getLogger("org.geotools.data.wfs"); /** * A filter 1.0 configuration to encode Filters issued to Ionic */ private static final Configuration filter_1_0_0_Configuration = new OGCConfiguration() { @Override protected void registerBindings(MutablePicoContainer container) { super.registerBindings(container); // override the binding for GML.BoxType to use the one producing an output Ionic // understands container.registerComponentImplementation(GML.BoxType, IonicGML2BoxTypeBinding.class); } }; /** * We can't use POST at all against Ionic cause it is not a WFS 1.1 implementation and expect * the filters to be encoded as per Filter 1.0, and I wasn't able of creating a WFS 1.1 with * Filter 1.0 {@link Configuration} that works. * * @return false * @see WFSStrategy#supportsPost() */ @Override public boolean supportsPost() { return false; } /** * Ionic does not declare the supported output formats in the caps, yet it fails if asked for * {@code text/xml; subtype=gml/3.1.1} but succeeds if asked for {@code GML3} */ @Override public String getDefaultOutputFormat(WFSProtocol wfs, WFSOperationType op) { if (WFSOperationType.GET_FEATURE != op) { throw new UnsupportedOperationException(String.valueOf(op)); } return "GML3"; } /** * @return a Filter 1.0 configuration since Ionic expects that instead of 1.1 */ @Override protected Configuration getFilterConfiguration() { return filter_1_0_0_Configuration; } /** * Ionic uses {@code urn:opengis:def:crs:ogc::83} instead of {@code EPSG:4269}. If that's the * case, the query srsName is replaced by the kown "EPSG:4269" code */ @Override public RequestComponents createGetFeatureRequest(WFSProtocol wfs, GetFeature query) throws IOException { RequestComponents req = super.createGetFeatureRequest(wfs, query); GetFeatureType getFeature = req.getServerRequest(); QueryType queryType = (QueryType) getFeature.getQuery().get(0); URI srsNameUri = queryType.getSrsName(); final String overrideSrs = "urn:opengis:def:crs:ogc::83"; if (srsNameUri != null && srsNameUri.toString().equalsIgnoreCase(overrideSrs)) { try { queryType.setSrsName(new URI("EPSG:4269")); } catch (URISyntaxException e) { throw new RuntimeException("shouln't happen: " + e.getMessage()); } Map<String, String> kvpParameters = req.getKvpParameters(); kvpParameters.put("SRSNAME", "EPSG:4269"); } return req; } /** * Ionic's capabilities may state the spatial operator {@code Intersect} instead of {@code * Intersects}. If so, we fix that here so intersects is actually recognized as a supported * filter. */ @Override public Filter[] splitFilters(Capabilities caps, Filter queryFilter) { FilterCapabilities filterCapabilities = caps.getContents(); SpatialCapabilities spatialCapabilities = filterCapabilities.getSpatialCapabilities(); if (spatialCapabilities != null) { SpatialOperators spatialOperators = spatialCapabilities.getSpatialOperators(); if (spatialOperators != null) { SpatialOperator missnamedIntersects = spatialOperators.getOperator("Intersect"); if (missnamedIntersects != null) { LOGGER.fine("Ionic capabilities states the spatial operator Intersect. " + "Assuming it is Intersects and adding Intersects as a " + "supported filter type"); caps.addName(Intersects.NAME); } } } return super.splitFilters(caps, queryFilter); } /** * A gml:Box binding to override the default one to adapt to the Ionic server that recognizes * {@code <gml:Box><gml:coordinates>} but not {@code <gml:Box><gml:coord>...} * * @author Gabriel Roldan */ public static class IonicGML2BoxTypeBinding extends GMLBoxTypeBinding { /** * Returns a {@link CoordinateSequence} for the {@code coordinates} property so its handled * by a {@link GMLCoordinatesTypeBinding} at encoding time as {@code gml:coordinates} that * Ionic understands */ @Override public Object getProperty(Object object, QName name) throws Exception { Envelope e = (Envelope) object; if (GML.coordinates.equals(name)) { double[] seq = { e.getMinX(), e.getMinY(), e.getMaxX(), e.getMaxY() }; CoordinateSequence coords = new PackedCoordinateSequence.Double(seq, 2); return coords; } return null; } } }