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

History | View | Annotate | Download (14 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.exception.CreateGeometryException;
42
import org.gvsig.fmap.geom.primitive.OrientablePrimitive;
43
import org.gvsig.fmap.geom.primitive.Point;
44
import org.gvsig.fmap.geom.primitive.Primitive;
45
import org.gvsig.fmap.geom.primitive.Ring;
46
import org.gvsig.fmap.geom.type.GeometryType;
47

    
48
import org.slf4j.Logger;
49
import org.slf4j.LoggerFactory;
50

    
51
import com.vividsolutions.jts.io.WKBConstants;
52

    
53
import org.gvsig.fmap.geom.primitive.Line;
54
import org.gvsig.fmap.geom.primitive.Polygon;
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(WKBParser3.class);
76
    private final GeometryManager geomManager = GeometryLocator.getGeometryManager();
77
    private final GeometryType[] pointGeometryTypes;
78

    
79
    public WKBParser3() {
80
        pointGeometryTypes
81
                = new GeometryType[]{
82
                    loadPointGeometryType(Geometry.SUBTYPES.GEOM2D, "2D"),
83
                    loadPointGeometryType(Geometry.SUBTYPES.GEOM3D, "3D"),
84
                    loadPointGeometryType(Geometry.SUBTYPES.GEOM2DM, "2DM"),
85
                    loadPointGeometryType(Geometry.SUBTYPES.GEOM3DM, "3DM")};
86
    }
87

    
88
    @SuppressWarnings("UseSpecificCatch")
89
    private GeometryType loadPointGeometryType(int subtype, String subTypeName) {
90
        try {
91
            return geomManager.getGeometryType(Geometry.TYPES.POINT, subtype);
92
        } catch (Exception e) {
93
            LOG.info("Unable to get a reference to the geometry "
94
                    + "type Point{}, to be cached", subTypeName);
95
            return null;
96
        }
97
    }
98

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

    
115
    /**
116
     * Parse a geometry starting at offset.
117
     *
118
     * @param data
119
     * @return
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
        }
200
        if (haveM) {
201
           point.setCoordinateAt(point.getDimension()-1, data.getDouble());
202
        }
203
        return point;
204
    }
205

    
206
    /**
207
     * @param haveZ
208
     * @param haveM
209
     * @return
210
     */
211
    private int getSubType(boolean haveZ, boolean haveM) {
212
        int subtype;
213

    
214
        if( haveZ ) {
215
            if( haveM ) {
216
                subtype = Geometry.SUBTYPES.GEOM3DM;
217
            } else {
218
                subtype = Geometry.SUBTYPES.GEOM3D;
219
            }
220
        } else {
221
            if( haveM ) {
222
                subtype = Geometry.SUBTYPES.GEOM2DM;
223
            } else {
224
                subtype = Geometry.SUBTYPES.GEOM2D;
225
            }
226
        }
227
       return subtype;
228
    }
229

    
230
    private MultiPrimitive parseMultiLineString(ByteBuffer data) throws CreateGeometryException {
231
        int subType = getSubType(gHaveZ, gHaveM);
232
        MultiPrimitive multiline = geomManager.createMultiLine(subType);
233

    
234
        int count = data.getInt();
235

    
236
        for (int i = 0; i < count; i++) {
237
            parseTypeAndSRID(data);
238
            Point[] points = parsePointArray(data, gHaveZ, gHaveM);
239
            Line line = geomManager.createLine(getSubType(gHaveZ, gHaveM));
240
            line.ensureCapacity(points.length);
241
            for (Point point : points) {
242
                line.addVertex(point);
243
            }
244
            multiline.addPrimitive(line);
245
        }
246
        return multiline;
247
    }
248

    
249
    private MultiPrimitive parseMultiPolygon(ByteBuffer data)
250
            throws CreateGeometryException {
251
        int count = data.getInt();
252

    
253
        int subType = getSubType(gHaveZ, gHaveM);
254
        MultiPrimitive multipolygon = geomManager.createMultiPolygon(subType);
255

    
256
        for (int i = 0; i < count; i++) {
257
            Polygon polygon = geomManager.createPolygon(subType);
258
            parseTypeAndSRID(data);
259
            int countRings = data.getInt();
260
            for (int nring = 0; nring < countRings; nring++) {
261
                OrientablePrimitive ring;
262
                if( nring==0 ) {
263
                    ring = polygon;
264
                } else {
265
                    ring = (Ring) geomManager.create(Geometry.TYPES.RING,subType);
266
                }
267
                Point[] points = parsePointsAsPointsArray(data, gHaveZ, gHaveM);
268

    
269
                    ring.ensureCapacity(points.length);
270
                int lastPoint = points.length - 1;
271
                for (int k = 0; k <= lastPoint; k++) {
272
                    ring.addVertex(points[k]);
273
                }
274
                if( nring!=0 ) {
275
                    polygon.addInteriorRing((Ring)ring);
276
                }
277
            }
278
            multipolygon.addPrimitive(polygon);
279
        }
280
        return multipolygon;
281
    }
282

    
283
    private Point[] parsePointsAsPointsArray(ByteBuffer data, boolean haveZ,
284
        boolean haveM) throws CreateGeometryException {
285
    int count = data.getInt();
286
    Point points[] = new Point[count];
287
    int subtype = getSubType(haveZ, haveM);
288

    
289
    for (int i = 0; i < count; i++) {
290
        points[i] = geomManager.createPoint(data.getDouble(), data.getDouble(), subtype);
291
        switch (subtype) {
292
            case Geometry.SUBTYPES.GEOM3D:
293
                points[i].setCoordinateAt(Geometry.DIMENSIONS.Z, data.getDouble()); // z
294
                break;
295
            case Geometry.SUBTYPES.GEOM2DM:
296
                points[i].setCoordinateAt(points[i].getDimension()-1, data.getDouble()); // m
297
                break;
298
            case Geometry.SUBTYPES.GEOM3DM:
299
                points[i].setCoordinateAt(Geometry.DIMENSIONS.Z, data.getDouble()); // z
300
                points[i].setCoordinateAt(points[i].getDimension()-1, data.getDouble()); // m
301
                break;
302
            default:
303
                break;
304
        }
305
    }
306
    return points;
307
}
308

    
309
    private MultiPrimitive parseCollection(ByteBuffer data) throws CreateGeometryException {
310
        int count = data.getInt();
311
        Geometry[] geoms = new Geometry[count];
312
        parseGeometryArray(data, geoms);
313
        MultiPrimitive multiPrimitive = (MultiPrimitive) geomManager.create(TYPES.AGGREGATE, SUBTYPES.GEOM2D);
314
        for (Geometry geom : geoms) {
315
            multiPrimitive.addPrimitive((Primitive) geom);
316
        }
317
        return multiPrimitive;
318
    }
319

    
320
    /**
321
     * Parse an Array of "full" Geometries
322
     *
323
     * @throws CreateGeometryException
324
     */
325
    private void parseGeometryArray(ByteBuffer data, Geometry[] container) throws CreateGeometryException {
326
        for (int i = 0; i < container.length; i++) {
327
            container[i] = parseGeometry(data);
328
        }
329
    }
330

    
331
    private Line parseLineString(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
332
        Point[] points = parsePointArray(data, haveZ, haveM);
333
        Line line = geomManager.createLine(getSubType(haveZ, haveM));
334
        line.ensureCapacity(points.length);
335
        for (Point point : points) {
336
            line.addVertex(point);
337
        }
338
        return line;
339
    }
340

    
341
    /**
342
     * Parse an Array of "slim" Points (without endianness and type, part of
343
     * LinearRing and Linestring, but not MultiPoint!
344
     *
345
     * @param haveZ
346
     * @param haveM
347
     * @throws CreateGeometryException
348
     */
349
    private Point[] parsePointArray(ByteBuffer data, boolean haveZ, boolean haveM)
350
            throws CreateGeometryException {
351
        int count = data.getInt();
352
        Point[] result = new Point[count];
353
        for (int i = 0; i < count; i++) {
354
            result[i] = parsePoint(data, haveZ, haveM);
355
        }
356
        return result;
357
    }
358

    
359
    private Polygon parsePolygon(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
360
        int count = data.getInt();
361
        int subType = getSubType(haveZ, haveM);
362

    
363
        Polygon polygon = geomManager.createPolygon(subType);
364
        fillLinearRing(data, polygon, haveZ, haveM);
365

    
366
        for (int i = 1; i < count; i++) {
367
            Ring ring = (Ring) geomManager.create(Geometry.TYPES.RING, subType);
368
            fillLinearRing(data, ring, haveZ, haveM);
369
            polygon.addInteriorRing(ring);
370
        }
371
        return polygon;
372
    }
373

    
374
    private void fillLinearRing(ByteBuffer data, OrientablePrimitive ring, boolean haveZ, boolean haveM)
375
        throws CreateGeometryException {
376
        Point[] points = parsePointArray(data, haveZ, haveM);
377
        int lastPoint = points.length - 1;
378
        ring.ensureCapacity(points.length);
379
        for (int i = 0; i <= lastPoint; i++) {
380
            ring.addVertex(points[i]);
381
        }
382
    }
383

    
384
    private MultiPoint parseMultiPoint(ByteBuffer data) throws CreateGeometryException {
385
        MultiPoint multipoint = geomManager.createMultiPoint(SUBTYPES.GEOM2D);
386
        int points = data.getInt();
387
        multipoint.ensureCapacity(points);
388
        for (int i = 0; i < points; i++) {
389
            parseTypeAndSRID(data);
390
            multipoint.addPrimitive(parsePoint(data, gHaveZ, gHaveM));
391
        }
392
        return multipoint;
393
    }
394

    
395
}