Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Feather Translator
4 : * Purpose: Implements OGRFeatherDriver.
5 : * Author: Even Rouault, <even.rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2022, Planet Labs
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_json.h"
14 : #include "cpl_time.h"
15 : #include "gdal_pam.h"
16 : #include "ogrsf_frmts.h"
17 : #include "ogr_p.h"
18 :
19 : #include <cinttypes>
20 : #include <limits>
21 : #include <map>
22 : #include <set>
23 : #include <utility>
24 :
25 : #include "ogr_feather.h"
26 :
27 : #include "../arrow_common/ograrrowlayer.hpp"
28 : #include "../arrow_common/ograrrowdataset.hpp"
29 :
30 : /************************************************************************/
31 : /* OGRFeatherLayer() */
32 : /************************************************************************/
33 :
34 535 : OGRFeatherLayer::OGRFeatherLayer(
35 : OGRFeatherDataset *poDS, const char *pszLayerName,
36 535 : std::shared_ptr<arrow::ipc::RecordBatchFileReader> &poRecordBatchFileReader)
37 : : OGRArrowLayer(poDS, pszLayerName), m_poDS(poDS),
38 535 : m_poRecordBatchFileReader(poRecordBatchFileReader)
39 : {
40 535 : EstablishFeatureDefn();
41 535 : CPLAssert(static_cast<int>(m_aeGeomEncoding.size()) ==
42 : m_poFeatureDefn->GetGeomFieldCount());
43 535 : }
44 :
45 : /************************************************************************/
46 : /* OGRFeatherLayer() */
47 : /************************************************************************/
48 :
49 10 : OGRFeatherLayer::OGRFeatherLayer(
50 : OGRFeatherDataset *poDS, const char *pszLayerName,
51 : std::shared_ptr<arrow::io::RandomAccessFile> poFile, bool bSeekable,
52 : const arrow::ipc::IpcReadOptions &oOptions,
53 : std::shared_ptr<arrow::ipc::RecordBatchStreamReader>
54 10 : &poRecordBatchStreamReader)
55 : : OGRArrowLayer(poDS, pszLayerName), m_poDS(poDS),
56 10 : m_poFile(std::move(poFile)), m_bSeekable(bSeekable), m_oOptions(oOptions),
57 10 : m_poRecordBatchReader(poRecordBatchStreamReader)
58 : {
59 10 : EstablishFeatureDefn();
60 10 : CPLAssert(static_cast<int>(m_aeGeomEncoding.size()) ==
61 : m_poFeatureDefn->GetGeomFieldCount());
62 10 : }
63 :
64 : /************************************************************************/
65 : /* GetDataset() */
66 : /************************************************************************/
67 :
68 5 : GDALDataset *OGRFeatherLayer::GetDataset()
69 : {
70 5 : return m_poDS;
71 : }
72 :
73 : /************************************************************************/
74 : /* LoadGeoMetadata() */
75 : /************************************************************************/
76 :
77 545 : void OGRFeatherLayer::LoadGeoMetadata(
78 : const arrow::KeyValueMetadata *kv_metadata, const std::string &key)
79 : {
80 545 : if (kv_metadata && kv_metadata->Contains(key))
81 : {
82 1060 : auto geo = kv_metadata->Get(key);
83 530 : if (geo.ok())
84 : {
85 1060 : CPLJSONDocument oDoc;
86 530 : if (oDoc.LoadMemory(*geo))
87 : {
88 1060 : auto oRoot = oDoc.GetRoot();
89 1590 : const auto osVersion = oRoot.GetString("schema_version");
90 530 : if (key != GDAL_GEO_FOOTER_KEY && osVersion != "0.1.0")
91 : {
92 397 : CPLDebug("FEATHER",
93 : "schema_version = %s not explicitly handled by "
94 : "the driver",
95 : osVersion.c_str());
96 : }
97 1590 : auto oColumns = oRoot.GetObj("columns");
98 530 : if (oColumns.IsValid())
99 : {
100 1060 : for (const auto &oColumn : oColumns.GetChildren())
101 : {
102 530 : m_oMapGeometryColumns[oColumn.GetName()] = oColumn;
103 : }
104 : }
105 : }
106 : else
107 : {
108 0 : CPLError(CE_Warning, CPLE_AppDefined,
109 : "Cannot parse 'geo' metadata");
110 : }
111 : }
112 : }
113 545 : }
114 :
115 : /************************************************************************/
116 : /* EstablishFeatureDefn() */
117 : /************************************************************************/
118 :
119 545 : void OGRFeatherLayer::EstablishFeatureDefn()
120 : {
121 1090 : m_poSchema = m_poRecordBatchFileReader ? m_poRecordBatchFileReader->schema()
122 1090 : : m_poRecordBatchReader->schema();
123 545 : const auto &kv_metadata = m_poSchema->metadata();
124 :
125 : #ifdef DEBUG
126 545 : if (kv_metadata)
127 : {
128 1073 : for (const auto &keyValue : kv_metadata->sorted_pairs())
129 : {
130 538 : CPLDebug("FEATHER", "%s = %s", keyValue.first.c_str(),
131 : keyValue.second.c_str());
132 : }
133 : }
134 : #endif
135 :
136 : auto poFooterMetadata = m_poRecordBatchFileReader
137 535 : ? m_poRecordBatchFileReader->metadata()
138 1625 : : nullptr;
139 669 : if (poFooterMetadata && poFooterMetadata->Contains(GDAL_GEO_FOOTER_KEY) &&
140 124 : CPLTestBool(CPLGetConfigOption("OGR_ARROW_READ_GDAL_FOOTER", "YES")))
141 : {
142 124 : LoadGeoMetadata(poFooterMetadata.get(), GDAL_GEO_FOOTER_KEY);
143 : }
144 : else
145 : {
146 421 : LoadGeoMetadata(kv_metadata.get(), "geo");
147 : }
148 : const auto oMapFieldNameToGDALSchemaFieldDefn =
149 1090 : LoadGDALSchema(kv_metadata.get());
150 :
151 545 : const auto &fields = m_poSchema->fields();
152 4082 : for (int i = 0; i < m_poSchema->num_fields(); ++i)
153 : {
154 3537 : const auto &field = fields[i];
155 3537 : const auto &fieldName = field->name();
156 :
157 3537 : const auto &field_kv_metadata = field->metadata();
158 3537 : std::string osExtensionName;
159 3537 : if (field_kv_metadata)
160 : {
161 : auto extension_name =
162 1294 : field_kv_metadata->Get(ARROW_EXTENSION_NAME_KEY);
163 647 : if (extension_name.ok())
164 : {
165 518 : osExtensionName = *extension_name;
166 : }
167 : #ifdef DEBUG
168 647 : CPLDebug("FEATHER", "Metadata field %s:", fieldName.c_str());
169 1551 : for (const auto &keyValue : field_kv_metadata->sorted_pairs())
170 : {
171 904 : CPLDebug("FEATHER", " %s = %s", keyValue.first.c_str(),
172 : keyValue.second.c_str());
173 : }
174 : #endif
175 : }
176 :
177 3537 : if (!m_osFIDColumn.empty() && fieldName == m_osFIDColumn)
178 : {
179 6 : m_iFIDArrowColumn = i;
180 6 : continue;
181 : }
182 :
183 3531 : bool bRegularField = true;
184 3531 : auto oIter = m_oMapGeometryColumns.find(fieldName);
185 3531 : if (oIter != m_oMapGeometryColumns.end() || !osExtensionName.empty())
186 : {
187 1066 : CPLJSONObject oJSONDef;
188 533 : if (oIter != m_oMapGeometryColumns.end())
189 530 : oJSONDef = oIter->second;
190 1599 : auto osEncoding = oJSONDef.GetString("encoding");
191 533 : if (osEncoding.empty() && !osExtensionName.empty())
192 3 : osEncoding = std::move(osExtensionName);
193 :
194 533 : OGRwkbGeometryType eGeomType = wkbUnknown;
195 533 : auto eGeomEncoding = OGRArrowGeomEncoding::WKB;
196 533 : if (IsValidGeometryEncoding(field, osEncoding,
197 1066 : oIter != m_oMapGeometryColumns.end(),
198 : eGeomType, eGeomEncoding))
199 : {
200 531 : bRegularField = false;
201 1062 : OGRGeomFieldDefn oField(fieldName.c_str(), wkbUnknown);
202 :
203 1593 : const auto osWKT = oJSONDef.GetString("crs");
204 531 : if (osWKT.empty())
205 : {
206 : #if 0
207 : CPLError(CE_Warning, CPLE_AppDefined,
208 : "Missing required 'crs' field for geometry column %s",
209 : fieldName.c_str());
210 : #endif
211 : }
212 : else
213 : {
214 136 : OGRSpatialReference *poSRS = new OGRSpatialReference();
215 136 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
216 :
217 136 : if (poSRS->importFromWkt(osWKT.c_str()) == OGRERR_NONE)
218 : {
219 136 : const double dfCoordEpoch = oJSONDef.GetDouble("epoch");
220 136 : if (dfCoordEpoch > 0)
221 2 : poSRS->SetCoordinateEpoch(dfCoordEpoch);
222 :
223 136 : oField.SetSpatialRef(poSRS);
224 : }
225 136 : poSRS->Release();
226 : }
227 :
228 : // m_aeGeomEncoding be filled before calling
229 : // ComputeGeometryColumnType()
230 531 : m_aeGeomEncoding.push_back(eGeomEncoding);
231 531 : if (eGeomType == wkbUnknown)
232 : {
233 705 : auto osType = oJSONDef.GetString("geometry_type");
234 235 : if (osType.empty())
235 235 : osType = oJSONDef.GetString("gdal:geometry_type");
236 470 : if (m_bSeekable && osType.empty() &&
237 235 : CPLTestBool(CPLGetConfigOption(
238 : "OGR_ARROW_COMPUTE_GEOMETRY_TYPE", "YES")))
239 : {
240 235 : eGeomType = ComputeGeometryColumnType(
241 235 : m_poFeatureDefn->GetGeomFieldCount(), i);
242 235 : if (m_poRecordBatchReader)
243 0 : ResetRecordBatchReader();
244 : }
245 : else
246 0 : eGeomType = GetGeometryTypeFromString(osType);
247 : }
248 :
249 531 : oField.SetType(eGeomType);
250 531 : oField.SetNullable(field->nullable());
251 531 : m_poFeatureDefn->AddGeomFieldDefn(&oField);
252 531 : m_anMapGeomFieldIndexToArrowColumn.push_back(i);
253 : }
254 : }
255 :
256 3531 : if (bRegularField)
257 : {
258 3000 : CreateFieldFromSchema(field, {i},
259 : oMapFieldNameToGDALSchemaFieldDefn);
260 : }
261 : }
262 :
263 545 : CPLAssert(static_cast<int>(m_anMapFieldIndexToArrowColumn.size()) ==
264 : m_poFeatureDefn->GetFieldCount());
265 545 : CPLAssert(static_cast<int>(m_anMapGeomFieldIndexToArrowColumn.size()) ==
266 : m_poFeatureDefn->GetGeomFieldCount());
267 545 : }
268 :
269 : /************************************************************************/
270 : /* ResetRecordBatchReader() */
271 : /************************************************************************/
272 :
273 12 : bool OGRFeatherLayer::ResetRecordBatchReader()
274 : {
275 12 : const auto nPos = *(m_poFile->Tell());
276 12 : CPL_IGNORE_RET_VAL(m_poFile->Seek(0));
277 : auto result =
278 24 : arrow::ipc::RecordBatchStreamReader::Open(m_poFile, m_oOptions);
279 12 : if (!result.ok())
280 : {
281 0 : CPLError(CE_Failure, CPLE_AppDefined,
282 : "RecordBatchStreamReader::Open() failed with %s",
283 0 : result.status().message().c_str());
284 0 : CPL_IGNORE_RET_VAL(m_poFile->Seek(nPos));
285 0 : return false;
286 : }
287 : else
288 : {
289 12 : m_poRecordBatchReader = *result;
290 12 : return true;
291 : }
292 : }
293 :
294 : /************************************************************************/
295 : /* ComputeGeometryColumnType() */
296 : /************************************************************************/
297 :
298 235 : OGRwkbGeometryType OGRFeatherLayer::ComputeGeometryColumnType(int iGeomCol,
299 : int iCol) const
300 : {
301 : // Compute type of geometry column by iterating over each geometry, and
302 : // looking at the WKB geometry type in the first 5 bytes of each geometry.
303 :
304 235 : OGRwkbGeometryType eGeomType = wkbNone;
305 :
306 235 : if (m_poRecordBatchReader != nullptr)
307 : {
308 0 : std::shared_ptr<arrow::RecordBatch> poBatch;
309 : while (true)
310 : {
311 0 : auto status = m_poRecordBatchReader->ReadNext(&poBatch);
312 0 : if (!status.ok())
313 : {
314 0 : CPLError(CE_Failure, CPLE_AppDefined, "ReadNext() failed: %s",
315 0 : status.message().c_str());
316 0 : break;
317 : }
318 0 : else if (!poBatch)
319 0 : break;
320 0 : eGeomType = ComputeGeometryColumnTypeProcessBatch(poBatch, iGeomCol,
321 : iCol, eGeomType);
322 0 : if (eGeomType == wkbUnknown)
323 0 : break;
324 0 : }
325 : }
326 : else
327 : {
328 470 : for (int iBatch = 0;
329 470 : iBatch < m_poRecordBatchFileReader->num_record_batches(); ++iBatch)
330 : {
331 235 : auto result = m_poRecordBatchFileReader->ReadRecordBatch(iBatch);
332 235 : if (!result.ok())
333 : {
334 0 : CPLError(CE_Failure, CPLE_AppDefined,
335 : "ReadRecordBatch() failed: %s",
336 0 : result.status().message().c_str());
337 0 : break;
338 : }
339 235 : eGeomType = ComputeGeometryColumnTypeProcessBatch(*result, iGeomCol,
340 : iCol, eGeomType);
341 235 : if (eGeomType == wkbUnknown)
342 0 : break;
343 : }
344 : }
345 :
346 235 : return eGeomType == wkbNone ? wkbUnknown : eGeomType;
347 : }
348 :
349 : /************************************************************************/
350 : /* BuildDomain() */
351 : /************************************************************************/
352 :
353 : std::unique_ptr<OGRFieldDomain>
354 19 : OGRFeatherLayer::BuildDomain(const std::string &osDomainName,
355 : int iFieldIndex) const
356 : {
357 19 : const int iArrowCol = m_anMapFieldIndexToArrowColumn[iFieldIndex][0];
358 19 : CPLAssert(m_poSchema->fields()[iArrowCol]->type()->id() ==
359 : arrow::Type::DICTIONARY);
360 :
361 19 : if (m_poRecordBatchReader)
362 : {
363 6 : if (m_poBatch)
364 : {
365 6 : return BuildDomainFromBatch(osDomainName, m_poBatch, iArrowCol);
366 : }
367 : }
368 13 : else if (m_poRecordBatchFileReader)
369 : {
370 13 : auto result = m_poRecordBatchFileReader->ReadRecordBatch(0);
371 13 : if (!result.ok())
372 : {
373 0 : CPLError(CE_Failure, CPLE_AppDefined,
374 : "ReadRecordBatch() failed: %s",
375 0 : result.status().message().c_str());
376 : }
377 13 : auto poBatch = *result;
378 13 : if (poBatch)
379 : {
380 13 : return BuildDomainFromBatch(osDomainName, poBatch, iArrowCol);
381 : }
382 : }
383 :
384 0 : return nullptr;
385 : }
386 :
387 : /************************************************************************/
388 : /* ResetReading() */
389 : /************************************************************************/
390 :
391 820 : void OGRFeatherLayer::ResetReading()
392 : {
393 820 : if (m_poRecordBatchReader != nullptr && m_iRecordBatch > 0)
394 : {
395 17 : if (m_iRecordBatch == 1 && m_poBatchIdx1)
396 : {
397 : // do nothing
398 : }
399 : else
400 : {
401 16 : m_bResetRecordBatchReaderAsked = true;
402 : }
403 : }
404 820 : OGRArrowLayer::ResetReading();
405 820 : }
406 :
407 : /************************************************************************/
408 : /* ReadNextBatch() */
409 : /************************************************************************/
410 :
411 1006 : bool OGRFeatherLayer::ReadNextBatch()
412 : {
413 1006 : if (m_poRecordBatchFileReader == nullptr)
414 : {
415 119 : return ReadNextBatchStream();
416 : }
417 : else
418 : {
419 887 : return ReadNextBatchFile();
420 : }
421 : }
422 :
423 : /************************************************************************/
424 : /* ReadNextBatchFile() */
425 : /************************************************************************/
426 :
427 887 : bool OGRFeatherLayer::ReadNextBatchFile()
428 : {
429 : while (true)
430 : {
431 887 : ++m_iRecordBatch;
432 887 : if (m_iRecordBatch == m_poRecordBatchFileReader->num_record_batches())
433 : {
434 449 : if (m_iRecordBatch == 1)
435 446 : m_iRecordBatch = 0;
436 : else
437 3 : m_poBatch.reset();
438 449 : return false;
439 : }
440 :
441 438 : m_nIdxInBatch = 0;
442 :
443 : auto result =
444 438 : m_poRecordBatchFileReader->ReadRecordBatch(m_iRecordBatch);
445 438 : if (!result.ok())
446 : {
447 0 : CPLError(CE_Failure, CPLE_AppDefined,
448 : "ReadRecordBatch() failed: %s",
449 0 : result.status().message().c_str());
450 0 : m_poBatch.reset();
451 0 : return false;
452 : }
453 438 : if ((*result)->num_rows() != 0)
454 : {
455 438 : SetBatch(*result);
456 438 : break;
457 : }
458 0 : }
459 :
460 438 : return true;
461 : }
462 :
463 : /************************************************************************/
464 : /* ReadNextBatchStream() */
465 : /************************************************************************/
466 :
467 154 : bool OGRFeatherLayer::ReadNextBatchStream()
468 : {
469 154 : m_nIdxInBatch = 0;
470 :
471 308 : std::shared_ptr<arrow::RecordBatch> poNextBatch;
472 0 : do
473 : {
474 154 : if (m_iRecordBatch == 0 && m_poBatchIdx0)
475 : {
476 1 : SetBatch(m_poBatchIdx0);
477 1 : m_iRecordBatch = 1;
478 101 : return true;
479 : }
480 :
481 153 : else if (m_iRecordBatch == 1 && m_poBatchIdx1)
482 : {
483 1 : SetBatch(m_poBatchIdx1);
484 1 : m_iRecordBatch = 2;
485 1 : return true;
486 : }
487 :
488 152 : else if (m_bSingleBatch)
489 : {
490 81 : CPLAssert(m_iRecordBatch == 0);
491 81 : CPLAssert(m_poBatch != nullptr);
492 81 : return false;
493 : }
494 :
495 71 : if (m_bResetRecordBatchReaderAsked)
496 : {
497 13 : if (!m_bSeekable)
498 : {
499 1 : CPLError(CE_Failure, CPLE_NotSupported,
500 : "Attempting to rewind non-seekable stream");
501 1 : return false;
502 : }
503 12 : if (!ResetRecordBatchReader())
504 0 : return false;
505 12 : m_bResetRecordBatchReaderAsked = false;
506 : }
507 :
508 70 : CPLAssert(m_poRecordBatchReader);
509 :
510 70 : ++m_iRecordBatch;
511 :
512 70 : poNextBatch.reset();
513 70 : auto status = m_poRecordBatchReader->ReadNext(&poNextBatch);
514 70 : if (!status.ok())
515 : {
516 0 : CPLError(CE_Failure, CPLE_AppDefined, "ReadNext() failed: %s",
517 0 : status.message().c_str());
518 0 : poNextBatch.reset();
519 : }
520 70 : if (poNextBatch == nullptr)
521 : {
522 17 : if (m_iRecordBatch == 1)
523 : {
524 3 : m_iRecordBatch = 0;
525 3 : m_bSingleBatch = true;
526 : }
527 : else
528 : {
529 14 : m_poBatch.reset();
530 14 : m_poBatchColumns.clear();
531 : }
532 17 : return false;
533 : }
534 53 : } while (poNextBatch->num_rows() == 0);
535 :
536 53 : SetBatch(poNextBatch);
537 :
538 53 : return true;
539 : }
540 :
541 : /************************************************************************/
542 : /* TryToCacheFirstTwoBatches() */
543 : /************************************************************************/
544 :
545 1 : void OGRFeatherLayer::TryToCacheFirstTwoBatches()
546 : {
547 2 : if (m_poRecordBatchReader != nullptr && m_iRecordBatch <= 0 &&
548 2 : !m_bSingleBatch && m_poBatchIdx0 == nullptr)
549 : {
550 1 : ResetReading();
551 1 : if (!m_poBatch)
552 : {
553 0 : CPL_IGNORE_RET_VAL(ReadNextBatchStream());
554 : }
555 1 : if (m_poBatch)
556 : {
557 2 : auto poBatchIdx0 = m_poBatch;
558 1 : if (ReadNextBatchStream())
559 : {
560 1 : CPLAssert(m_iRecordBatch == 1);
561 1 : m_poBatchIdx0 = poBatchIdx0;
562 1 : m_poBatchIdx1 = m_poBatch;
563 1 : SetBatch(poBatchIdx0);
564 1 : ResetReading();
565 : }
566 1 : ResetReading();
567 : }
568 : }
569 1 : }
570 :
571 : /************************************************************************/
572 : /* CanPostFilterArrowArray() */
573 : /************************************************************************/
574 :
575 20 : bool OGRFeatherLayer::CanPostFilterArrowArray(
576 : const struct ArrowSchema *schema) const
577 : {
578 20 : if (m_poRecordBatchReader)
579 10 : return false;
580 10 : return OGRArrowLayer::CanPostFilterArrowArray(schema);
581 : }
582 :
583 : /************************************************************************/
584 : /* InvalidateCachedBatches() */
585 : /************************************************************************/
586 :
587 109 : void OGRFeatherLayer::InvalidateCachedBatches()
588 : {
589 109 : if (m_poRecordBatchFileReader)
590 : {
591 63 : m_iRecordBatch = -1;
592 63 : ResetReading();
593 : }
594 109 : }
595 :
596 : /************************************************************************/
597 : /* GetFeatureCount() */
598 : /************************************************************************/
599 :
600 336 : GIntBig OGRFeatherLayer::GetFeatureCount(int bForce)
601 : {
602 628 : if (m_poRecordBatchFileReader != nullptr && m_poAttrQuery == nullptr &&
603 292 : m_poFilterGeom == nullptr)
604 : {
605 288 : auto result = m_poRecordBatchFileReader->CountRows();
606 288 : if (result.ok())
607 288 : return *result;
608 : }
609 48 : else if (m_poRecordBatchReader != nullptr)
610 : {
611 36 : if (!m_bSeekable && !bForce)
612 : {
613 1 : if (m_poAttrQuery == nullptr && m_poFilterGeom == nullptr)
614 : {
615 1 : TryToCacheFirstTwoBatches();
616 : }
617 :
618 1 : if (!m_bSingleBatch)
619 : {
620 1 : CPLError(
621 : CE_Failure, CPLE_AppDefined,
622 : "GetFeatureCount() cannot be run in non-forced mode on "
623 : "a non-seekable file made of several batches");
624 1 : return -1;
625 : }
626 : }
627 :
628 35 : if (m_poAttrQuery == nullptr && m_poFilterGeom == nullptr)
629 : {
630 23 : GIntBig nFeatures = 0;
631 23 : ResetReading();
632 23 : if (!m_poBatch)
633 3 : ReadNextBatchStream();
634 31 : while (m_poBatch)
635 : {
636 31 : nFeatures += m_poBatch->num_rows();
637 31 : if (!ReadNextBatchStream())
638 23 : break;
639 : }
640 23 : ResetReading();
641 23 : return nFeatures;
642 : }
643 : }
644 24 : return OGRLayer::GetFeatureCount(bForce);
645 : }
646 :
647 : /************************************************************************/
648 : /* CanRunNonForcedGetExtent() */
649 : /************************************************************************/
650 :
651 0 : bool OGRFeatherLayer::CanRunNonForcedGetExtent()
652 : {
653 0 : if (m_bSeekable)
654 0 : return true;
655 0 : TryToCacheFirstTwoBatches();
656 0 : if (!m_bSingleBatch)
657 : {
658 0 : CPLError(CE_Failure, CPLE_AppDefined,
659 : "GetExtent() cannot be run in non-forced mode on "
660 : "a non-seekable file made of several batches");
661 0 : return false;
662 : }
663 0 : return true;
664 : }
665 :
666 : /************************************************************************/
667 : /* TestCapability() */
668 : /************************************************************************/
669 :
670 292 : int OGRFeatherLayer::TestCapability(const char *pszCap)
671 : {
672 292 : if (EQUAL(pszCap, OLCFastFeatureCount))
673 : {
674 28 : return m_bSeekable && m_poAttrQuery == nullptr &&
675 28 : m_poFilterGeom == nullptr;
676 : }
677 :
678 274 : if (EQUAL(pszCap, OLCMeasuredGeometries))
679 16 : return true;
680 258 : if (EQUAL(pszCap, OLCZGeometries))
681 12 : return true;
682 :
683 246 : return OGRArrowLayer::TestCapability(pszCap);
684 : }
685 :
686 : /************************************************************************/
687 : /* GetMetadataItem() */
688 : /************************************************************************/
689 :
690 259 : const char *OGRFeatherLayer::GetMetadataItem(const char *pszName,
691 : const char *pszDomain)
692 : {
693 : // Mostly for unit test purposes
694 259 : if (pszDomain != nullptr && EQUAL(pszDomain, "_ARROW_"))
695 : {
696 9 : if (EQUAL(pszName, "FORMAT"))
697 : {
698 5 : return m_poRecordBatchFileReader ? "FILE" : "STREAM";
699 : }
700 4 : if (m_poRecordBatchFileReader != nullptr)
701 : {
702 4 : int iBatch = -1;
703 4 : if (EQUAL(pszName, "NUM_RECORD_BATCHES"))
704 : {
705 1 : return CPLSPrintf(
706 5 : "%d", m_poRecordBatchFileReader->num_record_batches());
707 : }
708 6 : else if (sscanf(pszName, "RECORD_BATCHES[%d]", &iBatch) == 1 &&
709 3 : strstr(pszName, ".NUM_ROWS"))
710 : {
711 : auto result =
712 6 : m_poRecordBatchFileReader->ReadRecordBatch(iBatch);
713 3 : if (!result.ok())
714 : {
715 0 : return nullptr;
716 : }
717 3 : return CPLSPrintf("%" PRId64, (*result)->num_rows());
718 : }
719 : }
720 0 : return nullptr;
721 : }
722 250 : else if (pszDomain != nullptr && EQUAL(pszDomain, "_ARROW_METADATA_"))
723 : {
724 : const auto kv_metadata =
725 5 : (m_poRecordBatchFileReader ? m_poRecordBatchFileReader->schema()
726 8 : : m_poRecordBatchReader->schema())
727 10 : ->metadata();
728 5 : if (kv_metadata && kv_metadata->Contains(pszName))
729 : {
730 5 : auto metadataItem = kv_metadata->Get(pszName);
731 5 : if (metadataItem.ok())
732 : {
733 5 : return CPLSPrintf("%s", metadataItem->c_str());
734 : }
735 : }
736 0 : return nullptr;
737 : }
738 476 : else if (m_poRecordBatchFileReader != nullptr && pszDomain != nullptr &&
739 231 : EQUAL(pszDomain, "_ARROW_FOOTER_METADATA_"))
740 : {
741 2 : const auto kv_metadata = m_poRecordBatchFileReader->metadata();
742 1 : if (kv_metadata && kv_metadata->Contains(pszName))
743 : {
744 1 : auto metadataItem = kv_metadata->Get(pszName);
745 1 : if (metadataItem.ok())
746 : {
747 1 : return CPLSPrintf("%s", metadataItem->c_str());
748 : }
749 : }
750 0 : return nullptr;
751 : }
752 244 : return OGRLayer::GetMetadataItem(pszName, pszDomain);
753 : }
754 :
755 : /************************************************************************/
756 : /* GetMetadata() */
757 : /************************************************************************/
758 :
759 34 : char **OGRFeatherLayer::GetMetadata(const char *pszDomain)
760 : {
761 : // Mostly for unit test purposes
762 34 : if (pszDomain != nullptr && EQUAL(pszDomain, "_ARROW_METADATA_"))
763 : {
764 5 : m_aosFeatherMetadata.Clear();
765 : const auto kv_metadata =
766 5 : (m_poRecordBatchFileReader ? m_poRecordBatchFileReader->schema()
767 8 : : m_poRecordBatchReader->schema())
768 10 : ->metadata();
769 5 : if (kv_metadata)
770 : {
771 11 : for (const auto &kv : kv_metadata->sorted_pairs())
772 : {
773 : m_aosFeatherMetadata.SetNameValue(kv.first.c_str(),
774 6 : kv.second.c_str());
775 : }
776 : }
777 5 : return m_aosFeatherMetadata.List();
778 : }
779 41 : if (m_poRecordBatchFileReader != nullptr && pszDomain != nullptr &&
780 12 : EQUAL(pszDomain, "_ARROW_FOOTER_METADATA_"))
781 : {
782 2 : m_aosFeatherMetadata.Clear();
783 4 : const auto kv_metadata = m_poRecordBatchFileReader->metadata();
784 2 : if (kv_metadata)
785 : {
786 3 : for (const auto &kv : kv_metadata->sorted_pairs())
787 : {
788 : m_aosFeatherMetadata.SetNameValue(kv.first.c_str(),
789 1 : kv.second.c_str());
790 : }
791 : }
792 2 : return m_aosFeatherMetadata.List();
793 : }
794 27 : return OGRLayer::GetMetadata(pszDomain);
795 : }
|