/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * (c) 2014 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.catalog; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.geotools.coverage.grid.io.GridCoverage2DReader; import org.geotools.util.Utilities; /** * Class containing main definition of a Coverage View, such as, originating coverageStore and composing coverageNames/bands. * * @author Daniele Romagnoli, GeoSolutions SAS * */ public class CoverageView implements Serializable { /** serialVersionUID */ private static final long serialVersionUID = 5504720319141832424L; @Override public String toString() { StringBuilder output = new StringBuilder("CoverageView name=").append(name); output.append("\n\tBands"); for (CoverageBand band : coverageBands) { output.append("\t").append(band.toString()); } return output.toString(); } public static final String BAND_SEPARATOR = "@"; /** * Composition Type, used to specify how output bands should be composed. * * BAND_SELECT: The output band is simply a band selected from the input bands. * FORMULA: The output band is computed by applying a formula on the input selected band(s). * As an instance, the output band could be defined like this: * Speed = SQRT(SELECTED_BAND_1^2 + SELECTED_BAND_2^2) * */ public static enum CompositionType { // Some pieces here have been commented. They should be uncommented once // we enable complex output bands definition using formula. BAND_SELECT { @Override public String displayValue() { return BAND_SELECTION_STRING; } }/*, FORMULA { @Override public String displayValue() { return FORMULA_STRING; } } */; public static CompositionType getDefault() { return BAND_SELECT; } public abstract String displayValue(); public String toValue() { return this.toString(); } final static String BAND_SELECTION_STRING = "Band Selection"; /* final static String FORMULA_STRING = "Formula"; */ } /** * Definition of Input Coverage Bands composing a single {@link CoverageBand} A {@link CoverageBand} may be composed of different * {@link InputCoverageBand}s. * * Current implementation only deal with {@link CoverageBand}s made of a single {@link InputCoverageBand}. Once we allows for Scripts and * Math on bands compositions (like WindSpeedBand = SQRT(UBand^2 + VBand^2)) we will have a {@link CoverageBand} built on top of multiple * {@link InputCoverageBand}s */ public static class InputCoverageBand implements Serializable { public InputCoverageBand() { } /** serialVersionUID */ private static final long serialVersionUID = -2200641260788001394L; @Override public String toString() { return "InputCoverageBand [coverageName=" + coverageName + ", band=" + band + "]"; } public InputCoverageBand(String coverageName, String band) { this.coverageName = coverageName; this.band = band; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((band == null) ? 0 : band.hashCode()); result = prime * result + ((coverageName == null) ? 0 : coverageName.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; InputCoverageBand other = (InputCoverageBand) obj; if (band == null) { if (other.band != null) return false; } else if (!band.equals(other.band)) return false; if (coverageName == null) { if (other.coverageName != null) return false; } else if (!coverageName.equals(other.coverageName)) return false; return true; } public String getCoverageName() { return coverageName; } public void setCoverageName(String coverageName) { this.coverageName = coverageName; } public String getBand() { return band; } public void setBand(String band) { this.band = band; } /** The name of the input coverage from which this band has been extracted. */ private String coverageName; /** Currently, we store here the index of the band in the input coverage. */ private String band; } /** * Definition of a {@link CoverageView}'s Band composing the {@link CoverageView}. * A {@link CoverageBand} is made of * <ul> * <li> a list of {@link InputCoverageBand}s defining which coverages and which bands have been used to compose this band </li> * <li> the type of composition used to configure this band (Currently, only BAND_SELECT is supported) </li> * <li> the definition of this band (It may contain the script, or the RULE to compose that band) </li> * <li> the index in the output coverage (Wondering if this can be removed) </li> * </ul> * */ public static class CoverageBand implements Serializable { /** serialVersionUID */ private static final long serialVersionUID = -7223081117287911988L; public CoverageBand() { } public CoverageBand(List<InputCoverageBand> inputCoverageBands, String definition, int index, CompositionType compositionType) { this.inputCoverageBands = inputCoverageBands; this.definition = definition; this.index = index; this.compositionType = compositionType; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((compositionType == null) ? 0 : compositionType.hashCode()); result = prime * result + ((definition == null) ? 0 : definition.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; CoverageBand other = (CoverageBand) obj; if (compositionType != other.compositionType) return false; if (definition == null) { if (other.definition != null) return false; } else if (!definition.equals(other.definition)) return false; return true; } @Override public String toString() { StringBuilder sb = new StringBuilder("OutputBand\n inputCoverageBands="); for (InputCoverageBand inputBand : inputCoverageBands) { sb.append("[").append(inputBand).append("]"); } sb.append(", definition=").append(definition).append(", index=").append(index) .append(", compositionType=").append(compositionType); return sb.toString(); } /** * The InputCoverageBands composing this band */ private List<InputCoverageBand> inputCoverageBands; /** * The definition of this coverage band. Currently it simply contains the name of the input band. Once we support different compositions, it * will contain the maths... something like, as an instance, speed = sqrt(u_component_of_the_wind@0 ^ 2 + v_component_of_the_wind@0^2). */ private String definition; /** * The index of the band in the output. (Is it really needed?) */ private int index; /** * Type of composition used to define this band. Currently, only {@link CompositionType#BAND_SELECT} is supported. */ private CompositionType compositionType; public String getDefinition() { return definition; } public void setDefinition(String definition) { this.definition = definition; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public CompositionType getCompositionType() { return compositionType; } public void setCompositionType(CompositionType compositionType) { this.compositionType = compositionType; } public List<InputCoverageBand> getInputCoverageBands() { return inputCoverageBands; } public void setInputCoverageBands(List<InputCoverageBand> inputCoverageBands) { this.inputCoverageBands = inputCoverageBands; } } /** * A key to be assigned to the {@link CoverageView} object into metadata map */ public static String COVERAGE_VIEW = "COVERAGE_VIEW"; public CoverageView() { } public CoverageView(String name, List<CoverageBand> coverageBands) { this.name = name; this.coverageBands = coverageBands; } /** The list of {@link CoverageBand}s composing this {@link CoverageView} */ private List<CoverageBand> coverageBands; /** The name assigned to the {@link CoverageView} */ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public List<CoverageBand> getCoverageBands() { return coverageBands; } public void setCoverageBands(List<CoverageBand> coverageBands) { this.coverageBands = coverageBands; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((coverageBands == null) ? 0 : coverageBands.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; CoverageView other = (CoverageView) obj; if (coverageBands == null) { if (other.coverageBands != null) return false; } else if (!coverageBands.equals(other.coverageBands)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } /** * Create a {@link CoverageInfo} * * @param builder * @param storeInfo * @param cinfo * @param name * */ private CoverageInfo buildCoverageInfo(CatalogBuilder builder, CoverageStoreInfo storeInfo, CoverageInfo cinfo, String name) throws Exception { Catalog catalog = storeInfo.getCatalog(); // Get a reader from the pool for this Sample CoverageInfo // (we have to pass it down a CoverageView definition) cinfo.setStore(storeInfo); cinfo.getMetadata().put(CoverageView.COVERAGE_VIEW, this); cinfo.setName(name); cinfo.setNativeCoverageName(name); GridCoverage2DReader reader = (GridCoverage2DReader) catalog.getResourcePool() .getGridCoverageReader(cinfo, name, null); builder.setStore(storeInfo); return builder.buildCoverage(reader, name, null); } /** * Create a new {@link CoverageInfo} for this {@link CoverageView} */ public CoverageInfo createCoverageInfo(String name, CoverageStoreInfo storeInfo, CatalogBuilder builder) throws Exception { Catalog catalog = storeInfo.getCatalog(); CoverageInfo coverageInfo = catalog.getFactory().createCoverage(); CoverageInfo info = buildCoverageInfo(builder, storeInfo, coverageInfo, name); info.getMetadata().put(CoverageView.COVERAGE_VIEW, this); info.setName(name); info.setNativeCoverageName(name); return info; } /** * Update the specified {@link CoverageInfo} with the updated {@link CoverageView} * stored within its metadata * * @param name * @param storeInfo * @param builder * @param coverageInfo */ public void updateCoverageInfo(String name, CoverageStoreInfo storeInfo, CatalogBuilder builder, CoverageInfo coverageInfo) throws Exception { Utilities.ensureNonNull("coverageInfo", coverageInfo); // clean up coverage dimensions for the update coverageInfo.getDimensions().clear(); CoverageInfo info = buildCoverageInfo(builder, storeInfo, coverageInfo, name); coverageInfo.getMetadata().put(CoverageView.COVERAGE_VIEW, this); coverageInfo.getDimensions().addAll(info.getDimensions()); } /** * Get the i-th {@link CoverageBand} * @param index * */ public CoverageBand getBand(final int index) { return coverageBands.get(index); } /** * Get the {@link CoverageBand}s related to the specified coverageName * @param coverageName * */ public List<CoverageBand> getBands(final String coverageName) { List<CoverageBand> bands = new ArrayList<CoverageBand>(); for (CoverageBand coverageBand : coverageBands) { for (InputCoverageBand inputBand : coverageBand.getInputCoverageBands()) { if (inputBand.getCoverageName().equalsIgnoreCase(coverageName)) { bands.add(coverageBand); } } } return bands; } /** * Return the number of {@link CoverageBand}s composing the {@link CoverageView} * */ public int getSize() { return coverageBands != null ? coverageBands.size() : 0; } }