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 @ 40559

History | View | Annotate | Download (14.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
/*
25
 * WKBParser.java
26
 * Based in
27
 * PostGIS extension for PostgreSQL JDBC driver - Binary Parser
28
 *
29
 * NOTA: Es posible que lo mejor sea crear un PostGisGeometry que implemente
30
 * la interfaz IGeometry, y as? nos sirve de base para tener IGeometries
31
 * que encapsulan otras posibles geometr?as. Por ejemplo, un JTSGeometry.
32
 * De esta forma, un driver no necesitar?a reescribirse.
33
 *
34
 * (C) 2005 Markus Schaber, schabios@logi-track.com
35
 *
36
 * This library is free software; you can redistribute it and/or modify it under
37
 * the terms of the GNU Lesser General Public License as published by the Free
38
 * Software Foundation, either version 2.1 of the License.
39
 *
40
 * This library is distributed in the hope that it will be useful, but WITHOUT
41
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
43
 * details.
44
 *
45
 * You should have received a copy of the GNU Lesser General Public License
46
 * along with this library; if not, write to the Free Software Foundation, Inc.,
47
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit the web at
48
 * http://www.gnu.org.
49
 *
50
 * $Id: WKBParser2.java 24154 2008-10-21 10:01:05Z jpiera $
51
 */
52
package org.gvsig.fmap.geom.operation.fromwkb;
53

    
54
import java.nio.ByteBuffer;
55
import java.nio.ByteOrder;
56

    
57
import org.gvsig.fmap.geom.Geometry;
58
import org.gvsig.fmap.geom.GeometryLocator;
59
import org.gvsig.fmap.geom.GeometryManager;
60
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
61
import org.gvsig.fmap.geom.Geometry.TYPES;
62
import org.gvsig.fmap.geom.aggregate.MultiPoint;
63
import org.gvsig.fmap.geom.aggregate.MultiPrimitive;
64
import org.gvsig.fmap.geom.aggregate.MultiSurface;
65
import org.gvsig.fmap.geom.exception.CreateGeometryException;
66
import org.gvsig.fmap.geom.primitive.Curve;
67
import org.gvsig.fmap.geom.primitive.OrientablePrimitive;
68
import org.gvsig.fmap.geom.primitive.Point;
69
import org.gvsig.fmap.geom.primitive.Primitive;
70
import org.gvsig.fmap.geom.primitive.Surface;
71
import org.gvsig.fmap.geom.primitive.impl.Point2D;
72
import org.gvsig.fmap.geom.type.GeometryType;
73
import org.gvsig.fmap.geom.type.GeometryTypeNotSupportedException;
74
import org.gvsig.fmap.geom.type.GeometryTypeNotValidException;
75
import org.slf4j.Logger;
76
import org.slf4j.LoggerFactory;
77

    
78
import com.vividsolutions.jts.io.WKBConstants;
79

    
80
/**
81
 * Parse binary representation of geometries. Currently, only text rep (hexed)
82
 * implementation is tested.
83
 * 
84
 * It should be easy to add char[] and CharSequence ByteGetter instances,
85
 * although the latter one is not compatible with older jdks.
86
 * 
87
 * I did not implement real unsigned 32-bit integers or emulate them with long,
88
 * as both java Arrays and Strings currently can have only 2^31-1 elements
89
 * (bytes), so we cannot even get or build Geometries with more than approx.
90
 * 2^28 coordinates (8 bytes each).
91
 * 
92
 * @author markus.schaber@logi-track.com
93
 * 
94
 */
95
// jomarlla
96
// Read 3D and build 3D geometries using gvSIG objects.
97
// XY, XYZ and XYM supported.
98
// MultiPoint2DM is not supported by gvSIG.
99
// When gvSIg edit a polygon it makes the polygon 2d, so the update
100
// operations will just write 2d geometries even though the postgis
101
// driver is ready for writing 3D geometries.
102

    
103
public class WKBParser3 {
104

    
105
        private boolean gHaveM, gHaveZ, gHaveS; // M, Z y SRID
106

    
107
        private static final Logger LOG = LoggerFactory.getLogger(WKBParser2.class);
108
        private GeometryManager geomManager = GeometryLocator.getGeometryManager();
109
        private GeometryType[] pointGeometryTypes;
110

    
111
        /**
112
         * @throws GeometryTypeNotValidException
113
         * @throws GeometryTypeNotSupportedException
114
         * 
115
         */
116
        public WKBParser3() {
117
                pointGeometryTypes =
118
                        new GeometryType[] {
119
                                loadPointGeometryType(Geometry.SUBTYPES.GEOM2D, "2D"),
120
                                loadPointGeometryType(Geometry.SUBTYPES.GEOM3D, "3D"),
121
                                loadPointGeometryType(Geometry.SUBTYPES.GEOM2DM, "2DM"),
122
                                loadPointGeometryType(Geometry.SUBTYPES.GEOM3DM, "3DM") };
123
        }
124

    
125
        private GeometryType loadPointGeometryType(int subtype, String subTypeName) {
126
                try {
127
                        return geomManager.getGeometryType(Geometry.TYPES.POINT, subtype);
128
                } catch (Exception e) {
129
                        LOG.warn("Unable to get a reference to the geometry "
130
                                        + "type Point{}, to be cached", subTypeName);
131
                        return null;
132
                }
133
        }
134

    
135
        /**
136
         * Parse a binary encoded geometry.
137
         * 
138
         * Is synchronized to protect offset counter. (Unfortunately, Java does not
139
         * have neither call by reference nor multiple return values.)
140
         * @throws CreateGeometryException 
141
         */
142
        public synchronized Geometry parse(byte[] value) throws CreateGeometryException {
143
                // BinaryByteGetter bytes = new ByteGetter.BinaryByteGetter(value);
144
                ByteBuffer buf = ByteBuffer.wrap(value);
145
                return parseGeometry(buf);
146
        }
147

    
148

    
149
        /** Parse a geometry starting at offset. 
150
         * @throws CreateGeometryException */
151
        protected Geometry parseGeometry(ByteBuffer data) throws CreateGeometryException {
152
                int realtype = parseTypeAndSRID(data);
153

    
154
                Geometry result1 = null;
155
                switch (realtype) {
156
                case WKBConstants.wkbPoint:
157
                        result1 = parsePoint(data, gHaveZ, gHaveM);
158
                        break;
159
                case WKBConstants.wkbLineString:
160
                        result1 = parseLineString(data, gHaveZ, gHaveM);
161
                        break;
162
                case WKBConstants.wkbPolygon:
163
                        result1 = parsePolygon(data, gHaveZ, gHaveM);
164
                        break;
165
                case WKBConstants.wkbMultiPoint:
166
                        result1 = parseMultiPoint(data);
167
                        break;
168
                case WKBConstants.wkbMultiLineString:
169
                        result1 = parseMultiLineString(data);
170
                        return result1;
171
                case WKBConstants.wkbMultiPolygon:
172
                        result1 = parseMultiPolygon(data);
173
                        break;
174
                case WKBConstants.wkbGeometryCollection:
175
                        result1 = parseCollection(data);
176
                        break;
177
                default:
178
                        //throw new IllegalArgumentException("Unknown Geometry Type!");
179
                }
180

    
181
                return result1;
182
        }
183

    
184
        protected int parseTypeAndSRID(ByteBuffer data) {
185
                byte endian = data.get(); // skip and test endian flag
186
                if (endian == 1) {
187
                        data.order(ByteOrder.LITTLE_ENDIAN);
188
                }
189
                int typeword = data.getInt();
190

    
191
                int realtype = typeword & 0x1FFFFFFF; // cut off high flag bits
192

    
193
                gHaveZ = (typeword & 0x80000000) != 0;
194
                gHaveM = (typeword & 0x40000000) != 0;
195
                gHaveS = (typeword & 0x20000000) != 0;
196
                
197
                // not used
198
                int srid = -1;
199

    
200
                if (gHaveS) {
201
                        srid = data.getInt();
202
                }
203

    
204
                return realtype;
205

    
206
        }
207

    
208
        private Point parsePoint(ByteBuffer data, boolean haveZ, boolean haveM) 
209
        throws CreateGeometryException {
210
                double x = data.getDouble();
211
                double y = data.getDouble();
212
                Point point;
213

    
214
                int subtype = getSubType(haveZ, haveM);
215

    
216
                // If we have a cached GeometryType use it, otherwise call the manager
217
                point = (Point) (pointGeometryTypes[subtype] == null ? geomManager.create(
218
                                Geometry.TYPES.POINT, subtype) : pointGeometryTypes[subtype].create());
219
                point.setX(x);
220
                point.setY(y);
221

    
222
                // Other dimensions
223
                if (haveZ) {
224
                        point.setCoordinateAt(Geometry.DIMENSIONS.Z, data.getDouble());
225
                        if (haveM) {
226
                                /*point.setCoordinateAt(Geometry.DIMENSIONS.Z + 1,
227
                                                data.getDouble());*/
228
                                data.getDouble();
229
                        }
230
                } else {
231
                        if (haveM) {
232
                                /*point.setCoordinateAt(Geometry.DIMENSIONS.Y + 1,
233
                                                data.getDouble());*/
234
                                data.getDouble();
235
                        }
236
                }
237

    
238
                return point;
239
        }
240

    
241
        /**
242
         * @param haveZ
243
         * @param haveM
244
         * @return
245
         */
246
        private int getSubType(boolean haveZ, boolean haveM) {
247
                /*int subtype =
248
                        haveZ ? (haveM ? Geometry.SUBTYPES.GEOM3DM
249
                                        : Geometry.SUBTYPES.GEOM3D) : (haveM
250
                                                        ? Geometry.SUBTYPES.GEOM2DM : Geometry.SUBTYPES.GEOM2D);*/
251
                //TODO: No hay soporte para M
252
                int subtype = haveZ ? Geometry.SUBTYPES.GEOM3D : Geometry.SUBTYPES.GEOM2D;
253
                return subtype;
254
        }
255

    
256
        private Curve parseMultiLineString(ByteBuffer data) throws CreateGeometryException {
257
                Curve curve = (Curve) geomManager.create(TYPES.CURVE, getSubType(gHaveZ, gHaveM));
258
                fillOrientablePrimitive(data, curve);
259
                return curve;
260
        }
261

    
262
        /**
263
         * @param data
264
         * @return
265
         * @throws CreateGeometryException
266
         */
267
        private void fillOrientablePrimitive(ByteBuffer data, OrientablePrimitive orientablePrimitive)
268
        throws CreateGeometryException {
269
                int count = data.getInt();
270

    
271
                for (int i=0; i < count; i++)
272
                {
273
                        parseTypeAndSRID(data);
274
                        Point[] points = parsePointArray(data, gHaveZ, gHaveM);
275

    
276
                        orientablePrimitive.addMoveToVertex(points[0]);
277
                        for (int j = 1; j < points.length; j++) {
278
                                orientablePrimitive.addVertex(points[j]);
279
                        }           
280
                }                
281
        }
282

    
283
        private MultiSurface parseMultiPolygon(ByteBuffer data)
284
        throws CreateGeometryException {
285
                int count = data.getInt();
286
                
287
                int subType = getSubType(gHaveZ, gHaveM);        
288
                MultiSurface multiSurface = (MultiSurface)geomManager.create(TYPES.MULTISURFACE, subType);        
289

    
290
                Point point;
291
                for (int i = 0; i < count; i++) {
292
                        Surface surface = (Surface)geomManager.create(TYPES.SURFACE, subType); 
293
                        parseTypeAndSRID(data);
294
                        int countRings = data.getInt();            
295
                        for (int j = 0; j < countRings; j++) {
296
                                double[][] points = parsePointsAsDoubleArray(data, gHaveZ, gHaveM);
297

    
298
                                //Add the initial point
299
                                point = geomManager.createPoint(points[0][0], points[0][1], subType);
300
                                if(gHaveZ)
301
                                        point.setCoordinateAt(Geometry.DIMENSIONS.Z, points[0][2]);
302
                                surface.addMoveToVertex(point);
303
                                
304

    
305
                                //Add the other points
306
                                int lastPoint = points.length - 1;
307
                                for (int k = 1; k < lastPoint; k++){                    
308
                                        point = geomManager.createPoint(points[k][0], points[k][1], subType);
309
                                        if(gHaveZ)
310
                                                point.setCoordinateAt(Geometry.DIMENSIONS.Z, points[0][2]);
311
                                        /*for (int l = 2; l < points[k].length; i++){
312
                                                point.setCoordinateAt(l, points[k][l]);
313
                                        }*/
314
                                        surface.addVertex(point);
315
                                }   
316
                                surface.closePrimitive();
317
                        }
318
                        multiSurface.addSurface(surface);
319
                }
320
                return multiSurface;
321
        }
322

    
323
        private double[][] parsePointsAsDoubleArray(ByteBuffer data, boolean haveZ,
324
                        boolean haveM) throws CreateGeometryException {
325
                int count = data.getInt();
326
                double points[][] = null;
327
                int subtype = getSubType(haveZ, haveM);
328

    
329
                switch (subtype) {
330
                case Geometry.SUBTYPES.GEOM2D:
331
                        points = new double[count][2];
332
                        break;
333
                case Geometry.SUBTYPES.GEOM3D:
334
                case Geometry.SUBTYPES.GEOM2DM:
335
                        points = new double[count][3];
336
                        break;
337
                case Geometry.SUBTYPES.GEOM3DM:
338
                        points = new double[count][4];
339
                        break;
340
                default:
341
                        break;
342
                }
343

    
344
                for (int i = 0; i < count; i++) {
345
                        points[i][0] = data.getDouble(); // x
346
                        points[i][1] = data.getDouble(); // y
347
                        switch (subtype) {
348
                        case Geometry.SUBTYPES.GEOM3D:
349
                        case Geometry.SUBTYPES.GEOM2DM:
350
                                points[i][2] = data.getDouble(); // z or m
351
                                break;
352
                        case Geometry.SUBTYPES.GEOM3DM:
353
                                points[i][2] = data.getDouble(); // z
354
                                points[i][3] = data.getDouble(); // m
355
                                break;
356
                        default:
357
                                break;
358
                        }
359
                        //TODO: Remove when M be supported
360
                        if(haveZ && haveM)
361
                                data.getDouble();
362
                }
363
                return points;
364
        }
365

    
366
        private MultiPrimitive parseCollection(ByteBuffer data) throws CreateGeometryException {
367
                int count = data.getInt();
368
                Geometry[] geoms = new Geometry[count];
369
                parseGeometryArray(data, geoms);
370
                MultiPrimitive multiPrimitive = (MultiPrimitive) geomManager.create(TYPES.AGGREGATE, SUBTYPES.GEOM2D);
371
                for (int i = 0 ; i < geoms.length ; i++){
372
                        multiPrimitive.addPrimitive((Primitive) geoms[i]);
373
                }
374
                return multiPrimitive;
375
        }        
376

    
377
        /** Parse an Array of "full" Geometries 
378
         * @throws CreateGeometryException */
379
        private void parseGeometryArray(ByteBuffer data, Geometry[] container) throws CreateGeometryException {
380
                for (int i = 0; i < container.length; i++) {
381
                        container[i] = parseGeometry(data);
382
                }
383
        }
384

    
385
        private Curve parseLineString(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
386
                Point[] points = parsePointArray(data, haveZ, haveM);
387
                Curve curve = (Curve) geomManager.create(TYPES.CURVE, getSubType(haveZ, haveM));    
388
                curve.addMoveToVertex(points[0]);
389
                for (int i = 1; i< points.length; i++) {
390
                        curve.addVertex(points[i]);
391
                }
392
                return curve;
393
        }
394

    
395
        /**
396
         * Parse an Array of "slim" Points (without endianness and type, part of
397
         * LinearRing and Linestring, but not MultiPoint!
398
         * 
399
         * @param haveZ
400
         * @param haveM
401
         * @throws CreateGeometryException
402
         */
403
        private Point[] parsePointArray(ByteBuffer data, boolean haveZ, boolean haveM) 
404
        throws CreateGeometryException {
405
                int count = data.getInt();
406
                Point[] result = new Point[count];
407
                for (int i = 0; i < count; i++) {
408
                        result[i] = parsePoint(data, haveZ, haveM);
409
                }
410
                return result;
411
        }
412

    
413

    
414
        private Surface parsePolygon(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
415
                int count = data.getInt();
416
                int subType = getSubType(haveZ, haveM);        
417

    
418
                Surface surface = (Surface) geomManager.create(TYPES.SURFACE, subType);
419

    
420
                for (int i = 0; i < count; i++) {
421
                        fillLinearRing(data, surface, haveZ, haveM); 
422
                }
423

    
424
                surface.closePrimitive();                
425

    
426
                return surface;
427
        }
428

    
429
        private void fillLinearRing(ByteBuffer data, OrientablePrimitive orientablePrimitive, boolean haveZ, boolean haveM) throws CreateGeometryException {
430
                Point[] points = parsePointArray(data, haveZ, haveM);
431

    
432
                orientablePrimitive.addMoveToVertex(points[0]);
433
                int lastPoint = points.length - 1;
434
                for (int i = 1; i< lastPoint; i++) {
435
                        orientablePrimitive.addVertex(points[i]);
436
                }        
437
        }
438

    
439
        private MultiPoint parseMultiPoint(ByteBuffer data) throws CreateGeometryException {
440
                MultiPoint multipoint = (MultiPoint) geomManager.create(TYPES.MULTIPOINT, SUBTYPES.GEOM2D);
441
                Point2D[] points = new Point2D[data.getInt()];
442
                for (int i = 0; i < points.length; i++) {
443
                        parseTypeAndSRID(data);
444
                        multipoint.addPoint(parsePoint(data, gHaveZ, gHaveM));
445
                }
446
                return multipoint;
447
        }
448

    
449
}