/* (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.wps.gs.download; import java.util.logging.Level; import java.util.logging.Logger; import org.geoserver.catalog.FeatureTypeInfo; import org.geotools.data.Query; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureSource; import org.geotools.factory.GeoTools; import org.geotools.filter.visitor.SimplifyingFilterVisitor; import org.geotools.resources.coverage.FeatureUtilities; import org.geotools.util.logging.Logging; import org.opengis.filter.Filter; import org.opengis.filter.spatial.Intersects; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.util.ProgressListener; import com.vividsolutions.jts.geom.Geometry; /** * Checks whether or not the provided request exceeds the provided download limits for a vectorial resource. * * @author Simone Giannecchini, GeoSolutions SAS * */ class VectorEstimator { private static final Logger LOGGER = Logging.getLogger(VectorEstimator.class); /** The downloadServiceConfiguration object containing the limits to check */ private DownloadServiceConfiguration downloadServiceConfiguration; /** * Constructor. * * @param limits an instance of the {@link DownloadEstimatorProcess} that contains the limits to enforce */ public VectorEstimator(DownloadServiceConfiguration limits) { this.downloadServiceConfiguration = limits; } /** * Checks whether or not the requests exceed download limits for vector data. * * @param resourceInfo the {@link FeatureTypeInfo} to download from * @param roi the {@link Geometry} for the clip/intersection * @param clip whether or not to clip the resulting data (useless for the moment) * @param filter the {@link Filter} to load the data * @param targetCRS the reproject {@link CoordinateReferenceSystem} (useless for the moment) * @param progressListener * @return <code>true</code> if we do not exceeds the limits, <code>false</code> otherwise. * @throws Exception in case something bad happens. */ public boolean execute(FeatureTypeInfo resourceInfo, Geometry roi, boolean clip, Filter filter, CoordinateReferenceSystem targetCRS, final ProgressListener progressListener) throws Exception { // // Do we need to do anything? // if (downloadServiceConfiguration.getMaxFeatures() <= 0) { return true; } // prepare native CRS CoordinateReferenceSystem nativeCRS = DownloadUtilities.getNativeCRS(resourceInfo); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Native CRS is " + nativeCRS.toWKT()); } // // STEP 0 - Push ROI back to native CRS (if ROI is provided) // ROIManager roiManager = null; if (roi != null) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Pushing ROI to native CRS"); } CoordinateReferenceSystem roiCRS = (CoordinateReferenceSystem) roi.getUserData(); roiManager = new ROIManager(roi, roiCRS); // set use nativeCRS roiManager.useNativeCRS(nativeCRS); } // // STEP 1 - Create the Filter // // access feature source and collection of features final SimpleFeatureSource featureSource = (SimpleFeatureSource) resourceInfo .getFeatureSource(null, GeoTools.getDefaultHints()); // basic filter preparation Filter ra = Filter.INCLUDE; if (filter != null) { ra = filter; if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Using filter " + ra); } } // and with the ROI if we have one if (roi != null) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Adding Geometry filter with ROI"); } final String dataGeomName = featureSource.getSchema().getGeometryDescriptor() .getLocalName(); final Intersects intersectionFilter = FeatureUtilities.DEFAULT_FILTER_FACTORY .intersects(FeatureUtilities.DEFAULT_FILTER_FACTORY.property(dataGeomName), FeatureUtilities.DEFAULT_FILTER_FACTORY.literal(roiManager .getSafeRoiInNativeCRS())); ra = FeatureUtilities.DEFAULT_FILTER_FACTORY.and(ra, intersectionFilter); } // simplify filter ra = (Filter) ra.accept(new SimplifyingFilterVisitor(), null); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Counting features"); } // read int count = featureSource.getCount(new Query("counter", ra)); if (count < 0) { // a value minor than "0" means that the store does not provide any counting feature ... lets proceed using the iterator SimpleFeatureCollection features = featureSource.getFeatures(ra); count = features.size(); } if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Feature size is " + count); } // finally checking the number of features accordingly to the "maxfeatures" limit final long maxFeatures = downloadServiceConfiguration.getMaxFeatures(); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Max features limit is " + maxFeatures); } if (maxFeatures > 0 && count > maxFeatures) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, "MaxFeatures limit exceeded. " + count + " > " + maxFeatures); } return false; } if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "MaxFeatures limit not exceeded."); } // limits were not exceeded return true; } }