/src/gdal/ogr/ogrsf_frmts/flatgeobuf/geometryreader.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: FlatGeobuf driver |
4 | | * Purpose: Implements GeometryReader class. |
5 | | * Author: Björn Harrtell <bjorn at wololo dot org> |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2018-2019, Björn Harrtell <bjorn at wololo dot org> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "ogrsf_frmts.h" |
14 | | #include "ogr_p.h" |
15 | | |
16 | | #include "geometryreader.h" |
17 | | #include "cplerrors.h" |
18 | | #include "ogr_flatgeobuf.h" |
19 | | |
20 | | using namespace flatbuffers; |
21 | | using namespace FlatGeobuf; |
22 | | using namespace ogr_flatgeobuf; |
23 | | |
24 | | static std::nullptr_t CPLErrorInvalidLength(const char *message) |
25 | 84 | { |
26 | 84 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid length detected: %s", |
27 | 84 | message); |
28 | 84 | return nullptr; |
29 | 84 | } |
30 | | |
31 | | OGRPoint *GeometryReader::readPoint() |
32 | 1.27k | { |
33 | 1.27k | const auto offsetXy = m_offset * 2; |
34 | | // +1 because we need to read x and y |
35 | 1.27k | if (offsetXy + 1 >= m_length) |
36 | 2 | return CPLErrorInvalidLength("XY data"); |
37 | 1.27k | if (m_hasZ) |
38 | 761 | { |
39 | 761 | const auto z = m_geometry->z(); |
40 | 761 | if (z == nullptr) |
41 | 0 | return CPLErrorInvalidPointer("Z data"); |
42 | 761 | if (m_offset >= z->size()) |
43 | 1 | return CPLErrorInvalidLength("Z data"); |
44 | 760 | const auto aZ = z->data(); |
45 | 760 | if (m_hasM) |
46 | 568 | { |
47 | 568 | const auto pM = m_geometry->m(); |
48 | 568 | if (pM == nullptr) |
49 | 0 | return CPLErrorInvalidPointer("M data"); |
50 | 568 | if (m_offset >= pM->size()) |
51 | 1 | return CPLErrorInvalidLength("M data"); |
52 | 567 | const auto aM = pM->data(); |
53 | 567 | return new OGRPoint{EndianScalar(m_xy[offsetXy + 0]), |
54 | 567 | EndianScalar(m_xy[offsetXy + 1]), |
55 | 567 | EndianScalar(aZ[m_offset]), |
56 | 567 | EndianScalar(aM[m_offset])}; |
57 | 568 | } |
58 | 192 | else |
59 | 192 | { |
60 | 192 | return new OGRPoint{EndianScalar(m_xy[offsetXy + 0]), |
61 | 192 | EndianScalar(m_xy[offsetXy + 1]), |
62 | 192 | EndianScalar(aZ[m_offset])}; |
63 | 192 | } |
64 | 760 | } |
65 | 511 | else if (m_hasM) |
66 | 154 | { |
67 | 154 | const auto pM = m_geometry->m(); |
68 | 154 | if (pM == nullptr) |
69 | 0 | return CPLErrorInvalidPointer("M data"); |
70 | 154 | if (m_offset >= pM->size()) |
71 | 1 | return CPLErrorInvalidLength("M data"); |
72 | 153 | const auto aM = pM->data(); |
73 | 153 | return OGRPoint::createXYM(EndianScalar(m_xy[offsetXy + 0]), |
74 | 153 | EndianScalar(m_xy[offsetXy + 1]), |
75 | 153 | EndianScalar(aM[m_offset])); |
76 | 154 | } |
77 | 357 | else |
78 | 357 | { |
79 | 357 | return new OGRPoint{EndianScalar(m_xy[offsetXy + 0]), |
80 | 357 | EndianScalar(m_xy[offsetXy + 1])}; |
81 | 357 | } |
82 | 1.27k | } |
83 | | |
84 | | OGRMultiPoint *GeometryReader::readMultiPoint() |
85 | 233 | { |
86 | 233 | auto length = m_length / 2; |
87 | 233 | if (length >= feature_max_buffer_size) |
88 | 0 | return CPLErrorInvalidLength("MultiPoint"); |
89 | 233 | auto mp = std::make_unique<OGRMultiPoint>(); |
90 | 1.36k | for (uint32_t i = 0; i < length; i++) |
91 | 1.13k | { |
92 | 1.13k | m_offset = i; |
93 | 1.13k | const auto p = readPoint(); |
94 | 1.13k | if (p == nullptr) |
95 | 3 | return nullptr; |
96 | 1.13k | mp->addGeometryDirectly(p); |
97 | 1.13k | } |
98 | 230 | return mp.release(); |
99 | 233 | } |
100 | | |
101 | | OGRMultiLineString *GeometryReader::readMultiLineString() |
102 | 72 | { |
103 | 72 | const auto ends = m_geometry->ends(); |
104 | 72 | auto mls = std::make_unique<OGRMultiLineString>(); |
105 | 72 | if (ends == nullptr || ends->size() < 2) |
106 | 42 | { |
107 | 42 | m_length = m_length / 2; |
108 | 42 | const auto part = readSimpleCurve<OGRLineString>(); |
109 | 42 | if (part == nullptr) |
110 | 1 | return nullptr; |
111 | 41 | mls->addGeometryDirectly(part); |
112 | 41 | } |
113 | 30 | else |
114 | 30 | { |
115 | 30 | m_offset = 0; |
116 | 354 | for (uint32_t i = 0; i < ends->size(); i++) |
117 | 353 | { |
118 | 353 | const auto e = ends->Get(i); |
119 | 353 | if (e < m_offset) |
120 | 1 | return CPLErrorInvalidLength("MultiLineString"); |
121 | 352 | m_length = e - m_offset; |
122 | 352 | const auto ls = readSimpleCurve<OGRLineString>(); |
123 | 352 | if (ls == nullptr) |
124 | 28 | return nullptr; |
125 | 324 | mls->addGeometryDirectly(ls); |
126 | 324 | m_offset = e; |
127 | 324 | } |
128 | 30 | } |
129 | 42 | return mls.release(); |
130 | 72 | } |
131 | | |
132 | | OGRErr GeometryReader::readSimpleCurve(OGRSimpleCurve *sc) |
133 | 6.58k | { |
134 | 6.58k | if (m_offset > feature_max_buffer_size || |
135 | 5.58k | m_length > feature_max_buffer_size - m_offset) |
136 | 1.29k | return CPLErrorInvalidSize("curve offset max"); |
137 | 5.28k | const uint32_t offsetLen = m_length + m_offset; |
138 | 5.28k | if (offsetLen > m_xylength / 2) |
139 | 370 | return CPLErrorInvalidSize("curve XY offset"); |
140 | 4.91k | const auto ogrXY = reinterpret_cast<const OGRRawPoint *>(m_xy) + m_offset; |
141 | 4.91k | if (m_hasZ) |
142 | 2.83k | { |
143 | 2.83k | const auto pZ = m_geometry->z(); |
144 | 2.83k | if (pZ == nullptr) |
145 | 0 | { |
146 | 0 | CPLErrorInvalidPointer("Z data"); |
147 | 0 | return OGRERR_CORRUPT_DATA; |
148 | 0 | } |
149 | 2.83k | if (offsetLen > pZ->size()) |
150 | 197 | return CPLErrorInvalidSize("curve Z offset"); |
151 | 2.63k | const auto aZ = pZ->data(); |
152 | 2.63k | if (m_hasM) |
153 | 401 | { |
154 | 401 | const auto pM = m_geometry->m(); |
155 | 401 | if (pM == nullptr) |
156 | 0 | { |
157 | 0 | CPLErrorInvalidPointer("M data"); |
158 | 0 | return OGRERR_CORRUPT_DATA; |
159 | 0 | } |
160 | 401 | if (offsetLen > pM->size()) |
161 | 1 | return CPLErrorInvalidSize("curve M offset"); |
162 | 400 | const auto aM = pM->data(); |
163 | 400 | #if CPL_IS_LSB |
164 | 400 | sc->setPoints(m_length, ogrXY, aZ + m_offset, aM + m_offset); |
165 | | #else |
166 | | sc->setNumPoints(m_length, false); |
167 | | for (uint32_t i = 0; i < m_length; i++) |
168 | | { |
169 | | sc->setPoint(i, EndianScalar(ogrXY[i].x), |
170 | | EndianScalar(ogrXY[i].y), |
171 | | EndianScalar(aZ[m_offset + i]), |
172 | | EndianScalar(aM[m_offset + i])); |
173 | | } |
174 | | #endif |
175 | 400 | } |
176 | 2.23k | else |
177 | 2.23k | { |
178 | 2.23k | #if CPL_IS_LSB |
179 | 2.23k | sc->setPoints(m_length, ogrXY, aZ + m_offset); |
180 | | #else |
181 | | sc->setNumPoints(m_length, false); |
182 | | for (uint32_t i = 0; i < m_length; i++) |
183 | | { |
184 | | sc->setPoint(i, EndianScalar(ogrXY[i].x), |
185 | | EndianScalar(ogrXY[i].y), |
186 | | EndianScalar(aZ[m_offset + i])); |
187 | | } |
188 | | #endif |
189 | 2.23k | } |
190 | 2.63k | } |
191 | 2.08k | else if (m_hasM) |
192 | 258 | { |
193 | 258 | const auto pM = m_geometry->m(); |
194 | 258 | if (pM == nullptr) |
195 | 0 | { |
196 | 0 | CPLErrorInvalidPointer("M data"); |
197 | 0 | return OGRERR_CORRUPT_DATA; |
198 | 0 | } |
199 | 258 | if (offsetLen > pM->size()) |
200 | 1 | return CPLErrorInvalidSize("curve M offset"); |
201 | 257 | const auto aM = pM->data(); |
202 | 257 | #if CPL_IS_LSB |
203 | 257 | sc->setPointsM(m_length, ogrXY, aM + m_offset); |
204 | | #else |
205 | | sc->setNumPoints(m_length, false); |
206 | | for (uint32_t i = 0; i < m_length; i++) |
207 | | { |
208 | | sc->setPointM(i, EndianScalar(ogrXY[i].x), EndianScalar(ogrXY[i].y), |
209 | | EndianScalar(aM[m_offset + i])); |
210 | | } |
211 | | #endif |
212 | 257 | } |
213 | 1.82k | else |
214 | 1.82k | { |
215 | 1.82k | #if CPL_IS_LSB |
216 | 1.82k | sc->setPoints(m_length, ogrXY); |
217 | | #else |
218 | | sc->setNumPoints(m_length, false); |
219 | | for (uint32_t i = 0; i < m_length; i++) |
220 | | { |
221 | | sc->setPoint(i, EndianScalar(ogrXY[i].x), EndianScalar(ogrXY[i].y)); |
222 | | } |
223 | | #endif |
224 | 1.82k | } |
225 | 4.71k | return OGRERR_NONE; |
226 | 4.91k | } |
227 | | |
228 | | OGRPolygon *GeometryReader::readPolygon() |
229 | 1.62k | { |
230 | 1.62k | const auto ends = m_geometry->ends(); |
231 | 1.62k | auto p = std::make_unique<OGRPolygon>(); |
232 | 1.62k | if (ends == nullptr || ends->size() < 2) |
233 | 1.29k | { |
234 | 1.29k | m_length = m_length / 2; |
235 | 1.29k | const auto lr = readSimpleCurve<OGRLinearRing>(); |
236 | 1.29k | if (lr == nullptr) |
237 | 1 | return nullptr; |
238 | 1.29k | p->addRingDirectly(lr); |
239 | 1.29k | } |
240 | 329 | else |
241 | 329 | { |
242 | 2.81k | for (uint32_t i = 0; i < ends->size(); i++) |
243 | 2.53k | { |
244 | 2.53k | const auto e = ends->Get(i); |
245 | 2.53k | if (e < m_offset) |
246 | 47 | return CPLErrorInvalidLength("Polygon"); |
247 | 2.48k | m_length = e - m_offset; |
248 | 2.48k | const auto lr = readSimpleCurve<OGRLinearRing>(); |
249 | 2.48k | m_offset = e; |
250 | 2.48k | if (lr == nullptr) |
251 | 1.73k | continue; |
252 | 756 | p->addRingDirectly(lr); |
253 | 756 | } |
254 | 282 | if (p->IsEmpty()) |
255 | 10 | return nullptr; |
256 | 282 | } |
257 | 1.57k | return p.release(); |
258 | 1.62k | } |
259 | | |
260 | | OGRMultiPolygon *GeometryReader::readMultiPolygon() |
261 | 654 | { |
262 | 654 | auto parts = m_geometry->parts(); |
263 | 654 | if (parts == nullptr) |
264 | 1 | return CPLErrorInvalidPointer("parts data"); |
265 | 653 | auto mp = std::make_unique<OGRMultiPolygon>(); |
266 | 1.88k | for (uoffset_t i = 0; i < parts->size(); i++) |
267 | 1.23k | { |
268 | 1.23k | auto g = std::unique_ptr<OGRGeometry>( |
269 | 1.23k | readPart(parts->Get(i), GeometryType::Polygon)); |
270 | 1.23k | if (g == nullptr) |
271 | 3 | return nullptr; |
272 | 1.22k | mp->addGeometryDirectly(g.release()->toPolygon()); |
273 | 1.22k | } |
274 | 650 | return mp.release(); |
275 | 653 | } |
276 | | |
277 | | OGRGeometryCollection *GeometryReader::readGeometryCollection() |
278 | 75 | { |
279 | 75 | auto parts = m_geometry->parts(); |
280 | 75 | if (parts == nullptr) |
281 | 3 | return CPLErrorInvalidPointer("parts data"); |
282 | 72 | auto gc = std::make_unique<OGRGeometryCollection>(); |
283 | 171 | for (uoffset_t i = 0; i < parts->size(); i++) |
284 | 104 | { |
285 | 104 | auto g = std::unique_ptr<OGRGeometry>(readPart(parts->Get(i))); |
286 | 104 | if (g == nullptr) |
287 | 5 | return nullptr; |
288 | 99 | gc->addGeometryDirectly(g.release()); |
289 | 99 | } |
290 | 67 | return gc.release(); |
291 | 72 | } |
292 | | |
293 | | OGRCompoundCurve *GeometryReader::readCompoundCurve() |
294 | 34 | { |
295 | 34 | auto parts = m_geometry->parts(); |
296 | 34 | if (parts == nullptr) |
297 | 1 | return CPLErrorInvalidPointer("parts data"); |
298 | 33 | auto cc = std::make_unique<OGRCompoundCurve>(); |
299 | 43 | for (uoffset_t i = 0; i < parts->size(); i++) |
300 | 20 | { |
301 | 20 | auto g = std::unique_ptr<OGRGeometry>(readPart(parts->Get(i))); |
302 | 20 | if (dynamic_cast<OGRCurve *>(g.get()) == nullptr) |
303 | 4 | return nullptr; |
304 | 16 | auto poCurve = g.release()->toCurve(); |
305 | 16 | if (cc->addCurveDirectly(poCurve) != OGRERR_NONE) |
306 | 6 | { |
307 | 6 | delete poCurve; |
308 | 6 | return nullptr; |
309 | 6 | } |
310 | 16 | } |
311 | 23 | return cc.release(); |
312 | 33 | } |
313 | | |
314 | | OGRCurvePolygon *GeometryReader::readCurvePolygon() |
315 | 583 | { |
316 | 583 | auto parts = m_geometry->parts(); |
317 | 583 | if (parts == nullptr) |
318 | 1 | return CPLErrorInvalidPointer("parts data"); |
319 | 582 | auto cp = std::make_unique<OGRCurvePolygon>(); |
320 | 1.56k | for (uoffset_t i = 0; i < parts->size(); i++) |
321 | 997 | { |
322 | 997 | auto g = std::unique_ptr<OGRGeometry>(readPart(parts->Get(i))); |
323 | 997 | if (dynamic_cast<OGRCurve *>(g.get()) == nullptr) |
324 | 11 | return nullptr; |
325 | 986 | auto poCurve = g.release()->toCurve(); |
326 | 986 | if (cp->addRingDirectly(poCurve) != OGRERR_NONE) |
327 | 5 | { |
328 | 5 | delete poCurve; |
329 | 5 | return nullptr; |
330 | 5 | } |
331 | 986 | } |
332 | 566 | return cp.release(); |
333 | 582 | } |
334 | | |
335 | | OGRMultiCurve *GeometryReader::readMultiCurve() |
336 | 678 | { |
337 | 678 | auto parts = m_geometry->parts(); |
338 | 678 | if (parts == nullptr) |
339 | 2 | return CPLErrorInvalidPointer("parts data"); |
340 | 676 | auto mc = std::make_unique<OGRMultiCurve>(); |
341 | 1.64k | for (uoffset_t i = 0; i < parts->size(); i++) |
342 | 977 | { |
343 | 977 | auto g = std::unique_ptr<OGRGeometry>(readPart(parts->Get(i))); |
344 | 977 | if (dynamic_cast<OGRCurve *>(g.get()) == nullptr) |
345 | 12 | return nullptr; |
346 | 965 | mc->addGeometryDirectly(g.release()); |
347 | 965 | } |
348 | 664 | return mc.release(); |
349 | 676 | } |
350 | | |
351 | | OGRMultiSurface *GeometryReader::readMultiSurface() |
352 | 48 | { |
353 | 48 | auto parts = m_geometry->parts(); |
354 | 48 | if (parts == nullptr) |
355 | 3 | return CPLErrorInvalidPointer("parts data"); |
356 | 45 | auto ms = std::make_unique<OGRMultiSurface>(); |
357 | 100 | for (uoffset_t i = 0; i < parts->size(); i++) |
358 | 65 | { |
359 | 65 | auto g = std::unique_ptr<OGRGeometry>(readPart(parts->Get(i))); |
360 | 65 | if (dynamic_cast<OGRSurface *>(g.get()) == nullptr) |
361 | 8 | return nullptr; |
362 | 57 | auto poSubGeom = g.release(); |
363 | 57 | if (ms->addGeometryDirectly(poSubGeom) != OGRERR_NONE) |
364 | 2 | { |
365 | 2 | delete poSubGeom; |
366 | 2 | return nullptr; |
367 | 2 | } |
368 | 57 | } |
369 | 35 | return ms.release(); |
370 | 45 | } |
371 | | |
372 | | OGRPolyhedralSurface *GeometryReader::readPolyhedralSurface() |
373 | 37 | { |
374 | 37 | auto parts = m_geometry->parts(); |
375 | 37 | if (parts == nullptr) |
376 | 1 | return CPLErrorInvalidPointer("parts data"); |
377 | 36 | auto ps = std::make_unique<OGRPolyhedralSurface>(); |
378 | 38 | for (uoffset_t i = 0; i < parts->size(); i++) |
379 | 15 | { |
380 | 15 | auto g = std::unique_ptr<OGRGeometry>(readPart(parts->Get(i))); |
381 | 15 | if (g == nullptr) |
382 | 3 | return nullptr; |
383 | 12 | auto poSubGeom = g.release(); |
384 | 12 | if (ps->addGeometryDirectly(poSubGeom) != OGRERR_NONE) |
385 | 10 | { |
386 | 10 | delete poSubGeom; |
387 | 10 | return nullptr; |
388 | 10 | } |
389 | 12 | } |
390 | 23 | return ps.release(); |
391 | 36 | } |
392 | | |
393 | | OGRTriangulatedSurface *GeometryReader::readTIN() |
394 | 161 | { |
395 | 161 | const auto ends = m_geometry->ends(); |
396 | 161 | auto ts = std::make_unique<OGRTriangulatedSurface>(); |
397 | 161 | if (ends == nullptr || ends->size() < 2) |
398 | 40 | { |
399 | 40 | m_length = m_length / 2; |
400 | 40 | if (m_length != 4) |
401 | 1 | return CPLErrorInvalidLength("TIN"); |
402 | 39 | const auto lr = readSimpleCurve<OGRLinearRing>(); |
403 | 39 | if (lr == nullptr) |
404 | 0 | return nullptr; |
405 | 39 | auto t = new OGRTriangle(); |
406 | 39 | t->addRingDirectly(lr); |
407 | 39 | ts->addGeometryDirectly(t); |
408 | 39 | } |
409 | 121 | else |
410 | 121 | { |
411 | 333 | for (uint32_t i = 0; i < ends->size(); i++) |
412 | 241 | { |
413 | 241 | const auto e = ends->Get(i); |
414 | 241 | if (e < m_offset) |
415 | 1 | return CPLErrorInvalidLength("TIN"); |
416 | 240 | m_length = e - m_offset; |
417 | 240 | if (m_length != 4) |
418 | 28 | return CPLErrorInvalidLength("TIN"); |
419 | 212 | const auto lr = readSimpleCurve<OGRLinearRing>(); |
420 | 212 | m_offset = e; |
421 | 212 | if (lr == nullptr) |
422 | 103 | continue; |
423 | 109 | auto t = new OGRTriangle(); |
424 | 109 | t->addRingDirectly(lr); |
425 | 109 | ts->addGeometryDirectly(t); |
426 | 109 | } |
427 | 92 | if (ts->IsEmpty()) |
428 | 1 | return nullptr; |
429 | 92 | } |
430 | 130 | return ts.release(); |
431 | 161 | } |
432 | | |
433 | | OGRTriangle *GeometryReader::readTriangle() |
434 | 12 | { |
435 | 12 | m_length = m_length / 2; |
436 | 12 | if (m_length != 4) |
437 | 1 | return CPLErrorInvalidLength("readTriangle"); |
438 | 11 | auto lr = readSimpleCurve<OGRLinearRing>(); |
439 | 11 | if (lr == nullptr) |
440 | 1 | return nullptr; |
441 | 10 | auto t = new OGRTriangle(); |
442 | 10 | t->addRingDirectly(lr); |
443 | 10 | return t; |
444 | 11 | } |
445 | | |
446 | | OGRGeometry *GeometryReader::read() |
447 | 6.53k | { |
448 | | // nested types |
449 | 6.53k | switch (m_geometryType) |
450 | 6.53k | { |
451 | 75 | case GeometryType::GeometryCollection: |
452 | 75 | return readGeometryCollection(); |
453 | 654 | case GeometryType::MultiPolygon: |
454 | 654 | return readMultiPolygon(); |
455 | 34 | case GeometryType::CompoundCurve: |
456 | 34 | return readCompoundCurve(); |
457 | 583 | case GeometryType::CurvePolygon: |
458 | 583 | return readCurvePolygon(); |
459 | 678 | case GeometryType::MultiCurve: |
460 | 678 | return readMultiCurve(); |
461 | 48 | case GeometryType::MultiSurface: |
462 | 48 | return readMultiSurface(); |
463 | 37 | case GeometryType::PolyhedralSurface: |
464 | 37 | return readPolyhedralSurface(); |
465 | 4.42k | default: |
466 | 4.42k | break; |
467 | 6.53k | } |
468 | | |
469 | | // if not nested must have geometry data |
470 | 4.42k | const auto pXy = m_geometry->xy(); |
471 | 4.42k | if (pXy == nullptr) |
472 | 21 | return CPLErrorInvalidPointer("XY data"); |
473 | 4.40k | if (m_hasZ && m_geometry->z() == nullptr) |
474 | 3 | return CPLErrorInvalidPointer("Z data"); |
475 | 4.39k | if (m_hasM && m_geometry->m() == nullptr) |
476 | 3 | return CPLErrorInvalidPointer("M data"); |
477 | 4.39k | const auto xySize = pXy->size(); |
478 | 4.39k | if (xySize >= (feature_max_buffer_size / sizeof(OGRRawPoint))) |
479 | 0 | return CPLErrorInvalidLength("XY data"); |
480 | 4.39k | m_length = xySize; |
481 | 4.39k | m_xylength = m_length; |
482 | 4.39k | m_xy = pXy->data(); |
483 | | |
484 | 4.39k | switch (m_geometryType) |
485 | 4.39k | { |
486 | 135 | case GeometryType::Point: |
487 | 135 | return readPoint(); |
488 | 233 | case GeometryType::MultiPoint: |
489 | 233 | return readMultiPoint(); |
490 | 68 | case GeometryType::LineString: |
491 | 68 | return readSimpleCurve<OGRLineString>(true); |
492 | 72 | case GeometryType::MultiLineString: |
493 | 72 | return readMultiLineString(); |
494 | 1.62k | case GeometryType::Polygon: |
495 | 1.62k | return readPolygon(); |
496 | 2.07k | case GeometryType::CircularString: |
497 | 2.07k | return readSimpleCurve<OGRCircularString>(true); |
498 | 12 | case GeometryType::Triangle: |
499 | 12 | return readTriangle(); |
500 | 161 | case GeometryType::TIN: |
501 | 161 | return readTIN(); |
502 | 16 | default: |
503 | 16 | CPLError(CE_Failure, CPLE_AppDefined, |
504 | 16 | "GeometryReader::read: Unknown type %d", |
505 | 16 | (int)m_geometryType); |
506 | 4.39k | } |
507 | 16 | return nullptr; |
508 | 4.39k | } |