001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032:
033: package com.vividsolutions.jump.feature;
034:
035: import java.io.Serializable;
036: import java.util.*;
037:
038: import com.vividsolutions.jts.geom.Envelope;
039:
040: /**
041: * Default implementation of FeatureCollection.
042: */
043: public class FeatureDataset implements FeatureCollection, Serializable {
044: private static final long serialVersionUID = 5573446944516446540L;
045: private FeatureSchema featureSchema;
046:
047: //<<TODO>> Possibly use hashtable to do spatial indexing [Jon Aquino]
048: private ArrayList features;
049: private Envelope envelope = null;
050:
051: /**
052: * Creates a FeatureDataset, initialized with a group of Features.
053: * @param newFeatures an initial group of features to add to this FeatureDataset
054: * @param featureSchema the types of the attributes of the features in this collection
055: */
056: public FeatureDataset(Collection newFeatures,
057: FeatureSchema featureSchema) {
058: features = new ArrayList(newFeatures);
059: this .featureSchema = featureSchema;
060: }
061:
062: /**
063: * Creates a FeatureDataset.
064: * @param featureSchema the types of the attributes of the features in this collection
065: */
066: public FeatureDataset(FeatureSchema featureSchema) {
067: this (new ArrayList(), featureSchema);
068: }
069:
070: /**
071: * Returns the Feature at the given index (zero-based).
072: */
073: public Feature getFeature(int index) {
074: return (Feature) features.get(index);
075: }
076:
077: public FeatureSchema getFeatureSchema() {
078: return featureSchema;
079: }
080:
081: /**
082: * Because the envelope is cached, the envelope may be incorrect if you
083: * later change a Feature's geometry using Feature#setGeometry.
084: */
085: public Envelope getEnvelope() {
086: if (envelope == null) {
087: envelope = new Envelope();
088:
089: for (Iterator i = features.iterator(); i.hasNext();) {
090: Feature feature = (Feature) i.next();
091: envelope.expandToInclude(feature.getGeometry()
092: .getEnvelopeInternal());
093: }
094: }
095:
096: return envelope;
097: }
098:
099: public List getFeatures() {
100: return Collections.unmodifiableList(features);
101: }
102:
103: public boolean isEmpty() {
104: return size() == 0;
105: }
106:
107: /**
108: *@return a List containing the features whose envelopes intersect the
109: * given envelope
110: */
111:
112: //<<TODO:DESIGN>> Perhaps return value should be a Set, not a List, because order
113: //doesn't matter. [Jon Aquino]
114: public List query(Envelope envelope) {
115: if (!envelope.intersects(getEnvelope())) {
116: return new ArrayList();
117: }
118:
119: //<<TODO:NAMING>> Rename this method to getFeatures(Envelope), to parallel
120: //getFeatures() [Jon Aquino]
121: ArrayList queryResult = new ArrayList();
122:
123: for (Iterator i = features.iterator(); i.hasNext();) {
124: Feature feature = (Feature) i.next();
125:
126: if (feature.getGeometry().getEnvelopeInternal().intersects(
127: envelope)) {
128: queryResult.add(feature);
129: }
130: }
131:
132: return queryResult;
133: }
134:
135: public void add(Feature feature) {
136: features.add(feature);
137: if (envelope != null) {
138: envelope.expandToInclude(feature.getGeometry()
139: .getEnvelopeInternal());
140: }
141: }
142:
143: /**
144: * Returns whether or not this Feature is in this collection
145: * @return true if this feature is in this collection, as determined using
146: * Feature#equals
147: */
148: public boolean contains(Feature feature) {
149: return features.contains(feature);
150: }
151:
152: /**
153: * Removes the features which intersect the given envelope
154: */
155: public Collection remove(Envelope env) {
156: Collection features = query(env);
157: removeAll(features);
158:
159: return features;
160: }
161:
162: public void remove(Feature feature) {
163: features.remove(feature);
164: invalidateEnvelope();
165: }
166:
167: /**
168: * Removes all features from this collection.
169: */
170: public void clear() {
171: invalidateEnvelope();
172: features.clear();
173: }
174:
175: public int size() {
176: return features.size();
177: }
178:
179: public Iterator iterator() {
180: return features.iterator();
181: }
182:
183: /**
184: * Clears the cached envelope of this FeatureDataset's Features. Call this method
185: * when a Feature's Geometry is modified.
186: */
187: public void invalidateEnvelope() {
188: envelope = null;
189: }
190:
191: public void addAll(Collection features) {
192: this .features.addAll(features);
193: if (envelope != null) {
194: for (Iterator i = features.iterator(); i.hasNext();) {
195: Feature feature = (Feature) i.next();
196: envelope.expandToInclude(feature.getGeometry()
197: .getEnvelopeInternal());
198: }
199: }
200: }
201:
202: public void removeAll(Collection features) {
203: this.features.removeAll(features);
204: invalidateEnvelope();
205: }
206: }
|