Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GeoPackage Translator
4 : * Purpose: Utility functions for OGR GeoPackage driver.
5 : * Author: Paul Ramsey, pramsey@boundlessgeo.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2013, Paul Ramsey <pramsey@boundlessgeo.com>
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 "ogrgeopackageutility.h"
30 : #include "ogr_p.h"
31 : #include "ogr_wkb.h"
32 : #include "sqlite/ogrsqlitebase.h"
33 : #include <limits>
34 :
35 : /* Requirement 20: A GeoPackage SHALL store feature table geometries */
36 : /* with the basic simple feature geometry types (Geometry, Point, */
37 : /* LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, */
38 : /* GeomCollection) */
39 : /* http://opengis.github.io/geopackage/#geometry_types */
40 2481 : OGRwkbGeometryType GPkgGeometryTypeToWKB(const char *pszGpkgType, bool bHasZ,
41 : bool bHasM)
42 : {
43 : OGRwkbGeometryType oType;
44 :
45 2481 : if (EQUAL("Geometry", pszGpkgType))
46 1525 : oType = wkbUnknown;
47 : /* The 1.0 spec is not completely clear on what should be used... */
48 956 : else if (EQUAL("GeomCollection", pszGpkgType) ||
49 956 : EQUAL("GeometryCollection", pszGpkgType))
50 2 : oType = wkbGeometryCollection;
51 : else
52 : {
53 954 : oType = OGRFromOGCGeomType(pszGpkgType);
54 954 : if (oType == wkbUnknown)
55 3 : oType = wkbNone;
56 : }
57 :
58 2481 : if ((oType != wkbNone) && bHasZ)
59 : {
60 54 : oType = wkbSetZ(oType);
61 : }
62 2481 : if ((oType != wkbNone) && bHasM)
63 : {
64 13 : oType = wkbSetM(oType);
65 : }
66 :
67 2481 : return oType;
68 : }
69 :
70 : /* Requirement 5: The columns of tables in a GeoPackage SHALL only be */
71 : /* declared using one of the data types specified in table GeoPackage */
72 : /* Data Types. */
73 : /* http://opengis.github.io/geopackage/#table_column_data_types */
74 : // return a OGRFieldType value or OFTMaxType + 1
75 3485 : int GPkgFieldToOGR(const char *pszGpkgType, OGRFieldSubType &eSubType,
76 : int &nMaxWidth)
77 : {
78 3485 : eSubType = OFSTNone;
79 3485 : nMaxWidth = 0;
80 :
81 : /* Integer types */
82 3485 : if (STRNCASECMP("INT", pszGpkgType, 3) == 0)
83 : {
84 1024 : if (!EQUAL("INT", pszGpkgType) && !EQUAL("INTEGER", pszGpkgType))
85 : {
86 0 : CPLError(CE_Warning, CPLE_AppDefined,
87 : "Field format '%s' not supported. "
88 : "Interpreted as INT",
89 : pszGpkgType);
90 : }
91 1024 : return OFTInteger64;
92 : }
93 2461 : else if (EQUAL("MEDIUMINT", pszGpkgType))
94 91 : return OFTInteger;
95 2370 : else if (EQUAL("SMALLINT", pszGpkgType))
96 : {
97 16 : eSubType = OFSTInt16;
98 16 : return OFTInteger;
99 : }
100 2354 : else if (EQUAL("TINYINT", pszGpkgType))
101 9 : return OFTInteger; // [-128, 127]
102 2345 : else if (EQUAL("BOOLEAN", pszGpkgType))
103 : {
104 19 : eSubType = OFSTBoolean;
105 19 : return OFTInteger;
106 : }
107 :
108 : /* Real types */
109 2326 : else if (EQUAL("FLOAT", pszGpkgType))
110 : {
111 16 : eSubType = OFSTFloat32;
112 16 : return OFTReal;
113 : }
114 2310 : else if (EQUAL("DOUBLE", pszGpkgType))
115 53 : return OFTReal;
116 2257 : else if (EQUAL("REAL", pszGpkgType))
117 490 : return OFTReal;
118 :
119 : // Only used normally in gpkg_data_column_constraints table, and we
120 : // need this only is reading it through ExecuteSQL()
121 1767 : else if (EQUAL("NUMERIC", pszGpkgType))
122 2 : return OFTReal;
123 :
124 : /* String/binary types */
125 1765 : else if (STRNCASECMP("TEXT", pszGpkgType, 4) == 0)
126 : {
127 1060 : if (pszGpkgType[4] == '(')
128 72 : nMaxWidth = atoi(pszGpkgType + 5);
129 988 : else if (pszGpkgType[4] != '\0')
130 : {
131 0 : CPLError(CE_Warning, CPLE_AppDefined,
132 : "Field format '%s' not supported. "
133 : "Interpreted as TEXT",
134 : pszGpkgType);
135 : }
136 1060 : return OFTString;
137 : }
138 :
139 705 : else if (STRNCASECMP("BLOB", pszGpkgType, 4) == 0)
140 : {
141 40 : if (pszGpkgType[4] != '(' && pszGpkgType[4] != '\0')
142 : {
143 0 : CPLError(CE_Warning, CPLE_AppDefined,
144 : "Field format '%s' not supported. "
145 : "Interpreted as BLOB",
146 : pszGpkgType);
147 : }
148 40 : return OFTBinary;
149 : }
150 :
151 : /* Date types */
152 665 : else if (EQUAL("DATE", pszGpkgType))
153 29 : return OFTDate;
154 636 : else if (EQUAL("DATETIME", pszGpkgType))
155 51 : return OFTDateTime;
156 :
157 : /* Illegal! */
158 : else
159 : {
160 585 : if (GPkgGeometryTypeToWKB(pszGpkgType, false, false) == wkbNone)
161 : {
162 3 : CPLError(CE_Warning, CPLE_AppDefined,
163 : "Field format '%s' not supported", pszGpkgType);
164 : }
165 585 : return OFTMaxType + 1;
166 : }
167 : }
168 :
169 : /* Requirement 5: The columns of tables in a GeoPackage SHALL only be */
170 : /* declared using one of the data types specified in table GeoPackage */
171 : /* Data Types. */
172 : /* http://opengis.github.io/geopackage/#table_column_data_types */
173 1679 : const char *GPkgFieldFromOGR(OGRFieldType eType, OGRFieldSubType eSubType,
174 : int nMaxWidth)
175 : {
176 1679 : switch (eType)
177 : {
178 870 : case OFTInteger:
179 : {
180 870 : if (eSubType == OFSTBoolean)
181 8 : return "BOOLEAN";
182 862 : else if (eSubType == OFSTInt16)
183 7 : return "SMALLINT";
184 : else
185 855 : return "MEDIUMINT";
186 : }
187 64 : case OFTInteger64:
188 64 : return "INTEGER";
189 108 : case OFTReal:
190 : {
191 108 : if (eSubType == OFSTFloat32)
192 12 : return "FLOAT";
193 : else
194 96 : return "REAL";
195 : }
196 530 : case OFTString:
197 : {
198 530 : if (nMaxWidth > 0)
199 44 : return CPLSPrintf("TEXT(%d)", nMaxWidth);
200 : else
201 486 : return "TEXT";
202 : }
203 14 : case OFTBinary:
204 14 : return "BLOB";
205 38 : case OFTDate:
206 38 : return "DATE";
207 53 : case OFTDateTime:
208 53 : return "DATETIME";
209 2 : default:
210 2 : return "TEXT";
211 : }
212 : }
213 :
214 : /* Requirement 19: A GeoPackage SHALL store feature table geometries
215 : * with or without optional elevation (Z) and/or measure (M) values in SQL
216 : * BLOBs using the Standard GeoPackageBinary format specified in table
217 : * GeoPackage SQL Geometry Binary Format and clause Geometry Encoding.
218 : *
219 : * http://opengis.github.io/geopackage/#gpb_format
220 : *
221 : * GeoPackageBinaryHeader {
222 : * byte[2] magic = 0x4750;
223 : * byte version;
224 : * byte flags;
225 : * int32 srs_id;
226 : * double[] envelope;
227 : * }
228 : *
229 : * StandardGeoPackageBinary {
230 : * GeoPackageBinaryHeader header;
231 : * WKBGeometry geometry;
232 : * }
233 : *
234 : * Flags byte contents:
235 : * Bit 7: Reserved for future
236 : * Bit 6: Reserved for future
237 : * Bit 5: Using Extended GPKG Binary?
238 : * Bit 4: Geometry is Empty?
239 : * Bit 3,2,1: Envelope contents (0 none, 1=X/Y, 2=X/Y/Z, 3=X/Y/M, 4=X/Y/Z/M)
240 : * Bit 0: Byte order of header (0=big/XDR, 1=little/NDR)
241 : *
242 : */
243 :
244 251353 : GByte *GPkgGeometryFromOGR(const OGRGeometry *poGeometry, int iSrsId,
245 : const OGRGeomCoordinateBinaryPrecision *psPrecision,
246 : size_t *pnWkbLen)
247 : {
248 251353 : CPLAssert(poGeometry != nullptr);
249 :
250 251353 : GByte byFlags = 0;
251 251353 : GByte byEnv = 1;
252 251353 : OGRwkbExportOptions wkbExportOptions;
253 251353 : if (psPrecision)
254 251325 : wkbExportOptions.sPrecision = *psPrecision;
255 251353 : wkbExportOptions.eByteOrder = static_cast<OGRwkbByteOrder>(CPL_IS_LSB);
256 : OGRErr err;
257 251353 : OGRBoolean bPoint = (wkbFlatten(poGeometry->getGeometryType()) == wkbPoint);
258 251353 : OGRBoolean bEmpty = poGeometry->IsEmpty();
259 : /* We voluntarily use getCoordinateDimension() so as to get only 2 for
260 : * XY/XYM */
261 : /* and 3 for XYZ/XYZM as we currently don't write envelopes with M extent.
262 : */
263 251353 : int iDims = poGeometry->getCoordinateDimension();
264 :
265 : /* Header has 8 bytes for sure, and optional extra space for bounds */
266 251353 : size_t nHeaderLen = 2 + 1 + 1 + 4;
267 251353 : if (!bPoint && !bEmpty)
268 : {
269 2884 : nHeaderLen += 8 * 2 * iDims;
270 : }
271 :
272 : /* Total BLOB size is header + WKB size */
273 251353 : size_t nWkbLen = nHeaderLen + poGeometry->WkbSize();
274 251353 : if (nWkbLen > static_cast<size_t>(std::numeric_limits<int>::max()))
275 : {
276 0 : CPLError(CE_Failure, CPLE_NotSupported, "too big geometry blob");
277 0 : return nullptr;
278 : }
279 251353 : GByte *pabyWkb = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbLen));
280 251353 : if (!pabyWkb)
281 0 : return nullptr;
282 251353 : if (pnWkbLen)
283 251353 : *pnWkbLen = nWkbLen;
284 :
285 : /* Header Magic */
286 251353 : pabyWkb[0] = 0x47;
287 251353 : pabyWkb[1] = 0x50;
288 :
289 : /* GPKG BLOB Version */
290 251353 : pabyWkb[2] = 0;
291 :
292 : /* Extended? No. */
293 :
294 : /* Envelope dimensionality? */
295 :
296 : /* Don't write envelope for point type */
297 251353 : if (bPoint)
298 248452 : byEnv = 0;
299 : else
300 : /* 3D envelope for 3D data */
301 2901 : if (iDims == 3)
302 27 : byEnv = 2;
303 : /* 2D envelope otherwise */
304 : else
305 2874 : byEnv = 1;
306 :
307 : /* Empty? No envelope then. */
308 251353 : if (bEmpty)
309 : {
310 1027 : byEnv = 0;
311 : /* Set empty flag */
312 1027 : byFlags |= (1 << 4);
313 : }
314 :
315 : /* Set envelope flags */
316 251353 : byFlags |= (byEnv << 1);
317 :
318 : /* Byte order of header? */
319 : /* Use native endianness */
320 251353 : byFlags |= wkbExportOptions.eByteOrder;
321 :
322 : /* Write flags byte */
323 251353 : pabyWkb[3] = byFlags;
324 :
325 : /* Write srs_id */
326 251353 : memcpy(pabyWkb + 4, &iSrsId, 4);
327 :
328 : /* Write envelope */
329 251353 : if (!bEmpty && !bPoint)
330 : {
331 2884 : double *padPtr = reinterpret_cast<double *>(pabyWkb + 8);
332 2884 : if (iDims == 3)
333 : {
334 27 : OGREnvelope3D oEnv3d;
335 27 : poGeometry->getEnvelope(&oEnv3d);
336 27 : padPtr[0] = oEnv3d.MinX;
337 27 : padPtr[1] = oEnv3d.MaxX;
338 27 : padPtr[2] = oEnv3d.MinY;
339 27 : padPtr[3] = oEnv3d.MaxY;
340 27 : padPtr[4] = oEnv3d.MinZ;
341 27 : padPtr[5] = oEnv3d.MaxZ;
342 : }
343 : else
344 : {
345 2857 : OGREnvelope oEnv;
346 2857 : poGeometry->getEnvelope(&oEnv);
347 2857 : padPtr[0] = oEnv.MinX;
348 2857 : padPtr[1] = oEnv.MaxX;
349 2857 : padPtr[2] = oEnv.MinY;
350 2857 : padPtr[3] = oEnv.MaxY;
351 : }
352 : }
353 :
354 251353 : GByte *pabyPtr = pabyWkb + nHeaderLen;
355 :
356 : /* Use the wkbVariantIso for ISO SQL/MM output (differs for 3d geometry) */
357 251353 : wkbExportOptions.eWkbVariant = wkbVariantIso;
358 251353 : err = poGeometry->exportToWkb(pabyPtr, &wkbExportOptions);
359 251353 : if (err != OGRERR_NONE)
360 : {
361 0 : CPLFree(pabyWkb);
362 0 : return nullptr;
363 : }
364 :
365 251353 : return pabyWkb;
366 : }
367 :
368 1312680 : OGRErr GPkgHeaderFromWKB(const GByte *pabyGpkg, size_t nGpkgLen,
369 : GPkgHeader *poHeader)
370 : {
371 1312680 : CPLAssert(pabyGpkg != nullptr);
372 1312680 : CPLAssert(poHeader != nullptr);
373 :
374 : /* Magic (match required) */
375 1312680 : if (nGpkgLen < 8 || pabyGpkg[0] != 0x47 || pabyGpkg[1] != 0x50 ||
376 1312620 : pabyGpkg[2] != 0) /* Version (only 0 supported at this time)*/
377 : {
378 67 : memset(poHeader, 0, sizeof(*poHeader));
379 67 : return OGRERR_FAILURE;
380 : }
381 :
382 : /* Flags */
383 1312620 : GByte byFlags = pabyGpkg[3];
384 1312620 : poHeader->bEmpty = (byFlags & (0x01 << 4)) >> 4;
385 1312620 : poHeader->bExtended = (byFlags & (0x01 << 5)) >> 5;
386 1312620 : poHeader->eByteOrder = static_cast<OGRwkbByteOrder>(byFlags & 0x01);
387 1312620 : poHeader->bExtentHasXY = false;
388 1312620 : poHeader->bExtentHasZ = false;
389 : #ifdef notdef
390 : poHeader->bExtentHasM = false;
391 : #endif
392 1312620 : OGRBoolean bSwap = OGR_SWAP(poHeader->eByteOrder);
393 :
394 : /* Envelope */
395 1312620 : int iEnvelope = (byFlags & (0x07 << 1)) >> 1;
396 1312620 : int nEnvelopeDim = 0;
397 1312620 : if (iEnvelope)
398 : {
399 16968 : poHeader->bExtentHasXY = true;
400 16968 : if (iEnvelope == 1)
401 : {
402 16799 : nEnvelopeDim = 2; /* 2D envelope */
403 : }
404 169 : else if (iEnvelope == 2)
405 : {
406 155 : poHeader->bExtentHasZ = true;
407 155 : nEnvelopeDim = 3; /* 2D+Z envelope */
408 : }
409 14 : else if (iEnvelope == 3)
410 : {
411 : #ifdef notdef
412 : poHeader->bExtentHasM = true;
413 : #endif
414 7 : nEnvelopeDim = 3; /* 2D+M envelope */
415 : }
416 7 : else if (iEnvelope == 4)
417 : {
418 7 : poHeader->bExtentHasZ = true;
419 : #ifdef notdef
420 : poHeader->bExtentHasM = true;
421 : #endif
422 7 : nEnvelopeDim = 4; /* 2D+ZM envelope */
423 : }
424 : else
425 : {
426 0 : return OGRERR_FAILURE;
427 : }
428 : }
429 :
430 : /* SrsId */
431 1312620 : int iSrsId = 0;
432 1312620 : memcpy(&iSrsId, pabyGpkg + 4, 4);
433 1312620 : if (bSwap)
434 : {
435 0 : iSrsId = CPL_SWAP32(iSrsId);
436 : }
437 1312620 : poHeader->iSrsId = iSrsId;
438 :
439 1312620 : if (nGpkgLen < static_cast<size_t>(8 + 8 * 2 * nEnvelopeDim))
440 : {
441 : // Not enough bytes
442 0 : return OGRERR_FAILURE;
443 : }
444 :
445 : /* Envelope */
446 1312620 : const double *padPtr = reinterpret_cast<const double *>(pabyGpkg + 8);
447 1312620 : if (poHeader->bExtentHasXY)
448 : {
449 16968 : poHeader->MinX = padPtr[0];
450 16968 : poHeader->MaxX = padPtr[1];
451 16968 : poHeader->MinY = padPtr[2];
452 16968 : poHeader->MaxY = padPtr[3];
453 16968 : if (bSwap)
454 : {
455 0 : CPL_SWAPDOUBLE(&(poHeader->MinX));
456 0 : CPL_SWAPDOUBLE(&(poHeader->MaxX));
457 0 : CPL_SWAPDOUBLE(&(poHeader->MinY));
458 0 : CPL_SWAPDOUBLE(&(poHeader->MaxY));
459 : }
460 : }
461 1312620 : if (poHeader->bExtentHasZ)
462 : {
463 162 : poHeader->MinZ = padPtr[4];
464 162 : poHeader->MaxZ = padPtr[5];
465 162 : if (bSwap)
466 : {
467 0 : CPL_SWAPDOUBLE(&(poHeader->MinZ));
468 0 : CPL_SWAPDOUBLE(&(poHeader->MaxZ));
469 : }
470 : }
471 : #ifdef notdef
472 : if (poHeader->bExtentHasM)
473 : {
474 : poHeader->MinM = padPtr[(poHeader->bExtentHasZ) ? 6 : 4];
475 : poHeader->MaxM = padPtr[(poHeader->bExtentHasZ) ? 7 : 5];
476 : if (bSwap)
477 : {
478 : CPL_SWAPDOUBLE(&(poHeader->MinM));
479 : CPL_SWAPDOUBLE(&(poHeader->MaxM));
480 : }
481 : }
482 : #endif
483 :
484 : /* Header size in byte stream */
485 1312620 : poHeader->nHeaderLen = 8 + 8 * 2 * nEnvelopeDim;
486 :
487 1312620 : return OGRERR_NONE;
488 : }
489 :
490 21738 : OGRGeometry *GPkgGeometryToOGR(const GByte *pabyGpkg, size_t nGpkgLen,
491 : OGRSpatialReference *poSrs)
492 : {
493 21738 : CPLAssert(pabyGpkg != nullptr);
494 :
495 : GPkgHeader oHeader;
496 :
497 : /* Read header */
498 21738 : OGRErr err = GPkgHeaderFromWKB(pabyGpkg, nGpkgLen, &oHeader);
499 21738 : if (err != OGRERR_NONE)
500 3 : return nullptr;
501 :
502 : /* WKB pointer */
503 21735 : const GByte *pabyWkb = pabyGpkg + oHeader.nHeaderLen;
504 21735 : size_t nWkbLen = nGpkgLen - oHeader.nHeaderLen;
505 :
506 : /* Parse WKB */
507 21735 : OGRGeometry *poGeom = nullptr;
508 21735 : err = OGRGeometryFactory::createFromWkb(pabyWkb, poSrs, &poGeom,
509 : static_cast<int>(nWkbLen));
510 21735 : if (err != OGRERR_NONE)
511 0 : return nullptr;
512 :
513 21735 : return poGeom;
514 : }
515 :
516 : /************************************************************************/
517 : /* OGRGeoPackageGetHeader() */
518 : /************************************************************************/
519 :
520 1235110 : bool OGRGeoPackageGetHeader(sqlite3_context * /*pContext*/, int /*argc*/,
521 : sqlite3_value **argv, GPkgHeader *psHeader,
522 : bool bNeedExtent, bool bNeedExtent3D, int iGeomIdx)
523 : {
524 :
525 : // Extent3D implies extent
526 1235110 : const bool bNeedAnyExtent{bNeedExtent || bNeedExtent3D};
527 :
528 1235110 : if (sqlite3_value_type(argv[iGeomIdx]) != SQLITE_BLOB)
529 : {
530 8 : memset(psHeader, 0, sizeof(*psHeader));
531 8 : return false;
532 : }
533 1235100 : const int nBLOBLen = sqlite3_value_bytes(argv[iGeomIdx]);
534 : const GByte *pabyBLOB =
535 1235100 : reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[iGeomIdx]));
536 :
537 1235100 : if (nBLOBLen < 8)
538 : {
539 4 : memset(psHeader, 0, sizeof(*psHeader));
540 4 : return false;
541 : }
542 1235100 : else if (GPkgHeaderFromWKB(pabyBLOB, nBLOBLen, psHeader) != OGRERR_NONE)
543 : {
544 10 : bool bEmpty = false;
545 10 : memset(psHeader, 0, sizeof(*psHeader));
546 10 : if (OGRSQLiteGetSpatialiteGeometryHeader(
547 : pabyBLOB, nBLOBLen, &(psHeader->iSrsId), nullptr, &bEmpty,
548 : &(psHeader->MinX), &(psHeader->MinY), &(psHeader->MaxX),
549 10 : &(psHeader->MaxY)) == OGRERR_NONE)
550 : {
551 9 : psHeader->bEmpty = bEmpty;
552 9 : psHeader->bExtentHasXY = !bEmpty;
553 9 : if (!bNeedExtent3D && !(bEmpty && bNeedAnyExtent))
554 9 : return true;
555 : }
556 :
557 1 : return false;
558 : }
559 :
560 1235090 : if (psHeader->bEmpty && bNeedAnyExtent)
561 : {
562 1 : return false;
563 : }
564 1235090 : else if (!psHeader->bExtentHasXY && bNeedExtent && !bNeedExtent3D)
565 : {
566 957673 : OGREnvelope sEnvelope;
567 957673 : if (OGRWKBGetBoundingBox(pabyBLOB + psHeader->nHeaderLen,
568 957673 : static_cast<size_t>(nBLOBLen) -
569 957673 : psHeader->nHeaderLen,
570 : sEnvelope))
571 : {
572 957673 : psHeader->MinX = sEnvelope.MinX;
573 957673 : psHeader->MaxX = sEnvelope.MaxX;
574 957673 : psHeader->MinY = sEnvelope.MinY;
575 957673 : psHeader->MaxY = sEnvelope.MaxY;
576 957673 : return true;
577 : }
578 0 : return false;
579 : }
580 277414 : else if (!psHeader->bExtentHasZ && bNeedExtent3D)
581 : {
582 17 : OGREnvelope3D sEnvelope3D;
583 17 : if (OGRWKBGetBoundingBox(pabyBLOB + psHeader->nHeaderLen,
584 17 : static_cast<size_t>(nBLOBLen) -
585 17 : psHeader->nHeaderLen,
586 : sEnvelope3D))
587 : {
588 17 : psHeader->MinX = sEnvelope3D.MinX;
589 17 : psHeader->MaxX = sEnvelope3D.MaxX;
590 17 : psHeader->MinY = sEnvelope3D.MinY;
591 17 : psHeader->MaxY = sEnvelope3D.MaxY;
592 17 : psHeader->MinZ = sEnvelope3D.MinZ;
593 17 : psHeader->MaxZ = sEnvelope3D.MaxZ;
594 17 : return true;
595 : }
596 0 : return false;
597 : }
598 277397 : return true;
599 : }
|