Statistics
| Revision:

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

History | View | Annotate | Download (15.3 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 modify it under
7
 * the terms of the GNU General Public License as published by the Free Software
8
 * Foundation; either version 3 of the License, or (at your option) any later
9
 * version.
10
 *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
 * details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with
17
 * this program; if not, write to the Free Software Foundation, Inc., 51
18
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
 *
20
 * For any additional information, do not hesitate to contact us at info AT
21
 * gvsig.com, or visit our website www.gvsig.com.
22
 */
23
package org.gvsig.fmap.geom.jts.operation.fromwkb;
24

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

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

    
54
import com.vividsolutions.jts.io.WKBConstants;
55

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

    
73
    private boolean gHaveM, gHaveZ, gHaveS; // M, Z y SRID
74

    
75
    private static final Logger LOG = LoggerFactory.getLogger(WKBParser2.class);
76
    private GeometryManager geomManager = GeometryLocator.getGeometryManager();
77
    private GeometryType[] pointGeometryTypes;
78

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

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

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

    
117
    /**
118
     * Parse a geometry starting at offset.
119
     *
120
     * @throws CreateGeometryException
121
     */
122
    protected Geometry parseGeometry(ByteBuffer data) throws CreateGeometryException {
123
        int realtype = parseTypeAndSRID(data);
124

    
125
        Geometry result1 = null;
126
        switch (realtype) {
127
            case WKBConstants.wkbPoint:
128
                result1 = parsePoint(data, gHaveZ, gHaveM);
129
                break;
130
            case WKBConstants.wkbLineString:
131
                result1 = parseLineString(data, gHaveZ, gHaveM);
132
                break;
133
            case WKBConstants.wkbPolygon:
134
                result1 = parsePolygon(data, gHaveZ, gHaveM);
135
                break;
136
            case WKBConstants.wkbMultiPoint:
137
                result1 = parseMultiPoint(data);
138
                break;
139
            case WKBConstants.wkbMultiLineString:
140
                result1 = parseMultiLineString(data);
141
                return result1;
142
            case WKBConstants.wkbMultiPolygon:
143
                result1 = parseMultiPolygon(data);
144
                break;
145
            case WKBConstants.wkbGeometryCollection:
146
                result1 = parseCollection(data);
147
                break;
148
            default:
149
            //throw new IllegalArgumentException("Unknown Geometry Type!");
150
        }
151

    
152
        return result1;
153
    }
154

    
155
    protected int parseTypeAndSRID(ByteBuffer data) {
156
        byte endian = data.get(); // skip and test endian flag
157
        if (endian == 1) {
158
            data.order(ByteOrder.LITTLE_ENDIAN);
159
        }
160
        int typeword = data.getInt();
161

    
162
        int realtype = typeword & 0x1FFFFFFF; // cut off high flag bits
163

    
164
        gHaveZ = (typeword & 0x80000000) != 0;
165
        gHaveM = (typeword & 0x40000000) != 0;
166
        gHaveS = (typeword & 0x20000000) != 0;
167

    
168
        // not used
169
        int srid = -1;
170

    
171
        if (gHaveS) {
172
            srid = data.getInt();
173
        }
174

    
175
        return realtype;
176

    
177
    }
178

    
179
    private Point parsePoint(ByteBuffer data, boolean haveZ, boolean haveM)
180
            throws CreateGeometryException {
181
        double x = data.getDouble();
182
        double y = data.getDouble();
183
        Point point;
184

    
185
        int subtype = getSubType(haveZ, haveM);
186

    
187
        // If we have a cached GeometryType use it, otherwise call the manager
188
        if (pointGeometryTypes[subtype] == null) {
189
            point = (Point) geomManager.create(Geometry.TYPES.POINT, subtype);
190
        } else {
191
            point = (Point) pointGeometryTypes[subtype].create();
192
        }
193
        point.setX(x);
194
        point.setY(y);
195

    
196
        // Other dimensions
197
        if (haveZ) {
198
            point.setCoordinateAt(Geometry.DIMENSIONS.Z, data.getDouble());
199
            if (haveM) {
200
                /*point.setCoordinateAt(Geometry.DIMENSIONS.Z + 1,
201
                 data.getDouble());*/
202
                data.getDouble();
203
            }
204
        } else {
205
            if (haveM) {
206
                /*point.setCoordinateAt(Geometry.DIMENSIONS.Y + 1,
207
                 data.getDouble());*/
208
                data.getDouble();
209
            }
210
        }
211

    
212
        return point;
213
    }
214

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

    
230
    private Curve parseMultiLineString(ByteBuffer data) throws CreateGeometryException {
231
        Curve curve = (Curve) geomManager.create(TYPES.CURVE, getSubType(gHaveZ, gHaveM));
232
        fillOrientablePrimitive(data, curve);
233
        return curve;
234
    }
235

    
236
    /**
237
     * @param data
238
     * @return
239
     * @throws CreateGeometryException
240
     */
241
    private void fillOrientablePrimitive(ByteBuffer data, OrientablePrimitive orientablePrimitive)
242
            throws CreateGeometryException {
243
        int count = data.getInt();
244

    
245
        for (int i = 0; i < count; i++) {
246
            parseTypeAndSRID(data);
247
            Point[] points = parsePointArray(data, gHaveZ, gHaveM);
248

    
249
            orientablePrimitive.addMoveToVertex(points[0]);
250
            for (int j = 1; j < points.length; j++) {
251
                orientablePrimitive.addVertex(points[j]);
252
            }
253
        }
254
    }
255

    
256
    private MultiSurface parseMultiPolygon(ByteBuffer data)
257
            throws CreateGeometryException {
258
        int count = data.getInt();
259

    
260
        int subType = getSubType(gHaveZ, gHaveM);
261
        MultiSurface multiSurface = (MultiSurface) geomManager.create(TYPES.MULTISURFACE, subType);
262

    
263
        Point point;
264
        for (int i = 0; i < count; i++) {
265
            Surface surface = (Surface) geomManager.create(TYPES.SURFACE, subType);
266
            parseTypeAndSRID(data);
267
            int countRings = data.getInt();
268
            for (int j = 0; j < countRings; j++) {
269
                double[][] points = parsePointsAsDoubleArray(data, gHaveZ, gHaveM);
270

    
271
                //Add the initial point
272
                point = geomManager.createPoint(points[0][0], points[0][1], subType);
273
                if (gHaveZ) {
274
                    point.setCoordinateAt(Geometry.DIMENSIONS.Z, points[0][2]);
275
                }
276
                surface.addMoveToVertex(point);
277

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

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

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

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

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

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

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

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

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

    
395
        Surface surface = (Surface) geomManager.create(TYPES.SURFACE, subType);
396

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

    
401
        surface.closePrimitive();
402

    
403
        return surface;
404
    }
405

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

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

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

    
427
}