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 / WKBParser2.java @ 40767

History | View | Annotate | Download (15.7 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 com.vividsolutions.jts.io.WKBConstants;
37

    
38
import org.slf4j.Logger;
39
import org.slf4j.LoggerFactory;
40

    
41
import org.gvsig.fmap.geom.Geometry;
42
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
43
import org.gvsig.fmap.geom.Geometry.TYPES;
44
import org.gvsig.fmap.geom.GeometryLocator;
45
import org.gvsig.fmap.geom.GeometryManager;
46
import org.gvsig.fmap.geom.aggregate.MultiCurve;
47
import org.gvsig.fmap.geom.aggregate.MultiPoint;
48
import org.gvsig.fmap.geom.aggregate.MultiPrimitive;
49
import org.gvsig.fmap.geom.aggregate.MultiSurface;
50
import org.gvsig.fmap.geom.exception.CreateGeometryException;
51
import org.gvsig.fmap.geom.primitive.Curve;
52
import org.gvsig.fmap.geom.primitive.OrientablePrimitive;
53
import org.gvsig.fmap.geom.primitive.Point;
54
import org.gvsig.fmap.geom.primitive.Primitive;
55
import org.gvsig.fmap.geom.primitive.Surface;
56
import org.gvsig.fmap.geom.primitive.impl.Point2D;
57
import org.gvsig.fmap.geom.type.GeometryType;
58
import org.gvsig.fmap.geom.type.GeometryTypeNotSupportedException;
59
import org.gvsig.fmap.geom.type.GeometryTypeNotValidException;
60

    
61
/**
62
 * Parse binary representation of geometries. Currently, only text rep (hexed)
63
 * implementation is tested.
64
 *
65
 * It should be easy to add char[] and CharSequence ByteGetter instances,
66
 * although the latter one is not compatible with older jdks.
67
 *
68
 * I did not implement real unsigned 32-bit integers or emulate them with long,
69
 * as both java Arrays and Strings currently can have only 2^31-1 elements
70
 * (bytes), so we cannot even get or build Geometries with more than approx.
71
 * 2^28 coordinates (8 bytes each).
72
 *
73
 * @author markus.schaber@logi-track.com
74
 *
75
 */
76
public class WKBParser2 {
77

    
78
    private static final Logger LOG = LoggerFactory.getLogger(WKBParser2.class);
79

    
80
        private GeometryManager geomManager = GeometryLocator.getGeometryManager();
81
    private boolean gHaveM, gHaveZ, gHaveS; // M, Z y SRID
82
    private GeometryType[] pointGeometryTypes;
83

    
84
    /**
85
     * @throws GeometryTypeNotValidException
86
     * @throws GeometryTypeNotSupportedException
87
     * 
88
     */
89
    public WKBParser2() {
90
        pointGeometryTypes =
91
            new GeometryType[] {
92
                loadPointGeometryType(Geometry.SUBTYPES.GEOM2D, "2D"),
93
                loadPointGeometryType(Geometry.SUBTYPES.GEOM3D, "3D"),
94
                loadPointGeometryType(Geometry.SUBTYPES.GEOM2DM, "2DM"),
95
                loadPointGeometryType(Geometry.SUBTYPES.GEOM3DM, "3DM") };
96
    }
97

    
98
    private GeometryType loadPointGeometryType(int subtype, String subTypeName) {
99
        try {
100
            return geomManager.getGeometryType(Geometry.TYPES.POINT, subtype);
101
        } catch (Exception e) {
102
            LOG.warn("Unable to get a reference to the geometry "
103
                + "type Point{}, to be cached", subTypeName);
104
            return null;
105
        }
106
    }
107

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

    
122
    protected void parseTypeAndSRID(ByteBuffer data)
123
    {
124
        byte endian = data.get(); //skip and test endian flag
125
        /* if (endian != data.endian) {
126
            throw new IllegalArgumentException("Endian inconsistency!");
127
        } */
128
        int typeword = data.getInt();
129

    
130
        int realtype = typeword & 0x1FFFFFFF; //cut off high flag bits
131

    
132
        gHaveZ = (typeword & 0x80000000) != 0;
133
        gHaveM = (typeword & 0x40000000) != 0;
134
        gHaveS = (typeword & 0x20000000) != 0;
135

    
136
        int srid = -1;
137

    
138
        if (gHaveS) {
139
            srid = data.getInt();
140
        }
141

    
142
    }
143

    
144

    
145
    /** Parse a geometry starting at offset. 
146
     * @throws CreateGeometryException */
147
    protected Geometry parseGeometry(ByteBuffer data) throws CreateGeometryException {
148
        byte endian = data.get(); //skip and test endian flag
149
        if (endian == 1)
150
        {
151
                data.order(ByteOrder.LITTLE_ENDIAN);
152
        }
153
        /* if (endian != data.endian) {
154
            throw new IllegalArgumentException("Endian inconsistency!");
155
        } */
156
        int typeword = data.getInt();
157

    
158
        int realtype = typeword & 0x1FFFFFFF; //cut off high flag bits
159

    
160
        boolean haveZ = (typeword & 0x80000000) != 0;
161
        boolean haveM = (typeword & 0x40000000) != 0;
162
        boolean haveS = (typeword & 0x20000000) != 0;
163

    
164
        int srid = -1;
165

    
166
        if (haveS) {
167
            srid = data.getInt();
168
        }
169
        Geometry result1;
170
        switch (realtype) {
171
        case WKBConstants.wkbPoint :
172
                result1 = parsePoint(data, haveZ, haveM);
173
            break;
174
        case WKBConstants.wkbLineString :
175
            result1 = parseLineString(data, haveZ, haveM);
176
            break;
177
        case WKBConstants.wkbPolygon :
178
            result1 = parsePolygon(data, haveZ, haveM);
179
            break;
180
        case WKBConstants.wkbMultiPoint:
181
            result1 = parseMultiPoint(data);
182
            break;
183
        case WKBConstants.wkbMultiLineString:
184
            result1 = parseMultiLineString(data);
185
            break;
186
        case WKBConstants.wkbMultiPolygon:
187
            result1 = parseMultiPolygon(data);
188
            break;
189
        case WKBConstants.wkbGeometryCollection :
190
            result1 = parseCollection(data);
191
            break;
192
        default :
193
            throw new IllegalArgumentException("Unknown Geometry Type!");
194
        }
195

    
196
        /*Geometry result = result1;
197

198
        if (haveS) {
199
            result.setSrid(srid);
200
        } */
201
        return result1;
202
    }
203

    
204
    private Point parsePoint(ByteBuffer data, boolean haveZ, boolean haveM)
205
        throws CreateGeometryException {
206
        double x = data.getDouble();
207
        double y = data.getDouble();
208
        Point point;
209

    
210
        int subtype = getSubType(haveZ, haveM);
211

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

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

    
234
        return point;
235
    }
236

    
237
    /**
238
     * @param haveZ
239
     * @param haveM
240
     * @return
241
     */
242
    private int getSubType(boolean haveZ, boolean haveM) {
243
        int subtype =
244
            haveZ ? (haveM ? Geometry.SUBTYPES.GEOM3DM
245
                : Geometry.SUBTYPES.GEOM3D) : (haveM
246
                ? Geometry.SUBTYPES.GEOM2DM : Geometry.SUBTYPES.GEOM2D);
247
        return subtype;
248
    }
249

    
250
    /** Parse an Array of "full" Geometries 
251
     * @throws CreateGeometryException */
252
    private void parseGeometryArray(ByteBuffer data, Geometry[] container) throws CreateGeometryException {
253
        for (int i = 0; i < container.length; i++) {
254
            container[i] = parseGeometry(data);
255
        }
256
    }
257

    
258
    /**
259
     * Parse an Array of "slim" Points (without endianness and type, part of
260
     * LinearRing and Linestring, but not MultiPoint!
261
     * 
262
     * @param haveZ
263
     * @param haveM
264
     * @throws CreateGeometryException
265
     */
266
    private Point[] parsePointArray(ByteBuffer data, boolean haveZ,
267
        boolean haveM) throws CreateGeometryException {
268
        int count = data.getInt();
269
        Point[] result = new Point[count];
270
        for (int i = 0; i < count; i++) {
271
            result[i] = parsePoint(data, haveZ, haveM);
272
        }
273
        return result;
274
    }
275

    
276
    private double[][] parsePointsAsDoubleArray(ByteBuffer data, boolean haveZ,
277
        boolean haveM) throws CreateGeometryException {
278
        int count = data.getInt();
279
        double points[][] = null;
280
        int subtype = getSubType(haveZ, haveM);
281

    
282
        switch (subtype) {
283
        case Geometry.SUBTYPES.GEOM2D:
284
            points = new double[count][2];
285
            break;
286
        case Geometry.SUBTYPES.GEOM3D:
287
        case Geometry.SUBTYPES.GEOM2DM:
288
            points = new double[count][3];
289
            break;
290
        case Geometry.SUBTYPES.GEOM3DM:
291
            points = new double[count][4];
292
            break;
293
        default:
294
            break;
295
        }
296

    
297
        for (int i = 0; i < count; i++) {
298
            points[i][0] = data.getDouble(); // x
299
            points[i][1] = data.getDouble(); // y
300
            switch (subtype) {
301
            case Geometry.SUBTYPES.GEOM3D:
302
            case Geometry.SUBTYPES.GEOM2DM:
303
                points[i][2] = data.getDouble(); // z or m
304
                break;
305
            case Geometry.SUBTYPES.GEOM3DM:
306
                points[i][2] = data.getDouble(); // z
307
                points[i][3] = data.getDouble(); // m
308
                break;
309
            default:
310
                break;
311
            }
312
        }
313
        return points;
314
    }
315

    
316
    private MultiPoint parseMultiPoint(ByteBuffer data) throws CreateGeometryException {
317
            MultiPoint multipoint = (MultiPoint) geomManager.create(TYPES.MULTIPOINT, SUBTYPES.GEOM2D);
318
            Point2D[] points = new Point2D[data.getInt()];
319
        for (int i=0; i < points.length; i++)
320
        {
321
                parseTypeAndSRID(data);
322
                multipoint.addPoint(parsePoint(data, gHaveZ, gHaveM));
323
        }
324
        return multipoint;
325
    }
326

    
327
    private Curve parseLineString(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
328
        Point[] points = parsePointArray(data, haveZ, haveM);
329
        Curve curve = (Curve) geomManager.create(TYPES.CURVE, getSubType(haveZ, haveM));    
330
        curve.addMoveToVertex(points[0]);
331
        for (int i = 1; i< points.length; i++)
332
        {
333
            curve.addVertex(points[i]);
334
        }
335
        return curve;
336
    }
337

    
338
    private void fillLinearRing(ByteBuffer data, OrientablePrimitive orientablePrimitive, boolean haveZ, boolean haveM) throws CreateGeometryException {
339
        Point[] points = parsePointArray(data, haveZ, haveM);
340
       
341
        orientablePrimitive.addMoveToVertex(points[0]);
342
        int lastPoint = points.length - 1;
343
        for (int i = 1; i< lastPoint; i++)
344
        {
345
            orientablePrimitive.addVertex(points[i]);
346
        }        
347
    }
348

    
349
    private Surface parsePolygon(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
350
                int count = data.getInt();
351
                int subType = getSubType(haveZ, haveM);        
352
                
353
                Surface surface = (Surface) geomManager.create(TYPES.SURFACE, subType);
354
                              
355
                for (int i = 0; i < count; i++) {
356
                    fillLinearRing(data, surface, haveZ, haveM); 
357
                }
358
                           
359
                surface.closePrimitive();                
360
               
361
                        return surface;
362
         }
363
    
364
    
365
    private MultiCurve parseMultiLineString(ByteBuffer data) throws CreateGeometryException {
366
                MultiCurve multiCurve = (MultiCurve) geomManager.create(TYPES.MULTICURVE, getSubType(gHaveZ, gHaveM));
367
        Curve curve = (Curve) geomManager.create(TYPES.CURVE, getSubType(gHaveZ, gHaveM));
368
        fillOrientablePrimitive(data, curve);
369
                multiCurve.addCurve(curve);
370
        return multiCurve;
371
}        
372
    /**
373
     * @param data
374
     * @return
375
     * @throws CreateGeometryException
376
     */
377
    private void fillOrientablePrimitive(ByteBuffer data, OrientablePrimitive orientablePrimitive)
378
        throws CreateGeometryException {
379
                int count = data.getInt();
380
         
381
        for (int i=0; i < count; i++)
382
        {
383
            parseTypeAndSRID(data);
384
            Point[] points = parsePointArray(data, gHaveZ, gHaveM);
385
 
386
            orientablePrimitive.addMoveToVertex(points[0]);
387
            for (int j = 1; j < points.length; j++) {
388
                orientablePrimitive.addVertex(points[j]);
389
            }           
390
        }                
391
        }
392
    
393
    private MultiSurface parseMultiPolygon(ByteBuffer data)
394
            throws CreateGeometryException {
395
            int count = data.getInt();
396
            
397
            int subType = getSubType(gHaveZ, gHaveM);
398
            MultiSurface multiSurface = (MultiSurface)geomManager.create(TYPES.MULTISURFACE, subType);
399
            
400
            Point point;
401
            for (int i = 0; i < count; i++) {
402
                parseTypeAndSRID(data);
403
                int countRings = data.getInt();
404
                Surface surface = (Surface)geomManager.create(TYPES.SURFACE, subType);
405
                for (int j = 0; j < countRings; j++) {
406
                    double[][] points =
407
                        parsePointsAsDoubleArray(data, gHaveZ, gHaveM);
408
                    
409
                    //Add the initial point
410
                    surface.addVertex(geomManager.createPoint(points[0][0], points[0][1], subType));
411
                    
412
                    //Add the other points
413
                    int lastPoint = points.length - 1;
414
                    for (int k=1 ; k<lastPoint ; k++){                    
415
                        point = geomManager.createPoint(points[k][0], points[k][1], subType);
416
                        for (int l=2 ; l<points[k].length ; i++){
417
                            point.setCoordinateAt(l, points[k][l]);
418
                        }
419
                        surface.addVertex(point);
420
                    }   
421
                    surface.closePrimitive();
422
                }
423
                multiSurface.addSurface(surface);
424
            }        
425
            return multiSurface;
426
    }
427
    
428
    private MultiPrimitive parseCollection(ByteBuffer data) throws CreateGeometryException {
429
        int count = data.getInt();
430
        Geometry[] geoms = new Geometry[count];
431
        parseGeometryArray(data, geoms);
432
        MultiPrimitive multiPrimitive = (MultiPrimitive) geomManager.create(TYPES.AGGREGATE, SUBTYPES.GEOM2D);
433
        for (int i=0 ; i<geoms.length ; i++){
434
                multiPrimitive.addPrimitive((Primitive) geoms[i]);
435
        }
436
        return multiPrimitive;
437
    }        
438
}