Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.plugin / org.gvsig.daltransform.app / org.gvsig.daltransform.app.eventtheme / src / main / java / org / gvsig / app / eventtheme / dal / feature / EventThemeTransform.java @ 43215

History | View | Annotate | Download (16.3 KB)

1 40558 jjdelcerro
/**
2
 * gvSIG. Desktop Geographic Information System.
3 40435 jjdelcerro
 *
4 40558 jjdelcerro
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6 40435 jjdelcerro
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8 40558 jjdelcerro
 * as published by the Free Software Foundation; either version 3
9 40435 jjdelcerro
 * of the License, or (at your option) any later version.
10 40558 jjdelcerro
 *
11 40435 jjdelcerro
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15 40558 jjdelcerro
 *
16 40435 jjdelcerro
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18 40558 jjdelcerro
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 40435 jjdelcerro
 * MA  02110-1301, USA.
20 40558 jjdelcerro
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23 40435 jjdelcerro
 */
24
/*
25
 * AUTHORS (In addition to CIT):
26
 * 2009 {Iver T.I.}   {Task}
27
 */
28
package org.gvsig.app.eventtheme.dal.feature;
29
30
import java.util.Arrays;
31
32 40482 jldominguez
import javax.swing.JOptionPane;
33 43215 jjdelcerro
import org.apache.commons.lang3.StringUtils;
34 40482 jldominguez
35 40435 jjdelcerro
import org.cresques.cts.IProjection;
36
37 40482 jldominguez
import org.gvsig.app.ApplicationLocator;
38 40435 jjdelcerro
import org.gvsig.fmap.dal.DataStore;
39
import org.gvsig.fmap.dal.DataTypes;
40
import org.gvsig.fmap.dal.exception.DataException;
41
import org.gvsig.fmap.dal.exception.InitializeException;
42
import org.gvsig.fmap.dal.feature.AbstractFeatureStoreTransform;
43
import org.gvsig.fmap.dal.feature.EditableFeature;
44
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
45
import org.gvsig.fmap.dal.feature.EditableFeatureType;
46
import org.gvsig.fmap.dal.feature.Feature;
47
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
48
import org.gvsig.fmap.dal.feature.FeatureSet;
49
import org.gvsig.fmap.dal.feature.FeatureStore;
50
import org.gvsig.fmap.dal.feature.FeatureType;
51 41295 jjdelcerro
import org.gvsig.fmap.dal.feature.exception.SetReadOnlyAttributeException;
52 41198 jldominguez
import org.gvsig.fmap.geom.Geometry;
53 40435 jjdelcerro
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
54
import org.gvsig.fmap.geom.Geometry.TYPES;
55
import org.gvsig.fmap.geom.GeometryLocator;
56
import org.gvsig.fmap.geom.GeometryManager;
57
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
58
import org.gvsig.fmap.geom.exception.CreateGeometryException;
59
import org.gvsig.fmap.geom.primitive.Envelope;
60
import org.gvsig.fmap.geom.primitive.Point;
61
import org.gvsig.fmap.geom.type.GeometryTypeNotSupportedException;
62
import org.gvsig.fmap.geom.type.GeometryTypeNotValidException;
63 40482 jldominguez
import org.gvsig.i18n.Messages;
64 40435 jjdelcerro
import org.gvsig.tools.ToolsLocator;
65
import org.gvsig.tools.dispose.DisposableIterator;
66
import org.gvsig.tools.dynobject.DynStruct;
67
import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
68
import org.gvsig.tools.persistence.PersistenceManager;
69
import org.gvsig.tools.persistence.PersistentState;
70
import org.gvsig.tools.persistence.exception.PersistenceException;
71 40482 jldominguez
import org.gvsig.tools.task.AbstractMonitorableTask;
72
import org.gvsig.tools.task.TaskStatusManager;
73
import org.slf4j.Logger;
74
import org.slf4j.LoggerFactory;
75 40435 jjdelcerro
76
/**
77 43215 jjdelcerro
 * This class implements a transformation for a events theme. The original
78
 * {@link DataStore} have to have a couple of attributes, one with the X
79
 * coordinate and the second one with the Y coordinate. The result of the
80
 * transformation is a {@link DataStore} that has a new geometric attribute and
81
 * its value is a point with the coordinates specified in the original
82
 * {@link DataStore}.
83
 *
84 40435 jjdelcerro
 * @author <a href="mailto:jpiera@gvsig.org">Jorge Piera</a>
85
 */
86 43215 jjdelcerro
public class EventThemeTransform
87
    extends AbstractFeatureStoreTransform {
88 40435 jjdelcerro
89 43215 jjdelcerro
    private static Logger logger = LoggerFactory.getLogger(
90
        EventThemeTransform.class);
91
92 40435 jjdelcerro
    public static final String PERSISTENCE_DEFINITION_NAME = "EventThemeTransform";
93 40482 jldominguez
    /**
94 43215 jjdelcerro
     * Max number of features used for initial estimation of extent. Many other
95
     * features will be ignored simply with "iter.next()"
96 40482 jldominguez
     */
97
    public static int MAX_INI_FEATURES = 200;
98 40435 jjdelcerro
99
    private String xFieldName = null;
100
    private String yFieldName = null;
101
    private String geometryFieldName = null;
102
    private IProjection projection = null;
103
    private FeatureType originalFeatureType;
104 43215 jjdelcerro
    private GeometryManager geometryManager = GeometryLocator.getGeometryManager();
105 40435 jjdelcerro
    private Envelope envelope;
106
107
    public EventThemeTransform() {
108
        super();
109
        geometryManager = GeometryLocator.getGeometryManager();
110
    }
111
112
    /**
113 43215 jjdelcerro
     * This method initializes the transformation, sets the name of the
114
     * parameters and sets the value of the {@link FeatureType} returned by the
115
     * transformation.
116
     *
117
     * @param store The original store.
118
     * @param geometryFieldName The field that contains the geometric attribute.
119
     * @param xFieldName The field that contains the X coordinate.
120
     * @param yFieldName The field that contains the Y coordinate.
121
     * @param projection
122 40435 jjdelcerro
     * @throws DataException
123
     */
124 43215 jjdelcerro
    public void setValues(
125
        FeatureStore store,
126
        String geometryFieldName,
127
        String xFieldName,
128
        String yFieldName,
129
        IProjection projection) throws DataException {
130
131 40435 jjdelcerro
        setFeatureStore(store);
132
        this.xFieldName = xFieldName;
133
        this.yFieldName = yFieldName;
134
        this.projection = projection;
135 43215 jjdelcerro
        if( StringUtils.isEmpty(geometryFieldName) ) {
136 40435 jjdelcerro
            this.geometryFieldName = "the_geom";
137 43215 jjdelcerro
        } else {
138 40435 jjdelcerro
            this.geometryFieldName = geometryFieldName;
139
        }
140 43215 jjdelcerro
    }
141
142
    @Override
143
    public void setUp() throws Exception {
144
145 40435 jjdelcerro
        this.originalFeatureType = this.getFeatureStore()
146 43215 jjdelcerro
            .getDefaultFeatureType();
147 40435 jjdelcerro
148
        EditableFeatureType type = originalFeatureType.getEditable();
149 43215 jjdelcerro
        if( type.get(this.geometryFieldName) == null ) {
150
            EditableFeatureAttributeDescriptor attributeDescriptor = type.add(this.geometryFieldName, DataTypes.GEOMETRY);
151 40435 jjdelcerro
            try {
152
                attributeDescriptor.setGeometryType(geometryManager.getGeometryType(TYPES.POINT, SUBTYPES.GEOM2D));
153
            } catch (GeometryTypeNotSupportedException e) {
154
                throw new InitializeException(e);
155
            } catch (GeometryTypeNotValidException e) {
156
                throw new InitializeException(e);
157 43215 jjdelcerro
            }
158 40435 jjdelcerro
            attributeDescriptor.setSRS(projection);
159
        }
160
161
        try {
162
            /*
163
             * creates and updates envelope with all features
164
             */
165 43215 jjdelcerro
            initEnvelope(this.getFeatureStore(), MAX_INI_FEATURES);
166 40435 jjdelcerro
        } catch (CreateEnvelopeException e) {
167
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
168
        }
169
170
        type.setDefaultGeometryAttributeName(this.geometryFieldName);
171 43215 jjdelcerro
        FeatureType[] types = new FeatureType[]{type.getNotEditableCopy()};
172 40435 jjdelcerro
        setFeatureTypes(Arrays.asList(types), types[0]);
173
    }
174
175
    /**
176
     * creates and updates envelope with all features
177 43215 jjdelcerro
     *
178 40435 jjdelcerro
     */
179 40482 jldominguez
    private void initEnvelope(FeatureStore fsto, int max_feats) throws CreateEnvelopeException {
180 43215 jjdelcerro
181 40435 jjdelcerro
        envelope = geometryManager.createEnvelope(SUBTYPES.GEOM2D);
182
        FeatureSet fset = null;
183
        DisposableIterator diter = null;
184
        Feature feat = null;
185
        try {
186
            fset = fsto.getFeatureSet();
187
            diter = fset.fastIterator();
188 43215 jjdelcerro
189 40482 jldominguez
            // int count = 0;
190
            int countused = 0;
191
            int nextwait = 1;
192
            int index = 0;
193 43215 jjdelcerro
194
            while( diter.hasNext() && (countused < max_feats) ) {
195
196 40435 jjdelcerro
                feat = (Feature) diter.next();
197 40482 jldominguez
                /*
198
                 * This loop will use about 70 features from the first
199
                 * 3000 features, more and more separated each time.
200
                 * Something like:
201
                 *
202
                 * 1st, 2nd ,3rd, 5th, 8th, 15, 25, 40, 100, 300...
203
                 *
204
                 * Other features are ignored with diter.next().
205
                 * That causes an acceptable behavior even if the store
206
                 * is very large.
207
                 *
208
                 * Afterwards, the extent is updated while drawing,
209
                 * so it's not very important.
210
                 */
211
                index++;
212 43215 jjdelcerro
                if( index == nextwait ) {
213
                    index = 0;
214
                    /*
215 40482 jldominguez
                         * This causes that the first 5 features
216
                         * will always be used.
217 43215 jjdelcerro
                     */
218
                    if( countused > 5 ) {
219
                        nextwait++;
220
                    }
221
                    this.updateEnvelope(feat);
222
                    countused++;
223 40482 jldominguez
                }
224
                // count++;
225 40435 jjdelcerro
            }
226 43215 jjdelcerro
227 40435 jjdelcerro
            diter.dispose();
228 43215 jjdelcerro
229 40435 jjdelcerro
        } catch (Exception dex) {
230
            throw new CreateEnvelopeException(SUBTYPES.GEOM2D, dex);
231
        }
232
    }
233 43215 jjdelcerro
234 40482 jldominguez
    public void setEnvelope(Envelope env) {
235 43215 jjdelcerro
        this.envelope = env;
236 40482 jldominguez
    }
237 40435 jjdelcerro
238 40482 jldominguez
    /*
239
     * Currently not used
240
     */
241
    private void launchFullExtentThread(DisposableIterator diter) {
242
243 43215 jjdelcerro
        ComputeExtentTask task = new ComputeExtentTask(diter, this);
244
        task.start();
245
    }
246
247
    @Override
248 40435 jjdelcerro
    public void applyTransform(Feature source, EditableFeature target)
249 43215 jjdelcerro
        throws DataException {
250
251 40435 jjdelcerro
        this.copySourceToTarget(source, target);
252
253 41198 jldominguez
        try {
254 43215 jjdelcerro
255
            Geometry point;
256 41198 jldominguez
            Object xval = source.get(xFieldName);
257
            Object yval = source.get(yFieldName);
258 43215 jjdelcerro
            if( xval == null || yval == null ) {
259 41198 jldominguez
                logger.info("Found row with null coordinates in event theme (created null geometry)");
260 41610 jjdelcerro
                target.set(geometryFieldName, null);
261 43215 jjdelcerro
                target.setDefaultGeometry(null);
262 41198 jldominguez
            } else {
263
                point = geometryManager.createPoint(
264
                    new Double(xval.toString()),
265
                    new Double(yval.toString()),
266
                    SUBTYPES.GEOM2D);
267
                target.set(geometryFieldName, point);
268 43215 jjdelcerro
                target.setDefaultGeometry(point);
269 41198 jldominguez
                envelope.add(point.getEnvelope());
270
            }
271 43215 jjdelcerro
        } catch (SetReadOnlyAttributeException e1) {
272 41295 jjdelcerro
            // Do nothing
273 43215 jjdelcerro
274 41198 jldominguez
        } catch (Exception e) {
275
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(
276
                TYPES.POINT, SUBTYPES.GEOM2D, e);
277 43215 jjdelcerro
        }
278
279 40435 jjdelcerro
    }
280 43215 jjdelcerro
281 40435 jjdelcerro
    /**
282
     * Used internally to initialize envelope
283 43215 jjdelcerro
     *
284 40435 jjdelcerro
     */
285
    private void updateEnvelope(Feature feat) throws CreateGeometryException {
286
287
        Point point = geometryManager.createPoint(
288
            new Double(feat.get(xFieldName).toString()),
289
            new Double(feat.get(yFieldName).toString()),
290
            SUBTYPES.GEOM2D);
291
        envelope.add(point.getEnvelope());
292
    }
293
294
    /**
295
     * @param source
296
     * @param target
297
     */
298
    private void copySourceToTarget(Feature source, EditableFeature target) {
299
        FeatureAttributeDescriptor attr, attrTrg;
300
        FeatureType ftSrc = source.getType();
301
        FeatureType ftTrg = target.getType();
302
303 43215 jjdelcerro
        for( int i = 0; i < source.getType().size(); i++ ) {
304 40435 jjdelcerro
            attr = ftSrc.getAttributeDescriptor(i);
305 43215 jjdelcerro
            if( ftTrg.getIndex(attr.getName()) > -1 ) {
306 40435 jjdelcerro
                try {
307
                    target.set(attr.getName(), source.get(i));
308
                } catch (IllegalArgumentException e) {
309
                    attrTrg = ftTrg.getAttributeDescriptor(attr.getName());
310
                    target.set(attrTrg.getIndex(), attrTrg.getDefaultValue());
311
                }
312
313
            }
314
        }
315
316
    }
317
318 43215 jjdelcerro
    @Override
319 40435 jjdelcerro
    public FeatureType getSourceFeatureTypeFrom(FeatureType targetFeatureType) {
320
        return this.originalFeatureType;
321
    }
322
323 43215 jjdelcerro
    @Override
324 40435 jjdelcerro
    public boolean isTransformsOriginalValues() {
325
        return true;
326
    }
327
328
    public static void registerPersistent() {
329
        PersistenceManager persistenceManager = ToolsLocator.getPersistenceManager();
330
331
        if( persistenceManager.getDefinition(AbstractFeatureStoreTransform.class) == null ) {
332
            AbstractFeatureStoreTransform.registerPersistent();
333
        }
334
335
        DynStruct definition = persistenceManager.getDefinition(PERSISTENCE_DEFINITION_NAME);
336
337 43215 jjdelcerro
        if( definition == null ) {
338 40435 jjdelcerro
            definition = persistenceManager.addDefinition(
339
                EventThemeTransform.class,
340
                PERSISTENCE_DEFINITION_NAME,
341
                "EventThemeTransform Persistence definition",
342 43215 jjdelcerro
                null,
343 40435 jjdelcerro
                null
344
            );
345
            definition.extend(PersistenceManager.PERSISTENCE_NAMESPACE,
346
                ABSTRACT_FEATURESTORE_DYNCLASS_NAME);
347
348
            definition.addDynFieldString("geometryFieldName").setMandatory(true);
349
            definition.addDynFieldString("xFieldName").setMandatory(true);
350
            definition.addDynFieldString("yFieldName").setMandatory(true);
351
            definition.addDynFieldObject("projection").setType(DataTypes.CRS);
352
        }
353
    }
354
355 43215 jjdelcerro
    @Override
356 40435 jjdelcerro
    public void saveToState(PersistentState state) throws PersistenceException {
357
        super.saveToState(state);
358
        state.set("geometryFieldName", this.geometryFieldName);
359
        state.set("xFieldName", this.xFieldName);
360
        state.set("yFieldName", this.yFieldName);
361 43215 jjdelcerro
        state.set("projection", this.projection);
362 40435 jjdelcerro
    }
363
364 43215 jjdelcerro
    @Override
365 40435 jjdelcerro
    public void loadFromState(PersistentState state)
366 43215 jjdelcerro
        throws PersistenceException {
367
        super.loadFromState(state);
368
        geometryFieldName = state.getString("geometryFieldName");
369
        xFieldName = state.getString("xFieldName");
370
        yFieldName = state.getString("yFieldName");
371
        projection = (IProjection) state.get("projection");
372 40435 jjdelcerro
    }
373
374 43215 jjdelcerro
    @Override
375 40435 jjdelcerro
    public Object getDynValue(String name) throws DynFieldNotFoundException {
376 43215 jjdelcerro
        if( DataStore.METADATA_CRS.equals(name) ) {
377 40435 jjdelcerro
            return projection;
378 43215 jjdelcerro
        } else if( DataStore.METADATA_ENVELOPE.equals(name) ) {
379 40435 jjdelcerro
            return envelope;
380
        }
381
        return null;
382
    }
383
384 43215 jjdelcerro
    @Override
385 40435 jjdelcerro
    public boolean hasDynValue(String name) {
386 43215 jjdelcerro
        return ((DataStore.METADATA_CRS.equals(name))
387
            || (DataStore.METADATA_ENVELOPE.equals(name)));
388
    }
389
390 40482 jldominguez
    /**
391 43215 jjdelcerro
     *
392 40482 jldominguez
     * A thread to compute the true extent (in case it has a lot of features)
393
     * Currently not used.
394 43215 jjdelcerro
     *
395 40482 jldominguez
     * @author jldominguez
396 43215 jjdelcerro
     *
397 40482 jldominguez
     * @deprecated This is not used because it causes issues with
398 43215 jjdelcerro
     * ConsurrentModificationException because the store is notified of a change
399
     * (the transformation). Anyway, this is not very important I think.
400 40482 jldominguez
     */
401 43215 jjdelcerro
    private class ComputeExtentTask
402
        extends AbstractMonitorableTask {
403
404
        private DisposableIterator disp_iter = null;
405
        private EventThemeTransform tra_toupdate = null;
406
407
        public ComputeExtentTask(DisposableIterator diter, EventThemeTransform ettra) {
408
            /*
409 40482 jldominguez
                     * Auto-added by task manager
410 43215 jjdelcerro
             */
411
            super(Messages.getText("_Extent_of_event_theme"), true);
412
            disp_iter = diter;
413
            tra_toupdate = ettra;
414
        }
415 40482 jldominguez
416 43215 jjdelcerro
        public void run() {
417
418
            Envelope env = null;
419
            Feature feat = null;
420
            Point point = null;
421
            int count = 99;
422
423
            try {
424
                while( disp_iter.hasNext() ) {
425 40482 jldominguez
                    feat = (Feature) disp_iter.next();
426
                    point = geometryManager.createPoint(
427 43215 jjdelcerro
                        Double.parseDouble(feat.get(xFieldName).toString()),
428
                        Double.parseDouble(feat.get(yFieldName).toString()),
429
                        SUBTYPES.GEOM2D);
430
                    if( env == null ) {
431
                        env = (Envelope) point.getEnvelope().clone();
432 40482 jldominguez
                    } else {
433 43215 jjdelcerro
                        env.add(point.getEnvelope());
434 40482 jldominguez
                    }
435
                    count++;
436
                    Thread.sleep(10);
437 43215 jjdelcerro
                    if( count % 100 == 0 ) {
438
                        System.out.println("COUNT = " + count);
439 40482 jldominguez
                    }
440
                }
441 43215 jjdelcerro
            } catch (Exception exc) {
442
443
                ApplicationLocator.getManager().message(
444
                    Messages.getText("_Error_while_getting_extent"),
445
                    JOptionPane.ERROR_MESSAGE);
446
                logger.info("Error while getting extent in thread.", exc);
447
448
            }
449
450
            disp_iter.dispose();
451
            // =================
452
            if( env != null ) {
453
                Envelope curr_env = (Envelope) tra_toupdate.getDynValue(
454
                    DataStore.METADATA_ENVELOPE);
455
                curr_env.add(env);
456
            }
457
            // =========== End
458
            TaskStatusManager man = this.getTaskStatus().getManager();
459
            man.remove(this.getTaskStatus());
460
        }
461
462 40482 jldominguez
    }
463 40435 jjdelcerro
}