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