Statistics
| Revision:

root / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / spatialindex / QuadtreeGt2.java @ 37951

History | View | Annotate | Download (10.6 KB)

1
/*
2
 * Created on 16-may-2006
3
 *
4
 * gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
5
 *
6
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU General Public License
10
 * as published by the Free Software Foundation; either version 2
11
 * of the License, or (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
21
 *
22
 * For more information, contact:
23
 *
24
 *  Generalitat Valenciana
25
 *   Conselleria d'Infraestructures i Transport
26
 *   Av. Blasco Ib??ez, 50
27
 *   46010 VALENCIA
28
 *   SPAIN
29
 *
30
 *      +34 963862235
31
 *   gvsig@gva.es
32
 *      www.gvsig.gva.es
33
 *
34
 *    or
35
 *
36
 *   IVER T.I. S.A
37
 *   Salamanca 50
38
 *   46005 Valencia
39
 *   Spain
40
 *
41
 *   +34 963163400
42
 *   dac@iver.es
43
 */
44
/* CVS MESSAGES:
45
*
46
* $Id: QuadtreeGt2.java 37951 2012-02-15 15:44:35Z fpenarrubia $
47
* $Log$
48
* Revision 1.3  2007-03-06 17:08:59  caballero
49
* Exceptions
50
*
51
* Revision 1.2  2006/11/29 19:27:59  azabala
52
* bug fixed (caused when we query for a bbox which is greater or equal to a layer bbox)
53
*
54
* Revision 1.1  2006/05/24 21:58:04  azabala
55
* *** empty log message ***
56
*
57
*
58
*/
59
package com.iver.cit.gvsig.fmap.spatialindex;
60

    
61
import java.awt.geom.Rectangle2D;
62
import java.io.File;
63
import java.io.IOException;
64
import java.nio.BufferUnderflowException;
65
import java.util.ArrayList;
66
import java.util.Collection;
67
import java.util.List;
68
import java.util.Stack;
69

    
70
import org.geotools.data.DataSourceException;
71
import org.geotools.index.TreeException;
72
import org.geotools.index.quadtree.Node;
73
import org.geotools.index.quadtree.QuadTree;
74
import org.geotools.index.quadtree.StoreException;
75
import org.geotools.index.quadtree.fs.FileSystemIndexStore;
76
import org.geotools.index.quadtree.fs.IndexHeader;
77

    
78
import com.vividsolutions.jts.geom.Envelope;
79
/**
80
 * This Quadtree spatial index implementation is based
81
 * in a fork of org.geotools.index.quadtree.Quadtree implementation.
82
 * <br>
83
 * This implementation offers us:
84
 * <ol>
85
 * <li>Persistence of spatial index</li>
86
 * </ol>
87
 * We had to fork geotools quadtree for many reasons:
88
 * <ol>
89
 * <li>It was strongly dependent of SHP format, so it returned not only
90
 * a num of rectangle, it also returned byte offset of this rectangle in shp file</li>
91
 * <li>
92
 * Query artifact wasnt run well at all
93
 * </li>
94
 * </ol>
95
 * @author azabala
96
 *
97
 */
98
public class QuadtreeGt2 implements IPersistentSpatialIndex {
99
        /**
100
         * Geotools quadtree implementation
101
         */
102
        QuadTree quadtree;
103
        /**
104
         * Persistent storage
105
         */
106
        String quadtreeFile;
107
        /**
108
         * Spatial index file extension
109
         */
110
        final String qExt = ".qix";
111
        /**
112
         * qix format has many versions, and allows
113
         * different byte orders.
114
         */
115
        String byteOrder;
116
        /**
117
         * Bounds of the layer to index
118
         */
119
        Envelope bounds;
120
        /**
121
         * Number of records of the layer to index
122
         */
123
        int numRecs = 0;
124

    
125
        boolean inMemory = false;
126
        /**
127
         * Constructor.
128
         * You must say a qix file path, and if you want to overwrite this file
129
         * if exists. Also, you must specify how many geometries are going to index,
130
         * and the bounding box of all the geometries.
131
         *
132
         *
133
         * @param quadtreeFile qix file path
134
         * @param byteOrder byte order (bigendian, etc)
135
         * @param bounds Rectangle2D of all the geometries to index
136
         * @param numRecords num of geometries to index
137
         * @param overwrite if we want to overwrite a possible existing qix file
138
         * @throws SpatialIndexException
139
         */
140
        public QuadtreeGt2(String quadtreeFile,
141
                        String byteOrder,
142
                        Rectangle2D bounds,
143
                        int numRecords,
144
                        boolean overwrite) throws SpatialIndexException{
145

    
146
                this.quadtreeFile = quadtreeFile +  qExt;
147
                this.byteOrder = byteOrder;
148
                this.bounds = toJtsEnvelope(bounds);
149
                this.numRecs = numRecords;
150

    
151
                if(exists()){
152
                        if(!overwrite){
153
                                load();
154
                                return;
155
                        }
156
                }
157
                // FJP: Change to avoid too much depth. (Produces a bug with very big layers)
158
                int maxCalculatedDepth = calculateMaxDepth(numRecords);
159
                if (maxCalculatedDepth > 14)
160
                        maxCalculatedDepth = 14;
161
                quadtree = new QuadTree(numRecs, maxCalculatedDepth, this.bounds);
162
        }
163
        
164
        private int calculateMaxDepth(int numShapes) {
165
        /* No max depth was defined, try to select a reasonable one
166
         * that implies approximately 8 shapes per node.
167
         */
168
        int numNodes = 1;
169
        int maxDepth = 0;
170
          
171
        while(numNodes * 4 < numShapes) {
172
            maxDepth += 1;
173
            numNodes = numNodes * 2;
174
        }
175
        return maxDepth;
176

    
177
        }
178
        // End FJP: depth
179

    
180
        /**
181
         * If the spatial index file exists and has content
182
         */
183
        public boolean exists(){
184
                return (new File(quadtreeFile).length() != 0);
185
        }
186

    
187
        public void load() throws SpatialIndexException{
188
                        try {
189
//                                openQuadTreeInMemory();
190
                                openQuadTree();
191
                        } catch (StoreException e) {
192
                                //throw new SpatialIndexException("Error al cargar el fichero qix", e);
193
                                throw new SpatialIndexException();
194
                        }
195

    
196
        }
197

    
198

    
199
        public synchronized List query(Rectangle2D rect) {
200
                try {
201
                        return (List) queryQuadTree(toJtsEnvelope(rect));
202
                } catch (IOException e) {
203
                        // TODO Auto-generated catch block
204
                        e.printStackTrace();
205
                } catch (TreeException e) {
206
                        // TODO Auto-generated catch block
207
                        e.printStackTrace();
208
                } catch (StoreException e) {
209
                        // TODO Auto-generated catch block
210
                        e.printStackTrace();
211
                }
212
                return new ArrayList();
213
        }
214

    
215

    
216
        public void insert(Rectangle2D rect, int index) {
217
                try {
218
                        quadtree.insert(index, toJtsEnvelope(rect));
219
                } catch (StoreException e) {
220
                        // TODO Auto-generated catch block
221
                        e.printStackTrace();
222
                }
223
        }
224

    
225
        public Envelope toJtsEnvelope(Rectangle2D rect){
226
                double xmin = rect.getMinX();
227
                double xmax = rect.getMaxX();
228
                double ymin = rect.getMinY();
229
                double ymax = rect.getMaxY();
230
                return new Envelope(xmin, xmax, ymin, ymax);
231
        }
232

    
233

    
234
        public void delete(Rectangle2D rect, int index) {
235
                if(inMemory)
236
                        quadtree.delete(toJtsEnvelope(rect), index);
237
        }
238

    
239
        void openQuadTree() throws StoreException{
240
                if (quadtree == null) {
241
                        File file = new File(quadtreeFile);
242
                        //Intento de cargar todo el quadtree en memoria
243
                        FileSystemIndexStore store = new FileSystemIndexStore(file);
244
                        quadtree = store.load();
245
                }
246
        }
247

    
248
         void openQuadTreeInMemory() throws StoreException {
249
                if (quadtree == null) {
250
                        File file = new File(quadtreeFile);
251
                        //Intento de cargar todo el quadtree en memoria
252
                        FileSystemIndexStore store = new FileSystemIndexStore(file);
253
                        QuadTree filequadtree = store.load();
254
                        quadtree = new QuadTree(filequadtree.getNumShapes(),
255
                                        filequadtree.getMaxDepth(),
256
                                        filequadtree.getRoot().getBounds());
257
                        Stack nodes = new Stack();
258
                        nodes.push(filequadtree.getRoot());
259
                        while(nodes.size() != 0){
260
                                Node node = (Node) nodes.pop();
261
                                Envelope nodeEnv = node.getBounds();
262
                                int[] shapeIds = node.getShapesId();
263
                                for(int i = 0; i < shapeIds.length; i++){
264
                                        quadtree.insert(shapeIds[i], nodeEnv);
265
                                }
266
                                int numSubnodes = node.getNumSubNodes();
267
                                for(int i = 0; i < numSubnodes; i++){
268
                                        nodes.push(node.getSubNode(i));
269
                                }
270
                        }//while
271
                        filequadtree.close();
272
                }
273
        }
274

    
275
        /**
276
         * QuadTree Query
277
         *
278
         * @param bbox
279
         *
280
         * @return
281
         *
282
         * @throws DataSourceException
283
         * @throws IOException
284
         * @throws TreeException
285
         *             DOCUMENT ME!
286
         * @throws StoreException
287
         */
288
        private Collection queryQuadTree(Envelope bbox) throws
289
                        IOException, TreeException, StoreException {
290

    
291
        List solution = null;
292
                if ((quadtree != null)){
293
                                //&& !bbox.contains(quadtree.getRoot().getBounds())) {
294
                        try {
295
                                solution = quadtree.query(bbox);
296
                        }
297
                        catch (Exception e) {
298
                                e.printStackTrace();
299
                                close();
300
                                openQuadTree();
301
                                solution = quadtree.query(bbox);
302
                        }
303
//            tmp = quadtree.search(bbox);
304
//            if( tmp==null || !tmp.isEmpty())
305
//                    return tmp;
306

    
307
                }else
308
                        solution = new ArrayList();
309
//                if( quadtree!=null )
310
//                        quadtree.close();
311
//            return null;
312
                return solution;
313
        }
314

    
315
        public void flush() {
316
                byte order = 0;
317
                if ((byteOrder == null) || byteOrder.equalsIgnoreCase("NM")) {
318
                        order = IndexHeader.NEW_MSB_ORDER;
319
                } else if (byteOrder.equalsIgnoreCase("NL")) {
320
                        order = IndexHeader.NEW_LSB_ORDER;
321
                }
322
            File file = new File(quadtreeFile);
323
            FileSystemIndexStore store = new FileSystemIndexStore(file, order);
324
            try {
325
                        store.store(quadtree);
326
                } catch (StoreException e) {
327
                        // TODO Auto-generated catch block
328
                        e.printStackTrace();
329
                }
330
        }
331

    
332
        public void close() {
333
                try {
334
                        quadtree.close();
335
                } catch (StoreException e) {
336
                        // TODO Auto-generated catch block
337
                        e.printStackTrace();
338
                }
339
        }
340

    
341

    
342

    
343

    
344

    
345
//        private void buildQuadTree() throws TreeException {
346
//                        ShapeFileIndexer indexer = new ShapeFileIndexer();
347
//                        indexer.setIdxType(ShapeFileIndexer.QUADTREE);
348
//                        indexer.setShapeFileName(shpURL.getPath());
349
//
350
//                        try {
351
//                                indexer.index(false, readWriteLock);
352
//                        } catch (MalformedURLException e) {
353
//                                throw new TreeException(e);
354
//                        } catch (LockTimeoutException e) {
355
//                                throw new TreeException(e);
356
//                        } catch (Exception e) {
357
//                                File f = new File(treeURL.getPath());
358
//
359
//                                if (f.exists()) {
360
//                                        f.delete();
361
//                                }
362
//
363
//                                if (e instanceof TreeException) {
364
//                                        throw (TreeException) e;
365
//                                } else {
366
//                                        throw new TreeException(e);
367
//                                }
368
//                        }
369
//                }
370
}
371

    
372

    
373

    
374

    
375
        /**
376
    private int buildRTree(ShapefileReader reader, File rtreeFile,
377
        boolean verbose)
378
        throws TreeException, LockTimeoutException, IOException {
379
        DataDefinition keyDef = new DataDefinition("US-ASCII");
380
        keyDef.addField(Integer.class);
381
        keyDef.addField(Long.class);
382

383
        FileSystemPageStore fps = new FileSystemPageStore(rtreeFile, keyDef,
384
                this.max, this.min, this.split);
385
        org.geotools.index.rtree.RTree rtree = new org.geotools.index.rtree.RTree(fps);
386
        Record record = null;
387
        Data data = null;
388

389
        int cnt = 0;
390

391
        while (reader.hasNext()) {
392
            record = reader.nextRecord();
393
            data = new Data(keyDef);
394

395
            //Aqu? estamos indexando a partir del n?mero de rectangulo
396
            //luego creo que el segundo valor lo podemos obviar.
397
            data.addValue(new Integer(++cnt));
398
            data.addValue(new Long(record.offset()));
399

400
            rtree.insert(new Envelope(record.minX, record.maxX, record.minY,
401
                    record.maxY), data);
402

403
            if (verbose && ((cnt % 500) == 0)) {
404
                System.out.print('.');
405
            }
406
        }
407

408
        rtree.close();
409

410
        return cnt;
411
    }
412
    */
413