/* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI * for visualizing and manipulating spatial features with geometry and attributes. * * Copyright (C) 2003 Vivid Solutions * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.vividsolutions.jump.feature; import java.io.Serializable; import java.util.*; import com.vividsolutions.jts.geom.Envelope; /** * Default implementation of FeatureCollection. */ public class FeatureDataset implements FeatureCollection, Serializable { private static final long serialVersionUID = 5573446944516446540L; private FeatureSchema featureSchema; //<<TODO>> Possibly use hashtable to do spatial indexing [Jon Aquino] private ArrayList features; private Envelope envelope = null; /** * Creates a FeatureDataset, initialized with a group of Features. * @param newFeatures an initial group of features to add to this FeatureDataset * @param featureSchema the types of the attributes of the features in this collection */ public FeatureDataset(Collection newFeatures, FeatureSchema featureSchema) { features = new ArrayList(newFeatures); this.featureSchema = featureSchema; } /** * Creates a FeatureDataset. * @param featureSchema the types of the attributes of the features in this collection */ public FeatureDataset(FeatureSchema featureSchema) { this(new ArrayList(), featureSchema); } /** * Returns the Feature at the given index (zero-based). */ public Feature getFeature(int index) { return (Feature) features.get(index); } public FeatureSchema getFeatureSchema() { return featureSchema; } /** * Because the envelope is cached, the envelope may be incorrect if you * later change a Feature's geometry using Feature#setGeometry. */ public Envelope getEnvelope() { if (envelope == null) { envelope = new Envelope(); for (Iterator i = features.iterator(); i.hasNext();) { Feature feature = (Feature) i.next(); envelope.expandToInclude(feature.getGeometry() .getEnvelopeInternal()); } } return envelope; } public List getFeatures() { return Collections.unmodifiableList(features); } public boolean isEmpty() { return size() == 0; } /** *@return a List containing the features whose envelopes intersect the * given envelope */ //<<TODO:DESIGN>> Perhaps return value should be a Set, not a List, because order //doesn't matter. [Jon Aquino] public List query(Envelope envelope) { if (!envelope.intersects(getEnvelope())) { return new ArrayList(); } //<<TODO:NAMING>> Rename this method to getFeatures(Envelope), to parallel //getFeatures() [Jon Aquino] ArrayList queryResult = new ArrayList(); for (Iterator i = features.iterator(); i.hasNext();) { Feature feature = (Feature) i.next(); if (feature.getGeometry().getEnvelopeInternal().intersects(envelope)) { queryResult.add(feature); } } return queryResult; } public void add(Feature feature) { features.add(feature); if (envelope != null) { envelope.expandToInclude(feature.getGeometry().getEnvelopeInternal()); } } /** * Returns whether or not this Feature is in this collection * @return true if this feature is in this collection, as determined using * Feature#equals */ public boolean contains(Feature feature) { return features.contains(feature); } /** * Removes the features which intersect the given envelope */ public Collection remove(Envelope env) { Collection features = query(env); removeAll(features); return features; } public void remove(Feature feature) { features.remove(feature); invalidateEnvelope(); } /** * Removes all features from this collection. */ public void clear() { invalidateEnvelope(); features.clear(); } public int size() { return features.size(); } public Iterator iterator() { return features.iterator(); } /** * Clears the cached envelope of this FeatureDataset's Features. Call this method * when a Feature's Geometry is modified. */ public void invalidateEnvelope() { envelope = null; } public void addAll(Collection features) { this.features.addAll(features); if (envelope != null) { for (Iterator i = features.iterator(); i.hasNext(); ) { Feature feature = (Feature) i.next(); envelope.expandToInclude(feature.getGeometry().getEnvelopeInternal()); } } } /*public void removeAll(Collection features) { System.out.println("debut removeAll:"+System.currentTimeMillis()); this.features.removeAll(features); invalidateEnvelope(); System.out.println(" fin removeAll:"+System.currentTimeMillis()); }*/ // [michaudm 2009-05-16] creating a map on the fly improves dramatically // the removeAll performance if c is large // note that the semantic is slightly changed as the FID is used to identify // features to remove rather than object Equality public void removeAll(Collection c) { java.util.Map<Integer,Feature> map = new java.util.HashMap<Integer,Feature>(); for (Iterator i = features.iterator(); i.hasNext(); ) { Feature f = (Feature)i.next(); map.put(f.getID(), f); } for (Iterator i = c.iterator(); i.hasNext(); ) { map.remove(((Feature)i.next()).getID()); } features = new ArrayList(); features.addAll(map.values()); invalidateEnvelope(); } }