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