/* (c) 2014 - 2016 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.catalog;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import org.geoserver.catalog.LayerGroupInfo.Mode;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;
/**
* Utility class to work with nested layer groups and extract selected sub-parts of it
*/
public class LayerGroupHelper {
private LayerGroupInfo group;
/**
*
* @param group
*/
public LayerGroupHelper(LayerGroupInfo group) {
this.group = group;
}
/**
*
*
*/
public List<LayerInfo> allLayers() {
List<LayerInfo> layers = new ArrayList<LayerInfo>();
allLayers(group, layers);
return layers;
}
private static void allLayers(LayerGroupInfo group, List<LayerInfo> layers) {
if (LayerGroupInfo.Mode.EO.equals(group.getMode())) {
layers.add(group.getRootLayer());
}
for (PublishedInfo p : group.getLayers()) {
if (p instanceof LayerInfo) {
LayerInfo l = (LayerInfo) p;
layers.add(l);
} else {
allLayers((LayerGroupInfo) p, layers);
}
}
}
/**
* Returns all the groups contained in this group (including the group itself)
* @return
*/
public List<LayerGroupInfo> allGroups() {
List<LayerGroupInfo> groups = new ArrayList<LayerGroupInfo>();
allGroups(group, groups);
return groups;
}
private static void allGroups(LayerGroupInfo group, List<LayerGroupInfo> groups) {
groups.add(group);
for (PublishedInfo p : group.getLayers()) {
if (p instanceof LayerGroupInfo) {
LayerGroupInfo g = (LayerGroupInfo) p;
allGroups(g, groups);
}
}
}
/**
*
*
*/
public List<StyleInfo> allStyles() {
List<StyleInfo> styles = new ArrayList<StyleInfo>();
allStyles(group, styles);
return styles;
}
private static void allStyles(LayerGroupInfo group, List<StyleInfo> styles) {
if (LayerGroupInfo.Mode.EO.equals(group.getMode())) {
styles.add(group.getRootLayerStyle());
}
int size = group.getLayers().size();
for (int i = 0; i < size; i++) {
PublishedInfo p = group.getLayers().get(i);
if (p instanceof LayerInfo) {
styles.add(group.getStyles().get(i));
} else {
allStyles((LayerGroupInfo) p, styles);
}
}
}
public List<LayerInfo> allLayersForRendering() {
List<LayerInfo> layers = new ArrayList<LayerInfo>();
allLayersForRendering(group, layers, true);
return layers;
}
private static void allLayersForRendering(LayerGroupInfo group, List<LayerInfo> layers, boolean root) {
switch (group.getMode()) {
case EO:
layers.add(group.getRootLayer());
break;
case CONTAINER:
if (root) {
throw new UnsupportedOperationException("LayerGroup mode " + Mode.CONTAINER.getName()
+ " can not be rendered");
}
// continue to default behaviour:
default:
for (PublishedInfo p : group.getLayers()) {
if (p instanceof LayerInfo) {
LayerInfo l = (LayerInfo) p;
layers.add(l);
} else {
allLayersForRendering((LayerGroupInfo) p, layers, false);
}
}
}
}
public List<StyleInfo> allStylesForRendering() {
List<StyleInfo> styles = new ArrayList<StyleInfo>();
allStylesForRendering(group, styles, true);
return styles;
}
private static void allStylesForRendering(LayerGroupInfo group, List<StyleInfo> styles, boolean root) {
switch (group.getMode()) {
case EO:
styles.add(group.getRootLayerStyle());
break;
case CONTAINER:
if (root) {
throw new UnsupportedOperationException("LayerGroup mode " + Mode.CONTAINER.getName()
+ " can not be rendered");
}
// continue to default behaviour:
default:
int size = group.getLayers().size();
for (int i = 0; i < size; i++) {
PublishedInfo p = group.getLayers().get(i);
if (p instanceof LayerInfo) {
styles.add(group.getStyles().get(i));
} else {
allStylesForRendering((LayerGroupInfo) p, styles, false);
}
}
}
}
/**
*
* @param crs
*/
public void calculateBounds(CoordinateReferenceSystem crs) throws Exception {
List<LayerInfo> layers = allLayers();
if (layers.isEmpty()) {
return;
}
LayerInfo l = layers.get(0);
ReferencedEnvelope bounds = new ReferencedEnvelope(crs);
for (int i = 0; i < layers.size(); i++) {
l = layers.get(i);
bounds.expandToInclude(transform(l.getResource().getLatLonBoundingBox(), crs));
}
group.setBounds(bounds);
}
/**
* Use the CRS's defined bounds to populate the LayerGroup bounds.
*
* If the CRS has no bounds then the layer group bounds are set to null instead
*
* @param crs
*/
public void calculateBoundsFromCRS(CoordinateReferenceSystem crs) {
Envelope crsEnvelope = CRS.getEnvelope(crs);
if (crsEnvelope != null) {
ReferencedEnvelope refEnvelope = new ReferencedEnvelope(crsEnvelope);
this.group.setBounds(refEnvelope);
} else {
this.group.setBounds(null);
}
}
/**
*
*/
public void calculateBounds() throws Exception {
List<LayerInfo> layers = allLayers();
if (layers.isEmpty()) {
return;
}
LayerInfo l = layers.get(0);
ReferencedEnvelope bounds = l.getResource().boundingBox();
boolean latlon = false;
if (bounds == null) {
bounds = l.getResource().getLatLonBoundingBox();
latlon = true;
}
if (bounds == null) {
throw new IllegalArgumentException(
"Could not calculate bounds from layer with no bounds, " + l.getName());
}
for (int i = 1; i < layers.size(); i++) {
l = layers.get(i);
ReferencedEnvelope re;
ResourceInfo resource = l.getResource();
if (latlon) {
re = resource.getLatLonBoundingBox();
} else {
re = resource.boundingBox();
if(re == null) {
re = resource.getLatLonBoundingBox();
}
}
re = transform(re, bounds.getCoordinateReferenceSystem());
if (re == null) {
throw new IllegalArgumentException(
"Could not calculate bounds from layer with no bounds, " + l.getName());
}
bounds.expandToInclude(re);
}
group.setBounds(bounds);
}
/**
* Helper method for transforming an envelope.
*/
private static ReferencedEnvelope transform(ReferencedEnvelope e, CoordinateReferenceSystem crs) throws TransformException, FactoryException {
if (!CRS.equalsIgnoreMetadata(crs, e.getCoordinateReferenceSystem())) {
return e.transform(crs, true);
}
return e;
}
public Stack<LayerGroupInfo> checkLoops() {
Stack<LayerGroupInfo> path = new Stack<LayerGroupInfo>();
if (checkLoops(group, path)) {
return path;
} else {
return null;
}
}
public String getLoopAsString(Stack<LayerGroupInfo> path) {
if (path == null) {
return "";
}
StringBuilder s = new StringBuilder();
for (LayerGroupInfo g : path) {
s.append("/").append(g.getName());
}
return s.toString();
}
private static boolean checkLoops(LayerGroupInfo group, Stack<LayerGroupInfo> path) {
path.push(group);
if (group.getLayers() != null) {
for (PublishedInfo child : group.getLayers()) {
if (child instanceof LayerGroupInfo) {
if (isGroupInStack((LayerGroupInfo) child, path)) {
path.push((LayerGroupInfo) child);
return true;
} else if (checkLoops((LayerGroupInfo) child, path)) {
return true;
}
}
}
}
path.pop();
return false;
}
private static boolean isGroupInStack(LayerGroupInfo group, Stack<LayerGroupInfo> path) {
for (LayerGroupInfo groupInPath : path) {
if (groupInPath.getId() != null && groupInPath.getId().equals(group.getId())) {
return true;
}
}
return false;
}
}