Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.geometry / org.gvsig.fmap.geometry.operation / src / main / java / org / gvsig / fmap / geom / operation / fromwkb / WKBParser3.java @ 40767

History | View | Annotate | Download (13.5 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
package org.gvsig.fmap.geom.operation.fromwkb;
25

    
26
/*
27
 * Based in
28
 * PostGIS extension for PostgreSQL JDBC driver - Binary Parser
29
 *
30
 * (C) 2005 Markus Schaber, schabios@logi-track.com
31
 */
32

    
33
import java.nio.ByteBuffer;
34
import java.nio.ByteOrder;
35

    
36
import org.gvsig.fmap.geom.Geometry;
37
import org.gvsig.fmap.geom.GeometryLocator;
38
import org.gvsig.fmap.geom.GeometryManager;
39
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
40
import org.gvsig.fmap.geom.Geometry.TYPES;
41
import org.gvsig.fmap.geom.aggregate.MultiPoint;
42
import org.gvsig.fmap.geom.aggregate.MultiPrimitive;
43
import org.gvsig.fmap.geom.aggregate.MultiSurface;
44
import org.gvsig.fmap.geom.exception.CreateGeometryException;
45
import org.gvsig.fmap.geom.primitive.Curve;
46
import org.gvsig.fmap.geom.primitive.OrientablePrimitive;
47
import org.gvsig.fmap.geom.primitive.Point;
48
import org.gvsig.fmap.geom.primitive.Primitive;
49
import org.gvsig.fmap.geom.primitive.Surface;
50
import org.gvsig.fmap.geom.primitive.impl.Point2D;
51
import org.gvsig.fmap.geom.type.GeometryType;
52
import org.gvsig.fmap.geom.type.GeometryTypeNotSupportedException;
53
import org.gvsig.fmap.geom.type.GeometryTypeNotValidException;
54
import org.slf4j.Logger;
55
import org.slf4j.LoggerFactory;
56

    
57
import com.vividsolutions.jts.io.WKBConstants;
58

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

    
82
public class WKBParser3 {
83

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

    
86
        private static final Logger LOG = LoggerFactory.getLogger(WKBParser2.class);
87
        private GeometryManager geomManager = GeometryLocator.getGeometryManager();
88
        private GeometryType[] pointGeometryTypes;
89

    
90
        /**
91
         * @throws GeometryTypeNotValidException
92
         * @throws GeometryTypeNotSupportedException
93
         * 
94
         */
95
        public WKBParser3() {
96
                pointGeometryTypes =
97
                        new GeometryType[] {
98
                                loadPointGeometryType(Geometry.SUBTYPES.GEOM2D, "2D"),
99
                                loadPointGeometryType(Geometry.SUBTYPES.GEOM3D, "3D"),
100
                                loadPointGeometryType(Geometry.SUBTYPES.GEOM2DM, "2DM"),
101
                                loadPointGeometryType(Geometry.SUBTYPES.GEOM3DM, "3DM") };
102
        }
103

    
104
        private GeometryType loadPointGeometryType(int subtype, String subTypeName) {
105
                try {
106
                        return geomManager.getGeometryType(Geometry.TYPES.POINT, subtype);
107
                } catch (Exception e) {
108
                        LOG.warn("Unable to get a reference to the geometry "
109
                                        + "type Point{}, to be cached", subTypeName);
110
                        return null;
111
                }
112
        }
113

    
114
        /**
115
         * Parse a binary encoded geometry.
116
         * 
117
         * Is synchronized to protect offset counter. (Unfortunately, Java does not
118
         * have neither call by reference nor multiple return values.)
119
         * @throws CreateGeometryException 
120
         */
121
        public synchronized Geometry parse(byte[] value) throws CreateGeometryException {
122
                // BinaryByteGetter bytes = new ByteGetter.BinaryByteGetter(value);
123
                ByteBuffer buf = ByteBuffer.wrap(value);
124
                return parseGeometry(buf);
125
        }
126

    
127

    
128
        /** Parse a geometry starting at offset. 
129
         * @throws CreateGeometryException */
130
        protected Geometry parseGeometry(ByteBuffer data) throws CreateGeometryException {
131
                int realtype = parseTypeAndSRID(data);
132

    
133
                Geometry result1 = null;
134
                switch (realtype) {
135
                case WKBConstants.wkbPoint:
136
                        result1 = parsePoint(data, gHaveZ, gHaveM);
137
                        break;
138
                case WKBConstants.wkbLineString:
139
                        result1 = parseLineString(data, gHaveZ, gHaveM);
140
                        break;
141
                case WKBConstants.wkbPolygon:
142
                        result1 = parsePolygon(data, gHaveZ, gHaveM);
143
                        break;
144
                case WKBConstants.wkbMultiPoint:
145
                        result1 = parseMultiPoint(data);
146
                        break;
147
                case WKBConstants.wkbMultiLineString:
148
                        result1 = parseMultiLineString(data);
149
                        return result1;
150
                case WKBConstants.wkbMultiPolygon:
151
                        result1 = parseMultiPolygon(data);
152
                        break;
153
                case WKBConstants.wkbGeometryCollection:
154
                        result1 = parseCollection(data);
155
                        break;
156
                default:
157
                        //throw new IllegalArgumentException("Unknown Geometry Type!");
158
                }
159

    
160
                return result1;
161
        }
162

    
163
        protected int parseTypeAndSRID(ByteBuffer data) {
164
                byte endian = data.get(); // skip and test endian flag
165
                if (endian == 1) {
166
                        data.order(ByteOrder.LITTLE_ENDIAN);
167
                }
168
                int typeword = data.getInt();
169

    
170
                int realtype = typeword & 0x1FFFFFFF; // cut off high flag bits
171

    
172
                gHaveZ = (typeword & 0x80000000) != 0;
173
                gHaveM = (typeword & 0x40000000) != 0;
174
                gHaveS = (typeword & 0x20000000) != 0;
175
                
176
                // not used
177
                int srid = -1;
178

    
179
                if (gHaveS) {
180
                        srid = data.getInt();
181
                }
182

    
183
                return realtype;
184

    
185
        }
186

    
187
        private Point parsePoint(ByteBuffer data, boolean haveZ, boolean haveM) 
188
        throws CreateGeometryException {
189
                double x = data.getDouble();
190
                double y = data.getDouble();
191
                Point point;
192

    
193
                int subtype = getSubType(haveZ, haveM);
194

    
195
                // If we have a cached GeometryType use it, otherwise call the manager
196
                point = (Point) (pointGeometryTypes[subtype] == null ? geomManager.create(
197
                                Geometry.TYPES.POINT, subtype) : pointGeometryTypes[subtype].create());
198
                point.setX(x);
199
                point.setY(y);
200

    
201
                // Other dimensions
202
                if (haveZ) {
203
                        point.setCoordinateAt(Geometry.DIMENSIONS.Z, data.getDouble());
204
                        if (haveM) {
205
                                /*point.setCoordinateAt(Geometry.DIMENSIONS.Z + 1,
206
                                                data.getDouble());*/
207
                                data.getDouble();
208
                        }
209
                } else {
210
                        if (haveM) {
211
                                /*point.setCoordinateAt(Geometry.DIMENSIONS.Y + 1,
212
                                                data.getDouble());*/
213
                                data.getDouble();
214
                        }
215
                }
216

    
217
                return point;
218
        }
219

    
220
        /**
221
         * @param haveZ
222
         * @param haveM
223
         * @return
224
         */
225
        private int getSubType(boolean haveZ, boolean haveM) {
226
                /*int subtype =
227
                        haveZ ? (haveM ? Geometry.SUBTYPES.GEOM3DM
228
                                        : Geometry.SUBTYPES.GEOM3D) : (haveM
229
                                                        ? Geometry.SUBTYPES.GEOM2DM : Geometry.SUBTYPES.GEOM2D);*/
230
                //TODO: No hay soporte para M
231
                int subtype = haveZ ? Geometry.SUBTYPES.GEOM3D : Geometry.SUBTYPES.GEOM2D;
232
                return subtype;
233
        }
234

    
235
        private Curve parseMultiLineString(ByteBuffer data) throws CreateGeometryException {
236
                Curve curve = (Curve) geomManager.create(TYPES.CURVE, getSubType(gHaveZ, gHaveM));
237
                fillOrientablePrimitive(data, curve);
238
                return curve;
239
        }
240

    
241
        /**
242
         * @param data
243
         * @return
244
         * @throws CreateGeometryException
245
         */
246
        private void fillOrientablePrimitive(ByteBuffer data, OrientablePrimitive orientablePrimitive)
247
        throws CreateGeometryException {
248
                int count = data.getInt();
249

    
250
                for (int i=0; i < count; i++)
251
                {
252
                        parseTypeAndSRID(data);
253
                        Point[] points = parsePointArray(data, gHaveZ, gHaveM);
254

    
255
                        orientablePrimitive.addMoveToVertex(points[0]);
256
                        for (int j = 1; j < points.length; j++) {
257
                                orientablePrimitive.addVertex(points[j]);
258
                        }           
259
                }                
260
        }
261

    
262
        private MultiSurface parseMultiPolygon(ByteBuffer data)
263
        throws CreateGeometryException {
264
                int count = data.getInt();
265
                
266
                int subType = getSubType(gHaveZ, gHaveM);        
267
                MultiSurface multiSurface = (MultiSurface)geomManager.create(TYPES.MULTISURFACE, subType);        
268

    
269
                Point point;
270
                for (int i = 0; i < count; i++) {
271
                        Surface surface = (Surface)geomManager.create(TYPES.SURFACE, subType); 
272
                        parseTypeAndSRID(data);
273
                        int countRings = data.getInt();            
274
                        for (int j = 0; j < countRings; j++) {
275
                                double[][] points = parsePointsAsDoubleArray(data, gHaveZ, gHaveM);
276

    
277
                                //Add the initial point
278
                                point = geomManager.createPoint(points[0][0], points[0][1], subType);
279
                                if(gHaveZ)
280
                                        point.setCoordinateAt(Geometry.DIMENSIONS.Z, points[0][2]);
281
                                surface.addMoveToVertex(point);
282
                                
283

    
284
                                //Add the other points
285
                                int lastPoint = points.length - 1;
286
                                for (int k = 1; k < lastPoint; k++){                    
287
                                        point = geomManager.createPoint(points[k][0], points[k][1], subType);
288
                                        if(gHaveZ)
289
                                                point.setCoordinateAt(Geometry.DIMENSIONS.Z, points[0][2]);
290
                                        /*for (int l = 2; l < points[k].length; i++){
291
                                                point.setCoordinateAt(l, points[k][l]);
292
                                        }*/
293
                                        surface.addVertex(point);
294
                                }   
295
                                surface.closePrimitive();
296
                        }
297
                        multiSurface.addSurface(surface);
298
                }
299
                return multiSurface;
300
        }
301

    
302
        private double[][] parsePointsAsDoubleArray(ByteBuffer data, boolean haveZ,
303
                        boolean haveM) throws CreateGeometryException {
304
                int count = data.getInt();
305
                double points[][] = null;
306
                int subtype = getSubType(haveZ, haveM);
307

    
308
                switch (subtype) {
309
                case Geometry.SUBTYPES.GEOM2D:
310
                        points = new double[count][2];
311
                        break;
312
                case Geometry.SUBTYPES.GEOM3D:
313
                case Geometry.SUBTYPES.GEOM2DM:
314
                        points = new double[count][3];
315
                        break;
316
                case Geometry.SUBTYPES.GEOM3DM:
317
                        points = new double[count][4];
318
                        break;
319
                default:
320
                        break;
321
                }
322

    
323
                for (int i = 0; i < count; i++) {
324
                        points[i][0] = data.getDouble(); // x
325
                        points[i][1] = data.getDouble(); // y
326
                        switch (subtype) {
327
                        case Geometry.SUBTYPES.GEOM3D:
328
                        case Geometry.SUBTYPES.GEOM2DM:
329
                                points[i][2] = data.getDouble(); // z or m
330
                                break;
331
                        case Geometry.SUBTYPES.GEOM3DM:
332
                                points[i][2] = data.getDouble(); // z
333
                                points[i][3] = data.getDouble(); // m
334
                                break;
335
                        default:
336
                                break;
337
                        }
338
                        //TODO: Remove when M be supported
339
                        if(haveZ && haveM)
340
                                data.getDouble();
341
                }
342
                return points;
343
        }
344

    
345
        private MultiPrimitive parseCollection(ByteBuffer data) throws CreateGeometryException {
346
                int count = data.getInt();
347
                Geometry[] geoms = new Geometry[count];
348
                parseGeometryArray(data, geoms);
349
                MultiPrimitive multiPrimitive = (MultiPrimitive) geomManager.create(TYPES.AGGREGATE, SUBTYPES.GEOM2D);
350
                for (int i = 0 ; i < geoms.length ; i++){
351
                        multiPrimitive.addPrimitive((Primitive) geoms[i]);
352
                }
353
                return multiPrimitive;
354
        }        
355

    
356
        /** Parse an Array of "full" Geometries 
357
         * @throws CreateGeometryException */
358
        private void parseGeometryArray(ByteBuffer data, Geometry[] container) throws CreateGeometryException {
359
                for (int i = 0; i < container.length; i++) {
360
                        container[i] = parseGeometry(data);
361
                }
362
        }
363

    
364
        private Curve parseLineString(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
365
                Point[] points = parsePointArray(data, haveZ, haveM);
366
                Curve curve = (Curve) geomManager.create(TYPES.CURVE, getSubType(haveZ, haveM));    
367
                curve.addMoveToVertex(points[0]);
368
                for (int i = 1; i< points.length; i++) {
369
                        curve.addVertex(points[i]);
370
                }
371
                return curve;
372
        }
373

    
374
        /**
375
         * Parse an Array of "slim" Points (without endianness and type, part of
376
         * LinearRing and Linestring, but not MultiPoint!
377
         * 
378
         * @param haveZ
379
         * @param haveM
380
         * @throws CreateGeometryException
381
         */
382
        private Point[] parsePointArray(ByteBuffer data, boolean haveZ, boolean haveM) 
383
        throws CreateGeometryException {
384
                int count = data.getInt();
385
                Point[] result = new Point[count];
386
                for (int i = 0; i < count; i++) {
387
                        result[i] = parsePoint(data, haveZ, haveM);
388
                }
389
                return result;
390
        }
391

    
392

    
393
        private Surface parsePolygon(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
394
                int count = data.getInt();
395
                int subType = getSubType(haveZ, haveM);        
396

    
397
                Surface surface = (Surface) geomManager.create(TYPES.SURFACE, subType);
398

    
399
                for (int i = 0; i < count; i++) {
400
                        fillLinearRing(data, surface, haveZ, haveM); 
401
                }
402

    
403
                surface.closePrimitive();                
404

    
405
                return surface;
406
        }
407

    
408
        private void fillLinearRing(ByteBuffer data, OrientablePrimitive orientablePrimitive, boolean haveZ, boolean haveM) throws CreateGeometryException {
409
                Point[] points = parsePointArray(data, haveZ, haveM);
410

    
411
                orientablePrimitive.addMoveToVertex(points[0]);
412
                int lastPoint = points.length - 1;
413
                for (int i = 1; i< lastPoint; i++) {
414
                        orientablePrimitive.addVertex(points[i]);
415
                }        
416
        }
417

    
418
        private MultiPoint parseMultiPoint(ByteBuffer data) throws CreateGeometryException {
419
                MultiPoint multipoint = (MultiPoint) geomManager.create(TYPES.MULTIPOINT, SUBTYPES.GEOM2D);
420
                Point2D[] points = new Point2D[data.getInt()];
421
                for (int i = 0; i < points.length; i++) {
422
                        parseTypeAndSRID(data);
423
                        multipoint.addPoint(parsePoint(data, gHaveZ, gHaveM));
424
                }
425
                return multipoint;
426
        }
427

    
428
}