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