Statistics
| Revision:

root / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / drivers / WKBParser3.java @ 35335

History | View | Annotate | Download (13.1 KB)

1
/*
2
 * WKBParser.java
3
 * Based in
4
 * PostGIS extension for PostgreSQL JDBC driver - Binary Parser
5
 *
6
 * NOTA: Es posible que lo mejor sea crear un PostGisGeometry que implemente
7
 * la interfaz IGeometry, y as? nos sirve de base para tener IGeometries
8
 * que encapsulan otras posibles geometr?as. Por ejemplo, un JTSGeometry.
9
 * De esta forma, un driver no necesitar?a reescribirse.
10
 *
11
 * (C) 2005 Markus Schaber, schabios@logi-track.com
12
 *
13
 * This library is free software; you can redistribute it and/or modify it under
14
 * the terms of the GNU Lesser General Public License as published by the Free
15
 * Software Foundation, either version 2.1 of the License.
16
 *
17
 * This library is distributed in the hope that it will be useful, but WITHOUT
18
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
20
 * details.
21
 *
22
 * You should have received a copy of the GNU Lesser General Public License
23
 * along with this library; if not, write to the Free Software Foundation, Inc.,
24
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit the web at
25
 * http://www.gnu.org.
26
 *
27
 * $Id: WKBParser2.java 24154 2008-10-21 10:01:05Z jpiera $
28
 */
29
package com.iver.cit.gvsig.fmap.drivers;
30

    
31
import java.nio.ByteBuffer;
32
import java.nio.ByteOrder;
33
import java.util.ArrayList;
34
import java.util.List;
35

    
36
import com.iver.cit.gvsig.fmap.core.FGeometry;
37
import com.iver.cit.gvsig.fmap.core.FGeometryCollection;
38
import com.iver.cit.gvsig.fmap.core.FGeometryM;
39
import com.iver.cit.gvsig.fmap.core.FMultiPoint2D;
40
import com.iver.cit.gvsig.fmap.core.FMultipoint3D;
41
import com.iver.cit.gvsig.fmap.core.FPoint2D;
42
import com.iver.cit.gvsig.fmap.core.FPoint2DM;
43
import com.iver.cit.gvsig.fmap.core.FPoint3D;
44
import com.iver.cit.gvsig.fmap.core.FPolygon2D;
45
import com.iver.cit.gvsig.fmap.core.FPolygon2DM;
46
import com.iver.cit.gvsig.fmap.core.FPolygon3D;
47
import com.iver.cit.gvsig.fmap.core.FPolyline2D;
48
import com.iver.cit.gvsig.fmap.core.FPolyline2DM;
49
import com.iver.cit.gvsig.fmap.core.FPolyline3D;
50
import com.iver.cit.gvsig.fmap.core.FShape;
51
import com.iver.cit.gvsig.fmap.core.FShapeM;
52
import com.iver.cit.gvsig.fmap.core.GeneralPathX;
53
import com.iver.cit.gvsig.fmap.core.IGeometry;
54
import com.iver.cit.gvsig.fmap.core.ShapeFactory;
55
import com.iver.cit.gvsig.fmap.core.ShapeMFactory;
56
import com.vividsolutions.jts.io.WKBConstants;
57

    
58
/**
59
 * Parse binary representation of geometries. Currently, only text rep (hexed)
60
 * implementation is tested.
61
 * 
62
 * It should be easy to add char[] and CharSequence ByteGetter instances,
63
 * although the latter one is not compatible with older jdks.
64
 * 
65
 * I did not implement real unsigned 32-bit integers or emulate them with long,
66
 * as both java Arrays and Strings currently can have only 2^31-1 elements
67
 * (bytes), so we cannot even get or build Geometries with more than approx.
68
 * 2^28 coordinates (8 bytes each).
69
 * 
70
 * @author markus.schaber@logi-track.com
71
 * 
72
 */
73
// jomarlla
74
// Read 3D and build 3D geometries using gvSIG objects.
75
// XY, XYZ and XYM supported.
76
// MultiPoint2DM is not supported by gvSIG.
77
// When gvSIg edit a polygon it makes the polygon 2d, so the update
78
// operations will just write 2d geometries even though the postgis
79
// driver is ready for writing 3D geometries.
80

    
81
public class WKBParser3 {
82

    
83
        private boolean gHaveM, gHaveZ, gHaveS; // M, Z y SRID
84

    
85
        /**
86
         * Parse a binary encoded geometry.
87
         * 
88
         * Is synchronized to protect offset counter. (Unfortunately, Java does not
89
         * have neither call by reference nor multiple return values.)
90
         */
91
        public synchronized IGeometry parse(byte[] value) {
92
                // BinaryByteGetter bytes = new ByteGetter.BinaryByteGetter(value);
93
                ByteBuffer buf = ByteBuffer.wrap(value);
94
                return parseGeometry(buf);
95
        }
96

    
97
        protected int parseTypeAndSRID(ByteBuffer data) {
98
                byte endian = data.get(); // skip and test endian flag
99
                if (endian == 1) {
100
                        data.order(ByteOrder.LITTLE_ENDIAN);
101
                }
102
                int typeword = data.getInt();
103

    
104
                int realtype = typeword & 0x1FFFFFFF; // cut off high flag bits
105

    
106
                gHaveZ = (typeword & 0x80000000) != 0;
107
                gHaveM = (typeword & 0x40000000) != 0;
108
                gHaveS = (typeword & 0x20000000) != 0;
109

    
110
                // not used
111
                int srid = -1;
112

    
113
                if (gHaveS) {
114
                        srid = data.getInt();
115
                }
116

    
117
                return realtype;
118

    
119
        }
120

    
121
        /** Parse a geometry starting at offset. */
122
        protected IGeometry parseGeometry(ByteBuffer data) {
123
                int realtype = parseTypeAndSRID(data);
124

    
125
                IGeometry result1;
126
                switch (realtype) {
127
                case WKBConstants.wkbPoint:
128
                        result1 = createGeometry(parsePoint(data, gHaveZ, gHaveM));
129
                        break;
130
                case WKBConstants.wkbLineString:
131
                        result1 = createGeometry(parseLineString(data, gHaveZ, gHaveM));
132
                        break;
133
                case WKBConstants.wkbPolygon:
134
                        result1 = createGeometry(parsePolygon(data, gHaveZ, gHaveM));
135
                        break;
136
                case WKBConstants.wkbMultiPoint:
137
                        result1 = parseMultiPoint(data);
138
                        break;
139
                case WKBConstants.wkbMultiLineString:
140
                        result1 = createGeometry(parseMultiLineString(data));
141
                        return result1;
142
                case WKBConstants.wkbMultiPolygon:
143
                        result1 = createGeometry(parseMultiPolygon(data));
144
                        break;
145
                case WKBConstants.wkbGeometryCollection:
146
                        result1 = parseCollection(data);
147
                        break;
148
                default:
149
                        throw new IllegalArgumentException("Unknown Geometry Type!");
150
                }
151

    
152
                /*
153
                 * Geometry result = result1;
154
                 * 
155
                 * if (haveS) { result.setSrid(srid); }
156
                 */
157
                return result1;
158
        }
159

    
160
        private FPoint2D parsePoint(ByteBuffer data, boolean haveZ, boolean haveM) {
161
                double X = data.getDouble();
162
                double Y = data.getDouble();
163
                FPoint2D result = new FPoint2D(X, Y);
164

    
165
                if ((haveZ) && (haveM)) {
166
                        // TODO: SUPPORT ZM (POR AHORA, LA Z TIENE PREFERENCIA
167
                        double Z = data.getDouble();
168
                        result = new FPoint3D(X, Y, Z);
169
                        double M = data.getDouble();
170
                        // TODO: create future FPoint3DM
171
                        
172
                        return result;
173
                        
174
                }
175
                if (haveM) {
176
                        double m = data.getDouble();
177
                        result = new FPoint2DM(X, Y, m);
178
                }
179

    
180
                if (haveZ) {
181
                        double Z = data.getDouble();
182
                        result = new FPoint3D(X, Y, Z);
183
                }
184

    
185
                return result;
186
        }
187

    
188
        /** Parse an Array of "full" Geometries */
189
        private void parseGeometryArray(ByteBuffer data, IGeometry[] container) {
190
                for (int i = 0; i < container.length; i++) {
191
                        container[i] = parseGeometry(data);
192
                }
193
        }
194

    
195
        /**
196
         * Parse an Array of "slim" Points (without endianness and type, part of
197
         * LinearRing and Linestring, but not MultiPoint!
198
         * 
199
         * @param haveZ
200
         * @param haveM
201
         */
202
        private FPoint2D[] parsePointArray(ByteBuffer data, boolean haveZ,
203
                        boolean haveM) {
204
                int count = data.getInt();
205
                FPoint2D[] result = new FPoint2D[count];
206
                for (int i = 0; i < count; i++) {
207
                        result[i] = parsePoint(data, haveZ, haveM);
208
                }
209
                return result;
210
        }
211

    
212
        private FMultiPoint2D parseMultiPoint(ByteBuffer data) {
213
                FPoint2D[] points = new FPoint2D[data.getInt()];
214

    
215
                double zs[] = null;
216
                if (gHaveZ)
217
                        zs = new double[points.length];
218

    
219
                for (int i = 0; i < points.length; i++) {
220
                        parseTypeAndSRID(data);
221
                        points[i] = parsePoint(data, gHaveZ, gHaveM);
222

    
223
                        if (gHaveZ)
224
                                zs[i] = ((FPoint3D) points[i]).getZ();
225
                }
226

    
227
                if (gHaveZ)
228
                        return new FMultipoint3D(points, zs);
229

    
230
                return new FMultiPoint2D(points);
231
        }
232

    
233
        private FPolyline2D parseLineString(ByteBuffer data, boolean haveZ,
234
                        boolean haveM) {
235
                FPoint2D[] points = parsePointArray(data, haveZ, haveM);
236
                GeneralPathX gp = new GeneralPathX();
237

    
238
                List<Double> d3 = null;
239

    
240
                int nDims = 2;
241
                if (gHaveZ || gHaveM)
242
                        nDims = 3;
243

    
244
                if (nDims == 3)
245
                        d3 = new ArrayList<Double>();
246

    
247
                for (int i = 0; i < points.length; i++) {
248
                        // parent has 3 dimensions
249
                        if (nDims == 3) {
250
                                if (gHaveZ)
251
                                        d3.add(((FPoint3D) points[i]).getZ());
252
                                else if (gHaveM)
253
                                        d3.add(((FPoint2DM) points[i]).getM());
254
                                else
255
                                        d3.add(0.0); // child does not have 3 dimensions
256
                        }
257
                        if (i == 0)
258
                                gp.moveTo(points[i].getX(), points[i].getY());
259
                        else
260
                                gp.lineTo(points[i].getX(), points[i].getY());
261
                }
262

    
263
                if (nDims == 3) {
264
                        double ad3[] = new double[d3.size()];
265
                        for (int i = 0; i < d3.size(); i++) {
266
                                ad3[i] = ((Double) d3.get(i)).doubleValue();
267
                        }
268

    
269
                        if (gHaveZ)
270
                                return new FPolyline3D(gp, ad3);
271
                        else
272
                                return new FPolyline2DM(gp, ad3);
273
                }
274
                return new FPolyline2D(gp);
275
        }
276

    
277
        private FShape parsePolygon(ByteBuffer data, boolean haveZ, boolean haveM) {
278
                GeneralPathX gp = new GeneralPathX();
279

    
280
                List<Double> d3 = null;
281

    
282
                int nDims = 2;
283
                if (gHaveZ || gHaveM)
284
                        nDims = 3;
285

    
286
                if (nDims == 3)
287
                        d3 = new ArrayList<Double>();
288

    
289
                int countRings = data.getInt();
290
                for (int j = 0; j < countRings; j++) {
291
                        FPoint2D[] points = parsePointArray(data, gHaveZ, gHaveM);
292
                        for (int k = 0; k < points.length; k++) {
293
                                if (k == points.length - 1) {
294
                                        gp.closePath();
295
                                        if (nDims == 3) {
296
                                                if (gHaveZ)
297
                                                        d3.add(((FPoint3D) points[0]).getZ());
298
                                                else if (gHaveM)
299
                                                        d3.add(((FPoint2DM) points[0]).getM());
300
                                                else
301
                                                        d3.add(0.0); // child does not have 3 dimensions
302
                                        }                                        
303
                                } else {
304
                                        // parent has 3 dimensions
305
                                        if (nDims == 3) {
306
                                                if (gHaveZ)
307
                                                        d3.add(((FPoint3D) points[k]).getZ());
308
                                                else if (gHaveM)
309
                                                        d3.add(((FPoint2DM) points[k]).getM());
310
                                                else
311
                                                        d3.add(0.0); // child does not have 3 dimensions
312
                                        }
313
                                        if (k == 0)
314
                                                gp.moveTo(points[k].getX(), points[k].getY());
315
                                        else
316
                                                gp.lineTo(points[k].getX(), points[k].getY());
317
                                }
318
                        }
319

    
320
                }
321

    
322
                if (nDims == 3) {
323
                        double ad3[] = new double[d3.size()];
324
                        for (int i = 0; i < d3.size(); i++) {
325
                                ad3[i] = ((Double) d3.get(i)).doubleValue();
326
                        }
327

    
328
                        if (gHaveZ)
329
                                return new FPolygon3D(gp, ad3);
330
                        else
331
                                return new FPolygon2DM(gp, ad3);
332
                }
333
                return new FPolygon2D(gp);
334
        }
335

    
336
        private FPolyline2D parseMultiLineString(ByteBuffer data) {
337
                int count = data.getInt();
338
                GeneralPathX gp = new GeneralPathX();
339

    
340
                List<Double> d3 = null;
341

    
342
                int nDims = 2;
343
                if (gHaveZ || gHaveM)
344
                        nDims = 3;
345

    
346
                if (nDims == 3)
347
                        d3 = new ArrayList<Double>();
348

    
349
                for (int i = 0; i < count; i++) {
350
                        parseTypeAndSRID(data);
351
                        FPoint2D[] points = parsePointArray(data, gHaveZ, gHaveM);
352

    
353
                        for (int j = 0; j < points.length; j++) {
354
                                // parent has 3 dimensions
355
                                if (nDims == 3) {
356
                                        if (gHaveZ)
357
                                                d3.add(((FPoint3D) points[j]).getZ());
358
                                        else if (gHaveM)
359
                                                d3.add(((FPoint2DM) points[j]).getM());
360
                                        else
361
                                                d3.add(0.0); // child does not have 3 dimensions
362
                                }
363
                                if (j == 0)
364
                                        gp.moveTo(points[j].getX(), points[j].getY());
365
                                else
366
                                        gp.lineTo(points[j].getX(), points[j].getY());
367
                        }
368

    
369
                }
370

    
371
                if (nDims == 3) {
372
                        double ad3[] = new double[d3.size()];
373
                        for (int i = 0; i < d3.size(); i++) {
374
                                ad3[i] = ((Double) d3.get(i)).doubleValue();
375
                        }
376

    
377
                        if (gHaveZ)
378
                                return new FPolyline3D(gp, ad3);
379
                        else
380
                                return new FPolyline2DM(gp, ad3);
381
                }
382
                return new FPolyline2D(gp);
383
        }
384

    
385
        private FGeometry createGeometry(FShape shp) {
386
                if (shp instanceof FShapeM)
387
                        return new FGeometryM((FShapeM) shp);
388
                return ShapeFactory.createGeometry(shp);
389
        }
390

    
391
        // Its FShape and not FPolygon2D because FPolygon2DM returns a FPolyline
392
        private FShape parseMultiPolygon(ByteBuffer data) {
393
                // it was expected not to find polygons with different srid or
394
                // coordiante dimension as subelements of the multipolygon
395
                // PostGIS avoid this behaviour, but OGC says it is allow.
396

    
397
                int count = data.getInt();
398
                GeneralPathX gp = new GeneralPathX();
399

    
400
                List<Double> d3 = null;
401

    
402
                int nDims = 2;
403
                if (gHaveZ || gHaveM)
404
                        nDims = 3;
405

    
406
                if (nDims == 3)
407
                        d3 = new ArrayList<Double>();
408

    
409
                for (int i = 0; i < count; i++) {
410
                        parseTypeAndSRID(data);
411
                        int countRings = data.getInt();
412
                        for (int j = 0; j < countRings; j++) {
413
                                FPoint2D[] points = parsePointArray(data, gHaveZ, gHaveM);
414

    
415
                                for (int k = 0; k < points.length; k++) {
416
                                        if (k == points.length - 1) {
417
                                                gp.closePath();
418
                                                if (nDims == 3) {
419
                                                        if (gHaveZ)
420
                                                                d3.add(((FPoint3D) points[0]).getZ());
421
                                                        else if (gHaveM)
422
                                                                d3.add(((FPoint2DM) points[0]).getM());
423
                                                        else
424
                                                                d3.add(0.0); // child does not have 3 dimensions
425
                                                }                                                                                        
426
                                        } else {
427
                                                // parent has 3 dimensions
428
                                                if (nDims == 3) {
429
                                                        if (gHaveZ)
430
                                                                d3.add(((FPoint3D) points[k]).getZ());
431
                                                        else if (gHaveM)
432
                                                                d3.add(((FPoint2DM) points[k]).getM());
433
                                                        else
434
                                                                d3.add(0.0); // child does not have 3 dimensions
435
                                                }
436
                                                if (k == 0)
437
                                                        gp.moveTo(points[k].getX(), points[k].getY());
438
                                                else
439
                                                        gp.lineTo(points[k].getX(), points[k].getY());
440
                                        }
441
                                }
442

    
443
                        }
444

    
445
                }
446

    
447
                if (nDims == 3) {
448
                        double ad3[] = new double[d3.size()];
449
                        for (int i = 0; i < d3.size(); i++) {
450
                                ad3[i] = ((Double) d3.get(i)).doubleValue();
451
                        }
452

    
453
                        if (gHaveZ)
454
                                return new FPolygon3D(gp, ad3);
455
                        else
456
                                return new FPolygon2DM(gp, ad3);
457
                }
458
                return new FPolygon2D(gp);
459

    
460
        }
461

    
462
        private FGeometryCollection parseCollection(ByteBuffer data) {
463
                int count = data.getInt();
464
                IGeometry[] geoms = new IGeometry[count];
465
                parseGeometryArray(data, geoms);
466
                return new FGeometryCollection(geoms);
467
        }
468

    
469
}