1 /***************************************************************************
2 qgsmssqlgeometryparser.cpp - SqlGeometry parser for mssql server
3 -------------------
4 begin : 2011-10-08
5 copyright : (C) 2011 by Tamas Szekeres
6 email : szekerest at gmail.com
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "qgsmssqlgeometryparser.h"
19 #include "qgsgeometry.h"
20 #include "qgslogger.h"
21 #include "qgsapplication.h"
22
23 /* SqlGeometry/SqlGeography serialization format
24
25 Simple Point (SerializationProps & IsSinglePoint)
26 [SRID][0x01][SerializationProps][Point][z][m]
27
28 Simple Line Segment (SerializationProps & IsSingleLineSegment)
29 [SRID][0x01][SerializationProps][Point1][Point2][z1][z2][m1][m2]
30
31 Complex Geometries
32 [SRID][VersionAttribute][SerializationProps][NumPoints][Point1]..[PointN][z1]..[zN][m1]..[mN]
33 [NumFigures][Figure]..[Figure][NumShapes][Shape]..[Shape]
34
35 Complex Geometries (FigureAttribute == Curve)
36 [SRID][VersionAttribute][SerializationProps][NumPoints][Point1]..[PointN][z1]..[zN][m1]..[mN]
37 [NumFigures][Figure]..[Figure][NumShapes][Shape]..[Shape][NumSegments][SegmentType]..[SegmentType]
38
39 VersionAttribute (1 byte)
40 0x01 = Katmai (MSSQL2008+)
41 0x02 = Denali (MSSQL2012+)
42
43 SRID
44 Spatial Reference Id (4 bytes)
45
46 SerializationProps (bitmask) 1 byte
47 0x01 = HasZValues
48 0x02 = HasMValues
49 0x04 = IsValid
50 0x08 = IsSinglePoint
51 0x10 = IsSingleLineSegment
52 0x20 = IsLargerThanAHemisphere
53
54 Point (2-4)x8 bytes, size depends on SerializationProps & HasZValues & HasMValues
55 [x][y] - SqlGeometry
56 [latitude][longitude] - SqlGeography
57
58 Figure
59 [FigureAttribute][PointOffset]
60
61 FigureAttribute - Katmai (1 byte)
62 0x00 = Interior Ring
63 0x01 = Stroke
64 0x02 = Exterior Ring
65
66 FigureAttribute - Denali (1 byte)
67 0x00 = None
68 0x01 = Line
69 0x02 = Arc
70 0x03 = Curve
71
72 Shape
73 [ParentFigureOffset][FigureOffset][ShapeType]
74
75 ShapeType (1 byte)
76 0x00 = Unknown
77 0x01 = Point
78 0x02 = LineString
79 0x03 = Polygon
80 0x04 = MultiPoint
81 0x05 = MultiLineString
82 0x06 = MultiPolygon
83 0x07 = GeometryCollection
84 -- Denali
85 0x08 = CircularString
86 0x09 = CompoundCurve
87 0x0A = CurvePolygon
88 0x0B = FullGlobe
89
90 SegmentType (1 byte)
91 0x00 = Line
92 0x01 = Arc
93 0x02 = FirstLine
94 0x03 = FirstArc
95
96 */
97
98 /************************************************************************/
99 /* Geometry parser macros */
100 /************************************************************************/
101
102 #define VA_KATMAI 0x01
103 #define VA_DENALI 0x02
104
105 #define SP_NONE 0
106 #define SP_HASZVALUES 1
107 #define SP_HASMVALUES 2
108 #define SP_ISVALID 4
109 #define SP_ISSINGLEPOINT 8
110 #define SP_ISSINGLELINESEGMENT 0x10
111 #define SP_ISLARGERTHANAHEMISPHERE 0x20
112
113 #define ST_UNKNOWN 0
114 #define ST_POINT 1
115 #define ST_LINESTRING 2
116 #define ST_POLYGON 3
117 #define ST_MULTIPOINT 4
118 #define ST_MULTILINESTRING 5
119 #define ST_MULTIPOLYGON 6
120 #define ST_GEOMETRYCOLLECTION 7
121 #define ST_CIRCULARSTRING 8
122 #define ST_COMPOUNDCURVE 9
123 #define ST_CURVEPOLYGON 10
124 #define ST_FULLGLOBE 11
125
126 #define FA_INTERIORRING 0x00
127 #define FA_STROKE 0x01
128 #define FA_EXTERIORRING 0x02
129
130 #define FA_NONE 0x00
131 #define FA_LINE 0x01
132 #define FA_ARC 0x02
133 #define FA_CURVE 0x03
134
135 #define SMT_LINE 0
136 #define SMT_ARC 1
137 #define SMT_FIRSTLINE 2
138 #define SMT_FIRSTARC 3
139
140 #define ReadInt32(nPos) (*((unsigned int*)(mData + (nPos))))
141
142 #define ReadByte(nPos) (mData[nPos])
143
144 #define ReadDouble(nPos) (*((double*)(mData + (nPos))))
145
146 #define ParentOffset(iShape) (ReadInt32(mShapePos + (iShape) * 9 ))
147 #define FigureOffset(iShape) (ReadInt32(mShapePos + (iShape) * 9 + 4))
148 #define ShapeType(iShape) (ReadByte(mShapePos + (iShape) * 9 + 8))
149 #define SegmentType(iSegment) (ReadByte(mSegmentPos + (iSegment)))
150
151 #define NextFigureOffset(iShape) (iShape + 1 < mNumShapes? FigureOffset((iShape) +1) : mNumFigures)
152
153 #define FigureAttribute(iFigure) (ReadByte(mFigurePos + (iFigure) * 5))
154 #define PointOffset(iFigure) (ReadInt32(mFigurePos + (iFigure) * 5 + 1))
155 #define NextPointOffset(iFigure) (iFigure + 1 < mNumFigures? PointOffset((iFigure) +1) : mNumPoints)
156
157 #define ReadX(iPoint) (ReadDouble(mPointPos + 16 * (iPoint)))
158 #define ReadY(iPoint) (ReadDouble(mPointPos + 16 * (iPoint) + 8))
159 #define ReadZ(iPoint) (ReadDouble(mPointPos + 16 * mNumPoints + 8 * (iPoint)))
160 #define ReadM(iPoint) (ReadDouble(mPointPos + 24 * mNumPoints + 8 * (iPoint)))
161
QgsMssqlGeometryParser()162 QgsMssqlGeometryParser::QgsMssqlGeometryParser()
163 {
164 }
165
DumpMemoryToLog(const char * pszMsg,unsigned char * pszInput,int nLen)166 void QgsMssqlGeometryParser::DumpMemoryToLog( const char *pszMsg, unsigned char *pszInput, int nLen )
167 {
168 #if 0
169 char buf[55];
170 int len = 0;
171 QFile file( "qgsmssql.log" );
172 file.open( QIODevice::Append );
173 file.write( pszMsg, strlen( pszMsg ) );
174 file.write( "\n" );
175 sprintf( buf + len, "%05d ", 0 );
176 len += 6;
177 for ( int i = 0; i < nLen; i++ )
178 {
179 sprintf( buf + len, "%02x ", pszInput[i] );
180 len += 3;
181 if ( len == 54 )
182 {
183 file.write( buf, len );
184 len = 0;
185 file.write( "\n" );
186 sprintf( buf + len, "%05d ", i + 1 );
187 len = 6;
188 }
189 }
190 file.write( "\n" );
191 file.close();
192 #else
193 Q_UNUSED( pszMsg )
194 Q_UNUSED( pszInput )
195 Q_UNUSED( nLen )
196 #endif
197 }
198
readCoordinates(int iPoint)199 QgsPoint QgsMssqlGeometryParser::readCoordinates( int iPoint )
200 {
201 if ( mIsGeography )
202 {
203 if ( ( mProps & SP_HASZVALUES ) && ( mProps & SP_HASMVALUES ) )
204 return QgsPoint( QgsWkbTypes::PointZM, ReadY( iPoint ), ReadX( iPoint ), ReadZ( iPoint ), ReadM( iPoint ) );
205 else if ( mProps & SP_HASZVALUES )
206 return QgsPoint( QgsWkbTypes::PointZ, ReadY( iPoint ), ReadX( iPoint ), ReadZ( iPoint ) );
207 else if ( mProps & SP_HASMVALUES )
208 return QgsPoint( QgsWkbTypes::PointM, ReadY( iPoint ), ReadX( iPoint ), 0.0, ReadZ( iPoint ) );
209 else
210 return QgsPoint( QgsWkbTypes::Point, ReadY( iPoint ), ReadX( iPoint ) );
211 }
212 else
213 {
214 if ( ( mProps & SP_HASZVALUES ) && ( mProps & SP_HASMVALUES ) )
215 return QgsPoint( QgsWkbTypes::PointZM, ReadX( iPoint ), ReadY( iPoint ), ReadZ( iPoint ), ReadM( iPoint ) );
216 else if ( mProps & SP_HASZVALUES )
217 return QgsPoint( QgsWkbTypes::PointZ, ReadX( iPoint ), ReadY( iPoint ), ReadZ( iPoint ) );
218 else if ( mProps & SP_HASMVALUES )
219 return QgsPoint( QgsWkbTypes::PointM, ReadX( iPoint ), ReadY( iPoint ), 0.0, ReadZ( iPoint ) );
220 else
221 return QgsPoint( QgsWkbTypes::Point, ReadX( iPoint ), ReadY( iPoint ) );
222 }
223 }
224
readCoordinates(int iPoint,int iNextPoint,double * x,double * y,double * z,double * m)225 void QgsMssqlGeometryParser::readCoordinates( int iPoint, int iNextPoint, double *x, double *y, double *z, double *m )
226 {
227 int i = 0;
228 if ( mIsGeography )
229 {
230 if ( ( mProps & SP_HASZVALUES ) && ( mProps & SP_HASMVALUES ) )
231 {
232 while ( iPoint < iNextPoint )
233 {
234 x[i] = ReadY( iPoint );
235 y[i] = ReadX( iPoint );
236 z[i] = ReadZ( iPoint );
237 m[i] = ReadM( iPoint );
238 ++iPoint;
239 ++i;
240 }
241 }
242 else if ( mProps & SP_HASZVALUES )
243 {
244 while ( iPoint < iNextPoint )
245 {
246 x[i] = ReadY( iPoint );
247 y[i] = ReadX( iPoint );
248 z[i] = ReadZ( iPoint );
249 ++iPoint;
250 ++i;
251 }
252 }
253 else if ( mProps & SP_HASMVALUES )
254 {
255 while ( iPoint < iNextPoint )
256 {
257 x[i] = ReadY( iPoint );
258 y[i] = ReadX( iPoint );
259 m[i] = ReadZ( iPoint );
260 ++iPoint;
261 ++i;
262 }
263 }
264 else
265 {
266 while ( iPoint < iNextPoint )
267 {
268 x[i] = ReadY( iPoint );
269 y[i] = ReadX( iPoint );
270 ++iPoint;
271 ++i;
272 }
273 }
274 }
275 else
276 {
277 if ( ( mProps & SP_HASZVALUES ) && ( mProps & SP_HASMVALUES ) )
278 {
279 while ( iPoint < iNextPoint )
280 {
281 x[i] = ReadX( iPoint );
282 y[i] = ReadY( iPoint );
283 z[i] = ReadZ( iPoint );
284 m[i] = ReadM( iPoint );
285 ++iPoint;
286 ++i;
287 }
288 }
289 else if ( mProps & SP_HASZVALUES )
290 {
291 while ( iPoint < iNextPoint )
292 {
293 x[i] = ReadX( iPoint );
294 y[i] = ReadY( iPoint );
295 z[i] = ReadZ( iPoint );
296 ++iPoint;
297 ++i;
298 }
299 }
300 else if ( mProps & SP_HASMVALUES )
301 {
302 while ( iPoint < iNextPoint )
303 {
304 x[i] = ReadX( iPoint );
305 y[i] = ReadY( iPoint );
306 m[i] = ReadZ( iPoint );
307 ++iPoint;
308 ++i;
309 }
310 }
311 else
312 {
313 while ( iPoint < iNextPoint )
314 {
315 x[i] = ReadX( iPoint );
316 y[i] = ReadY( iPoint );
317 ++iPoint;
318 ++i;
319 }
320 }
321 }
322 }
323
readPointSequence(int iPoint,int iNextPoint)324 const QgsPointSequence QgsMssqlGeometryParser::readPointSequence( int iPoint, int iNextPoint )
325 {
326 if ( iPoint >= iNextPoint )
327 return QgsPointSequence();
328
329 QgsPointSequence pts;
330
331 while ( iPoint < iNextPoint )
332 {
333 pts << readCoordinates( iPoint );
334 ++iPoint;
335 }
336
337 return pts;
338 }
339
readPoint(int iFigure)340 std::unique_ptr< QgsPoint > QgsMssqlGeometryParser::readPoint( int iFigure )
341 {
342 if ( iFigure < mNumFigures )
343 {
344 int iPoint = PointOffset( iFigure );
345 if ( iPoint < mNumPoints )
346 {
347 return qgis::make_unique< QgsPoint >( readCoordinates( iPoint ) );
348 }
349 }
350 return nullptr;
351 }
352
readMultiPoint(int iShape)353 std::unique_ptr< QgsMultiPoint > QgsMssqlGeometryParser::readMultiPoint( int iShape )
354 {
355 std::unique_ptr< QgsMultiPoint > poMultiPoint = qgis::make_unique< QgsMultiPoint >();
356 poMultiPoint->reserve( mNumShapes );
357 for ( int i = iShape + 1; i < mNumShapes; i++ )
358 {
359 if ( ParentOffset( i ) == ( unsigned int )iShape )
360 {
361 if ( ShapeType( i ) == ST_POINT )
362 poMultiPoint->addGeometry( readPoint( FigureOffset( i ) ).release() );
363 }
364 }
365
366 return poMultiPoint;
367 }
368
readLineString(int iPoint,int iNextPoint)369 std::unique_ptr< QgsLineString > QgsMssqlGeometryParser::readLineString( int iPoint, int iNextPoint )
370 {
371 QVector< double > xOut( iNextPoint - iPoint );
372 QVector< double > yOut( iNextPoint - iPoint );
373 QVector< double > zOut;
374 if ( mProps & SP_HASZVALUES )
375 zOut.resize( iNextPoint - iPoint );
376 QVector< double > mOut;
377 if ( mProps & SP_HASMVALUES )
378 mOut.resize( iNextPoint - iPoint );
379 double *x = xOut.data();
380 double *y = yOut.data();
381 double *z = zOut.data();
382 double *m = mOut.data();
383
384 readCoordinates( iPoint, iNextPoint, x, y, z, m );
385
386 return qgis::make_unique< QgsLineString >( xOut, yOut, zOut, mOut );
387 }
388
readLineString(int iFigure)389 std::unique_ptr< QgsLineString > QgsMssqlGeometryParser::readLineString( int iFigure )
390 {
391 return readLineString( PointOffset( iFigure ), NextPointOffset( iFigure ) );
392 }
393
readCircularString(int iPoint,int iNextPoint)394 std::unique_ptr< QgsCircularString > QgsMssqlGeometryParser::readCircularString( int iPoint, int iNextPoint )
395 {
396 std::unique_ptr< QgsCircularString > poCircularString = qgis::make_unique< QgsCircularString >();
397 poCircularString->setPoints( readPointSequence( iPoint, iNextPoint ) );
398 return poCircularString;
399 }
400
readCircularString(int iFigure)401 std::unique_ptr< QgsCircularString > QgsMssqlGeometryParser::readCircularString( int iFigure )
402 {
403 return readCircularString( PointOffset( iFigure ), NextPointOffset( iFigure ) );
404 }
405
readMultiLineString(int iShape)406 std::unique_ptr< QgsMultiLineString > QgsMssqlGeometryParser::readMultiLineString( int iShape )
407 {
408 std::unique_ptr< QgsMultiLineString > poMultiLineString = qgis::make_unique< QgsMultiLineString >();
409 poMultiLineString->reserve( mNumShapes );
410 for ( int i = iShape + 1; i < mNumShapes; i++ )
411 {
412 if ( ParentOffset( i ) == ( unsigned int )iShape )
413 {
414 if ( ShapeType( i ) == ST_LINESTRING )
415 poMultiLineString->addGeometry( readLineString( FigureOffset( i ) ).release() );
416 }
417 }
418
419 return poMultiLineString;
420 }
421
readPolygon(int iShape)422 std::unique_ptr< QgsPolygon > QgsMssqlGeometryParser::readPolygon( int iShape )
423 {
424 int iFigure;
425 int iRingCount = 0;
426 int iNextFigure = NextFigureOffset( iShape );
427
428 std::unique_ptr< QgsPolygon > poPoly = qgis::make_unique< QgsPolygon >();
429 for ( iFigure = FigureOffset( iShape ); iFigure < iNextFigure; iFigure++ )
430 {
431 if ( iRingCount == 0 )
432 poPoly->setExteriorRing( readLineString( iFigure ).release() );
433 else
434 poPoly->addInteriorRing( readLineString( iFigure ).release() );
435
436 ++iRingCount;
437 }
438 return poPoly;
439 }
440
readMultiPolygon(int iShape)441 std::unique_ptr< QgsMultiPolygon > QgsMssqlGeometryParser::readMultiPolygon( int iShape )
442 {
443 std::unique_ptr< QgsMultiPolygon > poMultiPolygon = qgis::make_unique< QgsMultiPolygon >();
444 poMultiPolygon->reserve( mNumShapes );
445 for ( int i = iShape + 1; i < mNumShapes; i++ )
446 {
447 if ( ParentOffset( i ) == ( unsigned int )iShape )
448 {
449 if ( ShapeType( i ) == ST_POLYGON )
450 poMultiPolygon->addGeometry( readPolygon( i ).release() );
451 }
452 }
453
454 return poMultiPolygon;
455 }
456
readCompoundCurve(int iFigure)457 std::unique_ptr< QgsCompoundCurve > QgsMssqlGeometryParser::readCompoundCurve( int iFigure )
458 {
459 int iPoint, iNextPoint, nPointsPrepared;
460 std::unique_ptr< QgsCompoundCurve > poCompoundCurve = qgis::make_unique< QgsCompoundCurve >();
461 iPoint = PointOffset( iFigure );
462 iNextPoint = NextPointOffset( iFigure ) - 1;
463
464 std::unique_ptr< QgsCurve > poGeom;
465
466 nPointsPrepared = 0;
467 bool isCurve = false;
468 while ( iPoint < iNextPoint && mSegment < mNumSegments )
469 {
470 switch ( SegmentType( mSegment ) )
471 {
472 case SMT_FIRSTLINE:
473 if ( nPointsPrepared > 0 )
474 {
475 if ( isCurve )
476 poCompoundCurve->addCurve( readCircularString( iPoint - nPointsPrepared, iPoint + 1 ).release() );
477 else
478 poCompoundCurve->addCurve( readLineString( iPoint - nPointsPrepared, iPoint + 1 ).release() );
479 }
480 isCurve = false;
481 nPointsPrepared = 1;
482 ++iPoint;
483 break;
484 case SMT_LINE:
485 ++nPointsPrepared;
486 ++iPoint;
487 break;
488 case SMT_FIRSTARC:
489 if ( nPointsPrepared > 0 )
490 {
491 if ( isCurve )
492 poCompoundCurve->addCurve( readCircularString( iPoint - nPointsPrepared, iPoint + 1 ).release() );
493 else
494 poCompoundCurve->addCurve( readLineString( iPoint - nPointsPrepared, iPoint + 1 ).release() );
495 }
496 isCurve = true;
497 nPointsPrepared = 2;
498 iPoint += 2;
499 break;
500 case SMT_ARC:
501 nPointsPrepared += 2;
502 iPoint += 2;
503 break;
504 }
505 ++mSegment;
506 }
507
508 // adding the last curve
509 if ( iPoint == iNextPoint )
510 {
511 if ( isCurve )
512 poCompoundCurve->addCurve( readCircularString( iPoint - nPointsPrepared, iPoint + 1 ).release() );
513 else
514 poCompoundCurve->addCurve( readLineString( iPoint - nPointsPrepared, iPoint + 1 ).release() );
515 }
516
517 return poCompoundCurve;
518 }
519
readCurvePolygon(int iShape)520 std::unique_ptr< QgsCurvePolygon > QgsMssqlGeometryParser::readCurvePolygon( int iShape )
521 {
522 int iFigure;
523 int iRingCount = 0;
524 int iNextFigure = NextFigureOffset( iShape );
525
526 std::unique_ptr< QgsCurvePolygon > poPoly = qgis::make_unique< QgsCurvePolygon >();
527 for ( iFigure = FigureOffset( iShape ); iFigure < iNextFigure; iFigure++ )
528 {
529 switch ( FigureAttribute( iFigure ) )
530 {
531 case FA_LINE:
532 if ( iRingCount == 0 )
533 poPoly->setExteriorRing( readLineString( iFigure ).release() );
534 else
535 poPoly->addInteriorRing( readLineString( iFigure ).release() );
536 break;
537 case FA_ARC:
538 if ( iRingCount == 0 )
539 poPoly->setExteriorRing( readCircularString( iFigure ).release() );
540 else
541 poPoly->addInteriorRing( readCircularString( iFigure ).release() );
542 break;
543 case FA_CURVE:
544 if ( iRingCount == 0 )
545 poPoly->setExteriorRing( readCompoundCurve( iFigure ).release() );
546 else
547 poPoly->addInteriorRing( readCompoundCurve( iFigure ).release() );
548 break;
549 }
550 ++iRingCount;
551 }
552 return poPoly;
553 }
554
readGeometryCollection(int iShape)555 std::unique_ptr< QgsGeometryCollection > QgsMssqlGeometryParser::readGeometryCollection( int iShape )
556 {
557 std::unique_ptr< QgsGeometryCollection> poGeomColl = qgis::make_unique< QgsGeometryCollection >();
558 poGeomColl->reserve( mNumShapes );
559 for ( int i = iShape + 1; i < mNumShapes; i++ )
560 {
561 if ( ParentOffset( i ) == ( unsigned int )iShape )
562 {
563 switch ( ShapeType( i ) )
564 {
565 case ST_POINT:
566 poGeomColl->addGeometry( readPoint( FigureOffset( i ) ).release() );
567 break;
568 case ST_LINESTRING:
569 poGeomColl->addGeometry( readLineString( FigureOffset( i ) ).release() );
570 break;
571 case ST_POLYGON:
572 poGeomColl->addGeometry( readPolygon( i ).release() );
573 break;
574 case ST_MULTIPOINT:
575 poGeomColl->addGeometry( readMultiPoint( i ).release() );
576 break;
577 case ST_MULTILINESTRING:
578 poGeomColl->addGeometry( readMultiLineString( i ).release() );
579 break;
580 case ST_MULTIPOLYGON:
581 poGeomColl->addGeometry( readMultiPolygon( i ).release() );
582 break;
583 case ST_GEOMETRYCOLLECTION:
584 poGeomColl->addGeometry( readGeometryCollection( i ).release() );
585 break;
586 case ST_CIRCULARSTRING:
587 poGeomColl->addGeometry( readCircularString( FigureOffset( i ) ).release() );
588 break;
589 case ST_COMPOUNDCURVE:
590 poGeomColl->addGeometry( readCompoundCurve( FigureOffset( i ) ).release() );
591 break;
592 case ST_CURVEPOLYGON:
593 poGeomColl->addGeometry( readCurvePolygon( i ).release() );
594 break;
595 }
596 }
597 }
598
599 return poGeomColl;
600 }
601
parseSqlGeometry(unsigned char * pszInput,int nLen)602 std::unique_ptr<QgsAbstractGeometry> QgsMssqlGeometryParser::parseSqlGeometry( unsigned char *pszInput, int nLen )
603 {
604 if ( nLen < 10 )
605 {
606 QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
607 DumpMemoryToLog( "Not enough data", pszInput, nLen );
608 return nullptr;
609 }
610
611 mData = pszInput;
612
613 /* store the SRS id for further use */
614 mSRSId = ReadInt32( 0 );
615
616 mVersion = ReadByte( 4 );
617
618 if ( mVersion == 0 || mVersion > 2 )
619 {
620 QgsDebugMsg( QStringLiteral( "ParseSqlGeometry corrupt data" ) );
621 DumpMemoryToLog( "Corrupt data", pszInput, nLen );
622 return nullptr;
623 }
624
625 mProps = ReadByte( 5 );
626
627 if ( mProps & SP_HASZVALUES && mProps & SP_HASMVALUES )
628 mPointSize = 32;
629 else if ( mProps & SP_HASZVALUES || mProps & SP_HASMVALUES )
630 mPointSize = 24;
631 else
632 mPointSize = 16;
633
634 std::unique_ptr< QgsAbstractGeometry> poGeom;
635
636 if ( mProps & SP_ISSINGLEPOINT )
637 {
638 // single point geometry
639 mNumPoints = 1;
640 mPointPos = 6;
641
642 if ( nLen < 6 + mPointSize )
643 {
644 QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
645 DumpMemoryToLog( "Not enough data", pszInput, nLen );
646 return nullptr;
647 }
648
649 poGeom = qgis::make_unique< QgsPoint >( readCoordinates( 0 ) );
650 }
651 else if ( mProps & SP_ISSINGLELINESEGMENT )
652 {
653 // single line segment with 2 points
654 mNumPoints = 2;
655 mPointPos = 6;
656
657 if ( nLen < 6 + 2 * mPointSize )
658 {
659 QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
660 DumpMemoryToLog( "Not enough data", pszInput, nLen );
661 return nullptr;
662 }
663
664 poGeom = qgis::make_unique< QgsLineString >( readCoordinates( 0 ), readCoordinates( 1 ) );
665 }
666 else
667 {
668 // complex geometries
669 mNumPoints = ReadInt32( 6 );
670
671 if ( mNumPoints <= 0 )
672 {
673 return nullptr;
674 }
675
676 // position of the point array
677 mPointPos = 10;
678
679 // position of the figures
680 mFigurePos = mPointPos + mPointSize * mNumPoints + 4;
681
682 if ( nLen < mFigurePos )
683 {
684 QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
685 DumpMemoryToLog( "Not enough data", pszInput, nLen );
686 return nullptr;
687 }
688
689 mNumFigures = ReadInt32( mFigurePos - 4 );
690
691 if ( mNumFigures <= 0 )
692 {
693 return nullptr;
694 }
695
696 // position of the shapes
697 mShapePos = mFigurePos + 5 * mNumFigures + 4;
698
699 if ( nLen < mShapePos )
700 {
701 QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
702 DumpMemoryToLog( "Not enough data", pszInput, nLen );
703 return nullptr;
704 }
705
706 mNumShapes = ReadInt32( mShapePos - 4 );
707
708 if ( nLen < mShapePos + 9 * mNumShapes )
709 {
710 QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
711 DumpMemoryToLog( "Not enough data", pszInput, nLen );
712 return nullptr;
713 }
714
715 if ( mNumShapes <= 0 )
716 {
717 return nullptr;
718 }
719
720 // position of the segments (for complex curve figures)
721 if ( mVersion == 0x02 )
722 {
723 mSegment = 0;
724 mSegmentPos = mShapePos + 9 * mNumShapes + 4;
725 if ( nLen > mSegmentPos )
726 {
727 // segment array is present
728 mNumSegments = ReadInt32( mSegmentPos - 4 );
729 if ( nLen < mSegmentPos + mNumSegments )
730 {
731 QgsDebugMsg( QStringLiteral( "ParseSqlGeometry not enough data" ) );
732 DumpMemoryToLog( "Not enough data", pszInput, nLen );
733 return nullptr;
734 }
735 }
736 }
737
738 // pick up the root shape
739 if ( ParentOffset( 0 ) != 0xFFFFFFFF )
740 {
741 QgsDebugMsg( QStringLiteral( "ParseSqlGeometry corrupt data" ) );
742 DumpMemoryToLog( "Not enough data", pszInput, nLen );
743 return nullptr;
744 }
745
746 // determine the shape type
747 switch ( ShapeType( 0 ) )
748 {
749 case ST_POINT:
750 poGeom = readPoint( FigureOffset( 0 ) );
751 break;
752 case ST_LINESTRING:
753 poGeom = readLineString( FigureOffset( 0 ) );
754 break;
755 case ST_POLYGON:
756 poGeom = readPolygon( 0 );
757 break;
758 case ST_MULTIPOINT:
759 poGeom = readMultiPoint( 0 );
760 break;
761 case ST_MULTILINESTRING:
762 poGeom = readMultiLineString( 0 );
763 break;
764 case ST_MULTIPOLYGON:
765 poGeom = readMultiPolygon( 0 );
766 break;
767 case ST_GEOMETRYCOLLECTION:
768 poGeom = readGeometryCollection( 0 );
769 break;
770 case ST_CIRCULARSTRING:
771 poGeom = readCircularString( FigureOffset( 0 ) );
772 break;
773 case ST_COMPOUNDCURVE:
774 poGeom = readCompoundCurve( FigureOffset( 0 ) );
775 break;
776 case ST_CURVEPOLYGON:
777 poGeom = readCurvePolygon( 0 );
778 break;
779 default:
780 QgsDebugMsg( QStringLiteral( "ParseSqlGeometry unsupported geometry type" ) );
781 DumpMemoryToLog( "Unsupported geometry type", pszInput, nLen );
782 return nullptr;
783 }
784 }
785
786 return poGeom;
787 }
788
789