/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wfs; import static org.custommonkey.xmlunit.XMLAssert.*; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import javax.xml.namespace.QName; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.ProjectionPolicy; import org.geoserver.data.test.SystemTestData; import org.geoserver.data.test.SystemTestData.LayerProperty; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; import org.junit.Test; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; import org.w3c.dom.Document; import org.w3c.dom.Element; public class ReprojectionTest extends WFSTestSupport { private static final String TARGET_CRS_CODE = "EPSG:900913"; public static QName NULL_GEOMETRIES = new QName(SystemTestData.CITE_URI, "NullGeometries", SystemTestData.CITE_PREFIX); public static QName GOOGLE = new QName(SystemTestData.CITE_URI, "GoogleFeatures", SystemTestData.CITE_PREFIX); static MathTransform tx; @Override protected void setUpInternal(SystemTestData dataDirectory) throws Exception { CoordinateReferenceSystem epsg4326 = CRS.decode(TARGET_CRS_CODE); CoordinateReferenceSystem epsg32615 = CRS.decode("EPSG:32615"); tx = CRS.findMathTransform(epsg32615, epsg4326); WFSInfo wfs = getWFS(); wfs.setFeatureBounding( true ); getGeoServer().save( wfs ); dataDirectory.addVectorLayer(NULL_GEOMETRIES, Collections.EMPTY_MAP, getClass(), getCatalog()); Map<LayerProperty, Object> extra = new HashMap<LayerProperty, Object>(); extra.put(LayerProperty.PROJECTION_POLICY, ProjectionPolicy.REPROJECT_TO_DECLARED); extra.put(LayerProperty.SRS, 900913); dataDirectory.addVectorLayer(GOOGLE, extra, getClass(), getCatalog()); } @Test public void testGetFeatureGet() throws Exception { Document dom1 = getAsDOM("wfs?request=getfeature&service=wfs&version=1.0.0&typename=" + SystemTestData.POLYGONS.getLocalPart()); Document dom2 = getAsDOM("wfs?request=getfeature&service=wfs&version=1.0.0&typename=" + SystemTestData.POLYGONS.getLocalPart() + "&srsName=" + TARGET_CRS_CODE); // print(dom1); // print(dom2); runTest(dom1,dom2, tx); } @Test public void testGetFeatureGetAutoCRS() throws Exception { Document dom1 = getAsDOM("wfs?request=getfeature&service=wfs&version=1.0.0&typename=" + SystemTestData.POLYGONS.getLocalPart()); Document dom2 = getAsDOM("wfs?request=getfeature&service=wfs&version=1.0.0&typename=" + SystemTestData.POLYGONS.getLocalPart() + "&srsName=AUTO:42001,9001,-93,0"); // print(dom1); // print(dom2); MathTransform tx = CRS.findMathTransform(CRS.decode("EPSG:32615"), CRS.decode("AUTO:42001,9001,-93,0")); runTest(dom1,dom2, tx); } @Test public void testGetFeatureAutoCRSBBox() throws Exception { CoordinateReferenceSystem auto = CRS.decode("AUTO:42001,9001,-93,0"); FeatureTypeInfo ftInfo = getCatalog().getFeatureTypeByName(getLayerId(SystemTestData.POLYGONS)); ReferencedEnvelope nativeEnv = ftInfo.getFeatureSource(null, null).getBounds(); ReferencedEnvelope reprojectedEnv = nativeEnv.transform(auto, true); Document dom1 = getAsDOM("wfs?request=getfeature&service=wfs&version=1.0.0&typename=" + SystemTestData.POLYGONS.getLocalPart()); Document dom2 = getAsDOM("wfs?request=getfeature&service=wfs&version=1.0.0&typename=" + SystemTestData.POLYGONS.getLocalPart() + "&srsName=AUTO:42001,9001,-93,00&bbox=" + reprojectedEnv.getMinX() + "," + reprojectedEnv.getMinY() + "," + reprojectedEnv.getMaxX() + "," + reprojectedEnv.getMaxY() + ",AUTO:42001,9001,-93,0"); // print(dom1); // print(dom2); MathTransform tx = CRS.findMathTransform(CRS.decode("EPSG:32615"), auto); runTest(dom1,dom2, tx); } @Test public void testGetFeatureReprojectedFeatureType() throws Exception { // bbox is 4,4,6,6 in wgs84, coordinates have been reprojected to 900913 Document dom = getAsDOM("wfs?request=getfeature&service=wfs&version=1.0.0&typename=" + GOOGLE.getLocalPart() + "&bbox=445000,445000,668000,668000"); print(dom); assertXpathEvaluatesTo("1", "count(//cite:GoogleFeatures)", dom); } @Test public void testGetFeaturePost() throws Exception { String xml = "<wfs:GetFeature " + "service=\"WFS\" " + "version=\"1.0.0\" " + "xmlns:cdf=\"http://www.opengis.net/cite/data\" " + "xmlns:ogc=\"http://www.opengis.net/ogc\" " + "xmlns:wfs=\"http://www.opengis.net/wfs\" " + "> " + "<wfs:Query typeName=\"" + SystemTestData.POLYGONS.getPrefix() + ":" + SystemTestData.POLYGONS.getLocalPart() + "\"> " + "<wfs:PropertyName>cgf:polygonProperty</wfs:PropertyName> " + "</wfs:Query> " + "</wfs:GetFeature>"; Document dom1 = postAsDOM("wfs", xml); xml = "<wfs:GetFeature " + "service=\"WFS\" " + "version=\"1.0.0\" " + "xmlns:cdf=\"http://www.opengis.net/cite/data\" " + "xmlns:ogc=\"http://www.opengis.net/ogc\" " + "xmlns:wfs=\"http://www.opengis.net/wfs\" " + "> " + "<wfs:Query srsName=\"" + TARGET_CRS_CODE + "\" typeName=\"" + SystemTestData.POLYGONS.getPrefix() + ":" + SystemTestData.POLYGONS.getLocalPart() + "\"> " + "<wfs:PropertyName>cgf:polygonProperty</wfs:PropertyName> " + "</wfs:Query> " + "</wfs:GetFeature>"; Document dom2 = postAsDOM("wfs", xml); runTest(dom1, dom2, tx); } @Test public void testReprojectNullGeometries() throws Exception { // see https://osgeo-org.atlassian.net/browse/GEOS-1612 String xml = "<wfs:GetFeature " + "service=\"WFS\" " + "version=\"1.0.0\" " + "xmlns:cdf=\"http://www.opengis.net/cite/data\" " + "xmlns:ogc=\"http://www.opengis.net/ogc\" " + "xmlns:wfs=\"http://www.opengis.net/wfs\" " + "> " + "<wfs:Query srsName=\"" + TARGET_CRS_CODE + "\" typeName=\"" + NULL_GEOMETRIES.getPrefix() + ":" + NULL_GEOMETRIES.getLocalPart() + "\"> " + "</wfs:Query> " + "</wfs:GetFeature>"; Document dom = postAsDOM("wfs", xml); // print(dom); assertEquals(1, dom.getElementsByTagName("wfs:FeatureCollection").getLength()); } @Test public void testGetFeatureWithProjectedBoxGet() throws Exception { Document dom; double[] cr = getTransformedPolygonsLayerBBox(); String q = "wfs?request=getfeature&service=wfs&version=1.0&typeName=" + SystemTestData.POLYGONS.getLocalPart() + "&bbox=" + cr[0] + "," + cr[1] + "," + cr[2] + "," + cr[3] + "," + TARGET_CRS_CODE; dom = getAsDOM( q ); assertEquals( 1, dom.getElementsByTagName( SystemTestData.POLYGONS.getPrefix() + ":" + SystemTestData.POLYGONS.getLocalPart()).getLength() ); } @Test public void testGetFeatureWithProjectedBoxPost() throws Exception { Document dom; double[] cr = getTransformedPolygonsLayerBBox(); String xml = "<wfs:GetFeature service=\"WFS\" version=\"1.0.0\"" + " xmlns:" + SystemTestData.POLYGONS.getPrefix() + "=\"" + SystemTestData.POLYGONS.getNamespaceURI() + "\"" + " xmlns:ogc=\"http://www.opengis.net/ogc\" " + " xmlns:gml=\"http://www.opengis.net/gml\" " + " xmlns:wfs=\"http://www.opengis.net/wfs\" " + "> " + "<wfs:Query typeName=\"" + SystemTestData.POLYGONS.getPrefix() + ":" + SystemTestData.POLYGONS.getLocalPart() + "\">" + "<wfs:PropertyName>cgf:polygonProperty</wfs:PropertyName> " + "<ogc:Filter>" + "<ogc:BBOX>" + "<ogc:PropertyName>polygonProperty</ogc:PropertyName>" + "<gml:Box srsName=\"" + TARGET_CRS_CODE + "\">" + "<gml:coord>" + "<gml:X>" + cr[0] + "</gml:X>" + "<gml:Y>" + cr[1] + "</gml:Y>" + "</gml:coord>" + "<gml:coord>" + "<gml:X>" + cr[2] + "</gml:X>" + "<gml:Y>" + cr[3] + "</gml:Y>" + "</gml:coord>" + "</gml:Box>" + "</ogc:BBOX>" + "</ogc:Filter>" + "</wfs:Query> " + "</wfs:GetFeature>"; dom = postAsDOM( "wfs", xml ); assertEquals( 1, dom.getElementsByTagName( SystemTestData.POLYGONS.getPrefix() + ":" + SystemTestData.POLYGONS.getLocalPart()).getLength() ); } /** * See GEOT-3760 */ @Test public void testGetFeatureWithProjectedBoxIntersectsPost() throws Exception { Document dom; double[] cr = getTransformedPolygonsLayerBBox(); String xml = "<wfs:GetFeature service=\"WFS\" version=\"1.0.0\"" + " xmlns:" + SystemTestData.POLYGONS.getPrefix() + "=\"" + SystemTestData.POLYGONS.getNamespaceURI() + "\"" + " xmlns:ogc=\"http://www.opengis.net/ogc\" " + " xmlns:gml=\"http://www.opengis.net/gml\" " + " xmlns:wfs=\"http://www.opengis.net/wfs\" " + "> " + "<wfs:Query typeName=\"" + SystemTestData.POLYGONS.getPrefix() + ":" + SystemTestData.POLYGONS.getLocalPart() + "\" srsName=\"" + TARGET_CRS_CODE + "\">" + "<wfs:PropertyName>cgf:polygonProperty</wfs:PropertyName> " + "<ogc:Filter>" + "<ogc:Intersects>" + "<ogc:PropertyName>polygonProperty</ogc:PropertyName>" + "<gml:Box>" + "<gml:coord>" + "<gml:X>" + cr[0] + "</gml:X>" + "<gml:Y>" + cr[1] + "</gml:Y>" + "</gml:coord>" + "<gml:coord>" + "<gml:X>" + cr[2] + "</gml:X>" + "<gml:Y>" + cr[3] + "</gml:Y>" + "</gml:coord>" + "</gml:Box>" + "</ogc:Intersects>" + "</ogc:Filter>" + "</wfs:Query> " + "</wfs:GetFeature>"; dom = postAsDOM( "wfs", xml ); assertEquals( 1, dom.getElementsByTagName( SystemTestData.POLYGONS.getPrefix() + ":" + SystemTestData.POLYGONS.getLocalPart()).getLength() ); } /** * Returns the transformed corners of the POLYGON layer bbox * * @throws TransformException */ private double[] getTransformedPolygonsLayerBBox() throws Exception, TransformException { String q = "wfs?request=getfeature&service=wfs&version=1.0&typeName=" + SystemTestData.POLYGONS.getLocalPart(); Document dom = getAsDOM( q ); Element envelope = getFirstElementByTagName(dom, "gml:Box" ); String coordinates = getFirstElementByTagName(envelope, "gml:coordinates").getFirstChild().getNodeValue(); String lc = coordinates.split(" ")[0]; String uc = coordinates.split(" ")[1] ; double[] c = new double[]{ Double.parseDouble(lc.split( "," )[0]), Double.parseDouble(lc.split( "," )[1]), Double.parseDouble(uc.split( "," )[0]), Double.parseDouble(uc.split( "," )[1]) }; double[] cr = new double[4]; tx.transform(c, 0, cr, 0, 2); return cr; } private void runTest( Document dom1, Document dom2, MathTransform tx ) throws Exception { Element box = getFirstElementByTagName(dom1.getDocumentElement(), "gml:Box"); Element coordinates = getFirstElementByTagName(box, "gml:coordinates"); double[] d1 = coordinates(coordinates.getFirstChild().getNodeValue()); box = getFirstElementByTagName(dom2.getDocumentElement(), "gml:Box"); coordinates = getFirstElementByTagName(box, "gml:coordinates"); double[] d2 = coordinates(coordinates.getFirstChild().getNodeValue()); double[] d3 = new double[d1.length]; tx.transform(d1, 0, d3, 0, d1.length/2); for ( int i = 0; i < d2.length; i++ ) { assertEquals( d2[i], d3[i], 0.001 ); } } private double[] coordinates(String string) { StringTokenizer st = new StringTokenizer(string, " "); double[] coordinates = new double[st.countTokens()*2]; int i = 0; while(st.hasMoreTokens()) { String tuple = st.nextToken(); coordinates[i++] = Double.parseDouble(tuple.split(",")[0]); coordinates[i++] = Double.parseDouble(tuple.split(",")[1]); } return coordinates; } }