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
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * 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
 *
16
 * 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
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
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
 */
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
import javax.swing.JOptionPane;
33
import org.apache.commons.lang3.StringUtils;
34

    
35
import org.cresques.cts.IProjection;
36

    
37
import org.gvsig.app.ApplicationLocator;
38
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
import org.gvsig.fmap.dal.feature.exception.SetReadOnlyAttributeException;
52
import org.gvsig.fmap.geom.Geometry;
53
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
import org.gvsig.i18n.Messages;
64
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
import org.gvsig.tools.task.AbstractMonitorableTask;
72
import org.gvsig.tools.task.TaskStatusManager;
73
import org.slf4j.Logger;
74
import org.slf4j.LoggerFactory;
75

    
76
/**
77
 * 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
 * @author <a href="mailto:jpiera@gvsig.org">Jorge Piera</a>
85
 */
86
public class EventThemeTransform
87
    extends AbstractFeatureStoreTransform {
88

    
89
    private static Logger logger = LoggerFactory.getLogger(
90
        EventThemeTransform.class);
91

    
92
    public static final String PERSISTENCE_DEFINITION_NAME = "EventThemeTransform";
93
    /**
94
     * Max number of features used for initial estimation of extent. Many other
95
     * features will be ignored simply with "iter.next()"
96
     */
97
    public static int MAX_INI_FEATURES = 200;
98

    
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
    private GeometryManager geometryManager = GeometryLocator.getGeometryManager();
105
    private Envelope envelope;
106

    
107
    public EventThemeTransform() {
108
        super();
109
        geometryManager = GeometryLocator.getGeometryManager();
110
    }
111

    
112
    /**
113
     * 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
     * @throws DataException
123
     */
124
    public void setValues(
125
        FeatureStore store,
126
        String geometryFieldName,
127
        String xFieldName,
128
        String yFieldName,
129
        IProjection projection) throws DataException {
130

    
131
        setFeatureStore(store);
132
        this.xFieldName = xFieldName;
133
        this.yFieldName = yFieldName;
134
        this.projection = projection;
135
        if( StringUtils.isEmpty(geometryFieldName) ) {
136
            this.geometryFieldName = "the_geom";
137
        } else {
138
            this.geometryFieldName = geometryFieldName;
139
        }
140
    }
141

    
142
    @Override
143
    public void setUp() throws Exception {
144

    
145
        this.originalFeatureType = this.getFeatureStore()
146
            .getDefaultFeatureType();
147

    
148
        EditableFeatureType type = originalFeatureType.getEditable();
149
        if( type.get(this.geometryFieldName) == null ) {
150
            EditableFeatureAttributeDescriptor attributeDescriptor = type.add(this.geometryFieldName, DataTypes.GEOMETRY);
151
            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
            }
158
            attributeDescriptor.setSRS(projection);
159
        }
160

    
161
        try {
162
            /*
163
             * creates and updates envelope with all features
164
             */
165
            initEnvelope(this.getFeatureStore(), MAX_INI_FEATURES);
166
        } catch (CreateEnvelopeException e) {
167
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(e);
168
        }
169

    
170
        type.setDefaultGeometryAttributeName(this.geometryFieldName);
171
        FeatureType[] types = new FeatureType[]{type.getNotEditableCopy()};
172
        setFeatureTypes(Arrays.asList(types), types[0]);
173
    }
174

    
175
    /**
176
     * creates and updates envelope with all features
177
     *
178
     */
179
    private void initEnvelope(FeatureStore fsto, int max_feats) throws CreateEnvelopeException {
180

    
181
        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

    
189
            // int count = 0;
190
            int countused = 0;
191
            int nextwait = 1;
192
            int index = 0;
193

    
194
            while( diter.hasNext() && (countused < max_feats) ) {
195

    
196
                feat = (Feature) diter.next();
197
                /*
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
                if( index == nextwait ) {
213
                    index = 0;
214
                    /*
215
                         * This causes that the first 5 features
216
                         * will always be used.
217
                     */
218
                    if( countused > 5 ) {
219
                        nextwait++;
220
                    }
221
                    this.updateEnvelope(feat);
222
                    countused++;
223
                }
224
                // count++;
225
            }
226

    
227
            diter.dispose();
228

    
229
        } catch (Exception dex) {
230
            throw new CreateEnvelopeException(SUBTYPES.GEOM2D, dex);
231
        }
232
    }
233

    
234
    public void setEnvelope(Envelope env) {
235
        this.envelope = env;
236
    }
237

    
238
    /*
239
     * Currently not used
240
     */
241
    private void launchFullExtentThread(DisposableIterator diter) {
242

    
243
        ComputeExtentTask task = new ComputeExtentTask(diter, this);
244
        task.start();
245
    }
246

    
247
    @Override
248
    public void applyTransform(Feature source, EditableFeature target)
249
        throws DataException {
250

    
251
        this.copySourceToTarget(source, target);
252

    
253
        try {
254

    
255
            Geometry point;
256
            Object xval = source.get(xFieldName);
257
            Object yval = source.get(yFieldName);
258
            if( xval == null || yval == null ) {
259
                logger.info("Found row with null coordinates in event theme (created null geometry)");
260
                target.set(geometryFieldName, null);
261
                target.setDefaultGeometry(null);
262
            } else {
263
                point = geometryManager.createPoint(
264
                    new Double(xval.toString()),
265
                    new Double(yval.toString()),
266
                    SUBTYPES.GEOM2D);
267
                target.set(geometryFieldName, point);
268
                target.setDefaultGeometry(point);
269
                envelope.add(point.getEnvelope());
270
            }
271
        } catch (SetReadOnlyAttributeException e1) {
272
            // Do nothing
273

    
274
        } catch (Exception e) {
275
            throw new org.gvsig.fmap.dal.feature.exception.CreateGeometryException(
276
                TYPES.POINT, SUBTYPES.GEOM2D, e);
277
        }
278

    
279
    }
280

    
281
    /**
282
     * Used internally to initialize envelope
283
     *
284
     */
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
        for( int i = 0; i < source.getType().size(); i++ ) {
304
            attr = ftSrc.getAttributeDescriptor(i);
305
            if( ftTrg.getIndex(attr.getName()) > -1 ) {
306
                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
    @Override
319
    public FeatureType getSourceFeatureTypeFrom(FeatureType targetFeatureType) {
320
        return this.originalFeatureType;
321
    }
322

    
323
    @Override
324
    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
        if( definition == null ) {
338
            definition = persistenceManager.addDefinition(
339
                EventThemeTransform.class,
340
                PERSISTENCE_DEFINITION_NAME,
341
                "EventThemeTransform Persistence definition",
342
                null,
343
                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
    @Override
356
    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
        state.set("projection", this.projection);
362
    }
363

    
364
    @Override
365
    public void loadFromState(PersistentState state)
366
        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
    }
373

    
374
    @Override
375
    public Object getDynValue(String name) throws DynFieldNotFoundException {
376
        if( DataStore.METADATA_CRS.equals(name) ) {
377
            return projection;
378
        } else if( DataStore.METADATA_ENVELOPE.equals(name) ) {
379
            return envelope;
380
        }
381
        return null;
382
    }
383

    
384
    @Override
385
    public boolean hasDynValue(String name) {
386
        return ((DataStore.METADATA_CRS.equals(name))
387
            || (DataStore.METADATA_ENVELOPE.equals(name)));
388
    }
389

    
390
    /**
391
     *
392
     * A thread to compute the true extent (in case it has a lot of features)
393
     * Currently not used.
394
     *
395
     * @author jldominguez
396
     *
397
     * @deprecated This is not used because it causes issues with
398
     * ConsurrentModificationException because the store is notified of a change
399
     * (the transformation). Anyway, this is not very important I think.
400
     */
401
    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
                     * Auto-added by task manager
410
             */
411
            super(Messages.getText("_Extent_of_event_theme"), true);
412
            disp_iter = diter;
413
            tra_toupdate = ettra;
414
        }
415

    
416
        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
                    feat = (Feature) disp_iter.next();
426
                    point = geometryManager.createPoint(
427
                        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
                    } else {
433
                        env.add(point.getEnvelope());
434
                    }
435
                    count++;
436
                    Thread.sleep(10);
437
                    if( count % 100 == 0 ) {
438
                        System.out.println("COUNT = " + count);
439
                    }
440
                }
441
            } 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
    }
463
}