/* * Constellation - An open source and standard compliant SDI * http://www.constellation-sdi.org * * Copyright 2014 Geomatys. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.constellation.provider; import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox; import org.apache.sis.storage.DataStoreException; import org.apache.sis.util.logging.Logging; import org.constellation.ServiceDef.Query; import org.geotoolkit.cql.CQL; import org.geotoolkit.cql.CQLException; import org.geotoolkit.display.PortrayalException; import org.geotoolkit.display2d.ext.legend.DefaultLegendService; import org.geotoolkit.display2d.ext.legend.LegendTemplate; import org.geotoolkit.display2d.service.DefaultGlyphService; import org.geotoolkit.factory.FactoryFinder; import org.geotoolkit.factory.Hints; import org.geotoolkit.map.MapItem; import org.geotoolkit.map.MapLayer; import org.geotoolkit.style.MutableFeatureTypeStyle; import org.geotoolkit.style.MutableRule; import org.geotoolkit.style.MutableStyle; import org.geotoolkit.util.DateRange; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory2; import org.opengis.geometry.Envelope; import org.opengis.metadata.extent.GeographicBoundingBox; import org.opengis.referencing.operation.TransformException; import org.opengis.style.Style; import java.awt.*; import java.awt.image.BufferedImage; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.SortedSet; import java.util.logging.Level; import java.util.logging.Logger; import org.opengis.util.GenericName; /** * Abstract layer, handle name and styles. * * @author Johann Sorel (Geomatys) * @author Cédric Briançon (Geomatys) */ public abstract class AbstractData implements Data{ protected static final Logger LOGGER = Logging.getLogger("org.constellation.provider"); /** * Favorites styles associated with this layer. */ @Deprecated protected final List<String> favorites; /** * Layer name */ protected final GenericName name; public AbstractData(GenericName name, List<String> favorites){ this.name = name; if(favorites == null){ this.favorites = Collections.emptyList(); }else{ this.favorites = Collections.unmodifiableList(favorites); } } /** * {@inheritDoc} */ @Override public GenericName getName() { return name; } @Override public Object getOrigin(){ return null; } /** * Returns the time range of this layer. The default implementation invoked * {@link #getAvailableTimes()} and extract the first and last date from it. * Subclasses are encouraged to provide more efficient implementation. */ @Override public DateRange getDateRange() throws DataStoreException { final SortedSet<Date> dates = getAvailableTimes(); if (dates != null && !dates.isEmpty()) { return new DateRange(dates.first(), dates.last()); } return null; } /** * {@inheritDoc} */ @Override public BufferedImage getLegendGraphic(Dimension dimension, final LegendTemplate template, final Style style, final String rule, final Double scale) throws PortrayalException { MutableStyle mutableStyle = null; if (style != null) { mutableStyle = (MutableStyle) style; } final MapItem mapItem = getMapLayer(mutableStyle, null); if(!(mapItem instanceof MapLayer)){ //we can't render a glyph for a muli-layer return DefaultLegendService.portray(template, mapItem, dimension); } final MapLayer maplayer = (MapLayer) mapItem; if (template == null) { if (dimension == null) { dimension = DefaultGlyphService.glyphPreferredSize(mutableStyle, dimension, null); } // If a rule is given, we try to find the matching one in the style. // If none matches, then we can just apply all the style. if (rule != null) { final MutableRule mr = findRuleByNameInStyle(rule, mutableStyle); if (mr != null) { return DefaultGlyphService.create(mr, dimension, maplayer); } } // Otherwise, if there is a scale, we can filter rules. if (scale != null) { final MutableRule mr = findRuleByScaleInStyle(scale, mutableStyle); if (mr != null) { return DefaultGlyphService.create(mr, dimension, maplayer); } } return DefaultGlyphService.create(mutableStyle, dimension, maplayer); } try { return DefaultLegendService.portray(template, mapItem, dimension); } catch (PortrayalException ex) { LOGGER.log(Level.INFO, ex.getMessage(), ex); } if (dimension == null) { dimension = DefaultGlyphService.glyphPreferredSize(mutableStyle, dimension, null); } // If a rule is given, we try to find the matching one in the style. // If none matches, then we can just apply all the style. if (rule != null) { final MutableRule mr = findRuleByNameInStyle(rule, mutableStyle); if (mr != null) { return DefaultGlyphService.create(mr, dimension, maplayer); } } // Otherwise, if there is a scale, we can filter rules. if (scale != null) { final MutableRule mr = findRuleByScaleInStyle(scale, mutableStyle); if (mr != null) { return DefaultGlyphService.create(mr, dimension, maplayer); } } return DefaultGlyphService.create(mutableStyle, dimension, maplayer); } /** * {@inheritDoc} */ @Override public Dimension getPreferredLegendSize(final LegendTemplate template, final MutableStyle ms) throws PortrayalException { final MapItem ml = getMapLayer(ms, null); return DefaultLegendService.legendPreferredSize(template, ml); } /** * Returns the {@linkplain MutableRule rule} which matches with the given name, or {@code null} * if none. * * @param ruleName The rule name to try finding in the given style. * @param ms The style for which we want to extract the rule. * @return The rule with the given name, or {@code null} if no one matches. */ private MutableRule findRuleByNameInStyle(final String ruleName, final MutableStyle ms) { if (ruleName == null) { return null; } for (final MutableFeatureTypeStyle mfts : ms.featureTypeStyles()) { for (final MutableRule mutableRule : mfts.rules()) { if (ruleName.equals(mutableRule.getName())) { return mutableRule; } } } return null; } /** * Returns the {@linkplain MutableRule rule} which can be applied for the given scale, or * {@code null} if no rules can be used at this scale. * * @param scale The scale. * @param ms The style for which we want to extract a rule for the given scale. * @return The first rule for the given scale that can be applied, or {@code null} if no * one matches. */ private MutableRule findRuleByScaleInStyle(final Double scale, final MutableStyle ms) { if (scale == null) { return null; } for (final MutableFeatureTypeStyle mfts : ms.featureTypeStyles()) { for (final MutableRule mutableRule : mfts.rules()) { if (scale < mutableRule.getMaxScaleDenominator() && scale > mutableRule.getMinScaleDenominator()) { return mutableRule; } } } return null; } /** * Always returns {@code true}. */ @Override public boolean isQueryable(final Query query) { return true; } /** * {@inheritDoc} */ @Override public final GeographicBoundingBox getGeographicBoundingBox() throws DataStoreException { try { final Envelope env = getEnvelope(); if (env != null) { final DefaultGeographicBoundingBox result = new DefaultGeographicBoundingBox(); result.setBounds(env); return result; } else { LOGGER.warning("Null boundingBox for Layer:" + name + ". Returning World BBOX."); return new DefaultGeographicBoundingBox(-180, 180, -90, 90); } } catch (TransformException ex) { throw new DataStoreException(ex); } } protected Filter buildCQLFilter(final String cql, final Filter filter) { final FilterFactory2 factory = (FilterFactory2) FactoryFinder.getFilterFactory(new Hints(Hints.FILTER_FACTORY,FilterFactory2.class)); try { final Filter cqlfilter = CQL.parseFilter(cql); if (filter != null) { return factory.and(cqlfilter, filter); } else { return cqlfilter; } } catch (CQLException ex) { LOGGER.log(Level.INFO, ex.getMessage(),ex); } return filter; } protected Filter buildDimFilter(final String dimName, final String dimValue, final Filter filter) { final FilterFactory2 factory = (FilterFactory2) FactoryFinder.getFilterFactory(new Hints(Hints.FILTER_FACTORY,FilterFactory2.class)); Object value = dimValue; try { value = Double.parseDouble(dimValue); } catch (NumberFormatException ex) { // not a number } final Filter extraDimFilter = factory.equals(factory.property(dimName), factory.literal(value)); if (filter != null) { return factory.and(extraDimFilter, filter); } else { return extraDimFilter; } } }