Line data Source code
1 : /****************************************************************************** 2 : * 3 : * Project: GDAL 4 : * Purpose: Class to abstract outputting to a vector layer 5 : * Author: Even Rouault <even dot rouault at spatialys.com> 6 : * 7 : ****************************************************************************** 8 : * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com> 9 : * 10 : * SPDX-License-Identifier: MIT 11 : ****************************************************************************/ 12 : 13 : #include "gdalalg_vector_output_abstract.h" 14 : 15 : #include "cpl_vsi.h" 16 : #include "ogrsf_frmts.h" 17 : 18 : //! @cond Doxygen_Suppress 19 : 20 : #ifndef _ 21 : #define _(x) (x) 22 : #endif 23 : 24 : /************************************************************************/ 25 : /* GDALVectorOutputAbstractAlgorithm::AddAllOutputArgs() */ 26 : /************************************************************************/ 27 : 28 36 : void GDALVectorOutputAbstractAlgorithm::AddAllOutputArgs() 29 : { 30 36 : AddOutputFormatArg(&m_outputFormat) 31 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, 32 108 : {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE}); 33 36 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR) 34 36 : .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT); 35 36 : AddCreationOptionsArg(&m_creationOptions); 36 36 : AddLayerCreationOptionsArg(&m_layerCreationOptions); 37 36 : AddOverwriteArg(&m_overwrite).SetMutualExclusionGroup("overwrite-update"); 38 36 : AddUpdateArg(&m_update).SetMutualExclusionGroup("overwrite-update"); 39 : AddArg("overwrite-layer", 0, 40 : _("Whether overwriting existing layer is allowed"), 41 72 : &m_overwriteLayer) 42 36 : .SetDefault(false) 43 : .AddValidationAction( 44 1 : [this] 45 : { 46 1 : GetArg(GDAL_ARG_NAME_UPDATE)->Set(true); 47 1 : return true; 48 36 : }); 49 : AddArg("append", 0, _("Whether appending to existing layer is allowed"), 50 72 : &m_appendLayer) 51 36 : .SetDefault(false) 52 : .AddValidationAction( 53 2 : [this] 54 : { 55 2 : GetArg(GDAL_ARG_NAME_UPDATE)->Set(true); 56 2 : return true; 57 36 : }); 58 : { 59 36 : auto &arg = AddLayerNameArg(&m_outputLayerName) 60 72 : .AddAlias("nln") 61 36 : .SetMinCharCount(0); 62 36 : if (!m_outputLayerName.empty()) 63 0 : arg.SetDefault(m_outputLayerName); 64 : } 65 36 : } 66 : 67 : /************************************************************************/ 68 : /* GDALVectorOutputAbstractAlgorithm::SetupOutputDataset() */ 69 : /************************************************************************/ 70 : 71 : GDALVectorOutputAbstractAlgorithm::SetupOutputDatasetRet 72 17 : GDALVectorOutputAbstractAlgorithm::SetupOutputDataset() 73 : { 74 17 : SetupOutputDatasetRet ret; 75 : 76 17 : GDALDataset *poDstDS = m_outputDataset.GetDatasetRef(); 77 17 : std::unique_ptr<GDALDataset> poRetDS; 78 17 : if (!poDstDS) 79 : { 80 13 : if (m_outputFormat.empty()) 81 : { 82 : const auto aosFormats = 83 : CPLStringList(GDALGetOutputDriversForDatasetName( 84 2 : m_outputDataset.GetName().c_str(), GDAL_OF_VECTOR, 85 : /* bSingleMatch = */ true, 86 2 : /* bWarn = */ true)); 87 2 : if (aosFormats.size() != 1) 88 : { 89 0 : ReportError(CE_Failure, CPLE_AppDefined, 90 : "Cannot guess driver for %s", 91 0 : m_outputDataset.GetName().c_str()); 92 0 : return ret; 93 : } 94 2 : m_outputFormat = aosFormats[0]; 95 : } 96 : 97 : auto poDriver = 98 13 : GetGDALDriverManager()->GetDriverByName(m_outputFormat.c_str()); 99 13 : if (!poDriver) 100 : { 101 : // shouldn't happen given checks done in GDALAlgorithm 102 0 : ReportError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s", 103 : m_outputFormat.c_str()); 104 0 : return ret; 105 : } 106 : 107 13 : poRetDS.reset(poDriver->Create( 108 13 : m_outputDataset.GetName().c_str(), 0, 0, 0, GDT_Unknown, 109 26 : CPLStringList(m_creationOptions).List())); 110 13 : if (!poRetDS) 111 0 : return ret; 112 : 113 13 : poDstDS = poRetDS.get(); 114 : } 115 : 116 17 : auto poDstDriver = poDstDS->GetDriver(); 117 17 : if (poDstDriver && EQUAL(poDstDriver->GetDescription(), "ESRI Shapefile") && 118 40 : EQUAL(CPLGetExtensionSafe(poDstDS->GetDescription()).c_str(), "shp") && 119 6 : poDstDS->GetLayerCount() <= 1) 120 : { 121 6 : m_outputLayerName = CPLGetBasenameSafe(poDstDS->GetDescription()); 122 : } 123 : 124 17 : auto poDstLayer = m_outputLayerName.empty() 125 17 : ? nullptr 126 16 : : poDstDS->GetLayerByName(m_outputLayerName.c_str()); 127 17 : if (poDstLayer) 128 : { 129 4 : if (m_overwriteLayer) 130 : { 131 1 : int iLayer = -1; 132 1 : const int nLayerCount = poDstDS->GetLayerCount(); 133 1 : for (iLayer = 0; iLayer < nLayerCount; iLayer++) 134 : { 135 1 : if (poDstDS->GetLayer(iLayer) == poDstLayer) 136 1 : break; 137 : } 138 : 139 1 : if (iLayer < nLayerCount) 140 : { 141 1 : if (poDstDS->DeleteLayer(iLayer) != OGRERR_NONE) 142 : { 143 0 : ReportError(CE_Failure, CPLE_AppDefined, 144 : "Cannot delete layer '%s'", 145 : m_outputLayerName.c_str()); 146 0 : return ret; 147 : } 148 : } 149 1 : poDstLayer = nullptr; 150 : } 151 3 : else if (!m_appendLayer) 152 : { 153 1 : ReportError(CE_Failure, CPLE_AppDefined, 154 : "Layer '%s' already exists. Specify the " 155 : "--overwrite-layer option to overwrite it, or --append " 156 : "to append to it.", 157 : m_outputLayerName.c_str()); 158 1 : return ret; 159 : } 160 : } 161 13 : else if (m_appendLayer || m_overwriteLayer) 162 : { 163 0 : ReportError(CE_Failure, CPLE_AppDefined, "Cannot find layer '%s'", 164 : m_outputLayerName.c_str()); 165 0 : return ret; 166 : } 167 : 168 16 : ret.newDS = std::move(poRetDS); 169 16 : ret.outDS = poDstDS; 170 16 : ret.layer = poDstLayer; 171 16 : return ret; 172 : } 173 : 174 : /************************************************************************/ 175 : /* GDALVectorOutputAbstractAlgorithm::SetDefaultOutputLayerNameIfNeeded */ 176 : /************************************************************************/ 177 : 178 16 : bool GDALVectorOutputAbstractAlgorithm::SetDefaultOutputLayerNameIfNeeded( 179 : GDALDataset *poOutDS) 180 : { 181 16 : if (m_outputLayerName.empty()) 182 : { 183 : VSIStatBufL sStat; 184 1 : auto poDriver = poOutDS->GetDriver(); 185 2 : if (VSIStatL(m_outputDataset.GetName().c_str(), &sStat) == 0 || 186 1 : (poDriver && EQUAL(poDriver->GetDescription(), "ESRI Shapefile"))) 187 : { 188 : m_outputLayerName = 189 0 : CPLGetBasenameSafe(m_outputDataset.GetName().c_str()); 190 : } 191 : } 192 16 : if (m_outputLayerName.empty()) 193 : { 194 1 : ReportError(CE_Failure, CPLE_AppDefined, 195 : "Argument 'layer' must be specified"); 196 1 : return false; 197 : } 198 15 : return true; 199 : } 200 : 201 : //! @endcond