Our clients complained that layer with 25000 point features (complex stylings) took about 2 sec to render. Redraws are often enough since some operations on map are performed. These stylings show some specifics of data and besides, show selection on map.

After my optimizations (dont know should I put this word in quotes :joke:) rendering time changed from 2 sec to 700 miliseconds.

What I did:
0) Profiled my application with YourKit profiler (you can get a fully functional 1 month trial copy for free) - where I found problems. 1) Replaced all CQL quieries like "Attr=1 OR Attr=5 OR Attr=7" with my own AttributeInFilter class (attached). This AttributeInFilter (being a dirty hack) just check the value of specific feature attribute to belong to set of possible values. 2) Replaced all CQL quieries like "(TeamId=1 AND PersonId=2) OR (TeamId=2 AND PersonId=7) OR (TeamId=3 AND PersonId=10)" with my own AttributesInFilter class (attached). This AttributesInFilter (being a dirty hack) checks the tupple of values of specific feature to be contained in particular tupple set. 3) Due to some reasons, I had filters like and(and(f1,not(f2)),f3). I replaced them with and(f1,not(f2),f3).
4) At CashingFeatureSource there is:

        protected Iterator openIterator() {
            Iterator it = features.iterator();
            if(filter != null) {
               it = new FilteringIterator<Feature>(it, filter);
            }
            if(targetSchema != sourceSchema) {
                it = new ReTypingIterator(it, sourceSchema, targetSchema);
            }
            return it;
        }

I changed it to (commented out the ReTypingIterator):
     protected Iterator openIterator() {
            Iterator it = features.iterator();
            if(filter != null) {
               it = new FilteringIterator<Feature>(it, filter);
            }
           /* if(targetSchema != sourceSchema) {
                it = new ReTypingIterator(it, sourceSchema, targetSchema);
            }*/
            return it;
        }
Dont know exactly why it was here, but it works without it, anyway.
5) At FidFilterImplementation (my version is attached) - I added specific code to work with SimpleFeature.
OLD Version:
public boolean evaluate(Object feature) {
        if (feature == null) {
            return false;
        }

        final Set fids = fids();

        final String attPath = "@id";

PropertyAccessor accessor = PropertyAccessors.findPropertyAccessor(feature, attPath, null, null);

        if (accessor == null) {
            return false;
        }
        Object id = accessor.get(feature, attPath, null);
        return fids.contains(id);
    }

NEW Version:
public boolean evaluate(Object feature) {
        if (feature == null) {
            return false;
        }

        final Set fids = fids();

/// BEGIN ADDED CODE
        if (feature instanceof SimpleFeature){
            SimpleFeature simpleFeature = (SimpleFeature)feature;
            return fids.contains(simpleFeature.getID());
        }
/// END ADDED CODE

        final String attPath = "@id";

PropertyAccessor accessor = PropertyAccessors.findPropertyAccessor(feature, attPath, null, null);

        if (accessor == null) {
            return false;
        }
        Object id = accessor.get(feature, attPath, null);
        return fids.contains(id);
}

I understand this to be non-universal and error prone, but for shapefiles (which is the only case for me) this seems to be working.

Hope this helps,
Sergey


package com.excelsior.lms.gis.data.cache;

import java.util.ArrayList;
import java.util.Set;

import org.geotools.filter.AndImpl;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.FilterFactory;

public class AttributeInFilter extends AndImpl {

  private Set attrValues;
  private String attrName;

  public AttributeInFilter(FilterFactory factory, String attrName, Set 
attrValues) {
    super(factory, new ArrayList());
    this.attrName = attrName;
    this.attrValues = attrValues;
  }

  @Override
  public boolean evaluate(SimpleFeature feature) {
    return attrValues.contains(feature.getAttribute(attrName));
  }

  @Override
  public boolean evaluate(Object object) {
    return evaluate((SimpleFeature) object);
  }
}
/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 * 
 *    (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
 *    
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.filter;

// Geotools dependencies
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Logger;

import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.expression.PropertyAccessor;
import org.geotools.filter.expression.PropertyAccessors;
import org.geotools.filter.expression.SimpleFeaturePropertyAccessorFactory;
import org.opengis.filter.FilterVisitor;
import org.opengis.filter.identity.Identifier;

import org.opengis.feature.simple.SimpleFeature;

/**
 * Defines a ID filter, which holds a list of IDs ( usually feature id;s ). This
 * filter stores a series of IDs, which are used to distinguish features
 * uniquely.
 * <p>
 * Please note that addAllFids( Collection ) may be a performance hog; uDig
 * makes use of its own implementation of FidFilter in order to reuse the
 * internal set of fids between uses.
 * </p>
 * 
 * @author Rob Hranac, TOPP
 * @author Justin Deoliveira, TOPP
 * 
 * TODO: this class shoul be renamed to IdFilterImpl
 * 
 * @source $URL:
 *         
http://svn.geotools.org/geotools/trunk/gt/modules/library/main/src/main/java/org/geotools/filter/FidFilterImpl.java
 $
 * @version $Id: FidFilterImpl.java 31682 2008-10-19 13:23:25Z aaime $
 */
public class FidFilterImpl extends AbstractFilterImpl implements FidFilter {
        /** Logger for the default core module. */
        private static final Logger LOGGER = 
org.geotools.util.logging.Logging.getLogger("org.geotools.core");

        /** List of the Identifer. */
        private Set fids = new HashSet();

        /**
         * Empty constructor.
         * 
         * @deprecated use {@link #FidFilterImpl(Set)}
         */
        protected FidFilterImpl() {
                super(CommonFactoryFinder.getFilterFactory(null));
                filterType = AbstractFilter.FID;
        }

        /**
         * Constructor with first fid set
         * 
         * @param initialFid
         *            The type of comparison.
         * @deprecated use {@link #FidFilterImpl(Set)}
         */
        protected FidFilterImpl(String initialFid) {
                super(CommonFactoryFinder.getFilterFactory(null));
                filterType = AbstractFilter.FID;
                addFid(initialFid);
        }

        /**
         * Constructor which takes {@link 
org.opengis.filter.identity.Identifier},
         * not String.
         * 
         */
        protected FidFilterImpl(Set/* <Identiger> */fids) {
                super(CommonFactoryFinder.getFilterFactory(null));
                filterType = AbstractFilter.FID;
                // check these are really identifiers
                for (Iterator it = fids.iterator(); it.hasNext();) {
                        Object next = it.next();
                        if (!(next instanceof Identifier))
                                throw new ClassCastException("Fids must 
implement Identifier, "
                                                + next.getClass() + " does 
not");
                }
                this.fids = fids;
        }

        /**
         * Returns all the fids in this filter.
         * 
         * @return An array of all the fids in this filter.
         * 
         * @deprecated use {@link #getIDs()}
         */
        public final String[] getFids() {
                return (String[]) fids().toArray(new String[0]);
        }

        /**
         * @see org.opengis.filter.Id#getIDs()
         */
        public Set getIDs() {
                return getFidsSet();
        }

        /**
         * @see org.opengis.filter.Id#getIdentifiers()
         */
        public Set getIdentifiers() {
                return fids;
        }

        /**
         * @see org.opengis.filter.identity.FeatureId#setIDs(Set)
         */
        public void setIDs(Set ids) {
                fids = new HashSet();
                addAllFids(ids);
        }

        /**
         * Accessor method for fid set as Strings.
         * 
         * @return the internally stored fids.
         */
        public Set getFidsSet() {
                return fids();
        }

        /**
         * Helper method to pull out strings from featureId set.
         * 
         * @return
         */
        private Set fids() {
                Set set = new TreeSet();
                for (Iterator i = fids.iterator(); i.hasNext();) {
                        Identifier id = (Identifier) i.next();
                        set.add(id.toString());
                }

                return set;
        }

        /**
         * Adds a feature ID to the filter.
         * 
         * @param fid
         *            A single feature ID.
         * @deprecated
         */
        public final void addFid(String fid) {
                LOGGER.finest("got fid: " + fid);
                fids.add(factory.featureId(fid));
        }

        /**
         * Adds a collection of feature IDs to the filter.
         * 
         * @param fidsToAdd
         *            A collection of feature IDs as strings.
         */
        public void addAllFids(Collection fidsToAdd) {
                if (fidsToAdd == null)
                        return;

                for (Iterator i = fidsToAdd.iterator(); i.hasNext();) {
                        String fid = (String) i.next();
                        addFid(fid);
                }
        }

        /**
         * Removes a feature ID from the filter.
         * 
         * @param fid
         *            A single feature ID.
         */
        public final void removeFid(String fid) {
                if (fid == null) {
                        return;
                }

                for (Iterator f = fids.iterator(); f.hasNext();) {
                        Identifier featureId = (Identifier) f.next();
                        if (fid.equals(featureId.toString())) {
                                f.remove();
                        }
                }

        }

        /**
         * Removes a collection of feature IDs from the filter.
         * 
         * @param fidsToRemove
         *            A collection of feature IDs.
         */
        public void removeAllFids(Collection fidsToRemove) {
                if (fidsToRemove == null)
                        return;

                for (Iterator f = fidsToRemove.iterator(); f.hasNext();) {
                        String fid = (String) f.next();
                        removeFid(fid);
                }
        }

        /**
         * Determines whether or not the given feature's ID matches this filter.
         * <p>
         * In order to get the object's ID, the {@link PropertyAccessor} 
capable of
         * dealing with <code>feature</code> has to support the request of the
         * expression <code>"@id"</code>
         * </p>
         * 
         * @param feature
         *            Specified feature to examine.
         * 
         * @return <tt>true</tt> if the feature's ID matches an fid held by this
         *         filter, <tt>false</tt> otherwise.
         * @see SimpleFeaturePropertyAccessorFactory
         */
        public boolean evaluate(Object feature) {
                if (feature == null) {
                        return false;
                }

                final Set fids = fids();
                
                if (feature instanceof SimpleFeature){
                        SimpleFeature simpleFeature = (SimpleFeature)feature;
                        return fids.contains(simpleFeature.getID());
                }       
                
                final String attPath = "@id";
                
                PropertyAccessor accessor = 
PropertyAccessors.findPropertyAccessor(feature, attPath, null, null);

                if (accessor == null) {
                        return false;
                }
                Object id = accessor.get(feature, attPath, null);
                return fids.contains(id);
        }

        /**
         * Returns a string representation of this filter.
         * 
         * @return String representation of the compare filter.
         */
        public String toString() {
                StringBuffer fidFilter = new StringBuffer();

                Iterator fidIterator = fids.iterator();

                while (fidIterator.hasNext()) {
                        fidFilter.append(fidIterator.next().toString());

                        if (fidIterator.hasNext()) {
                                fidFilter.append(", ");
                        }
                }

                return "[ " + fidFilter.toString() + " ]";
        }

        /**
         * Used by FilterVisitors to perform some action on this filter 
instance.
         * Typicaly used by Filter decoders, but may also be used by any thing 
which
         * needs infomration from filter structure. Implementations should 
always
         * call: visitor.visit(this); It is importatant that this is not left 
to a
         * parent class unless the parents API is identical.
         * 
         * @param visitor
         *            The visitor which requires access to this filter, the 
method
         *            must call visitor.visit(this);
         */
        public Object accept(FilterVisitor visitor, Object extraData) {
                return visitor.visit(this, extraData);
        }

        /**
         * Returns a flag indicating object equality.
         * 
         * @param filter
         *            the filter to test equality on.
         * 
         * @return String representation of the compare filter.
         */
        public boolean equals(Object filter) {
                LOGGER.finest("condition: " + filter);

                if ((filter != null) && (filter.getClass() == this.getClass())) 
{
                        LOGGER.finest("condition: " + ((FidFilterImpl) 
filter).filterType);

                        if (((FidFilterImpl) filter).filterType == 
AbstractFilter.FID) {
                                return fids.equals(((FidFilterImpl) 
filter).fids);
                        } else {
                                return false;
                        }
                } else {
                        return false;
                }
        }

        /**
         * Override of hashCode method.
         * 
         * @return a hash code value for this fid filter object.
         */
        public int hashCode() {
                return fids.hashCode();
        }
}
package com.excelsior.lms.gis.data.cache;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.geotools.filter.OrImpl;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.FilterFactory;


/**
 * @author lsa80
 * Filters features when subset of its attribute belongs to specified set of 
tupples
 */
public class AttributesInFilter extends OrImpl{
  
  private Set<Map<String, Object>> attributeValuesSet; // all tuples should 
have same structure
  private Collection<String> attributes;
  
  public AttributesInFilter(FilterFactory factory, Set<Map<String, Object>> 
attributeValuesSet) {
    super(factory, new ArrayList());
    this.attributeValuesSet = attributeValuesSet;
    this.attributes = attributeValuesSet.iterator().next().keySet();
  }

  @Override
  public boolean evaluate(SimpleFeature feature) {
    Map<String, Object> attributeValues = new HashMap<String, Object>();
    for (String attribute : attributes) {
      attributeValues.put(attribute, feature.getAttribute(attribute));
    }
    return attributeValuesSet.contains(attributeValues);
  }

  @Override
  public boolean evaluate(Object object) {
    return evaluate((SimpleFeature) object);
  }
}
------------------------------------------------------------------------------
Magic Quadrant for Content-Aware Data Loss Prevention
Research study explores the data loss prevention market. Includes in-depth
analysis on the changes within the DLP market, and the criteria used to
evaluate the strengths and weaknesses of these DLP solutions.
http://www.accelacomm.com/jaw/sfnl/114/51385063/
_______________________________________________
Geotools-gt2-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/geotools-gt2-users

Reply via email to