22 * $Id$
33 *
44 * Project: MapServer
5- * Purpose: MS SQL 2008 (Katmai) Layer Connector
5+ * Purpose: MS SQL Server Layer Connector
66 * Author: Richard Hillman - based on PostGIS and SpatialDB connectors
7+ * Tamas Szekeres - maintenance
78 *
89 ******************************************************************************
910 * Copyright (c) 2007 IS Consulting (www.mapdotnet.com)
5051#include <string.h>
5152#include <ctype.h> /* tolower() */
5253
53- /* SqlGeometry serialization format
54+ /* SqlGeometry/SqlGeography serialization format
5455
5556Simple Point (SerializationProps & IsSinglePoint)
5657 [SRID][0x01][SerializationProps][Point][z][m]
@@ -59,9 +60,17 @@ Simple Line Segment (SerializationProps & IsSingleLineSegment)
5960 [SRID][0x01][SerializationProps][Point1][Point2][z1][z2][m1][m2]
6061
6162Complex Geometries
62- [SRID][0x01 ][SerializationProps][NumPoints][Point1]..[PointN][z1]..[zN][m1]..[mN]
63+ [SRID][VersionAttribute ][SerializationProps][NumPoints][Point1]..[PointN][z1]..[zN][m1]..[mN]
6364 [NumFigures][Figure]..[Figure][NumShapes][Shape]..[Shape]
6465
66+ Complex Geometries (FigureAttribute == Curve)
67+ [SRID][VersionAttribute][SerializationProps][NumPoints][Point1]..[PointN][z1]..[zN][m1]..[mN]
68+ [NumFigures][Figure]..[Figure][NumShapes][Shape]..[Shape][NumSegments][SegmentType]..[SegmentType]
69+
70+ VersionAttribute (1 byte)
71+ 0x01 = Katmai (MSSQL2008+)
72+ 0x02 = Denali (MSSQL2012+)
73+
6574SRID
6675 Spatial Reference Id (4 bytes)
6776
@@ -71,7 +80,7 @@ SerializationProps (bitmask) 1 byte
7180 0x04 = IsValid
7281 0x08 = IsSinglePoint
7382 0x10 = IsSingleLineSegment
74- 0x20 = IsWholeGlobe
83+ 0x20 = IsLargerThanAHemisphere
7584
7685Point (2-4)x8 bytes, size depends on SerializationProps & HasZValues & HasMValues
7786 [x][y] - SqlGeometry
@@ -80,11 +89,17 @@ Point (2-4)x8 bytes, size depends on SerializationProps & HasZValues & HasMValue
8089Figure
8190 [FigureAttribute][PointOffset]
8291
83- FigureAttribute (1 byte)
92+ FigureAttribute - Katmai (1 byte)
8493 0x00 = Interior Ring
8594 0x01 = Stroke
8695 0x02 = Exterior Ring
8796
97+ FigureAttribute - Denali (1 byte)
98+ 0x00 = None
99+ 0x01 = Line
100+ 0x02 = Arc
101+ 0x03 = Curve
102+
88103Shape
89104 [ParentFigureOffset][FigureOffset][ShapeType]
90105
@@ -97,6 +112,17 @@ ShapeType (1 byte)
97112 0x05 = MultiLineString
98113 0x06 = MultiPolygon
99114 0x07 = GeometryCollection
115+ -- Denali
116+ 0x08 = CircularString
117+ 0x09 = CompoundCurve
118+ 0x0A = CurvePolygon
119+ 0x0B = FullGlobe
120+
121+ SegmentType (1 byte)
122+ 0x00 = Line
123+ 0x01 = Arc
124+ 0x02 = FirstLine
125+ 0x03 = FirstArc
100126
101127*/
102128
@@ -125,7 +151,7 @@ ShapeType (1 byte)
125151#define SP_ISVALID 4
126152#define SP_ISSINGLEPOINT 8
127153#define SP_ISSINGLELINESEGMENT 0x10
128- #define SP_ISWHOLEGLOBE 0x20
154+ #define SP_ISLARGERTHANAHEMISPHERE 0x20
129155
130156#define ST_UNKNOWN 0
131157#define ST_POINT 1
@@ -135,6 +161,15 @@ ShapeType (1 byte)
135161#define ST_MULTILINESTRING 5
136162#define ST_MULTIPOLYGON 6
137163#define ST_GEOMETRYCOLLECTION 7
164+ #define ST_CIRCULARSTRING 8
165+ #define ST_COMPOUNDCURVE 9
166+ #define ST_CURVEPOLYGON 10
167+ #define ST_FULLGLOBE 11
168+
169+ #define SMT_LINE 0
170+ #define SMT_ARC 1
171+ #define SMT_FIRSTLINE 2
172+ #define SMT_FIRSTARC 3
138173
139174#define ReadInt32 (nPos ) (*((unsigned int*)(gpi->pszData + (nPos))))
140175
@@ -145,6 +180,7 @@ ShapeType (1 byte)
145180#define ParentOffset (iShape ) (ReadInt32(gpi->nShapePos + (iShape) * 9 ))
146181#define FigureOffset (iShape ) (ReadInt32(gpi->nShapePos + (iShape) * 9 + 4))
147182#define ShapeType (iShape ) (ReadByte(gpi->nShapePos + (iShape) * 9 + 8))
183+ #define SegmentType (iSegment ) (ReadByte(gpi->nSegmentPos + (iSegment)))
148184
149185#define NextFigureOffset (iShape ) (iShape + 1 < gpi->nNumShapes? FigureOffset((iShape) +1) : gpi->nNumFigures)
150186
@@ -157,11 +193,17 @@ ShapeType (1 byte)
157193#define ReadZ (iPoint ) (ReadDouble(gpi->nPointPos + 16 * gpi->nNumPoints + 8 * (iPoint)))
158194#define ReadM (iPoint ) (ReadDouble(gpi->nPointPos + 24 * gpi->nNumPoints + 8 * (iPoint)))
159195
196+ #define FP_EPSILON 1e-12
197+ #define SEGMENT_ANGLE 5.0
198+ #define SEGMENT_MINPOINTS 10
199+
160200/* Native geometry parser struct */
161201typedef struct msGeometryParserInfo_t {
162202 unsigned char * pszData ;
163203 int nLen ;
164- /* serialization propeties */
204+ /* version */
205+ char chVersion ;
206+ /* serialization properties */
165207 char chProps ;
166208 /* point array */
167209 int nPointSize ;
@@ -175,6 +217,9 @@ typedef struct msGeometryParserInfo_t {
175217 int nShapePos ;
176218 int nNumShapes ;
177219 int nSRSId ;
220+ /* segment array */
221+ int nSegmentPos ;
222+ int nNumSegments ;
178223 /* geometry or geography */
179224 int nColType ;
180225 /* bounds */
@@ -258,7 +303,112 @@ void ReadPoint(msGeometryParserInfo* gpi, pointObj* p, int iPoint)
258303 p -> z = 0.0 ;
259304 p -> m = ReadZ (iPoint );
260305 }
306+ else
307+ {
308+ p -> z = 0.0 ;
309+ p -> m = 0.0 ;
310+ }
311+ #endif
312+ }
313+
314+ int StrokeArcToLine (msGeometryParserInfo * gpi , lineObj * line , int index )
315+ {
316+ if (index > 1 ) {
317+ double x , y , x1 , y1 , x2 , y2 , x3 , y3 , dxa , dya , sxa , sya , dxb , dyb ;
318+ double d , sxb , syb , ox , oy , a1 , a3 , sa , da , a , radius ;
319+ int numpoints ;
320+ #ifdef USE_POINT_Z_M
321+ double z ;
322+ z = line -> point [index ].z ; /* must be equal for arc segments */
323+ #endif
324+ /* first point */
325+ x1 = line -> point [index - 2 ].x ;
326+ y1 = line -> point [index - 2 ].y ;
327+ /* second point */
328+ x2 = line -> point [index - 1 ].x ;
329+ y2 = line -> point [index - 1 ].y ;
330+ /* third point */
331+ x3 = line -> point [index ].x ;
332+ y3 = line -> point [index ].y ;
333+
334+ sxa = (x1 + x2 );
335+ sya = (y1 + y2 );
336+ dxa = x2 - x1 ;
337+ dya = y2 - y1 ;
338+
339+ sxb = (x2 + x3 );
340+ syb = (y2 + y3 );
341+ dxb = x3 - x2 ;
342+ dyb = y3 - y2 ;
343+
344+ d = (dxa * dyb - dya * dxb ) * 2 ;
345+
346+ if (fabs (d ) < FP_EPSILON ) {
347+ /* points are colinear, nothing to do here */
348+ return index ;
349+ }
350+
351+ /* calculating the center of circle */
352+ ox = ((sya - syb ) * dya * dyb + sxa * dyb * dxa - sxb * dya * dxb ) / d ;
353+ oy = ((sxb - sxa ) * dxa * dxb + syb * dyb * dxa - sya * dya * dxb ) / d ;
354+
355+ radius = sqrt ((x1 - ox ) * (x1 - ox ) + (y1 - oy ) * (y1 - oy ));
356+
357+ /* calculating the angle to be used */
358+ a1 = atan2 (y1 - oy , x1 - ox );
359+ a3 = atan2 (y3 - oy , x3 - ox );
360+
361+ if (d > 0 ) {
362+ /* draw counterclockwise */
363+ if (a3 > a1 ) /* Wrapping past 180? */
364+ sa = a3 - a1 ;
365+ else
366+ sa = a3 - a1 + 2.0 * M_PI ;
367+ }
368+ else {
369+ if (a3 > a1 ) /* Wrapping past 180? */
370+ sa = a3 - a1 + 2.0 * M_PI ;
371+ else
372+ sa = a3 - a1 ;
373+ }
374+
375+ numpoints = (int )floor (fabs (sa ) * 180 / SEGMENT_ANGLE / M_PI );
376+ if (numpoints < SEGMENT_MINPOINTS )
377+ numpoints = SEGMENT_MINPOINTS ;
378+
379+ da = sa / numpoints ;
380+
381+ /* extend the point array */
382+ line -> numpoints += numpoints - 2 ;
383+ line -> point = msSmallRealloc (line -> point , sizeof (pointObj ) * line -> numpoints );
384+ -- index ;
385+
386+ a = a1 + da ;
387+ while (numpoints > 1 ) {
388+ line -> point [index ].x = x = ox + radius * cos (a );
389+ line -> point [index ].y = y = oy + radius * sin (a );
390+ #ifdef USE_POINT_Z_M
391+ line -> point [index ].z = z ;
392+ #endif
393+
394+ /* calculate bounds */
395+ if (gpi -> minx > x ) gpi -> minx = x ;
396+ else if (gpi -> maxx < x ) gpi -> maxx = x ;
397+ if (gpi -> miny > y ) gpi -> miny = y ;
398+ else if (gpi -> maxy < y ) gpi -> maxy = y ;
399+
400+ a += da ;
401+ ++ index ;
402+ -- numpoints ;
403+ }
404+ /* set last point */
405+ line -> point [index ].x = x3 ;
406+ line -> point [index ].y = y3 ;
407+ #ifdef USE_POINT_Z_M
408+ line -> point [index ].z = z ;
261409#endif
410+ }
411+ return index ;
262412}
263413
264414int ParseSqlGeometry (msMSSQL2008LayerInfo * layerinfo , shapeObj * shape )
@@ -275,7 +425,9 @@ int ParseSqlGeometry(msMSSQL2008LayerInfo* layerinfo, shapeObj *shape)
275425 /* store the SRS id for further use */
276426 gpi -> nSRSId = ReadInt32 (0 );
277427
278- if ( ReadByte (4 ) != 1 ) {
428+ gpi -> chVersion = ReadByte (4 );
429+
430+ if (gpi -> chVersion > 2 ) {
279431 msDebug ("ParseSqlGeometry CORRUPT_DATA\n" );
280432 return CORRUPT_DATA ;
281433 }
@@ -324,7 +476,7 @@ int ParseSqlGeometry(msMSSQL2008LayerInfo* layerinfo, shapeObj *shape)
324476 ReadPoint (gpi , & shape -> line [0 ].point [0 ], 0 );
325477 ReadPoint (gpi , & shape -> line [0 ].point [1 ], 1 );
326478 } else {
327- int iShape , iFigure ;
479+ int iShape , iFigure , iSegment ;
328480 // complex geometries
329481 gpi -> nNumPoints = ReadInt32 (6 );
330482
@@ -380,33 +532,84 @@ int ParseSqlGeometry(msMSSQL2008LayerInfo* layerinfo, shapeObj *shape)
380532 if (shapeType == ST_POINT || shapeType == ST_MULTIPOINT ) {
381533 shape -> type = MS_SHAPE_POINT ;
382534 break ;
383- } else if (shapeType == ST_LINESTRING || shapeType == ST_MULTILINESTRING ) {
535+ } else if (shapeType == ST_LINESTRING || shapeType == ST_MULTILINESTRING ||
536+ shapeType == ST_CIRCULARSTRING || shapeType == ST_COMPOUNDCURVE ) {
384537 shape -> type = MS_SHAPE_LINE ;
385538 break ;
386- } else if (shapeType == ST_POLYGON || shapeType == ST_MULTIPOLYGON ) {
539+ } else if (shapeType == ST_POLYGON || shapeType == ST_MULTIPOLYGON ||
540+ shapeType == ST_CURVEPOLYGON )
541+ {
387542 shape -> type = MS_SHAPE_POLYGON ;
388543 break ;
389544 }
390545 }
391546
392547 shape -> line = (lineObj * ) msSmallMalloc (sizeof (lineObj ) * gpi -> nNumFigures );
393548 shape -> numlines = gpi -> nNumFigures ;
549+ gpi -> nNumSegments = 0 ;
550+
394551 // read figures
395552 for (iFigure = 0 ; iFigure < gpi -> nNumFigures ; iFigure ++ ) {
396553 int iPoint , iNextPoint , i ;
397554 iPoint = PointOffset (iFigure );
398555 iNextPoint = NextPointOffset (iFigure );
399556
400557 shape -> line [iFigure ].point = (pointObj * ) msSmallMalloc (sizeof (pointObj )* (iNextPoint - iPoint ));
401-
558+ shape -> line [ iFigure ]. numpoints = iNextPoint - iPoint ;
402559 i = 0 ;
403- while (iPoint < iNextPoint ) {
404- ReadPoint (gpi , & shape -> line [iFigure ].point [i ], iPoint );
405- ++ iPoint ;
406- ++ i ;
407- }
408560
409- shape -> line [iFigure ].numpoints = i ;
561+ if (gpi -> chVersion == 0x02 && FigureAttribute (iFigure ) >= 0x02 ) {
562+ int nPointPrepared = 0 ;
563+ lineObj * line = & shape -> line [iFigure ];
564+ if (FigureAttribute (iFigure ) == 0x03 ) {
565+ if (gpi -> nNumSegments == 0 ) {
566+ /* position of the segment types */
567+ gpi -> nSegmentPos = gpi -> nShapePos + 9 * gpi -> nNumShapes + 4 ;
568+ gpi -> nNumSegments = ReadInt32 (gpi -> nSegmentPos - 4 );
569+ if (gpi -> nLen < gpi -> nSegmentPos + gpi -> nNumSegments ) {
570+ msDebug ("ParseSqlGeometry NOT_ENOUGH_DATA\n" );
571+ return NOT_ENOUGH_DATA ;
572+ }
573+ iSegment = 0 ;
574+ }
575+
576+ while (iPoint < iNextPoint && iSegment < gpi -> nNumSegments ) {
577+ ReadPoint (gpi , & line -> point [i ], iPoint );
578+ ++ iPoint ;
579+ ++ nPointPrepared ;
580+
581+ if (nPointPrepared == 2 && (SegmentType (iSegment ) == SMT_FIRSTLINE || SegmentType (iSegment ) == SMT_LINE )) {
582+ ++ iSegment ;
583+ nPointPrepared = 1 ;
584+ }
585+ else if (nPointPrepared == 3 && (SegmentType (iSegment ) == SMT_FIRSTARC || SegmentType (iSegment ) == SMT_ARC )) {
586+ i = StrokeArcToLine (gpi , line , i );
587+ ++ iSegment ;
588+ nPointPrepared = 1 ;
589+ }
590+ ++ i ;
591+ }
592+ }
593+ else {
594+ while (iPoint < iNextPoint ) {
595+ ReadPoint (gpi , & line -> point [i ], iPoint );
596+ ++ iPoint ;
597+ ++ nPointPrepared ;
598+ if (nPointPrepared == 3 ) {
599+ i = StrokeArcToLine (gpi , line , i );
600+ nPointPrepared = 1 ;
601+ }
602+ ++ i ;
603+ }
604+ }
605+ }
606+ else {
607+ while (iPoint < iNextPoint ) {
608+ ReadPoint (gpi , & shape -> line [iFigure ].point [i ], iPoint );
609+ ++ iPoint ;
610+ ++ i ;
611+ }
612+ }
410613 }
411614 }
412615
0 commit comments