1 /******************************************************************************
2 * $Id: ogrmssqlgeometryparser.cpp 24918 2012-09-07 12:02:01Z tamas $
3 *
4 * Project: MSSQL Spatial driver
5 * Purpose: Implements OGRMSSQLGeometryParser class to parse native SqlGeometries.
6 * Author: Tamas Szekeres, szekerest at gmail.com
7 *
8 ******************************************************************************
9 * Copyright (c) 2010, Tamas Szekeres
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 ****************************************************************************/
29
30 #include "cpl_conv.h"
31 #include "ogr_mssqlspatial.h"
32
33 CPL_CVSID("$Id: ogrmssqlgeometryparser.cpp 24918 2012-09-07 12:02:01Z tamas $");
34
35 /* SqlGeometry serialization format
36
37 Simple Point (SerializationProps & IsSinglePoint)
38 [SRID][0x01][SerializationProps][Point][z][m]
39
40 Simple Line Segment (SerializationProps & IsSingleLineSegment)
41 [SRID][0x01][SerializationProps][Point1][Point2][z1][z2][m1][m2]
42
43 Complex Geometries
44 [SRID][0x01][SerializationProps][NumPoints][Point1]..[PointN][z1]..[zN][m1]..[mN]
45 [NumFigures][Figure]..[Figure][NumShapes][Shape]..[Shape]
46
47 SRID
48 Spatial Reference Id (4 bytes)
49
50 SerializationProps (bitmask) 1 byte
51 0x01 = HasZValues
52 0x02 = HasMValues
53 0x04 = IsValid
54 0x08 = IsSinglePoint
55 0x10 = IsSingleLineSegment
56 0x20 = IsWholeGlobe
57
58 Point (2-4)x8 bytes, size depends on SerializationProps & HasZValues & HasMValues
59 [x][y] - SqlGeometry
60 [latitude][longitude] - SqlGeography
61
62 Figure
63 [FigureAttribute][PointOffset]
64
65 FigureAttribute (1 byte)
66 0x00 = Interior Ring
67 0x01 = Stroke
68 0x02 = Exterior Ring
69
70 Shape
71 [ParentFigureOffset][FigureOffset][ShapeType]
72
73 ShapeType (1 byte)
74 0x00 = Unknown
75 0x01 = Point
76 0x02 = LineString
77 0x03 = Polygon
78 0x04 = MultiPoint
79 0x05 = MultiLineString
80 0x06 = MultiPolygon
81 0x07 = GeometryCollection
82
83 */
84
85 /************************************************************************/
86 /* Geometry parser macros */
87 /************************************************************************/
88
89 #define SP_NONE 0
90 #define SP_HASZVALUES 1
91 #define SP_HASMVALUES 2
92 #define SP_ISVALID 4
93 #define SP_ISSINGLEPOINT 8
94 #define SP_ISSINGLELINESEGMENT 0x10
95 #define SP_ISWHOLEGLOBE 0x20
96
97 #define ST_UNKNOWN 0
98 #define ST_POINT 1
99 #define ST_LINESTRING 2
100 #define ST_POLYGON 3
101 #define ST_MULTIPOINT 4
102 #define ST_MULTILINESTRING 5
103 #define ST_MULTIPOLYGON 6
104 #define ST_GEOMETRYCOLLECTION 7
105
106 #define ReadInt32(nPos) (*((unsigned int*)(pszData + (nPos))))
107
108 #define ReadByte(nPos) (pszData[nPos])
109
110 #define ReadDouble(nPos) (*((double*)(pszData + (nPos))))
111
112 #define ParentOffset(iShape) (ReadInt32(nShapePos + (iShape) * 9 ))
113 #define FigureOffset(iShape) (ReadInt32(nShapePos + (iShape) * 9 + 4))
114 #define ShapeType(iShape) (ReadByte(nShapePos + (iShape) * 9 + 8))
115
116 #define NextFigureOffset(iShape) (iShape + 1 < nNumShapes? FigureOffset((iShape) +1) : nNumFigures)
117
118 #define FigureAttribute(iFigure) (ReadByte(nFigurePos + (iFigure) * 5))
119 #define PointOffset(iFigure) (ReadInt32(nFigurePos + (iFigure) * 5 + 1))
120 #define NextPointOffset(iFigure) (iFigure + 1 < nNumFigures? PointOffset((iFigure) +1) : nNumPoints)
121
122 #define ReadX(iPoint) (ReadDouble(nPointPos + 16 * (iPoint)))
123 #define ReadY(iPoint) (ReadDouble(nPointPos + 16 * (iPoint) + 8))
124 #define ReadZ(iPoint) (ReadDouble(nPointPos + 16 * nNumPoints + 8 * (iPoint)))
125 #define ReadM(iPoint) (ReadDouble(nPointPos + 24 * nNumPoints + 8 * (iPoint)))
126
127 /************************************************************************/
128 /* OGRMSSQLGeometryParser() */
129 /************************************************************************/
130
OGRMSSQLGeometryParser(int nGeomColumnType)131 OGRMSSQLGeometryParser::OGRMSSQLGeometryParser(int nGeomColumnType)
132 {
133 nColType = nGeomColumnType;
134 }
135
136 /************************************************************************/
137 /* ReadPoint() */
138 /************************************************************************/
139
ReadPoint(int iShape)140 OGRPoint* OGRMSSQLGeometryParser::ReadPoint(int iShape)
141 {
142 int iFigure = FigureOffset(iShape);
143 if ( iFigure < nNumFigures )
144 {
145 int iPoint = PointOffset(iFigure);
146 if ( iPoint < nNumPoints )
147 {
148 if (nColType == MSSQLCOLTYPE_GEOGRAPHY)
149 {
150 if ( chProps & SP_HASZVALUES )
151 return new OGRPoint( ReadY(iPoint), ReadX(iPoint), ReadZ(iPoint) );
152 else
153 return new OGRPoint( ReadY(iPoint), ReadX(iPoint) );
154 }
155 else
156 {
157 if ( chProps & SP_HASZVALUES )
158 return new OGRPoint( ReadX(iPoint), ReadY(iPoint), ReadZ(iPoint) );
159 else
160 return new OGRPoint( ReadX(iPoint), ReadY(iPoint) );
161 }
162 }
163 }
164 return NULL;
165 }
166
167 /************************************************************************/
168 /* ReadMultiPoint() */
169 /************************************************************************/
170
ReadMultiPoint(int iShape)171 OGRMultiPoint* OGRMSSQLGeometryParser::ReadMultiPoint(int iShape)
172 {
173 int i;
174 OGRMultiPoint* poMultiPoint = new OGRMultiPoint();
175 OGRGeometry* poGeom;
176
177 for (i = iShape + 1; i < nNumShapes; i++)
178 {
179 poGeom = NULL;
180 if (ParentOffset(i) == (unsigned int)iShape)
181 {
182 if ( ShapeType(i) == ST_POINT )
183 poGeom = ReadPoint(i);
184 }
185 if ( poGeom )
186 poMultiPoint->addGeometryDirectly( poGeom );
187 }
188
189 return poMultiPoint;
190 }
191
192 /************************************************************************/
193 /* ReadLineString() */
194 /************************************************************************/
195
ReadLineString(int iShape)196 OGRLineString* OGRMSSQLGeometryParser::ReadLineString(int iShape)
197 {
198 int iFigure, iPoint, iNextPoint, i;
199 iFigure = FigureOffset(iShape);
200
201 OGRLineString* poLineString = new OGRLineString();
202 iPoint = PointOffset(iFigure);
203 iNextPoint = NextPointOffset(iFigure);
204 poLineString->setNumPoints(iNextPoint - iPoint);
205 i = 0;
206 while (iPoint < iNextPoint)
207 {
208 if (nColType == MSSQLCOLTYPE_GEOGRAPHY)
209 {
210 if ( chProps & SP_HASZVALUES )
211 poLineString->setPoint(i, ReadY(iPoint), ReadX(iPoint), ReadZ(iPoint) );
212 else
213 poLineString->setPoint(i, ReadY(iPoint), ReadX(iPoint) );
214 }
215 else
216 {
217 if ( chProps & SP_HASZVALUES )
218 poLineString->setPoint(i, ReadX(iPoint), ReadY(iPoint), ReadZ(iPoint) );
219 else
220 poLineString->setPoint(i, ReadX(iPoint), ReadY(iPoint) );
221 }
222
223 ++iPoint;
224 ++i;
225 }
226
227 return poLineString;
228 }
229
230 /************************************************************************/
231 /* ReadMultiLineString() */
232 /************************************************************************/
233
ReadMultiLineString(int iShape)234 OGRMultiLineString* OGRMSSQLGeometryParser::ReadMultiLineString(int iShape)
235 {
236 int i;
237 OGRMultiLineString* poMultiLineString = new OGRMultiLineString();
238 OGRGeometry* poGeom;
239
240 for (i = iShape + 1; i < nNumShapes; i++)
241 {
242 poGeom = NULL;
243 if (ParentOffset(i) == (unsigned int)iShape)
244 {
245 if ( ShapeType(i) == ST_LINESTRING )
246 poGeom = ReadLineString(i);
247 }
248 if ( poGeom )
249 poMultiLineString->addGeometryDirectly( poGeom );
250 }
251
252 return poMultiLineString;
253 }
254
255 /************************************************************************/
256 /* ReadPolygon() */
257 /************************************************************************/
258
ReadPolygon(int iShape)259 OGRPolygon* OGRMSSQLGeometryParser::ReadPolygon(int iShape)
260 {
261 int iFigure, iPoint, iNextPoint, i;
262 int iNextFigure = NextFigureOffset(iShape);
263
264 OGRPolygon* poPoly = new OGRPolygon();
265 for (iFigure = FigureOffset(iShape); iFigure < iNextFigure; iFigure++)
266 {
267 OGRLinearRing* poRing = new OGRLinearRing();
268 iPoint = PointOffset(iFigure);
269 iNextPoint = NextPointOffset(iFigure);
270 poRing->setNumPoints(iNextPoint - iPoint);
271 i = 0;
272 while (iPoint < iNextPoint)
273 {
274 if (nColType == MSSQLCOLTYPE_GEOGRAPHY)
275 {
276 if ( chProps & SP_HASZVALUES )
277 poRing->setPoint(i, ReadY(iPoint), ReadX(iPoint), ReadZ(iPoint) );
278 else
279 poRing->setPoint(i, ReadY(iPoint), ReadX(iPoint) );
280 }
281 else
282 {
283 if ( chProps & SP_HASZVALUES )
284 poRing->setPoint(i, ReadX(iPoint), ReadY(iPoint), ReadZ(iPoint) );
285 else
286 poRing->setPoint(i, ReadX(iPoint), ReadY(iPoint) );
287 }
288
289 ++iPoint;
290 ++i;
291 }
292 poPoly->addRingDirectly( poRing );
293 }
294 return poPoly;
295 }
296
297 /************************************************************************/
298 /* ReadMultiPolygon() */
299 /************************************************************************/
300
ReadMultiPolygon(int iShape)301 OGRMultiPolygon* OGRMSSQLGeometryParser::ReadMultiPolygon(int iShape)
302 {
303 int i;
304 OGRMultiPolygon* poMultiPolygon = new OGRMultiPolygon();
305 OGRGeometry* poGeom;
306
307 for (i = iShape + 1; i < nNumShapes; i++)
308 {
309 poGeom = NULL;
310 if (ParentOffset(i) == (unsigned int)iShape)
311 {
312 if ( ShapeType(i) == ST_POLYGON )
313 poGeom = ReadPolygon(i);
314 }
315 if ( poGeom )
316 poMultiPolygon->addGeometryDirectly( poGeom );
317 }
318
319 return poMultiPolygon;
320 }
321
322 /************************************************************************/
323 /* ReadGeometryCollection() */
324 /************************************************************************/
325
ReadGeometryCollection(int iShape)326 OGRGeometryCollection* OGRMSSQLGeometryParser::ReadGeometryCollection(int iShape)
327 {
328 int i;
329 OGRGeometryCollection* poGeomColl = new OGRGeometryCollection();
330 OGRGeometry* poGeom;
331
332 for (i = iShape + 1; i < nNumShapes; i++)
333 {
334 poGeom = NULL;
335 if (ParentOffset(i) == (unsigned int)iShape)
336 {
337 switch (ShapeType(i))
338 {
339 case ST_POINT:
340 poGeom = ReadPoint(i);
341 break;
342 case ST_LINESTRING:
343 poGeom = ReadLineString(i);
344 break;
345 case ST_POLYGON:
346 poGeom = ReadPolygon(i);
347 break;
348 case ST_MULTIPOINT:
349 poGeom = ReadMultiPoint(i);
350 break;
351 case ST_MULTILINESTRING:
352 poGeom = ReadMultiLineString(i);
353 break;
354 case ST_MULTIPOLYGON:
355 poGeom = ReadMultiPolygon(i);
356 break;
357 case ST_GEOMETRYCOLLECTION:
358 poGeom = ReadGeometryCollection(i);
359 break;
360 }
361 }
362 if ( poGeom )
363 poGeomColl->addGeometryDirectly( poGeom );
364 }
365
366 return poGeomColl;
367 }
368
369 /************************************************************************/
370 /* ParseSqlGeometry() */
371 /************************************************************************/
372
373
ParseSqlGeometry(unsigned char * pszInput,int nLen,OGRGeometry ** poGeom)374 OGRErr OGRMSSQLGeometryParser::ParseSqlGeometry(unsigned char* pszInput,
375 int nLen, OGRGeometry **poGeom)
376 {
377 if (nLen < 10)
378 return OGRERR_NOT_ENOUGH_DATA;
379
380 pszData = pszInput;
381
382 /* store the SRS id for further use */
383 nSRSId = ReadInt32(0);
384
385 if ( ReadByte(4) != 1 )
386 {
387 return OGRERR_CORRUPT_DATA;
388 }
389
390 chProps = ReadByte(5);
391
392 if ( chProps & SP_HASMVALUES )
393 nPointSize = 32;
394 else if ( chProps & SP_HASZVALUES )
395 nPointSize = 24;
396 else
397 nPointSize = 16;
398
399 if ( chProps & SP_ISSINGLEPOINT )
400 {
401 // single point geometry
402 nNumPoints = 1;
403 nPointPos = 6;
404
405 if (nLen < 6 + nPointSize)
406 {
407 return OGRERR_NOT_ENOUGH_DATA;
408 }
409
410 if (nColType == MSSQLCOLTYPE_GEOGRAPHY)
411 {
412 if (chProps & SP_HASZVALUES)
413 *poGeom = new OGRPoint(ReadY(0), ReadX(0), ReadZ(0));
414 else
415 *poGeom = new OGRPoint(ReadY(0), ReadX(0));
416 }
417 else
418 {
419 if (chProps & SP_HASZVALUES)
420 *poGeom = new OGRPoint(ReadX(0), ReadY(0), ReadZ(0));
421 else
422 *poGeom = new OGRPoint(ReadX(0), ReadY(0));
423 }
424 }
425 else if ( chProps & SP_ISSINGLELINESEGMENT )
426 {
427 // single line segment with 2 points
428 nNumPoints = 2;
429 nPointPos = 6;
430
431 if (nLen < 6 + 2 * nPointSize)
432 {
433 return OGRERR_NOT_ENOUGH_DATA;
434 }
435
436 OGRLineString* line = new OGRLineString();
437 line->setNumPoints(2);
438
439 if (nColType == MSSQLCOLTYPE_GEOGRAPHY)
440 {
441 if ( chProps & SP_HASZVALUES )
442 {
443 line->setPoint(0, ReadY(0), ReadX(0), ReadZ(0));
444 line->setPoint(1, ReadY(1), ReadX(1), ReadZ(1));
445 }
446 else
447 {
448 line->setPoint(0, ReadY(0), ReadX(0));
449 line->setPoint(1, ReadY(1), ReadX(1));
450 }
451 }
452 else
453 {
454 if ( chProps & SP_HASZVALUES )
455 {
456 line->setPoint(0, ReadX(0), ReadY(0), ReadZ(0));
457 line->setPoint(1, ReadX(1), ReadY(1), ReadZ(1));
458 }
459 else
460 {
461 line->setPoint(0, ReadX(0), ReadY(0));
462 line->setPoint(1, ReadX(1), ReadY(1));
463 }
464 }
465
466 *poGeom = line;
467 }
468 else
469 {
470 // complex geometries
471 nNumPoints = ReadInt32(6);
472
473 if ( nNumPoints <= 0 )
474 {
475 return OGRERR_NONE;
476 }
477
478 // position of the point array
479 nPointPos = 10;
480
481 // position of the figures
482 nFigurePos = nPointPos + nPointSize * nNumPoints + 4;
483
484 if (nLen < nFigurePos)
485 {
486 return OGRERR_NOT_ENOUGH_DATA;
487 }
488
489 nNumFigures = ReadInt32(nFigurePos - 4);
490
491 if ( nNumFigures <= 0 )
492 {
493 return OGRERR_NONE;
494 }
495
496 // position of the shapes
497 nShapePos = nFigurePos + 5 * nNumFigures + 4;
498
499 if (nLen < nShapePos)
500 {
501 return OGRERR_NOT_ENOUGH_DATA;
502 }
503
504 nNumShapes = ReadInt32(nShapePos - 4);
505
506 if (nLen < nShapePos + 9 * nNumShapes)
507 {
508 return OGRERR_NOT_ENOUGH_DATA;
509 }
510
511 if ( nNumShapes <= 0 )
512 {
513 return OGRERR_NONE;
514 }
515
516 // pick up the root shape
517 if ( ParentOffset(0) != 0xFFFFFFFF)
518 {
519 return OGRERR_CORRUPT_DATA;
520 }
521
522 // determine the shape type
523 switch (ShapeType(0))
524 {
525 case ST_POINT:
526 *poGeom = ReadPoint(0);
527 break;
528 case ST_LINESTRING:
529 *poGeom = ReadLineString(0);
530 break;
531 case ST_POLYGON:
532 *poGeom = ReadPolygon(0);
533 break;
534 case ST_MULTIPOINT:
535 *poGeom = ReadMultiPoint(0);
536 break;
537 case ST_MULTILINESTRING:
538 *poGeom = ReadMultiLineString(0);
539 break;
540 case ST_MULTIPOLYGON:
541 *poGeom = ReadMultiPolygon(0);
542 break;
543 case ST_GEOMETRYCOLLECTION:
544 *poGeom = ReadGeometryCollection(0);
545 break;
546 default:
547 return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
548 }
549 }
550
551 return OGRERR_NONE;
552 }
553
554