1 /*
2 * WKB4J - WKB reader for geographical mapping toolkits
3 * (C) 2002,2003, David Garnier, dgarnier@users.sourceforge.net
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation,
8 * version 2.1 of the License.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * or visit the web to http://www.gnu.org.
19 *
20 */
21
22 package org.wkb4j.engine;
23
24 import java.io.DataInputStream;
25 import java.io.IOException;
26
27 import org.apache.log4j.Logger;
28
29 /***
30 * Generate events from the submitted array of bytes holding binary data in the Well-Known Binary format.
31 * It generates a beginGeometry event when it sees the beginning of a given Geometry,
32 * an endGeometry event when it sees the end of a given Geometry, and an abortGeometry
33 * event if something happens that makes the completion of the current Geometry impossible.
34 * <br/>Points are treated differently from other Geometries: all the points in a Geometry are collected into a single
35 * array of byte and passed to the WKBFactory all at once.
36 *
37 * Events are nested, meaning that for a classical MultiLineString element with two LineStrings
38 * made of respectively 3 and 2 Points each, the following events will occur:
39 *
40 * <code>
41 * <ul>
42 * <li> beginWork()</li>
43 * <li> ...</li>
44 * <li> beginUnit()
45 * <ul>
46 * <li>beginMultiLineString(2)
47 * <ul>
48 * <li>beginLineString(3)</li>
49 * <li>addPoints(points)</li>
50 * <li>endLineString()</li>
51 * <li>beginLineString(2)</li>
52 * <li>addPoints(points)</li>
53 * <li>endLineString()</li>
54 * </ul>
55 * </li>
56 * <li>endMultiLineString()</li>
57 * </ul>
58 * <li> endUnit()</li>
59 * <li> ...</li>
60 * <li> endWork()</li>
61 * </ul>
62 * </code>
63 * <br/>
64 *
65 * The basic idea is to provide the author of each factory with as much freedom as possible while
66 * allowing for strong performance.
67 *
68 * wkbByteOrder aren't taken into account because will rely on the database to provide the data in the Big-Endian
69 * format (MSB, Most Significant Byte), the Endian used in Java.
70 *
71 * <br/>Inspired by the SAX API.
72 *
73 * <br/>Creation date: 6 juil. 2002 23:49:28
74 * @author David Garnier
75 * @version $Revision: 1.12 $ $Date: 2003/07/28 22:21:26 $
76 */
77
78 public class WKBParser implements WKBGeometryTypes {
79 protected static Logger log = Logger.getLogger(WKBParser.class);
80
81 protected WKBFactory factory = null;
82
83 public static final int DIMENSION2 = 2;
84 public static final int DIMENSION3 = 3;
85
86 /* This ResetableByteArrayInputStream is resetted each time getCoordinates is called,
87 so the initial value is not important.*/
88 protected ResetableByteArrayInputStream internalBuffer =
89 new ResetableByteArrayInputStream(new byte[1]);
90 protected DataInputStream inputStream = new DataInputStream(internalBuffer);
91
92 protected String[] words;
93
94 protected final boolean endianess;
95
96 // private static int debugCounter = 0;
97 // private static int faultCounter = 0;
98
99 /***
100 * Constructor for WKBParser.
101 * @param _factory that will receive the events.
102 */
103 public WKBParser(WKBFactory _factory) {
104 factory = _factory;
105 words = new String[0];
106 endianess = false;
107 }
108
109 /***
110 * Constructor for WKBParser.
111 * @param _factory that will receive the events.
112 */
113 public WKBParser(
114 WKBFactory _factory,
115 String[] _words,
116 boolean _endianess) {
117 factory = _factory;
118 words = _words;
119 endianess = _endianess;
120 }
121
122 /***
123 * Load the raw bytes in the WKB format representing one record of the DB and generates the events.
124 *
125 * The <code>sridBuffer<code> should contain an array of bytes holding the SRID of the current geometry.
126 * If sridBuffer is null or longer than 4 bytes, the SRID of the current geometry is set to -1
127 * @param buffer array of bytes holding the data in the WKB format
128 * @param sridBuffer .
129 * @parem _endianess Endianess of the sridBuffer, the main buffer should always be BigEndian. True is BigEndian, False is LittleEndian
130 * */
131 public void parseData(byte[] buffer, byte[] sridBuffer)
132 throws IOException {
133 int srid = parseInt(sridBuffer, endianess);
134 parseData(buffer, srid);
135 }
136
137 protected int parseInt(byte[] intBuffer, boolean endianess)
138 throws IOException {
139 int srid = -2;
140 if (intBuffer != null && intBuffer.length == 4) {
141 if (endianess) {
142 srid =
143 (intBuffer[0])
144 << 24 | (intBuffer[1] & 0xff)
145 << 16 | (intBuffer[2] & 0xff)
146 << 8 | (intBuffer[3] & 0xff);
147 } else {
148 srid =
149 (intBuffer[3])
150 << 24 | (intBuffer[2] & 0xff)
151 << 16 | (intBuffer[1] & 0xff)
152 << 8 | (intBuffer[0] & 0xff);
153 }
154 }
155 return srid;
156 }
157
158 /***
159 * Load the raw bytes in the WKB format and generates the events.
160 * @param buffer array of bytes holding the data in the WKB format
161 * @param srid The SRID of the current geometry.
162 * */
163 public void parseData(byte[] buffer, byte[][] datas) throws IOException {
164
165 // if (words.length != datas.length) {
166 // log.error(
167 // "Words and datas have different size. Emptying everything");
168 // words = new String[0];
169 // datas = new byte[0][0];
170 // }
171 int[] values = new int[words.length];
172 for (int i = 0; i < values.length; i++) {
173 values[i] = parseInt(datas[i], endianess);
174 // log.debug(""+ values[i]);
175 }
176 getInternalBuffer().reset(buffer);
177 getInputStream().reset();
178 decodeData(words, values);
179 }
180
181 /***
182 * Load the raw bytes in the WKB format and generates the events.
183 * @param buffer array of bytes holding the data in the WKB format
184 * @param srid The SRID of the current geometry.
185 * */
186 public void parseData(byte[] buffer, int srid) throws IOException {
187 // if (log.isDebugEnabled()) {
188 // log.debug("" + srid);
189 // }
190 getInternalBuffer().reset(buffer);
191 getInputStream().reset();
192 decodeData(new String[] { "srid" }, new int[] { srid });
193 }
194
195 /***
196 * Returns the ResetableByteArrayInputStream
197 * @return ResetableByteArrayInputStream
198 */
199 private ResetableByteArrayInputStream getInternalBuffer() {
200 return internalBuffer;
201 }
202
203 /***
204 * Sets the ResetableByteArrayInputStream
205 * @param internalBuffer The ResetableByteArrayInputStream to set
206 */
207 private void setInternalBuffer(ResetableByteArrayInputStream internalBuffer) {
208 this.internalBuffer = internalBuffer;
209 }
210
211 /***
212 * Returns the DataInputStream used to transform bytes into native types (<code>int</code> and <code>double</code> in our case).
213 * @return DataInputStream
214 */
215 private DataInputStream getInputStream() {
216 return inputStream;
217 }
218
219 /***
220 * Sets the DataInputStream used to transform bytes into native types (<code>int</code> and <code>double</code> in our case).
221 .
222 * @param inputStream The inputStream to set
223 */
224 // private void setInputStream(DataInputStream inputStream) {
225 // this.inputStream = inputStream;
226 // }
227
228 /*** Read the raw WKB data from the DataInputStream and generate the beginUnit/endUnit calls and
229 * the calls to the top-level geometries (<code>MultiLineString, LineString, Polygon, MultiPolygon,
230 * GeometryCollection, Point<code>)<br/> All the readGeometry method can throw IOException if something goes wrong.
231 * @param srid defines the SRID applied to all the geometries contained in the current record.
232 *
233 * */
234 protected void decodeData(String[] words, int[] values) {
235 /* Right now if something goes wrong we just abort the curren unit and continue with the next record.*/
236 try {
237
238 /* Against our best code, the result is encoded as little, so it must be reversed.*/
239 if (inputStream.readByte() == 1) {
240
241 }
242 //read the wkbType()
243 int geotype = inputStream.readInt();
244 if (geotype > 1000) {
245 log.debug("Geotype:" + geotype);
246 }
247 factory.beginUnit(words, values);
248 parseType(geotype, DIMENSION2);
249 factory.endUnit();
250 } catch (IOException ioe) {
251 log.error("Problem is the current record:\n", ioe);
252 factory.abortUnit();
253 }
254 }
255
256 /***
257 * Read the given and generates the corresponding events. Do not call beginUnit(); nor endUnit():
258 * @param geotype type of the next Geometry.
259 * @param dimension dimension of the points contained in the next geometry.
260 * @throws IOException
261 */
262 protected boolean parseType(final int geotype, final int dimension)
263 throws IOException {
264 // if (debugCounter % 10000 == 0) {
265 // log.info("Debug counter: " + debugCounter);
266 // //readAll();
267 // // return true;
268 // }
269 //
270 // debugCounter++;
271
272 switch (geotype) {
273 case (wkbMultiLineString) :
274 readMultiLineString();
275 break;
276 case (wkbLineString) :
277 readLineString(dimension);
278 break;
279 case (wkbPolygon) :
280 readPolygon(dimension);
281 break;
282 case (wkbMultiPolygon) :
283 readMultiPolygon(dimension);
284 break;
285 case (wkbPoint) :
286 readPoint(dimension);
287 break;
288 case (wkbMultiPoint) :
289 readMultiPoint();
290 break;
291 case (wkbGeometryCollection) :
292 readGeometryCollection();
293 break;
294 default :
295 log.error(
296 "The current buffer of bytes doesn't start with a valid GeometryType but with: "
297 + geotype
298 + "\n Moving to the next record as a result.");
299 }
300 return true;
301 }
302
303 /***
304 * Reads a MultiLineString.
305 * @throws IOException
306 */
307 protected void readMultiLineString() throws IOException {
308 int size = inputStream.readInt();
309 factory.beginMultiLineString(size);
310 for (int i = 0; i < size; i++) {
311 //skip the byteOrder byte
312 inputStream.skipBytes(1);
313 int tmp = inputStream.readInt();
314 if (tmp != wkbLineString) {
315 factory.abortMultiLineString();
316 return;
317 }
318 readLineString(DIMENSION2);
319 }
320 factory.endMultiLineString();
321 }
322
323 /***
324 * Reads a LineString.
325 * @param dimension dimension of the points contained in this geometry.
326 * @throws IOException
327 */
328 protected void readLineString(final int dimension) throws IOException {
329 int size = inputStream.readInt();
330 factory.beginLineString(size);
331 readPoints(size, dimension);
332 factory.endLineString();
333 }
334
335 /***
336 * Reads a number of Points.
337 * @param pointCount
338 * @param dimension unused parameter
339 * @throws IOException
340 */
341 protected void readPoints(int pointCount, int dimension)
342 throws IOException {
343 pointCount <<= 1;
344 double[] sendVal = new double[pointCount];
345 for (int i = 0; i < pointCount; i++) {
346 sendVal[i] = (float) inputStream.readDouble();
347 }
348 factory.addPoints(sendVal);
349 }
350
351 /***
352 * Reads a MultiPolygon.
353 * @param dimension dimension of the points contained in this geometry.
354 * @throws IOException
355 */
356 protected void readMultiPolygon(int dimension) throws IOException {
357 int size = inputStream.readInt();
358 factory.beginMultiPolygon(size);
359 for (int i = 0; i < size; i++) {
360 //skip the byteOrder byte
361 inputStream.skipBytes(1);
362 if (inputStream.readInt() != wkbPolygon) {
363 factory.abortMultiPolygon();
364 return;
365 }
366 readPolygon(DIMENSION2);
367 }
368 factory.endMultiPolygon();
369 }
370
371 /***
372 * Reads a Polygon.
373 * @param dimension dimension of the points contained in this geometry.
374 * @throws IOException
375 */
376 protected void readPolygon(int dimension) throws IOException {
377 int size = inputStream.readInt();
378 factory.beginPolygon(size);
379 for (int i = 0; i < size; i++) {
380 readLinearRing(dimension);
381 }
382 factory.endPolygon();
383 }
384
385 /***
386 * Method readLinearRing.
387 * @param dimension dimension of the points contained in this geometry.
388 * @throws IOException
389 */
390 protected void readLinearRing(int dimension) throws IOException {
391 int size = inputStream.readInt();
392 factory.beginLinearRing(size);
393 readPoints(size, dimension);
394 factory.endLinearRing();
395 }
396
397 /***
398 * Reads a MultiPoint.
399 * @throws IOException
400 */
401 /* An optimization of this method is possible.
402 * Right now it just call readPoint repetively, which is correct because a MultiPoint
403 * is made of Points. However, it is possible to reduce overhead by reading the coordinates directly.
404 * All factories would have to be modified.*/
405 protected void readMultiPoint() throws IOException {
406 int size = inputStream.readInt();
407 factory.beginMultiPoint(size);
408 for (int i = 0; i < size; i++) {
409 //skip the byteOrder byte
410 inputStream.skipBytes(1);
411 if (inputStream.readInt() != wkbPoint) {
412 factory.abortMultiPoint();
413 return;
414 }
415 readPoint(DIMENSION2);
416 }
417 factory.endMultiPoint();
418 }
419
420 /***
421 * Reads a GeometryCollection.
422 * @param dimension dimension of the points contained in this geometry.
423 * @throws IOException
424 */
425 protected void readGeometryCollection() throws IOException {
426 int size = inputStream.readInt();
427 factory.beginGeometryCollection(size);
428 for (int i = 0; i < size; i++) {
429 //skip the byteOrder byte
430 inputStream.skipBytes(1);
431 if (!parseType(inputStream.readInt(), DIMENSION2)) {
432 factory.abortGeometryCollection();
433 return;
434 }
435 factory.newGeometryCollectionComponent();
436 }
437 factory.endGeometryCollection();
438 }
439
440 /***
441 * Reads a Point.
442 * @param dimension dimension of the points contained in this geometry.
443 * @throws IOException
444 */
445 protected void readPoint(int dimension) throws IOException {
446 factory.beginPoint();
447 readPoints(1, dimension);
448 factory.endPoint();
449 }
450
451 // /***
452 // * Reads a GeometryCollection.
453 // * @param dimension dimension of the points contained in this geometry.
454 // * @throws IOException
455 // */
456 // protected void readAll() throws IOException {
457 // for (int i = 0; i < 6; i++) {
458 // try {
459 //
460 // System.out.print(inputStream.readInt() + ":");
461 // } catch (Exception e) {
462 // // System.exit(0);
463 // }
464 // }
465 // System.out.print("\n");
466 // }
467 }
This page was automatically generated by Maven