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 42330 fdiaz
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6 42377 jjdelcerro
 * 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 42330 fdiaz
 *
11 42377 jjdelcerro
 * 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 42330 fdiaz
 *
16 42377 jjdelcerro
 * 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 42330 fdiaz
 *
20 42377 jjdelcerro
 * For any additional information, do not hesitate to contact us at info AT
21
 * gvsig.com, or visit our website www.gvsig.com.
22 42330 fdiaz
 */
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 42478 fdiaz
import org.gvsig.fmap.geom.primitive.Ring;
46 42330 fdiaz
import org.gvsig.fmap.geom.type.GeometryType;
47 42478 fdiaz
48 42330 fdiaz
import org.slf4j.Logger;
49
import org.slf4j.LoggerFactory;
50
51
import com.vividsolutions.jts.io.WKBConstants;
52 42478 fdiaz
53 42378 jjdelcerro
import org.gvsig.fmap.geom.primitive.Line;
54
import org.gvsig.fmap.geom.primitive.Polygon;
55 42330 fdiaz
56
/**
57
 * Parse binary representation of geometries. Currently, only text rep (hexed)
58
 * implementation is tested.
59 42377 jjdelcerro
 *
60 42330 fdiaz
 * It should be easy to add char[] and CharSequence ByteGetter instances,
61
 * although the latter one is not compatible with older jdks.
62 42377 jjdelcerro
 *
63 42330 fdiaz
 * 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 42377 jjdelcerro
 *
68 42330 fdiaz
 * @author markus.schaber@logi-track.com
69 42771 jbadia
 *
70
 * https://github.com/postgis/postgis-java/blob/master/jdbc/src/main/java/org/postgis/binary/BinaryWriter.java
71 42377 jjdelcerro
 *
72 42330 fdiaz
 */
73 42771 jbadia
public class PostGISEWKBParser {
74 42330 fdiaz
75 42377 jjdelcerro
    private boolean gHaveM, gHaveZ, gHaveS; // M, Z y SRID
76 42330 fdiaz
77 42771 jbadia
    private static final Logger LOG = LoggerFactory.getLogger(PostGISEWKBParser.class);
78 42378 jjdelcerro
    private final GeometryManager geomManager = GeometryLocator.getGeometryManager();
79
    private final GeometryType[] pointGeometryTypes;
80 42330 fdiaz
81 42771 jbadia
    public PostGISEWKBParser() {
82 42377 jjdelcerro
        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 42330 fdiaz
90 42378 jjdelcerro
    @SuppressWarnings("UseSpecificCatch")
91 42377 jjdelcerro
    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 42330 fdiaz
101 42377 jjdelcerro
    /**
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 42378 jjdelcerro
     * @param value
108 42478 fdiaz
     * @return
109 42377 jjdelcerro
     * @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 42330 fdiaz
117 42377 jjdelcerro
    /**
118
     * Parse a geometry starting at offset.
119
     *
120 42378 jjdelcerro
     * @param data
121 42478 fdiaz
     * @return
122 42377 jjdelcerro
     * @throws CreateGeometryException
123
     */
124
    protected Geometry parseGeometry(ByteBuffer data) throws CreateGeometryException {
125
        int realtype = parseTypeAndSRID(data);
126 42330 fdiaz
127 42377 jjdelcerro
        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 42771 jbadia
                result1 = parseMultiPoint(data, gHaveZ, gHaveM);
140 42377 jjdelcerro
                break;
141
            case WKBConstants.wkbMultiLineString:
142 42771 jbadia
                result1 = parseMultiLineString(data, gHaveZ, gHaveM);
143 42377 jjdelcerro
                return result1;
144
            case WKBConstants.wkbMultiPolygon:
145 42771 jbadia
                result1 = parseMultiPolygon(data, gHaveZ, gHaveM);
146 42377 jjdelcerro
                break;
147
            case WKBConstants.wkbGeometryCollection:
148
                result1 = parseCollection(data);
149
                break;
150
            default:
151
            //throw new IllegalArgumentException("Unknown Geometry Type!");
152
        }
153 42330 fdiaz
154 42377 jjdelcerro
        return result1;
155
    }
156 42330 fdiaz
157 42377 jjdelcerro
    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 42729 dmartinezizquierdo
164
        int realtype = typeword & 0x1FFFFFFF; // cut off high flag bits
165 42330 fdiaz
166 42729 dmartinezizquierdo
        gHaveZ = (typeword & 0x80000000) != 0;
167
        gHaveM = (typeword & 0x40000000) != 0;
168
        gHaveS = (typeword & 0x20000000) != 0;
169
170 42377 jjdelcerro
        // not used
171
        int srid = -1;
172 42330 fdiaz
173 42377 jjdelcerro
        if (gHaveS) {
174
            srid = data.getInt();
175
        }
176 43020 jjdelcerro
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 42377 jjdelcerro
        return realtype;
198 42330 fdiaz
199 42377 jjdelcerro
    }
200 42330 fdiaz
201 42377 jjdelcerro
    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 42330 fdiaz
207 42377 jjdelcerro
        int subtype = getSubType(haveZ, haveM);
208 42330 fdiaz
209 42377 jjdelcerro
        // 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 42330 fdiaz
218 42377 jjdelcerro
        // Other dimensions
219
        if (haveZ) {
220
            point.setCoordinateAt(Geometry.DIMENSIONS.Z, data.getDouble());
221 42378 jjdelcerro
        }
222
        if (haveM) {
223 42379 jjdelcerro
           point.setCoordinateAt(point.getDimension()-1, data.getDouble());
224 42377 jjdelcerro
        }
225
        return point;
226
    }
227 42330 fdiaz
228 42377 jjdelcerro
    /**
229
     * @param haveZ
230
     * @param haveM
231
     * @return
232
     */
233
    private int getSubType(boolean haveZ, boolean haveM) {
234 42379 jjdelcerro
        int subtype;
235 42478 fdiaz
236 42379 jjdelcerro
        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 42378 jjdelcerro
       return subtype;
250 42377 jjdelcerro
    }
251 42330 fdiaz
252 42771 jbadia
    private MultiPrimitive parseMultiLineString(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
253
        int subType = getSubType(haveZ, haveM);
254 42378 jjdelcerro
        MultiPrimitive multiline = geomManager.createMultiLine(subType);
255 42330 fdiaz
256 42377 jjdelcerro
        int count = data.getInt();
257 42330 fdiaz
258 42377 jjdelcerro
        for (int i = 0; i < count; i++) {
259
            parseTypeAndSRID(data);
260 42771 jbadia
            Point[] points = parsePointArray(data, haveZ, haveM);
261
            Line line = geomManager.createLine(getSubType(haveZ, haveM));
262 42378 jjdelcerro
            line.ensureCapacity(points.length);
263
            for (Point point : points) {
264
                line.addVertex(point);
265 42377 jjdelcerro
            }
266 42378 jjdelcerro
            multiline.addPrimitive(line);
267 42377 jjdelcerro
        }
268 42378 jjdelcerro
        return multiline;
269 42377 jjdelcerro
    }
270 42330 fdiaz
271 42771 jbadia
    private MultiPrimitive parseMultiPolygon(ByteBuffer data, boolean haveZ, boolean haveM)
272 42377 jjdelcerro
            throws CreateGeometryException {
273
        int count = data.getInt();
274 42330 fdiaz
275 42771 jbadia
        int subType = getSubType(haveZ, haveM);
276 42378 jjdelcerro
        MultiPrimitive multipolygon = geomManager.createMultiPolygon(subType);
277 42330 fdiaz
278 42377 jjdelcerro
        for (int i = 0; i < count; i++) {
279 42771 jbadia
                parseTypeAndSRID(data);
280 42378 jjdelcerro
            Polygon polygon = geomManager.createPolygon(subType);
281 42377 jjdelcerro
            int countRings = data.getInt();
282 42378 jjdelcerro
            for (int nring = 0; nring < countRings; nring++) {
283
                OrientablePrimitive ring;
284
                if( nring==0 ) {
285
                    ring = polygon;
286
                } else {
287 42478 fdiaz
                    ring = (Ring) geomManager.create(Geometry.TYPES.RING,subType);
288 42378 jjdelcerro
                }
289 42771 jbadia
                Point[] points = parsePointsAsPointsArray(data, haveZ, haveM);
290 42330 fdiaz
291 42478 fdiaz
                    ring.ensureCapacity(points.length);
292 42377 jjdelcerro
                int lastPoint = points.length - 1;
293 42478 fdiaz
                for (int k = 0; k <= lastPoint; k++) {
294
                    ring.addVertex(points[k]);
295 42377 jjdelcerro
                }
296 42378 jjdelcerro
                if( nring!=0 ) {
297 42478 fdiaz
                    polygon.addInteriorRing((Ring)ring);
298 42378 jjdelcerro
                }
299 42377 jjdelcerro
            }
300 42378 jjdelcerro
            multipolygon.addPrimitive(polygon);
301 42377 jjdelcerro
        }
302 42378 jjdelcerro
        return multipolygon;
303 42377 jjdelcerro
    }
304 42330 fdiaz
305 42478 fdiaz
    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 42330 fdiaz
311 42478 fdiaz
    for (int i = 0; i < count; i++) {
312
        points[i] = geomManager.createPoint(data.getDouble(), data.getDouble(), subtype);
313 42377 jjdelcerro
        switch (subtype) {
314
            case Geometry.SUBTYPES.GEOM3D:
315 42478 fdiaz
                points[i].setCoordinateAt(Geometry.DIMENSIONS.Z, data.getDouble()); // z
316
                break;
317 42377 jjdelcerro
            case Geometry.SUBTYPES.GEOM2DM:
318 42478 fdiaz
                points[i].setCoordinateAt(points[i].getDimension()-1, data.getDouble()); // m
319 42377 jjdelcerro
                break;
320
            case Geometry.SUBTYPES.GEOM3DM:
321 42478 fdiaz
                points[i].setCoordinateAt(Geometry.DIMENSIONS.Z, data.getDouble()); // z
322
                points[i].setCoordinateAt(points[i].getDimension()-1, data.getDouble()); // m
323 42377 jjdelcerro
                break;
324
            default:
325
                break;
326
        }
327
    }
328 42478 fdiaz
    return points;
329
}
330 42330 fdiaz
331 42377 jjdelcerro
    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 42378 jjdelcerro
        for (Geometry geom : geoms) {
337
            multiPrimitive.addPrimitive((Primitive) geom);
338 42377 jjdelcerro
        }
339
        return multiPrimitive;
340
    }
341 42330 fdiaz
342 42377 jjdelcerro
    /**
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 42330 fdiaz
353 42378 jjdelcerro
    private Line parseLineString(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
354 42377 jjdelcerro
        Point[] points = parsePointArray(data, haveZ, haveM);
355 42378 jjdelcerro
        Line line = geomManager.createLine(getSubType(haveZ, haveM));
356
        line.ensureCapacity(points.length);
357
        for (Point point : points) {
358
            line.addVertex(point);
359 42377 jjdelcerro
        }
360 42378 jjdelcerro
        return line;
361 42377 jjdelcerro
    }
362 42330 fdiaz
363 42377 jjdelcerro
    /**
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 42330 fdiaz
381 42378 jjdelcerro
    private Polygon parsePolygon(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
382 42377 jjdelcerro
        int count = data.getInt();
383
        int subType = getSubType(haveZ, haveM);
384 42330 fdiaz
385 42378 jjdelcerro
        Polygon polygon = geomManager.createPolygon(subType);
386
        fillLinearRing(data, polygon, haveZ, haveM);
387 42330 fdiaz
388 42378 jjdelcerro
        for (int i = 1; i < count; i++) {
389 42478 fdiaz
            Ring ring = (Ring) geomManager.create(Geometry.TYPES.RING, subType);
390 42378 jjdelcerro
            fillLinearRing(data, ring, haveZ, haveM);
391
            polygon.addInteriorRing(ring);
392 42377 jjdelcerro
        }
393 42378 jjdelcerro
        return polygon;
394 42377 jjdelcerro
    }
395 42330 fdiaz
396 42478 fdiaz
    private void fillLinearRing(ByteBuffer data, OrientablePrimitive ring, boolean haveZ, boolean haveM)
397
        throws CreateGeometryException {
398 42377 jjdelcerro
        Point[] points = parsePointArray(data, haveZ, haveM);
399
        int lastPoint = points.length - 1;
400 42478 fdiaz
        ring.ensureCapacity(points.length);
401
        for (int i = 0; i <= lastPoint; i++) {
402
            ring.addVertex(points[i]);
403 42377 jjdelcerro
        }
404
    }
405 42330 fdiaz
406 42771 jbadia
    private MultiPoint parseMultiPoint(ByteBuffer data, boolean haveZ, boolean haveM) throws CreateGeometryException {
407
            int subType = getSubType(haveZ, haveM);
408
        MultiPoint multipoint = geomManager.createMultiPoint(subType);
409 42377 jjdelcerro
        int points = data.getInt();
410
        multipoint.ensureCapacity(points);
411
        for (int i = 0; i < points; i++) {
412
            parseTypeAndSRID(data);
413 42771 jbadia
            multipoint.addPoint(parsePoint(data, haveZ, haveM));
414 42377 jjdelcerro
        }
415
        return multipoint;
416
    }
417 42330 fdiaz
418
}