Skip to content

Commit 7fe9b2b

Browse files
authored
Merge pull request #5745 from szekerest/mssqlcurve
MSSQL: Add support for curve geometries (#5743)
2 parents cb81699 + 151efbd commit 7fe9b2b

1 file changed

Lines changed: 221 additions & 18 deletions

File tree

mapmssql2008.c

Lines changed: 221 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
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)
@@ -50,7 +51,7 @@
5051
#include <string.h>
5152
#include <ctype.h> /* tolower() */
5253

53-
/* SqlGeometry serialization format
54+
/* SqlGeometry/SqlGeography serialization format
5455
5556
Simple 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
6162
Complex 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+
6574
SRID
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
7685
Point (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
8089
Figure
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+
88103
Shape
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 */
161201
typedef 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

264414
int 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

Comments
 (0)