Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.geometry / org.gvsig.fmap.geometry.operation.jts / src / main / java / org / gvsig / fmap / geom / operation / fromwkb / WKBParser2.java @ 42280

History | View | Annotate | Download (15.6 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.type.GeometryType;
57
import org.gvsig.fmap.geom.type.GeometryTypeNotSupportedException;
58
import org.gvsig.fmap.geom.type.GeometryTypeNotValidException;
59

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

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

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

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

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

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

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

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

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

    
135
        int srid = -1;
136

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

    
141
    }
142

    
143

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

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

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

    
163
        int srid = -1;
164

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

    
195
        /*Geometry result = result1;
196

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

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

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

    
211
        // If we have a cached GeometryType use it, otherwise call the manager
212
        if( pointGeometryTypes[subtype] == null ) {
213
            point = (Point) geomManager.create(Geometry.TYPES.POINT, subtype);
214
        } else {
215
            point = (Point) pointGeometryTypes[subtype].create();
216
        }
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
        int points = data.getInt();
319
        multipoint.ensureCapacity(points);
320
        for (int i=0; i < points; i++) {
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
}