Line data Source code
1 : /****************************************************************************** 2 : * 3 : * Project: LV BAG Translator 4 : * Purpose: Implements OGRLVBAGDriver. 5 : * Author: Laixer B.V., info at laixer dot com 6 : * 7 : ****************************************************************************** 8 : * Copyright (c) 2020, Laixer B.V. <info at laixer dot com> 9 : * 10 : * SPDX-License-Identifier: MIT 11 : ****************************************************************************/ 12 : 13 : #include "ogr_lvbag.h" 14 : #include "ogrsf_frmts.h" 15 : 16 : /************************************************************************/ 17 : /* Identify() */ 18 : /************************************************************************/ 19 : 20 56987 : static int OGRLVBAGDriverIdentify(GDALOpenInfo *poOpenInfo) 21 : { 22 56987 : if (!poOpenInfo->bStatOK) 23 : { 24 49254 : return FALSE; 25 : } 26 7733 : if (poOpenInfo->bIsDirectory) 27 : { 28 1562 : return -1; // Check later 29 : } 30 6171 : if (poOpenInfo->fpL == nullptr || poOpenInfo->nHeaderBytes == 0) 31 : { 32 157 : return FALSE; 33 : } 34 : 35 6014 : auto pszPtr = reinterpret_cast<const char *>(poOpenInfo->pabyHeader); 36 6014 : if (pszPtr[0] != '<') 37 : { 38 4788 : return FALSE; 39 : } 40 : 41 1226 : if (poOpenInfo->IsSingleAllowedDriver("LVBAG")) 42 : { 43 2 : return TRUE; 44 : } 45 : 46 : // Can't handle mutations just yet 47 1224 : if (strstr(pszPtr, 48 : "http://www.kadaster.nl/schemas/mutatielevering-generiek/1.0") != 49 : nullptr) 50 : { 51 0 : return FALSE; 52 : } 53 : 54 1224 : if (strstr(pszPtr, 55 : "http://www.kadaster.nl/schemas/standlevering-generiek/1.0") == 56 : nullptr) 57 : { 58 1172 : return FALSE; 59 : } 60 : 61 : // Pin the driver to XSD version 'v20200601' 62 52 : if (strstr(pszPtr, "http://www.kadaster.nl/schemas/lvbag/" 63 : "extract-deelbestand-lvc/v20200601") == nullptr) 64 : { 65 1 : return FALSE; 66 : } 67 : 68 51 : return TRUE; 69 : } 70 : 71 : /************************************************************************/ 72 : /* Open() */ 73 : /************************************************************************/ 74 : 75 743 : GDALDataset *OGRLVBAGDriverOpen(GDALOpenInfo *poOpenInfo) 76 : { 77 743 : if (!OGRLVBAGDriverIdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update) 78 : { 79 87 : return nullptr; 80 : } 81 : 82 656 : const char *pszFilename = poOpenInfo->pszFilename; 83 1312 : auto poDS = std::unique_ptr<OGRLVBAGDataSource>{new OGRLVBAGDataSource{}}; 84 656 : poDS->SetDescription(pszFilename); 85 : 86 656 : if (!poOpenInfo->bIsDirectory && poOpenInfo->fpL != nullptr) 87 : { 88 24 : if (!poDS->Open(pszFilename, poOpenInfo->papszOpenOptions)) 89 : { 90 0 : poDS.reset(); 91 : } 92 : } 93 632 : else if (poOpenInfo->bIsDirectory && poOpenInfo->fpL == nullptr) 94 : { 95 632 : int nProbedFileCount = 0; 96 632 : bool bFound = false; 97 632 : char **papszNames = VSIReadDir(pszFilename); 98 24937 : for (int i = 0; papszNames != nullptr && papszNames[i] != nullptr; ++i) 99 : { 100 24305 : if (!EQUAL(CPLGetExtensionSafe(papszNames[i]).c_str(), "xml")) 101 24300 : continue; 102 : 103 : const CPLString oSubFilename = 104 27 : CPLFormFilenameSafe(pszFilename, papszNames[i], nullptr); 105 : 106 27 : if (EQUAL(papszNames[i], ".") || EQUAL(papszNames[i], "..")) 107 : { 108 0 : continue; 109 : } 110 : 111 : // Give up on /vsi filesystems if after 10 files we haven't found 112 : // a single BAG file 113 27 : if (nProbedFileCount == 10 && !bFound && 114 1 : STARTS_WITH(pszFilename, "/vsi")) 115 : { 116 0 : const bool bCheckAllFiles = CPLTestBool( 117 : CPLGetConfigOption("OGR_LVBAG_CHECK_ALL_FILES", "NO")); 118 0 : if (!bCheckAllFiles) 119 : { 120 0 : break; 121 : } 122 : } 123 : 124 27 : nProbedFileCount++; 125 27 : GDALOpenInfo oOpenInfo{oSubFilename, GA_ReadOnly}; 126 27 : if (!OGRLVBAGDriverIdentify(&oOpenInfo)) 127 : { 128 22 : continue; 129 : } 130 : 131 5 : if (poDS->Open(oSubFilename, poOpenInfo->papszOpenOptions)) 132 : { 133 5 : bFound = true; 134 : } 135 : } 136 : 137 632 : CSLDestroy(papszNames); 138 632 : if (!poDS->GetLayerCount()) 139 : { 140 630 : poDS.reset(); 141 630 : return nullptr; 142 2 : } 143 : } 144 : else 145 : { 146 0 : poDS.reset(); 147 0 : return nullptr; 148 : } 149 : 150 26 : return poDS.release(); 151 : } 152 : 153 : /************************************************************************/ 154 : /* RegisterOGRLVBAG() */ 155 : /************************************************************************/ 156 : 157 1935 : void RegisterOGRLVBAG() 158 : { 159 1935 : if (GDALGetDriverByName("LVBAG")) 160 : { 161 282 : return; 162 : } 163 : 164 3306 : auto poDriver = std::make_unique<GDALDriver>(); 165 : 166 1653 : poDriver->SetDescription("LVBAG"); 167 1653 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES"); 168 1653 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Kadaster LV BAG Extract 2.0"); 169 1653 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "xml"); 170 1653 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/lvbag.html"); 171 1653 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); 172 1653 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE"); 173 : 174 1653 : poDriver->SetMetadataItem( 175 : GDAL_DMD_OPENOPTIONLIST, 176 : "<OpenOptionList>" 177 : " <Option name='AUTOCORRECT_INVALID_DATA' type='boolean' " 178 : "description='whether driver should try to fix invalid data' " 179 : "default='NO'/>" 180 : " <Option name='LEGACY_ID' type='boolean' description='whether driver " 181 : "should use the BAG 1.0 identifiers' default='NO'/>" 182 1653 : "</OpenOptionList>"); 183 : 184 1653 : poDriver->pfnOpen = OGRLVBAGDriverOpen; 185 1653 : poDriver->pfnIdentify = OGRLVBAGDriverIdentify; 186 : 187 1653 : GetGDALDriverManager()->RegisterDriver(poDriver.release()); 188 : }