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

History | View | Annotate | Download (16.2 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 15669 2007-10-30 16:19:37Z vcaballero $
51
 */
52
package org.gvsig.fmap.geom.operation.fromwkb;
53

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

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

    
59
import org.slf4j.Logger;
60
import org.slf4j.LoggerFactory;
61

    
62
import org.gvsig.fmap.geom.Geometry;
63
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
64
import org.gvsig.fmap.geom.Geometry.TYPES;
65
import org.gvsig.fmap.geom.GeometryLocator;
66
import org.gvsig.fmap.geom.GeometryManager;
67
import org.gvsig.fmap.geom.aggregate.MultiPoint;
68
import org.gvsig.fmap.geom.aggregate.MultiPrimitive;
69
import org.gvsig.fmap.geom.exception.CreateGeometryException;
70
import org.gvsig.fmap.geom.primitive.Curve;
71
import org.gvsig.fmap.geom.primitive.OrientablePrimitive;
72
import org.gvsig.fmap.geom.primitive.Point;
73
import org.gvsig.fmap.geom.primitive.Primitive;
74
import org.gvsig.fmap.geom.primitive.Surface;
75
import org.gvsig.fmap.geom.primitive.impl.Point2D;
76
import org.gvsig.fmap.geom.type.GeometryType;
77
import org.gvsig.fmap.geom.type.GeometryTypeNotSupportedException;
78
import org.gvsig.fmap.geom.type.GeometryTypeNotValidException;
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
public class WKBParser2 {
96

    
97
    private static final Logger LOG = LoggerFactory.getLogger(WKBParser2.class);
98

    
99
        private GeometryManager geomManager = GeometryLocator.getGeometryManager();
100
    private boolean gHaveM, gHaveZ, gHaveS; // M, Z y SRID
101
    private GeometryType[] pointGeometryTypes;
102

    
103
    /**
104
     * @throws GeometryTypeNotValidException
105
     * @throws GeometryTypeNotSupportedException
106
     * 
107
     */
108
    public WKBParser2() {
109
        pointGeometryTypes =
110
            new GeometryType[] {
111
                loadPointGeometryType(Geometry.SUBTYPES.GEOM2D, "2D"),
112
                loadPointGeometryType(Geometry.SUBTYPES.GEOM3D, "3D"),
113
                loadPointGeometryType(Geometry.SUBTYPES.GEOM2DM, "2DM"),
114
                loadPointGeometryType(Geometry.SUBTYPES.GEOM3DM, "3DM") };
115
    }
116

    
117
    private GeometryType loadPointGeometryType(int subtype, String subTypeName) {
118
        try {
119
            return geomManager.getGeometryType(Geometry.TYPES.POINT, subtype);
120
        } catch (Exception e) {
121
            LOG.warn("Unable to get a reference to the geometry "
122
                + "type Point{}, to be cached", subTypeName);
123
            return null;
124
        }
125
    }
126

    
127
    /**
128
     * Parse a binary encoded geometry.
129
     * 
130
     * Is synchronized to protect offset counter. (Unfortunately, Java does not
131
     * have neither call by reference nor multiple return values.)
132
     * 
133
     * @throws CreateGeometryException
134
     */
135
    public synchronized Geometry parse(byte[] value) throws CreateGeometryException {
136
        // BinaryByteGetter bytes = new ByteGetter.BinaryByteGetter(value);
137
        ByteBuffer buf = ByteBuffer.wrap(value);
138
        return parseGeometry(buf);
139
    }
140

    
141
    protected void parseTypeAndSRID(ByteBuffer data)
142
    {
143
        byte endian = data.get(); //skip and test endian flag
144
        /* if (endian != data.endian) {
145
            throw new IllegalArgumentException("Endian inconsistency!");
146
        } */
147
        int typeword = data.getInt();
148

    
149
        int realtype = typeword & 0x1FFFFFFF; //cut off high flag bits
150

    
151
        gHaveZ = (typeword & 0x80000000) != 0;
152
        gHaveM = (typeword & 0x40000000) != 0;
153
        gHaveS = (typeword & 0x20000000) != 0;
154

    
155
        int srid = -1;
156

    
157
        if (gHaveS) {
158
            srid = data.getInt();
159
        }
160

    
161
    }
162

    
163

    
164
    /** Parse a geometry starting at offset. 
165
     * @throws CreateGeometryException */
166
    protected Geometry parseGeometry(ByteBuffer data) throws CreateGeometryException {
167
        byte endian = data.get(); //skip and test endian flag
168
        if (endian == 1)
169
        {
170
                data.order(ByteOrder.LITTLE_ENDIAN);
171
        }
172
        /* if (endian != data.endian) {
173
            throw new IllegalArgumentException("Endian inconsistency!");
174
        } */
175
        int typeword = data.getInt();
176

    
177
        int realtype = typeword & 0x1FFFFFFF; //cut off high flag bits
178

    
179
        boolean haveZ = (typeword & 0x80000000) != 0;
180
        boolean haveM = (typeword & 0x40000000) != 0;
181
        boolean haveS = (typeword & 0x20000000) != 0;
182

    
183
        int srid = -1;
184

    
185
        if (haveS) {
186
            srid = data.getInt();
187
        }
188
        Geometry result1;
189
        switch (realtype) {
190
        case WKBConstants.wkbPoint :
191
                result1 = parsePoint(data, haveZ, haveM);
192
            break;
193
        case WKBConstants.wkbLineString :
194
            result1 = parseLineString(data, haveZ, haveM);
195
            break;
196
        case WKBConstants.wkbPolygon :
197
            result1 = parsePolygon(data, haveZ, haveM);
198
            break;
199
        case WKBConstants.wkbMultiPoint:
200
            result1 = parseMultiPoint(data);
201
            break;
202
        case WKBConstants.wkbMultiLineString:
203
            result1 = parseMultiLineString(data);
204
            break;
205
        case WKBConstants.wkbMultiPolygon:
206
            result1 = parseMultiPolygon(data);
207
            break;
208
        case WKBConstants.wkbGeometryCollection :
209
            result1 = parseCollection(data);
210
            break;
211
        default :
212
            throw new IllegalArgumentException("Unknown Geometry Type!");
213
        }
214

    
215
        /*Geometry result = result1;
216

217
        if (haveS) {
218
            result.setSrid(srid);
219
        } */
220
        return result1;
221
    }
222

    
223
    private Point parsePoint(ByteBuffer data, boolean haveZ, boolean haveM)
224
        throws CreateGeometryException {
225
        double x = data.getDouble();
226
        double y = data.getDouble();
227
        Point point;
228

    
229
        int subtype = getSubType(haveZ, haveM);
230

    
231
        // If we have a cached GeometryType use it, otherwise call the manager
232
        point =
233
            (Point) (pointGeometryTypes[subtype] == null ? geomManager.create(
234
                Geometry.TYPES.POINT, subtype) : pointGeometryTypes[subtype]
235
                .create());
236
        point.setX(x);
237
        point.setY(y);
238

    
239
        // Other dimensions
240
        if (haveZ) {
241
            point.setCoordinateAt(Geometry.DIMENSIONS.Z, data.getDouble());
242
            if (haveM) {
243
                point.setCoordinateAt(Geometry.DIMENSIONS.Z + 1,
244
                    data.getDouble());
245
            }
246
        } else {
247
            if (haveM) {
248
                point.setCoordinateAt(Geometry.DIMENSIONS.Y + 1,
249
                    data.getDouble());
250
            }
251
        }
252

    
253
        return point;
254
    }
255

    
256
    /**
257
     * @param haveZ
258
     * @param haveM
259
     * @return
260
     */
261
    private int getSubType(boolean haveZ, boolean haveM) {
262
        int subtype =
263
            haveZ ? (haveM ? Geometry.SUBTYPES.GEOM3DM
264
                : Geometry.SUBTYPES.GEOM3D) : (haveM
265
                ? Geometry.SUBTYPES.GEOM2DM : Geometry.SUBTYPES.GEOM2D);
266
        return subtype;
267
    }
268

    
269
    /** Parse an Array of "full" Geometries 
270
     * @throws CreateGeometryException */
271
    private void parseGeometryArray(ByteBuffer data, Geometry[] container) throws CreateGeometryException {
272
        for (int i = 0; i < container.length; i++) {
273
            container[i] = parseGeometry(data);
274
        }
275
    }
276

    
277
    /**
278
     * Parse an Array of "slim" Points (without endianness and type, part of
279
     * LinearRing and Linestring, but not MultiPoint!
280
     * 
281
     * @param haveZ
282
     * @param haveM
283
     * @throws CreateGeometryException
284
     */
285
    private Point[] parsePointArray(ByteBuffer data, boolean haveZ,
286
        boolean haveM) throws CreateGeometryException {
287
        int count = data.getInt();
288
        Point[] result = new Point[count];
289
        for (int i = 0; i < count; i++) {
290
            result[i] = parsePoint(data, haveZ, haveM);
291
        }
292
        return result;
293
    }
294

    
295
    private double[][] parsePointsAsDoubleArray(ByteBuffer data, boolean haveZ,
296
        boolean haveM) throws CreateGeometryException {
297
        int count = data.getInt();
298
        double points[][] = null;
299
        int subtype = getSubType(haveZ, haveM);
300

    
301
        switch (subtype) {
302
        case Geometry.SUBTYPES.GEOM2D:
303
            points = new double[count][2];
304
            break;
305
        case Geometry.SUBTYPES.GEOM3D:
306
        case Geometry.SUBTYPES.GEOM2DM:
307
            points = new double[count][3];
308
            break;
309
        case Geometry.SUBTYPES.GEOM3DM:
310
            points = new double[count][4];
311
            break;
312
        default:
313
            break;
314
        }
315

    
316
        for (int i = 0; i < count; i++) {
317
            points[i][0] = data.getDouble(); // x
318
            points[i][1] = data.getDouble(); // y
319
            switch (subtype) {
320
            case Geometry.SUBTYPES.GEOM3D:
321
            case Geometry.SUBTYPES.GEOM2DM:
322
                points[i][2] = data.getDouble(); // z or m
323
                break;
324
            case Geometry.SUBTYPES.GEOM3DM:
325
                points[i][2] = data.getDouble(); // z
326
                points[i][3] = data.getDouble(); // m
327
                break;
328
            default:
329
                break;
330
            }
331
        }
332
        return points;
333
    }
334

    
335
    private MultiPoint parseMultiPoint(ByteBuffer data) throws CreateGeometryException {
336
            MultiPoint multipoint = (MultiPoint) geomManager.create(TYPES.MULTIPOINT, SUBTYPES.GEOM2D);
337
            Point2D[] points = new Point2D[data.getInt()];
338
        for (int i=0; i < points.length; i++)
339
        {
340
                parseTypeAndSRID(data);
341
                multipoint.addPoint(parsePoint(data, gHaveZ, gHaveM));
342
        }
343
        return multipoint;
344
    }
345

    
346
    private Curve parseLineString(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
347
        Point[] points = parsePointArray(data, haveZ, haveM);
348
        Curve curve = (Curve) geomManager.create(TYPES.CURVE, getSubType(haveZ, haveM));    
349
        curve.addMoveToVertex(points[0]);
350
        for (int i = 1; i< points.length; i++)
351
        {
352
            curve.addVertex(points[i]);
353
        }
354
        return curve;
355
    }
356

    
357
    private void fillLinearRing(ByteBuffer data, OrientablePrimitive orientablePrimitive, boolean haveZ, boolean haveM) throws CreateGeometryException {
358
        Point[] points = parsePointArray(data, haveZ, haveM);
359
       
360
        orientablePrimitive.addMoveToVertex(points[0]);
361
        int lastPoint = points.length - 1;
362
        for (int i = 1; i< lastPoint; i++)
363
        {
364
            orientablePrimitive.addVertex(points[i]);
365
        }        
366
    }
367

    
368
    private Surface parsePolygon(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
369
                int count = data.getInt();
370
                int subType = getSubType(haveZ, haveM);        
371
                
372
                Surface surface = (Surface) geomManager.create(TYPES.SURFACE, subType);
373
                              
374
                for (int i = 0; i < count; i++) {
375
                    fillLinearRing(data, surface, haveZ, haveM); 
376
                }
377
                           
378
                surface.closePrimitive();                
379
               
380
                        return surface;
381
         }
382
    
383
    private Curve parseMultiLineString(ByteBuffer data) throws CreateGeometryException {
384
        Curve curve = (Curve) geomManager.create(TYPES.CURVE, getSubType(gHaveZ, gHaveM));
385
        fillOrientablePrimitive(data, curve);
386
        return curve;
387
    }
388

    
389
    /**
390
     * @param data
391
     * @return
392
     * @throws CreateGeometryException
393
     */
394
    private void fillOrientablePrimitive(ByteBuffer data, OrientablePrimitive orientablePrimitive)
395
        throws CreateGeometryException {
396
                int count = data.getInt();
397
         
398
        for (int i=0; i < count; i++)
399
        {
400
            parseTypeAndSRID(data);
401
            Point[] points = parsePointArray(data, gHaveZ, gHaveM);
402
 
403
            orientablePrimitive.addMoveToVertex(points[0]);
404
            for (int j = 1; j < points.length; j++) {
405
                orientablePrimitive.addVertex(points[j]);
406
            }           
407
        }                
408
        }
409

    
410
    private Surface parseMultiPolygon(ByteBuffer data)
411
        throws CreateGeometryException {
412
        int count = data.getInt();
413
        
414
        int subType = getSubType(gHaveZ, gHaveM);        
415
        Surface surface = (Surface)geomManager.create(TYPES.SURFACE, subType);        
416
        
417
        Point point;
418
        for (int i = 0; i < count; i++) {
419
            parseTypeAndSRID(data);
420
            int countRings = data.getInt();            
421
            for (int j = 0; j < countRings; j++) {
422
                double[][] points =
423
                    parsePointsAsDoubleArray(data, gHaveZ, gHaveM);
424
                
425
                //Add the initial point
426
                surface.addMoveToVertex(geomManager.createPoint(points[0][0], points[0][1], subType));
427
                
428
                //Add the other points
429
                int lastPoint = points.length - 1;
430
                for (int k=1 ; k<lastPoint ; k++){                    
431
                    point = geomManager.createPoint(points[k][0], points[k][1], subType);
432
                    for (int l=2 ; l<points[k].length ; i++){
433
                        point.setCoordinateAt(l, points[k][l]);
434
                    }
435
                    surface.addVertex(point);
436
                }   
437
                surface.closePrimitive();
438
            }
439
        }
440
        return surface;
441
    }
442

    
443
    private MultiPrimitive parseCollection(ByteBuffer data) throws CreateGeometryException {
444
        int count = data.getInt();
445
        Geometry[] geoms = new Geometry[count];
446
        parseGeometryArray(data, geoms);
447
        MultiPrimitive multiPrimitive = (MultiPrimitive) geomManager.create(TYPES.AGGREGATE, SUBTYPES.GEOM2D);
448
        for (int i=0 ; i<geoms.length ; i++){
449
                multiPrimitive.addPrimitive((Primitive) geoms[i]);
450
        }
451
        return multiPrimitive;
452
    }        
453
}