1 /******************************************************************************
2  *
3  * Project:  MSSQL Spatial driver
4  * Purpose:  Implements OGRMSSQLGeometryWriter class to write native SqlGeometries.
5  * Author:   Tamas Szekeres, szekerest at gmail.com
6  *
7  ******************************************************************************
8  * Copyright (c) 2016, Tamas Szekeres
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 #include "cpl_conv.h"
30 #include "ogr_mssqlspatial.h"
31 
32 CPL_CVSID("$Id: ogrmssqlgeometrywriter.cpp 2f995c0de5706bc1082e557173ca6b66064250aa 2019-06-25 21:55:08 +0200 Tamas Szekeres $")
33 
34 /*   SqlGeometry/SqlGeography serialization format
35 
36 Simple Point (SerializationProps & IsSinglePoint)
37   [SRID][0x01][SerializationProps][Point][z][m]
38 
39 Simple Line Segment (SerializationProps & IsSingleLineSegment)
40   [SRID][0x01][SerializationProps][Point1][Point2][z1][z2][m1][m2]
41 
42 Complex Geometries
43   [SRID][VersionAttribute][SerializationProps][NumPoints][Point1]..[PointN][z1]..[zN][m1]..[mN]
44   [NumFigures][Figure]..[Figure][NumShapes][Shape]..[Shape]
45 
46 Complex Geometries (FigureAttribute == Curve)
47   [SRID][VersionAttribute][SerializationProps][NumPoints][Point1]..[PointN][z1]..[zN][m1]..[mN]
48   [NumFigures][Figure]..[Figure][NumShapes][Shape]..[Shape][NumSegments][SegmentType]..[SegmentType]
49 
50 VersionAttribute (1 byte)
51   0x01 = Katmai (MSSQL2008+)
52   0x02 = Denali (MSSQL2012+)
53 
54 SRID
55   Spatial Reference Id (4 bytes)
56 
57 SerializationProps (bitmask) 1 byte
58   0x01 = HasZValues
59   0x02 = HasMValues
60   0x04 = IsValid
61   0x08 = IsSinglePoint
62   0x10 = IsSingleLineSegment
63   0x20 = IsLargerThanAHemisphere
64 
65 Point (2-4)x8 bytes, size depends on SerializationProps & HasZValues & HasMValues
66   [x][y]                  - SqlGeometry
67   [latitude][longitude]   - SqlGeography
68 
69 Figure
70   [FigureAttribute][PointOffset]
71 
72 FigureAttribute - Katmai (1 byte)
73   0x00 = Interior Ring
74   0x01 = Stroke
75   0x02 = Exterior Ring
76 
77 FigureAttribute - Denali (1 byte)
78   0x00 = None
79   0x01 = Line
80   0x02 = Arc
81   0x03 = Curve
82 
83 Shape
84   [ParentFigureOffset][FigureOffset][ShapeType]
85 
86 ShapeType (1 byte)
87   0x00 = Unknown
88   0x01 = Point
89   0x02 = LineString
90   0x03 = Polygon
91   0x04 = MultiPoint
92   0x05 = MultiLineString
93   0x06 = MultiPolygon
94   0x07 = GeometryCollection
95   -- Denali
96   0x08 = CircularString
97   0x09 = CompoundCurve
98   0x0A = CurvePolygon
99   0x0B = FullGlobe
100 
101 SegmentType (1 byte)
102   0x00 = Line
103   0x01 = Arc
104   0x02 = FirstLine
105   0x03 = FirstArc
106 
107 */
108 
109 /************************************************************************/
110 /*                         Geometry writer macros                       */
111 /************************************************************************/
112 
113 #define WriteInt32(nPos, value) (*((unsigned int*)(pszData + (nPos))) = value)
114 
115 #define WriteByte(nPos, value) (pszData[nPos] = value)
116 
117 #define WriteDouble(nPos, value) (*((double*)(pszData + (nPos))) = value)
118 
119 #define ParentOffset(iShape) (nShapePos + (iShape) * 9 )
120 #define FigureOffset(iShape) (nShapePos + (iShape) * 9 + 4)
121 #define ShapeType(iShape) (nShapePos + (iShape) * 9 + 8)
122 #define SegmentType(iSegment) (nSegmentPos + (iSegment))
123 
124 #define FigureAttribute(iFigure) (nFigurePos + (iFigure) * 5)
125 #define PointOffset(iFigure) (nFigurePos + (iFigure) * 5 + 1)
126 
127 #define WriteX(iPoint, value) (WriteDouble(nPointPos + 16 * (iPoint), value))
128 #define WriteY(iPoint, value) (WriteDouble(nPointPos + 16 * (iPoint) + 8, value))
129 #define WriteZ(iPoint, value) (WriteDouble(nPointPos + 16 * nNumPoints + 8 * (iPoint), value))
130 #define WriteM(iPoint, value) (WriteDouble(nPointPos + 24 * nNumPoints + 8 * (iPoint), value))
131 
132 /************************************************************************/
133 /*                   OGRMSSQLGeometryWriter()                           */
134 /************************************************************************/
135 
OGRMSSQLGeometryWriter(OGRGeometry * poGeometry,int nGeomColumnType,int nSRS)136 OGRMSSQLGeometryWriter::OGRMSSQLGeometryWriter(OGRGeometry *poGeometry, int nGeomColumnType, int nSRS)
137 {
138     nColType = nGeomColumnType;
139     nSRSId = nSRS;
140     poGeom2 = poGeometry;
141 
142     chProps = 0;
143 
144     /* calculate required buffer length and the attributes */
145     nPointSize = 16;
146     if (poGeom2->getCoordinateDimension() == 3)
147     {
148         chProps |= SP_HASZVALUES;
149         nPointSize += 8;
150     }
151 
152     if (poGeom2->IsMeasured())
153     {
154         chProps |= SP_HASMVALUES;
155         nPointSize += 8;
156     }
157 
158     iPoint = 0;
159     nNumPoints = 0;
160     iFigure = 0;
161     nNumFigures = 0;
162     iShape = 0;
163     nNumShapes = 0;
164     iSegment = 0;
165     nNumSegments = 0;
166 
167     /* calculate points figures, shapes and segments*/
168     chVersion = VA_KATMAI;
169     TrackGeometry(poGeom2);
170     ++nNumShapes;
171 
172     OGRwkbGeometryType geomType = wkbFlatten(poGeom2->getGeometryType());
173 
174     if (nNumPoints == 1 && geomType == wkbPoint)
175     {
176         /* writing a single point */
177         chProps |= SP_ISSINGLEPOINT | SP_ISVALID;
178         nPointPos = 6;
179         nLen = nPointPos + nPointSize;
180     }
181     else if (nNumPoints == 2 && geomType == wkbLineString)
182     {
183         /* writing a single line */
184         chProps |= SP_ISSINGLELINESEGMENT | SP_ISVALID;
185         nPointPos = 6;
186         nLen = nPointPos + nPointSize * 2;
187     }
188     else
189     {
190         /* complex geometry */
191         nPointPos = 10;
192         nFigurePos = nPointPos + nPointSize * nNumPoints + 4;
193         nShapePos = nFigurePos  + 5 * nNumFigures + 4;
194         nSegmentPos = nShapePos + 9 * nNumShapes + 4;
195         if (nNumSegments > 0)
196             nLen = nSegmentPos + nNumSegments;
197         else
198             nLen = nShapePos + 9 * nNumShapes;
199     }
200 }
201 
202 /************************************************************************/
203 /*                         WritePoint()                                 */
204 /************************************************************************/
205 
WritePoint(OGRPoint * poGeom)206 void OGRMSSQLGeometryWriter::WritePoint(OGRPoint* poGeom)
207 {
208     if ((chProps & SP_HASZVALUES) && (chProps & SP_HASMVALUES))
209         WritePoint(poGeom->getX(), poGeom->getY(), poGeom->getZ(), poGeom->getM());
210     else if (chProps & SP_HASZVALUES)
211         WritePoint(poGeom->getX(), poGeom->getY(), poGeom->getZ());
212     else if (chProps & SP_HASMVALUES)
213         WritePoint(poGeom->getX(), poGeom->getY(), poGeom->getM());
214     else
215         WritePoint(poGeom->getX(), poGeom->getY());
216 }
217 
WritePoint(double x,double y)218 void OGRMSSQLGeometryWriter::WritePoint(double x, double y)
219 {
220     if (nColType == MSSQLCOLTYPE_GEOGRAPHY)
221     {
222         WriteY(iPoint, x);
223         WriteX(iPoint, y);
224     }
225     else
226     {
227         WriteX(iPoint, x);
228         WriteY(iPoint, y);
229     }
230     ++iPoint;
231 }
232 
WritePoint(double x,double y,double z)233 void OGRMSSQLGeometryWriter::WritePoint(double x, double y, double z)
234 {
235     WriteZ(iPoint, z);
236     WritePoint(x, y);
237 }
238 
WritePoint(double x,double y,double z,double m)239 void OGRMSSQLGeometryWriter::WritePoint(double x, double y, double z, double m)
240 {
241     WriteZ(iPoint, z);
242     WriteM(iPoint, m);
243     WritePoint(x, y);
244 }
245 
246 /************************************************************************/
247 /*                         WriteSimpleCurve()                           */
248 /************************************************************************/
249 
WriteSimpleCurve(OGRSimpleCurve * poGeom,int iStartIndex,int nCount)250 void OGRMSSQLGeometryWriter::WriteSimpleCurve(OGRSimpleCurve* poGeom, int iStartIndex, int nCount)
251 {
252     if ((chProps & SP_HASZVALUES) && (chProps & SP_HASMVALUES))
253     {
254         for (int i = iStartIndex; i < iStartIndex + nCount; i++)
255             WritePoint(poGeom->getX(i), poGeom->getY(i), poGeom->getZ(i), poGeom->getM(i));
256     }
257     else if (chProps & SP_HASZVALUES)
258     {
259         for (int i = iStartIndex; i < iStartIndex + nCount; i++)
260             WritePoint(poGeom->getX(i), poGeom->getY(i), poGeom->getZ(i));
261     }
262     else if (chProps & SP_HASMVALUES)
263     {
264         for (int i = iStartIndex; i < iStartIndex + nCount; i++)
265             WritePoint(poGeom->getX(i), poGeom->getY(i), poGeom->getM(i));
266     }
267     else
268     {
269         for (int i = iStartIndex; i < iStartIndex + nCount; i++)
270             WritePoint(poGeom->getX(i), poGeom->getY(i));
271     }
272 }
273 
WriteSimpleCurve(OGRSimpleCurve * poGeom,int iStartIndex)274 void OGRMSSQLGeometryWriter::WriteSimpleCurve(OGRSimpleCurve* poGeom, int iStartIndex)
275 {
276     WriteSimpleCurve(poGeom, iStartIndex, poGeom->getNumPoints() - iStartIndex);
277 }
278 
279 
WriteSimpleCurve(OGRSimpleCurve * poGeom)280 void OGRMSSQLGeometryWriter::WriteSimpleCurve(OGRSimpleCurve* poGeom)
281 {
282     WriteSimpleCurve(poGeom, 0, poGeom->getNumPoints());
283 }
284 
285 /************************************************************************/
286 /*                         WriteCompoundCurve()                         */
287 /************************************************************************/
288 
WriteCompoundCurve(OGRCompoundCurve * poGeom)289 void OGRMSSQLGeometryWriter::WriteCompoundCurve(OGRCompoundCurve* poGeom)
290 {
291     OGRSimpleCurve* poSubGeom;
292     WriteByte(FigureAttribute(iFigure), FA_CURVE);
293     WriteInt32(PointOffset(iFigure), iPoint);
294     for (int i = 0; i < poGeom->getNumCurves(); i++)
295     {
296         poSubGeom = poGeom->getCurve(i)->toSimpleCurve();
297         switch (wkbFlatten(poSubGeom->getGeometryType()))
298         {
299         case wkbLineString:
300             if (i == 0)
301                 WriteSimpleCurve(poSubGeom);
302             else
303                 WriteSimpleCurve(poSubGeom, 1);
304             for (int j = 1; j < poSubGeom->getNumPoints(); j++)
305             {
306                 if (j == 1)
307                     WriteByte(SegmentType(iSegment++), SMT_FIRSTLINE);
308                 else
309                     WriteByte(SegmentType(iSegment++), SMT_LINE);
310             }
311             break;
312 
313         case wkbCircularString:
314             if (i == 0)
315                 WriteSimpleCurve(poSubGeom);
316             else
317                 WriteSimpleCurve(poSubGeom, 1);
318             for (int j = 2; j < poSubGeom->getNumPoints(); j += 2)
319             {
320                 if (j == 2)
321                     WriteByte(SegmentType(iSegment++), SMT_FIRSTARC);
322                 else
323                     WriteByte(SegmentType(iSegment++), SMT_ARC);
324             }
325             break;
326 
327         default:
328             break;
329         }
330     }
331 }
332 
333 /************************************************************************/
334 /*                         WriteCurve()                                 */
335 /************************************************************************/
336 
WriteCurve(OGRCurve * poGeom)337 void OGRMSSQLGeometryWriter::WriteCurve(OGRCurve* poGeom)
338 {
339     switch (wkbFlatten(poGeom->getGeometryType()))
340     {
341     case wkbLineString:
342     case wkbLinearRing:
343         WriteByte(FigureAttribute(iFigure), FA_LINE);
344         WriteInt32(PointOffset(iFigure), iPoint);
345         WriteSimpleCurve(poGeom->toSimpleCurve());
346         ++iFigure;
347         break;
348 
349     case wkbCircularString:
350         WriteByte(FigureAttribute(iFigure), FA_ARC);
351         WriteInt32(PointOffset(iFigure), iPoint);
352         WriteSimpleCurve(poGeom->toSimpleCurve());
353         ++iFigure;
354         break;
355 
356     case wkbCompoundCurve:
357         WriteCompoundCurve(poGeom->toCompoundCurve());
358         ++iFigure;
359         break;
360 
361     default:
362         break;
363     }
364 }
365 
366 /************************************************************************/
367 /*                         WritePolygon()                               */
368 /************************************************************************/
369 
WritePolygon(OGRPolygon * poGeom)370 void OGRMSSQLGeometryWriter::WritePolygon(OGRPolygon* poGeom)
371 {
372     int r;
373     OGRLinearRing *poRing = poGeom->getExteriorRing();
374 
375     if (poRing == nullptr)
376         return;
377 
378     if (chVersion == VA_KATMAI)
379         WriteByte(FigureAttribute(iFigure), FA_EXTERIORRING);
380     else
381         WriteByte(FigureAttribute(iFigure), FA_LINE);
382 
383     WriteInt32(PointOffset(iFigure), iPoint);
384     WriteSimpleCurve(poRing);
385     ++iFigure;
386     for (r = 0; r < poGeom->getNumInteriorRings(); r++)
387     {
388         /* write interior rings */
389         poRing = poGeom->getInteriorRing(r);
390         if (chVersion == VA_KATMAI)
391             WriteByte(FigureAttribute(iFigure), FA_INTERIORRING);
392         else
393             WriteByte(FigureAttribute(iFigure), FA_LINE);
394 
395         WriteInt32(PointOffset(iFigure), iPoint);
396         WriteSimpleCurve(poRing);
397         ++iFigure;
398     }
399 }
400 
401 /************************************************************************/
402 /*                         WriteCurvePolygon()                          */
403 /************************************************************************/
404 
WriteCurvePolygon(OGRCurvePolygon * poGeom)405 void OGRMSSQLGeometryWriter::WriteCurvePolygon(OGRCurvePolygon* poGeom)
406 {
407     if (poGeom->getExteriorRingCurve() == nullptr)
408         return;
409 
410     WriteCurve(poGeom->getExteriorRingCurve());
411     for (int r = 0; r < poGeom->getNumInteriorRings(); r++)
412     {
413         /* write interior rings */
414         WriteCurve(poGeom->getInteriorRingCurve(r));
415     }
416 }
417 
418 /************************************************************************/
419 /*                         WriteGeometryCollection()                    */
420 /************************************************************************/
421 
WriteGeometryCollection(OGRGeometryCollection * poGeom,int iParent)422 void OGRMSSQLGeometryWriter::WriteGeometryCollection(OGRGeometryCollection* poGeom, int iParent)
423 {
424     for (int i = 0; i < poGeom->getNumGeometries(); i++)
425         WriteGeometry(poGeom->getGeometryRef(i), iParent);
426 }
427 
428 /************************************************************************/
429 /*                         WriteGeometry()                              */
430 /************************************************************************/
431 
WriteGeometry(OGRGeometry * poGeom,int iParent)432 void OGRMSSQLGeometryWriter::WriteGeometry(OGRGeometry* poGeom, int iParent)
433 {
434     /* write shape */
435     int iCurrentFigure = iFigure;
436     int iCurrentShape = iShape;
437     WriteInt32(ParentOffset(iShape), iParent);
438 
439     iParent = iShape;
440 
441     switch (wkbFlatten(poGeom->getGeometryType()))
442     {
443     case wkbPoint:
444         WriteByte(ShapeType(iShape++), ST_POINT);
445         if (!poGeom->IsEmpty())
446         {
447             if (chVersion == VA_KATMAI)
448                 WriteByte(FigureAttribute(iFigure), FA_STROKE);
449             else
450                 WriteByte(FigureAttribute(iFigure), FA_LINE);
451             WriteInt32(PointOffset(iFigure), iPoint);
452             WritePoint(poGeom->toPoint());
453             ++iFigure;
454         }
455         break;
456 
457     case wkbLineString:
458         WriteByte(ShapeType(iShape++), ST_LINESTRING);
459         if (!poGeom->IsEmpty())
460         {
461             if (chVersion == VA_KATMAI)
462                 WriteByte(FigureAttribute(iFigure), FA_STROKE);
463             else
464                 WriteByte(FigureAttribute(iFigure), FA_LINE);
465             WriteInt32(PointOffset(iFigure), iPoint);
466             WriteSimpleCurve(poGeom->toSimpleCurve());
467             ++iFigure;
468         }
469         break;
470 
471     case wkbCircularString:
472         WriteByte(ShapeType(iShape++), ST_CIRCULARSTRING);
473         if (!poGeom->IsEmpty())
474         {
475             if (chVersion == VA_KATMAI)
476                 WriteByte(FigureAttribute(iFigure), FA_STROKE);
477             else
478                 WriteByte(FigureAttribute(iFigure), FA_ARC);
479             WriteInt32(PointOffset(iFigure), iPoint);
480             WriteSimpleCurve(poGeom->toSimpleCurve());
481             ++iFigure;
482         }
483         break;
484 
485     case wkbCompoundCurve:
486         WriteByte(ShapeType(iShape++), ST_COMPOUNDCURVE);
487         if (!poGeom->IsEmpty())
488         {
489             WriteCompoundCurve(poGeom->toCompoundCurve());
490             ++iFigure;
491         }
492         break;
493 
494     case wkbPolygon:
495         WriteByte(ShapeType(iShape++), ST_POLYGON);
496         WritePolygon(poGeom->toPolygon());
497         break;
498 
499     case wkbCurvePolygon:
500         WriteByte(ShapeType(iShape++), ST_CURVEPOLYGON);
501         WriteCurvePolygon(poGeom->toCurvePolygon());
502         break;
503 
504     case wkbMultiPoint:
505         WriteByte(ShapeType(iShape++), ST_MULTIPOINT);
506         WriteGeometryCollection(poGeom->toGeometryCollection(), iParent);
507         break;
508 
509     case wkbMultiLineString:
510         WriteByte(ShapeType(iShape++), ST_MULTILINESTRING);
511         WriteGeometryCollection(poGeom->toGeometryCollection(), iParent);
512         break;
513 
514     case wkbMultiPolygon:
515         WriteByte(ShapeType(iShape++), ST_MULTIPOLYGON);
516         WriteGeometryCollection(poGeom->toGeometryCollection(), iParent);
517         break;
518 
519     case wkbGeometryCollection:
520         WriteByte(ShapeType(iShape++), ST_GEOMETRYCOLLECTION);
521         WriteGeometryCollection(poGeom->toGeometryCollection(), iParent);
522         break;
523 
524     default:
525         return;
526     }
527     // check if new figures have been added to shape
528     WriteInt32(FigureOffset(iCurrentShape), iFigure == iCurrentFigure? 0xFFFFFFFF : iCurrentFigure);
529 }
530 
531 /************************************************************************/
532 /*                         TrackGeometry()                              */
533 /************************************************************************/
534 
TrackGeometry(OGRGeometry * poGeom)535 void OGRMSSQLGeometryWriter::TrackGeometry(OGRGeometry* poGeom)
536 {
537     switch (wkbFlatten(poGeom->getGeometryType()))
538     {
539     case wkbPoint:
540         if (!poGeom->IsEmpty())
541         {
542             ++nNumFigures;
543             ++nNumPoints;
544         }
545         break;
546 
547     case wkbLineString:
548         if (!poGeom->IsEmpty())
549         {
550             ++nNumFigures;
551             nNumPoints += poGeom->toLineString()->getNumPoints();
552         }
553         break;
554 
555     case wkbCircularString:
556         chVersion = VA_DENALI;
557         if (!poGeom->IsEmpty())
558         {
559             ++nNumFigures;
560             nNumPoints += poGeom->toCircularString()->getNumPoints();
561         }
562         break;
563 
564     case wkbCompoundCurve:
565         {
566             int c;
567             chVersion = VA_DENALI;
568             if (!poGeom->IsEmpty())
569             {
570                 OGRCompoundCurve* g = poGeom->toCompoundCurve();
571                 OGRCurve* poSubGeom;
572                 ++nNumFigures;
573                 for (int i = 0; i < g->getNumCurves(); i++)
574                 {
575                     poSubGeom = g->getCurve(i);
576                     switch (wkbFlatten(poSubGeom->getGeometryType()))
577                     {
578                     case wkbLineString:
579                         c = poSubGeom->toLineString()->getNumPoints();
580                         if (c > 1)
581                         {
582                             if (i == 0)
583                                 nNumPoints += c;
584                             else
585                                 nNumPoints += c - 1;
586                             nNumSegments += c - 1;
587                         }
588                         break;
589 
590                     case wkbCircularString:
591                         c = poSubGeom->toCircularString()->getNumPoints();
592                         if (c > 2)
593                         {
594                             if (i == 0)
595                                 nNumPoints += c;
596                             else
597                                 nNumPoints += c - 1;
598                             nNumSegments += (int)((c - 1) / 2);
599                         }
600                         break;
601 
602                     default:
603                         break;
604                     }
605                 }
606             }
607         }
608         break;
609 
610     case wkbPolygon:
611         {
612             OGRPolygon* g = poGeom->toPolygon();
613             for( auto&& poIter: *g )
614                 TrackGeometry(poIter);
615         }
616         break;
617 
618     case wkbCurvePolygon:
619         {
620             chVersion = VA_DENALI;
621             OGRCurvePolygon* g = poGeom->toCurvePolygon();
622             for (auto&& poIter : *g)
623                 TrackGeometry(poIter);
624         }
625         break;
626 
627     case wkbMultiPoint:
628     case wkbMultiLineString:
629     case wkbMultiPolygon:
630     case wkbGeometryCollection:
631         {
632             OGRGeometryCollection* g = poGeom->toGeometryCollection();
633             for( auto&& poMember: *g )
634             {
635                 TrackGeometry(poMember);
636                 ++nNumShapes;
637             }
638         }
639         break;
640 
641     default:
642         break;
643     }
644 }
645 
646 /************************************************************************/
647 /*                         WriteSqlGeometry()                           */
648 /************************************************************************/
649 
WriteSqlGeometry(unsigned char * pszBuffer,int nBufLen)650 OGRErr OGRMSSQLGeometryWriter::WriteSqlGeometry(unsigned char* pszBuffer, int nBufLen)
651 {
652     pszData = pszBuffer;
653 
654     if (nBufLen < nLen)
655         return OGRERR_FAILURE;
656 
657     OGRwkbGeometryType geomType = wkbFlatten(poGeom2->getGeometryType());
658 
659     if (nNumPoints == 1 && geomType == wkbPoint)
660     {
661         /* writing a single point */
662         OGRPoint* g = poGeom2->toPoint();
663         WriteInt32(0, nSRSId);
664         WriteByte(4, VA_KATMAI);
665         WriteByte(5, chProps);
666         WritePoint(g);
667     }
668     else if (nNumPoints == 2 && geomType == wkbLineString)
669     {
670         /* writing a single line */
671         OGRLineString* g = poGeom2->toLineString();
672         WriteInt32(0, nSRSId);
673         WriteByte(4, VA_KATMAI);
674         WriteByte(5, chProps);
675 
676         if ((chProps & SP_HASZVALUES) && (chProps & SP_HASMVALUES))
677         {
678             WritePoint(g->getX(0), g->getY(0), g->getZ(0), g->getM(0));
679             WritePoint(g->getX(1), g->getY(1), g->getZ(1), g->getM(1));
680         }
681         else if (chProps & SP_HASZVALUES)
682         {
683             WritePoint(g->getX(0), g->getY(0), g->getZ(0));
684             WritePoint(g->getX(1), g->getY(1), g->getZ(1));
685         }
686         else if (chProps & SP_HASMVALUES)
687         {
688             WritePoint(g->getX(0), g->getY(0), g->getM(0));
689             WritePoint(g->getX(1), g->getY(1), g->getM(1));
690         }
691         else
692         {
693             WritePoint(g->getX(0), g->getY(0));
694             WritePoint(g->getX(1), g->getY(1));
695         }
696     }
697     else
698     {
699         /* complex geometry */
700         if (poGeom2->IsValid())
701             chProps |= SP_ISVALID;
702 
703         WriteInt32(0, nSRSId);
704         WriteByte(4, chVersion);
705         WriteByte(5, chProps);
706         WriteInt32(nPointPos - 4 , nNumPoints);
707         WriteInt32(nFigurePos - 4 , nNumFigures);
708         WriteInt32(nShapePos - 4 , nNumShapes);
709         if (nNumSegments > 0)
710             WriteInt32(nSegmentPos - 4, nNumSegments);
711 
712         WriteGeometry(poGeom2, 0xFFFFFFFF);
713     }
714     return OGRERR_NONE;
715 }
716