/* * 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 com.vividsolutions.jts.geom.GeometryFactory; import org.apache.sis.measure.MeasurementRange; import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox; import org.apache.sis.storage.DataStoreException; import org.geotoolkit.coverage.grid.GridCoverage2D; import org.geotoolkit.data.FeatureCollection; import org.geotoolkit.data.FeatureIterator; import org.geotoolkit.data.FeatureStore; import org.geotoolkit.data.query.Query; import org.geotoolkit.data.query.QueryBuilder; import org.geotoolkit.display.PortrayalException; import org.geotoolkit.factory.FactoryFinder; import org.geotoolkit.feature.type.AttributeDescriptor; import org.geotoolkit.feature.type.FeatureType; import org.geotoolkit.feature.type.PropertyDescriptor; import org.geotoolkit.map.FeatureMapLayer; import org.geotoolkit.map.MapBuilder; import org.geotoolkit.map.MapLayer; import org.geotoolkit.style.MutableStyle; import org.geotoolkit.style.RandomStyleBuilder; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory; import org.opengis.filter.PropertyIsEqualTo; import org.opengis.filter.expression.PropertyName; import org.opengis.geometry.Envelope; import org.opengis.metadata.extent.GeographicBoundingBox; import java.awt.*; import java.io.IOException; import java.util.Date; import java.util.List; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import java.util.logging.Level; import org.geotoolkit.feature.Feature; import org.opengis.util.GenericName; /** * Default layer details for a datastore type. * * @author Johann Sorel (Geomatys) */ public class DefaultFeatureData extends AbstractData implements FeatureData { protected static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory(); protected static final GeographicBoundingBox DUMMY_BBOX = new DefaultGeographicBoundingBox(-180, 180, -77, +77); /** * Defines the number of pixels we want to add to the specified coordinates given by * the GetFeatureInfo request. */ protected static final int MARGIN = 4; protected final FeatureStore store; protected final PropertyName dateStartField; protected final PropertyName dateEndField; protected final PropertyName elevationStartField; protected final PropertyName elevationEndField; /** * Data version date. Use to query Features is input FeatureStore is versioned. */ protected final Date versionDate; /** * Build a FeatureLayerDetails with layer name, store and favorite style names. * * @param name layer name * @param store FeatureStore * @param favorites style names */ public DefaultFeatureData(GenericName name, FeatureStore store, List<String> favorites){ this(name,store,favorites,null,null,null,null,null); } /** * Build a FeatureLayerDetails with layer name, store, favorite style names and data version date. * * @param name layer name * @param store FeatureStore * @param favorites style names * @param versionDate data version date of the layer (can be null) */ public DefaultFeatureData(GenericName name, FeatureStore store, List<String> favorites, Date versionDate){ this(name,store,favorites,null,null,null,null, versionDate); } /** * Build a FeatureLayerDetails with layer name, store, favorite style names and temporal/elevation filters. * * @param name layer name * @param store FeatureStore * @param favorites style names * @param dateStart temporal filter start * @param dateEnd temporal filter end * @param elevationStart elevation filter start * @param elevationEnd elevation filter end */ public DefaultFeatureData(GenericName name, FeatureStore store, List<String> favorites, String dateStart, String dateEnd, String elevationStart, String elevationEnd){ this(name,store,favorites,dateStart,dateEnd,elevationStart,elevationEnd , null); } /** * Build a FeatureLayerDetails with layer name, store, favorite style names, temporal/elevation filters and * data version date. * * @param name layer name * @param store FeatureStore * @param favorites style names * @param dateStart temporal filter start * @param dateEnd temporal filter end * @param elevationStart elevation filter start * @param elevationEnd elevation filter end * @param versionDate data version date of the layer (can be null) */ public DefaultFeatureData(GenericName name, FeatureStore store, List<String> favorites, String dateStart, String dateEnd, String elevationStart, String elevationEnd, Date versionDate){ super(name,favorites); if(store == null){ throw new IllegalArgumentException("FeatureSource can not be null."); } /*try { if (!store.getNames().contains(name)) { throw new IllegalArgumentException("Provided name " + name + " is not in the datastore known names"); } } catch (DataStoreException ex) { LOGGER.log(Level.WARNING, ex.getLocalizedMessage(), ex); }*/ this.store = store; this.versionDate = versionDate; final FilterFactory ff = FactoryFinder.getFilterFactory(null); if(dateStart != null) this.dateStartField = ff.property(dateStart); else this.dateStartField = null; if(dateEnd != null) this.dateEndField = ff.property(dateEnd); else this.dateEndField = null; if(elevationStart != null) this.elevationStartField = ff.property(elevationStart); else this.elevationStartField = null; if(elevationEnd != null) this.elevationEndField = ff.property(elevationEnd); else this.elevationEndField = null; } protected MapLayer createMapLayer(MutableStyle style, final Map<String, Object> params) throws DataStoreException { if(style == null && favorites.size() > 0){ //no style provided, try to get the favorite one //there are some favorites styles final String namedStyle = favorites.get(0); style = StyleProviders.getInstance().get(namedStyle); } final FeatureType featureType = store.getFeatureType(name); if(style == null){ //no favorites defined, create a default one style = RandomStyleBuilder.createDefaultVectorStyle(featureType); } final FeatureMapLayer layer = MapBuilder.createFeatureLayer((FeatureCollection)getOrigin(), style); final String title = getName().tip().toString(); layer.setName(title); layer.setDescription(StyleProviders.STYLE_FACTORY.description(title,title)); return layer; } /** * {@inheritDoc} */ @Override public FeatureStore getStore(){ return store; } /** * {@inheritDoc} */ @Override public final MapLayer getMapLayer(MutableStyle style, final Map<String, Object> params) throws PortrayalException{ final MapLayer layer; try { layer = createMapLayer(style, params); } catch (DataStoreException ex) { throw new PortrayalException(ex); } // EXTRA FILTER extra parameter //////////////////////////////////////// if (params != null && layer instanceof FeatureMapLayer) { final Map<String,?> extras = (Map<String, ?>) params.get(KEY_EXTRA_PARAMETERS); if (extras != null){ Filter filter = null; for (String key : extras.keySet()) { if (key.equalsIgnoreCase("cql_filter")) { final Object extra = extras.get(key); String cqlFilter = null; if (extra instanceof List) { cqlFilter = ((List) extra).get(0).toString(); } else if (extra instanceof String){ cqlFilter = (String)extra; } if (cqlFilter != null) { filter = buildCQLFilter(cqlFilter, filter); } } else if (key.startsWith("dim_") || key.startsWith("DIM_")) { final String dimValue = ((List) extras.get(key)).get(0).toString(); final String dimName = key.substring(4); filter = buildDimFilter(dimName, dimValue, filter); } } if (filter != null) { final FeatureMapLayer fml = (FeatureMapLayer) layer; final FeatureType type = fml.getCollection().getFeatureType(); if (filter instanceof PropertyIsEqualTo) { final String propName = ((PropertyName)((PropertyIsEqualTo)filter).getExpression1()).getPropertyName(); for (PropertyDescriptor desc : type.getDescriptors()) { if (desc.getName().tip().toString().equalsIgnoreCase(propName)) { fml.setQuery(QueryBuilder.filtered(type.getName(), filter)); break; } } } } } } //////////////////////////////////////////////////////////////////////// return layer; } /** * {@inheritDoc} */ @Override public Envelope getEnvelope() throws DataStoreException { final QueryBuilder query = new QueryBuilder(name); query.setVersionDate(versionDate); return store.getEnvelope(query.buildQuery()); } /** * {@inheritDoc} */ @Override public SortedSet<Date> getAvailableTimes() throws DataStoreException { final SortedSet<Date> dates = new TreeSet<>(); FeatureIterator features = null; if(dateStartField != null){ try{ final AttributeDescriptor desc = (AttributeDescriptor) dateStartField.evaluate(store.getFeatureType(name)); if(desc == null){ LOGGER.log(Level.WARNING , "Invalide field : "+ dateStartField + " Doesnt exists in layer :" + name); return dates; } final Class type = desc.getType().getBinding(); if( !(Date.class.isAssignableFrom(type)) ){ LOGGER.log(Level.WARNING , "Invalide field type for dates, layer " + name +", must be a Date, found a " + type); return dates; } final QueryBuilder builder = new QueryBuilder(); builder.setTypeName(name); builder.setProperties(new String[]{dateStartField.getPropertyName()}); builder.setVersionDate(versionDate); final Query query = builder.buildQuery(); final FeatureCollection coll = store.createSession(false).getFeatureCollection(query); features = coll.iterator(); while(features.hasNext()){ final Feature sf = features.next(); final Date date = dateStartField.evaluate(sf,Date.class); if(date != null){ dates.add(date); } } } catch(DataStoreException ex) { LOGGER.log(Level.WARNING , "Could not evaluate dates",ex); } finally { if(features != null) features.close(); } } return dates; } /** * {@inheritDoc} */ @Override public SortedSet<Number> getAvailableElevations() throws DataStoreException { final SortedSet<Number> elevations = new TreeSet<>(); FeatureIterator features = null; if (elevationStartField != null) { try { final AttributeDescriptor desc = (AttributeDescriptor) elevationStartField.evaluate(store.getFeatureType(name)); if(desc == null){ LOGGER.log(Level.WARNING , "Invalide field : "+ elevationStartField + " Doesnt exists in layer :" + name); return elevations; } final Class type = desc.getType().getBinding(); if (!(Number.class.isAssignableFrom(type)) ){ LOGGER.log(Level.WARNING , "Invalide field type for elevations, layer " + name +", must be a Number, found a " + type); return elevations; } final QueryBuilder builder = new QueryBuilder(); builder.setTypeName(name); builder.setProperties(new String[]{elevationStartField.getPropertyName()}); builder.setVersionDate(versionDate); final Query query = builder.buildQuery(); final FeatureCollection coll = store.createSession(false).getFeatureCollection(query); features = coll.iterator(); while(features.hasNext()){ final Feature sf = features.next(); final Number ele = elevationStartField.evaluate(sf,Number.class); if(ele != null){ elevations.add(ele); } } } catch(DataStoreException ex) { LOGGER.log(Level.WARNING , "Could not evaluate elevationss",ex); } finally { if(features != null) features.close(); } } return elevations; } /** * {@inheritDoc} */ @Override public MeasurementRange<?>[] getSampleValueRanges() { return new MeasurementRange<?>[0]; } /** * {@inheritDoc} private MutableStyle getDefaultStyle(){ try { return StyleProviderProxy.STYLE_RANDOM_FACTORY.createDefaultVectorStyle(store.getFeatureType(name)); } catch (DataStoreException ex) { return StyleProviderProxy.STYLE_RANDOM_FACTORY.createPolygonStyle(); } }*/ /** * Returns {@code null}. This method should not be used in this context. * * @todo the super class should probably not define this method as abstract. */ @Override public GridCoverage2D getCoverage(final Envelope envelope, final Dimension dimension, final Double elevation, final Date time) throws DataStoreException, IOException { return null; } /** * Returns a {@linkplain FeatureCollection feature collection} containing all the data. */ @Override public Object getOrigin() { final QueryBuilder builder = new QueryBuilder(); builder.setTypeName(name); //build query using versionDate if not null and sotre support versioning. if (store.getQueryCapabilities().handleVersioning()) { if (versionDate != null) { builder.setVersionDate(versionDate); } } final Query query = builder.buildQuery(); return store.createSession(false).getFeatureCollection(query); } /** * Specifies that the type of this layer is feature. */ @Override public TYPE getType() { return TYPE.FEATURE; } }