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 / PostGISEWKBParser.java @ 43020

History | View | Annotate | Download (14.9 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
 * https://github.com/postgis/postgis-java/blob/master/jdbc/src/main/java/org/postgis/binary/BinaryWriter.java
71
 *
72
 */
73
public class PostGISEWKBParser {
74

    
75
    private boolean gHaveM, gHaveZ, gHaveS; // M, Z y SRID
76

    
77
    private static final Logger LOG = LoggerFactory.getLogger(PostGISEWKBParser.class);
78
    private final GeometryManager geomManager = GeometryLocator.getGeometryManager();
79
    private final GeometryType[] pointGeometryTypes;
80

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

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

    
101
    /**
102
     * Parse a binary encoded geometry.
103
     *
104
     * Is synchronized to protect offset counter. (Unfortunately, Java does not
105
     * have neither call by reference nor multiple return values.)
106
     *
107
     * @param value
108
     * @return
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
     * @param data
121
     * @return
122
     * @throws CreateGeometryException
123
     */
124
    protected Geometry parseGeometry(ByteBuffer data) throws CreateGeometryException {
125
        int realtype = parseTypeAndSRID(data);
126

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

    
154
        return result1;
155
    }
156

    
157
    protected int parseTypeAndSRID(ByteBuffer data) {
158
        byte endian = data.get(); // skip and test endian flag
159
        if (endian == 1) {
160
            data.order(ByteOrder.LITTLE_ENDIAN);
161
        }
162
        int typeword = data.getInt();
163
        
164
        int realtype = typeword & 0x1FFFFFFF; // cut off high flag bits
165

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

    
170
        // not used
171
        int srid = -1;
172

    
173
        if (gHaveS) {
174
            srid = data.getInt();
175
        }
176
        
177
        // Intento de dar soporte a WKB junto a EWKB
178
        // https://en.wikipedia.org/wiki/Well-known_text#Well-known_binary
179
        if( realtype >= 3000 ) {
180
            gHaveM = true;
181
            gHaveZ = true;
182
            gHaveS = false;
183
            realtype -= 3000;
184
            
185
        } else if( realtype >= 2000 ) {
186
            gHaveM = true;
187
            gHaveZ = false;
188
            gHaveS = false;
189
            realtype -= 2000;
190
            
191
        } else if( realtype >= 1000 ) {
192
            gHaveM = false;
193
            gHaveZ = true;
194
            gHaveS = false;
195
            realtype -= 1000;
196
        }
197
        return realtype;
198

    
199
    }
200

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

    
207
        int subtype = getSubType(haveZ, haveM);
208

    
209
        // If we have a cached GeometryType use it, otherwise call the manager
210
        if (pointGeometryTypes[subtype] == null) {
211
            point = (Point) geomManager.create(Geometry.TYPES.POINT, subtype);
212
        } else {
213
            point = (Point) pointGeometryTypes[subtype].create();
214
        }
215
        point.setX(x);
216
        point.setY(y);
217

    
218
        // Other dimensions
219
        if (haveZ) {
220
            point.setCoordinateAt(Geometry.DIMENSIONS.Z, data.getDouble());
221
        }
222
        if (haveM) {
223
           point.setCoordinateAt(point.getDimension()-1, data.getDouble());
224
        }
225
        return point;
226
    }
227

    
228
    /**
229
     * @param haveZ
230
     * @param haveM
231
     * @return
232
     */
233
    private int getSubType(boolean haveZ, boolean haveM) {
234
        int subtype;
235

    
236
        if( haveZ ) {
237
            if( haveM ) {
238
                subtype = Geometry.SUBTYPES.GEOM3DM;
239
            } else {
240
                subtype = Geometry.SUBTYPES.GEOM3D;
241
            }
242
        } else {
243
            if( haveM ) {
244
                subtype = Geometry.SUBTYPES.GEOM2DM;
245
            } else {
246
                subtype = Geometry.SUBTYPES.GEOM2D;
247
            }
248
        }
249
       return subtype;
250
    }
251

    
252
    private MultiPrimitive parseMultiLineString(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
253
        int subType = getSubType(haveZ, haveM);
254
        MultiPrimitive multiline = geomManager.createMultiLine(subType);
255

    
256
        int count = data.getInt();
257

    
258
        for (int i = 0; i < count; i++) {
259
            parseTypeAndSRID(data);
260
            Point[] points = parsePointArray(data, haveZ, haveM);
261
            Line line = geomManager.createLine(getSubType(haveZ, haveM));
262
            line.ensureCapacity(points.length);
263
            for (Point point : points) {
264
                line.addVertex(point);
265
            }
266
            multiline.addPrimitive(line);
267
        }
268
        return multiline;
269
    }
270

    
271
    private MultiPrimitive parseMultiPolygon(ByteBuffer data, boolean haveZ, boolean haveM)
272
            throws CreateGeometryException {
273
        int count = data.getInt();
274

    
275
        int subType = getSubType(haveZ, haveM);
276
        MultiPrimitive multipolygon = geomManager.createMultiPolygon(subType);
277

    
278
        for (int i = 0; i < count; i++) {
279
                parseTypeAndSRID(data);
280
            Polygon polygon = geomManager.createPolygon(subType);
281
            int countRings = data.getInt();
282
            for (int nring = 0; nring < countRings; nring++) {
283
                OrientablePrimitive ring;
284
                if( nring==0 ) {
285
                    ring = polygon;
286
                } else {
287
                    ring = (Ring) geomManager.create(Geometry.TYPES.RING,subType);
288
                }
289
                Point[] points = parsePointsAsPointsArray(data, haveZ, haveM);
290

    
291
                    ring.ensureCapacity(points.length);
292
                int lastPoint = points.length - 1;
293
                for (int k = 0; k <= lastPoint; k++) {
294
                    ring.addVertex(points[k]);
295
                }
296
                if( nring!=0 ) {
297
                    polygon.addInteriorRing((Ring)ring);
298
                }
299
            }
300
            multipolygon.addPrimitive(polygon);
301
        }
302
        return multipolygon;
303
    }
304

    
305
    private Point[] parsePointsAsPointsArray(ByteBuffer data, boolean haveZ,
306
        boolean haveM) throws CreateGeometryException {
307
    int count = data.getInt();
308
    Point points[] = new Point[count];
309
    int subtype = getSubType(haveZ, haveM);
310

    
311
    for (int i = 0; i < count; i++) {
312
        points[i] = geomManager.createPoint(data.getDouble(), data.getDouble(), subtype);
313
        switch (subtype) {
314
            case Geometry.SUBTYPES.GEOM3D:
315
                points[i].setCoordinateAt(Geometry.DIMENSIONS.Z, data.getDouble()); // z
316
                break;
317
            case Geometry.SUBTYPES.GEOM2DM:
318
                points[i].setCoordinateAt(points[i].getDimension()-1, data.getDouble()); // m
319
                break;
320
            case Geometry.SUBTYPES.GEOM3DM:
321
                points[i].setCoordinateAt(Geometry.DIMENSIONS.Z, data.getDouble()); // z
322
                points[i].setCoordinateAt(points[i].getDimension()-1, data.getDouble()); // m
323
                break;
324
            default:
325
                break;
326
        }
327
    }
328
    return points;
329
}
330

    
331
    private MultiPrimitive parseCollection(ByteBuffer data) throws CreateGeometryException {
332
        int count = data.getInt();
333
        Geometry[] geoms = new Geometry[count];
334
        parseGeometryArray(data, geoms);
335
        MultiPrimitive multiPrimitive = (MultiPrimitive) geomManager.create(TYPES.AGGREGATE, SUBTYPES.GEOM2D);
336
        for (Geometry geom : geoms) {
337
            multiPrimitive.addPrimitive((Primitive) geom);
338
        }
339
        return multiPrimitive;
340
    }
341

    
342
    /**
343
     * Parse an Array of "full" Geometries
344
     *
345
     * @throws CreateGeometryException
346
     */
347
    private void parseGeometryArray(ByteBuffer data, Geometry[] container) throws CreateGeometryException {
348
        for (int i = 0; i < container.length; i++) {
349
            container[i] = parseGeometry(data);
350
        }
351
    }
352

    
353
    private Line parseLineString(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
354
        Point[] points = parsePointArray(data, haveZ, haveM);
355
        Line line = geomManager.createLine(getSubType(haveZ, haveM));
356
        line.ensureCapacity(points.length);
357
        for (Point point : points) {
358
            line.addVertex(point);
359
        }
360
        return line;
361
    }
362

    
363
    /**
364
     * Parse an Array of "slim" Points (without endianness and type, part of
365
     * LinearRing and Linestring, but not MultiPoint!
366
     *
367
     * @param haveZ
368
     * @param haveM
369
     * @throws CreateGeometryException
370
     */
371
    private Point[] parsePointArray(ByteBuffer data, boolean haveZ, boolean haveM)
372
            throws CreateGeometryException {
373
        int count = data.getInt();
374
        Point[] result = new Point[count];
375
        for (int i = 0; i < count; i++) {
376
            result[i] = parsePoint(data, haveZ, haveM);
377
        }
378
        return result;
379
    }
380

    
381
    private Polygon parsePolygon(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
382
        int count = data.getInt();
383
        int subType = getSubType(haveZ, haveM);
384

    
385
        Polygon polygon = geomManager.createPolygon(subType);
386
        fillLinearRing(data, polygon, haveZ, haveM);
387

    
388
        for (int i = 1; i < count; i++) {
389
            Ring ring = (Ring) geomManager.create(Geometry.TYPES.RING, subType);
390
            fillLinearRing(data, ring, haveZ, haveM);
391
            polygon.addInteriorRing(ring);
392
        }
393
        return polygon;
394
    }
395

    
396
    private void fillLinearRing(ByteBuffer data, OrientablePrimitive ring, boolean haveZ, boolean haveM)
397
        throws CreateGeometryException {
398
        Point[] points = parsePointArray(data, haveZ, haveM);
399
        int lastPoint = points.length - 1;
400
        ring.ensureCapacity(points.length);
401
        for (int i = 0; i <= lastPoint; i++) {
402
            ring.addVertex(points[i]);
403
        }
404
    }
405

    
406
    private MultiPoint parseMultiPoint(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
407
            int subType = getSubType(haveZ, haveM);
408
        MultiPoint multipoint = geomManager.createMultiPoint(subType);
409
        int points = data.getInt();
410
        multipoint.ensureCapacity(points);
411
        for (int i = 0; i < points; i++) {
412
            parseTypeAndSRID(data);
413
            multipoint.addPoint(parsePoint(data, haveZ, haveM));
414
        }
415
        return multipoint;
416
    }
417

    
418
}