/* (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.kml;
import static org.custommonkey.xmlunit.XMLAssert.assertXpathEvaluatesTo;
import static org.custommonkey.xmlunit.XMLAssert.assertXpathExists;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.xml.namespace.QName;
import org.custommonkey.xmlunit.XMLUnit;
import org.custommonkey.xmlunit.XpathEngine;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.data.test.MockData;
import org.geoserver.data.test.SystemTestData;
import org.geoserver.data.test.SystemTestData.LayerProperty;
import org.geoserver.ows.util.KvpUtils;
import org.geoserver.wms.WMSTestSupport;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope3D;
import org.geotools.referencing.CRS;
import org.junit.Before;
import org.junit.Test;
import org.w3c.dom.Document;
public class KMLSuperOverlayTest extends WMSTestSupport {
public static QName DISPERSED_FEATURES = new QName(MockData.SF_URI, "Dispersed", MockData.SF_PREFIX);
public static QName BOULDER = new QName(MockData.SF_URI, "boulder", MockData.SF_PREFIX);
private XpathEngine xpath;
@Before
public void setupXPath() {
xpath = XMLUnit.newXpathEngine();
}
@Override
protected void setUpTestData(SystemTestData testData) throws Exception {
super.setUpTestData(testData);
testData.setUpWcs10RasterLayers();
}
@Override
protected void onSetUp(SystemTestData testData) throws Exception {
super.onSetUp(testData);
Catalog catalog = getCatalog();
testData.addStyle("allsymbolizers", "allsymbolizers.sld", getClass(), catalog);
testData.addStyle("SingleFeature", "singlefeature.sld", getClass(), catalog);
testData.addStyle("Bridge", "bridge.sld", getClass(), catalog);
testData.copyTo(getClass().getResourceAsStream("bridge.png"), "styles/bridge.png");
testData.addVectorLayer(DISPERSED_FEATURES, Collections.EMPTY_MAP, "Dispersed.properties",
getClass(), catalog);
Map<SystemTestData.LayerProperty, Object> properties = new HashMap<SystemTestData.LayerProperty, Object>();
properties.put(LayerProperty.LATLON_ENVELOPE, new ReferencedEnvelope(-105.336,
-105.112, 39.9, 40.116, CRS.decode("EPSG:4326")));
properties.put(LayerProperty.ENVELOPE, new ReferencedEnvelope(3045967, 3108482, 1206627, 1285209, CRS.decode("EPSG:2876")));
properties.put(LayerProperty.SRS, 2876);
testData.addVectorLayer(BOULDER, properties, "boulder.properties", getClass(), catalog);
// set a low regionation limit so that superoverlays actually have something to do
FeatureTypeInfo ft = getCatalog().getFeatureTypeByName(getLayerId(MockData.BASIC_POLYGONS));
ft.getMetadata().put("kml.regionateFeatureLimit", 1);
getCatalog().save(ft);
}
/**
* Verify that the tiles are produced for a request that encompasses the world.
*/
@Test
public void testWorldBoundsSuperOverlay() throws Exception {
Document document = getAsDOM("wms/kml?layers=" + getLayerId(MockData.BASIC_POLYGONS) + ","
+ getLayerId(DISPERSED_FEATURES) + "&mode=superoverlay");
// print(document);
assertEquals("kml", document.getDocumentElement().getNodeName());
// two folders, one per layer
assertEquals("2", xpath.evaluate("count(//kml:Folder)", document));
// regions
assertEquals(9, document.getElementsByTagName("Region").getLength());
// links
assertEquals("8", xpath.evaluate("count(//kml:NetworkLink)", document));
// no ground overlays, direct links to contents instead
assertEquals("0", xpath.evaluate("count(//kml:GroundOverlay)", document));
// overall bbox
assertXpathEvaluatesTo("90.0", "//kml:Region/kml:LatLonAltBox/kml:north", document);
assertXpathEvaluatesTo("-90.0", "//kml:Region/kml:LatLonAltBox/kml:south", document);
assertXpathEvaluatesTo("180.0", "//kml:Region/kml:LatLonAltBox/kml:east", document);
assertXpathEvaluatesTo("-180.0", "//kml:Region/kml:LatLonAltBox/kml:west", document);
// check we have contents starting from the top
assertXpathExists("//kml:NetworkLink[kml:name='contents-0']", document);
assertXpathExists("//kml:NetworkLink[kml:name='contents-1']", document);
}
/**
* Checks what happens when the data bbox is at the crossing of a parent tile
* that is two levels above the bbox itself
*/
@Test
public void testCrossingSuperoverlay() throws Exception {
Document document = getAsDOM("wms/kml?layers=" + getLayerId(BOULDER) + "&mode=superoverlay");
// print(document);
// check the overall bbox (the top-most tile that contains all data)
assertXpathEvaluatesTo("40.78125", "//kml:Region/kml:LatLonAltBox/kml:north", document);
assertXpathEvaluatesTo("39.375", "//kml:Region/kml:LatLonAltBox/kml:south", document);
assertXpathEvaluatesTo("-104.0625", "//kml:Region/kml:LatLonAltBox/kml:east", document);
assertXpathEvaluatesTo("-105.46875", "//kml:Region/kml:LatLonAltBox/kml:west", document);
// however the lookats are pointing to the center of the data set
assertXpathEvaluatesTo("-105.22419118401743", "//kml:Document/kml:LookAt/kml:longitude", document);
assertXpathEvaluatesTo("40.008056082289826", "//kml:Document/kml:LookAt/kml:latitude", document);
assertEquals(-105.2243,
Double.parseDouble(xpath.evaluate("//kml:Document/kml:Folder/kml:LookAt/kml:longitude", document)), 1E-4);
assertEquals(40.0081,
Double.parseDouble(xpath.evaluate("//kml:Document/kml:Folder/kml:LookAt/kml:latitude", document)), 1E-4);
}
/**
* Check the link contents a bit
*/
@Test
public void testSuperOverlayLinkContents() throws Exception {
Document document = getAsDOM("wms/kml?layers=" + getLayerId(MockData.BASIC_POLYGONS) + "&mode=superoverlay");
// print(document);
assertEquals("kml", document.getDocumentElement().getNodeName());
// two folders, one per layer
assertEquals("1", xpath.evaluate("count(//kml:Folder)", document));
// regions
assertEquals(5, document.getElementsByTagName("Region").getLength());
// links
assertEquals("4", xpath.evaluate("count(//kml:NetworkLink)", document));
// no ground overlays, direct links to contents instead
assertEquals("0", xpath.evaluate("count(//kml:GroundOverlay)", document));
// check sub-link 0, it should still got to the network link builder
String link0 = xpath.evaluate("//kml:NetworkLink[kml:name='0']/kml:Link/kml:href", document);
Map<String, Object> kvp0 = KvpUtils.parseQueryString(link0);
assertEquals(NetworkLinkMapOutputFormat.KML_MIME_TYPE, kvp0.get("format"));
assertEquals("-180.0,-90.0,0.0,90.0", kvp0.get("bbox"));
// check sub-link 1, the other side of the world
String link1 = xpath.evaluate("//kml:NetworkLink[kml:name='1']/kml:Link/kml:href", document);
Map<String, Object> kvp1 = KvpUtils.parseQueryString(link1);
assertEquals(NetworkLinkMapOutputFormat.KML_MIME_TYPE, kvp1.get("format"));
assertEquals("0.0,-90.0,180.0,90.0", kvp1.get("bbox"));
}
/**
* Verify that when a tile smaller than one hemisphere is requested, then subtiles are included
* in the result (but only the ones necessary for the data at hand)
*/
@Test
public void testSubtileSuperOverlay() throws Exception {
Document document = getAsDOM("wms/kml?layers=" + getLayerId(MockData.BASIC_POLYGONS) + ","
+ getLayerId(DISPERSED_FEATURES) + "&mode=superoverlay&bbox=0,-90,180,90");
// print(document);
assertEquals("kml", document.getDocumentElement().getNodeName());
// only three regions, the root one and one per network link
assertEquals(3, document.getElementsByTagName("Region").getLength());
// only network links to the data, we don't have enough feature to have sublinks generate
assertEquals(2, document.getElementsByTagName("NetworkLink").getLength());
// no ground overlays
assertEquals(0, document.getElementsByTagName("GroundOverlay").getLength());
}
@Test
public void testGWCIntegration() throws Exception {
Document document = getAsDOM("wms/kml?layers=" + getLayerId(MockData.USA_WORLDIMG)
+ "&mode=superoverlay&superoverlay_mode=cached");
// print(document);
assertEquals("kml", document.getDocumentElement().getNodeName());
// only three regions, the root one and one per network link
assertEquals(1, document.getElementsByTagName("Region").getLength());
// only network links to the data, we don't have enough feature to have sublinks generate
assertEquals(1, document.getElementsByTagName("NetworkLink").getLength());
// no ground overlays
assertEquals(0, document.getElementsByTagName("GroundOverlay").getLength());
// check we have a direct link to GWC
assertEquals("http://localhost:8080/geoserver/gwc/service/kml/cdf:usa.png.kml",
xpath.evaluate("//kml:NetworkLink/kml:Link/kml:href", document));
assertEquals("never",
xpath.evaluate("//kml:NetworkLink/kml:Link/kml:viewRefreshMode", document));
}
@Test
public void testGWCIntegrationFailing() throws Exception {
// force placemarks, this prevents usage of gwc
Document document = getAsDOM("wms/kml?layers=" + getLayerId(MockData.USA_WORLDIMG)
+ "&mode=superoverlay&superoverlay_mode=cached&kmplacemark=true");
// print(document);
assertEquals("kml", document.getDocumentElement().getNodeName());
assertEquals(6, document.getElementsByTagName("Region").getLength());
assertEquals(4, document.getElementsByTagName("NetworkLink").getLength());
// no ground overlays
assertEquals(1, document.getElementsByTagName("GroundOverlay").getLength());
// check we do not have a direct link to GWC, but back to the wms
assertTrue("http://localhost:8080/geoserver/gwc/service/kml/cdf:usa.png.kml",
xpath.evaluate("//kml:NetworkLink/kml:Link/kml:href", document).contains("geoserver/wms"));
}
@Test
public void testKmlTitleFormatOption() throws Exception {
Document document = getAsDOM("wms/kml?layers=" + getLayerId(MockData.BASIC_POLYGONS) + ","
+ getLayerId(DISPERSED_FEATURES) + "&mode=superoverlay&bbox=0,-90,180,90&format_options=kmltitle:myCustomLayerTitle");
// print(document);
assertEquals("kml", document.getDocumentElement().getNodeName());
assertEquals("myCustomLayerTitle", xpath.evaluate("//kml:Document/kml:name", document));
}
}