Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OGR
4 : * Purpose: Implements OGRNASDataSource class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_conv.h"
15 : #include "cpl_string.h"
16 : #include "ogr_nas.h"
17 :
18 : static const char *const apszURNNames[] = {
19 : "DE_DHDN_3GK2_*", "EPSG:31466", "DE_DHDN_3GK3_*", "EPSG:31467",
20 : "ETRS89_UTM32", "EPSG:25832", "ETRS89_UTM33", "EPSG:25833",
21 : nullptr, nullptr};
22 :
23 : /************************************************************************/
24 : /* OGRNASDataSource() */
25 : /************************************************************************/
26 :
27 5 : OGRNASDataSource::OGRNASDataSource()
28 5 : : papoLayers(nullptr), nLayers(0), poReader(nullptr)
29 : {
30 5 : }
31 :
32 : /************************************************************************/
33 : /* ~OGRNASDataSource() */
34 : /************************************************************************/
35 :
36 10 : OGRNASDataSource::~OGRNASDataSource()
37 :
38 : {
39 10 : for (int i = 0; i < nLayers; i++)
40 5 : delete papoLayers[i];
41 :
42 5 : CPLFree(papoLayers);
43 :
44 5 : if (poReader)
45 5 : delete poReader;
46 10 : }
47 :
48 : /************************************************************************/
49 : /* Open() */
50 : /************************************************************************/
51 :
52 5 : int OGRNASDataSource::Open(const char *pszNewName)
53 :
54 : {
55 5 : poReader = CreateNASReader();
56 5 : if (poReader == nullptr)
57 : {
58 0 : CPLError(CE_Failure, CPLE_AppDefined,
59 : "File %s appears to be NAS but the NAS reader cannot\n"
60 : "be instantiated, likely because Xerces support was not\n"
61 : "configured in.",
62 : pszNewName);
63 0 : return FALSE;
64 : }
65 :
66 5 : poReader->SetSourceFile(pszNewName);
67 :
68 5 : bool bHaveSchema = false;
69 5 : bool bHaveTemplate = false;
70 :
71 : // Is some NAS Feature Schema (.gfs) TEMPLATE required?
72 5 : const char *pszNASTemplateName = CPLGetConfigOption("NAS_GFS_TEMPLATE", "");
73 5 : if (!EQUAL(pszNASTemplateName, ""))
74 : {
75 : // Load the TEMPLATE.
76 0 : if (!poReader->LoadClasses(pszNASTemplateName))
77 : {
78 0 : CPLError(CE_Failure, CPLE_AppDefined,
79 : "NAS schema %s could not be loaded\n", pszNASTemplateName);
80 0 : return FALSE;
81 : }
82 :
83 0 : bHaveTemplate = true;
84 :
85 0 : CPLDebug("NAS", "Schema loaded.");
86 : }
87 : else
88 : {
89 : /* --------------------------------------------------------------------
90 : */
91 : /* Can we find a NAS Feature Schema (.gfs) for the input file? */
92 : /* --------------------------------------------------------------------
93 : */
94 : const std::string osGFSFilename =
95 10 : CPLResetExtensionSafe(pszNewName, "gfs");
96 : VSIStatBufL sGFSStatBuf;
97 5 : if (VSIStatL(osGFSFilename.c_str(), &sGFSStatBuf) == 0)
98 : {
99 : VSIStatBufL sNASStatBuf;
100 0 : if (VSIStatL(pszNewName, &sNASStatBuf) == 0 &&
101 0 : sNASStatBuf.st_mtime > sGFSStatBuf.st_mtime)
102 : {
103 0 : CPLDebug("NAS",
104 : "Found %s but ignoring because it appears "
105 : "be older than the associated NAS file.",
106 : osGFSFilename.c_str());
107 : }
108 : else
109 : {
110 0 : bHaveSchema = poReader->LoadClasses(osGFSFilename.c_str());
111 : }
112 : }
113 :
114 5 : if (!bHaveSchema)
115 : {
116 5 : CPLError(CE_Failure, CPLE_AppDefined,
117 : "No schema information loaded");
118 : }
119 : }
120 :
121 : /* -------------------------------------------------------------------- */
122 : /* Force a first pass to establish the schema. The loaded schema */
123 : /* if any will be cleaned from any unavailable classes. */
124 : /* -------------------------------------------------------------------- */
125 5 : CPLErrorReset();
126 7 : if (!bHaveSchema && !poReader->PrescanForSchema(TRUE) &&
127 2 : CPLGetLastErrorType() == CE_Failure)
128 : {
129 : // Assume an error has been reported.
130 1 : return FALSE;
131 : }
132 :
133 : /* -------------------------------------------------------------------- */
134 : /* Save the schema file if possible. Do not make a fuss if we */
135 : /* cannot. It could be read-only directory or something. */
136 : /* -------------------------------------------------------------------- */
137 4 : if (!bHaveTemplate && !bHaveSchema && poReader->GetClassCount() > 0 &&
138 3 : !STARTS_WITH_CI(pszNewName, "/vsitar/") &&
139 3 : !STARTS_WITH_CI(pszNewName, "/vsizip/") &&
140 3 : !STARTS_WITH_CI(pszNewName, "/vsigzip/vsi") &&
141 3 : !STARTS_WITH_CI(pszNewName, "/vsigzip//vsi") &&
142 11 : !STARTS_WITH_CI(pszNewName, "/vsicurl/") &&
143 3 : !STARTS_WITH_CI(pszNewName, "/vsicurl_streaming/"))
144 : {
145 3 : VSILFILE *fp = nullptr;
146 :
147 : const std::string osGFSFilename =
148 6 : CPLResetExtensionSafe(pszNewName, "gfs");
149 : VSIStatBufL sGFSStatBuf;
150 6 : if (VSIStatL(osGFSFilename.c_str(), &sGFSStatBuf) != 0 &&
151 3 : (fp = VSIFOpenL(osGFSFilename.c_str(), "wt")) != nullptr)
152 : {
153 3 : VSIFCloseL(fp);
154 3 : poReader->SaveClasses(osGFSFilename.c_str());
155 : }
156 : else
157 : {
158 0 : CPLDebug("NAS",
159 : "Not saving %s. File already exists or can't be created.",
160 : osGFSFilename.c_str());
161 : }
162 : }
163 :
164 : /* -------------------------------------------------------------------- */
165 : /* Translate the GMLFeatureClasses into layers. */
166 : /* -------------------------------------------------------------------- */
167 8 : papoLayers = (OGRLayer **)CPLCalloc(sizeof(OGRNASLayer *),
168 4 : poReader->GetClassCount() + 1);
169 4 : nLayers = 0;
170 :
171 9 : while (nLayers < poReader->GetClassCount())
172 : {
173 5 : papoLayers[nLayers] = TranslateNASSchema(poReader->GetClass(nLayers));
174 5 : nLayers++;
175 : }
176 :
177 4 : return TRUE;
178 : }
179 :
180 : /************************************************************************/
181 : /* TranslateNASSchema() */
182 : /************************************************************************/
183 :
184 5 : OGRNASLayer *OGRNASDataSource::TranslateNASSchema(GMLFeatureClass *poClass)
185 :
186 : {
187 : /* -------------------------------------------------------------------- */
188 : /* Translate SRS. */
189 : /* -------------------------------------------------------------------- */
190 5 : const char *pszSRSName = poClass->GetSRSName();
191 5 : OGRSpatialReference *poSRS = nullptr;
192 5 : if (pszSRSName)
193 : {
194 0 : const char *pszHandle = strrchr(pszSRSName, ':');
195 0 : if (pszHandle != nullptr)
196 : {
197 0 : pszHandle += 1;
198 :
199 0 : poSRS = new OGRSpatialReference();
200 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
201 :
202 0 : for (int i = 0; apszURNNames[i * 2 + 0] != nullptr; i++)
203 : {
204 0 : const char *pszTarget = apszURNNames[i * 2 + 0];
205 0 : const int nTLen = static_cast<int>(strlen(pszTarget));
206 :
207 : // Are we just looking for a prefix match?
208 0 : if (pszTarget[nTLen - 1] == '*')
209 : {
210 0 : if (EQUALN(pszTarget, pszHandle, nTLen - 1))
211 0 : pszSRSName = apszURNNames[i * 2 + 1];
212 : }
213 : else
214 : {
215 0 : if (EQUAL(pszTarget, pszHandle))
216 0 : pszSRSName = apszURNNames[i * 2 + 1];
217 : }
218 : }
219 :
220 0 : if (poSRS->SetFromUserInput(
221 : pszSRSName,
222 : OGRSpatialReference::
223 0 : SET_FROM_USER_INPUT_LIMITATIONS_get()) != OGRERR_NONE)
224 : {
225 0 : CPLDebug("NAS", "Failed to translate srsName='%s'", pszSRSName);
226 0 : delete poSRS;
227 0 : poSRS = nullptr;
228 : }
229 : }
230 : }
231 :
232 : /* -------------------------------------------------------------------- */
233 : /* Create an empty layer. */
234 : /* -------------------------------------------------------------------- */
235 5 : OGRNASLayer *poLayer = new OGRNASLayer(poClass->GetName(), this);
236 :
237 : /* -------------------------------------------------------------------- */
238 : /* Added attributes (properties). */
239 : /* -------------------------------------------------------------------- */
240 81 : for (int iField = 0; iField < poClass->GetPropertyCount(); iField++)
241 : {
242 76 : GMLPropertyDefn *poProperty = poClass->GetProperty(iField);
243 : OGRFieldType eFType;
244 :
245 76 : if (poProperty->GetType() == GMLPT_Untyped)
246 4 : eFType = OFTString;
247 72 : else if (poProperty->GetType() == GMLPT_String)
248 47 : eFType = OFTString;
249 25 : else if (poProperty->GetType() == GMLPT_Integer)
250 12 : eFType = OFTInteger;
251 13 : else if (poProperty->GetType() == GMLPT_Real)
252 2 : eFType = OFTReal;
253 11 : else if (poProperty->GetType() == GMLPT_StringList)
254 7 : eFType = OFTStringList;
255 4 : else if (poProperty->GetType() == GMLPT_IntegerList)
256 0 : eFType = OFTIntegerList;
257 4 : else if (poProperty->GetType() == GMLPT_RealList)
258 0 : eFType = OFTRealList;
259 : else
260 4 : eFType = OFTString;
261 :
262 152 : OGRFieldDefn oField(poProperty->GetName(), eFType);
263 76 : if (STARTS_WITH_CI(oField.GetNameRef(), "ogr:"))
264 0 : oField.SetName(poProperty->GetName() + 4);
265 76 : if (poProperty->GetWidth() > 0)
266 42 : oField.SetWidth(poProperty->GetWidth());
267 :
268 76 : poLayer->GetLayerDefn()->AddFieldDefn(&oField);
269 : }
270 :
271 7 : for (int iField = 0; iField < poClass->GetGeometryPropertyCount(); iField++)
272 : {
273 : GMLGeometryPropertyDefn *poProperty =
274 2 : poClass->GetGeometryProperty(iField);
275 : OGRGeomFieldDefn oField(poProperty->GetName(),
276 4 : (OGRwkbGeometryType)poProperty->GetType());
277 4 : if (poClass->GetGeometryPropertyCount() == 1 &&
278 2 : poClass->GetFeatureCount() == 0)
279 : {
280 0 : oField.SetType(wkbUnknown);
281 : }
282 :
283 2 : oField.SetSpatialRef(poSRS);
284 2 : oField.SetNullable(poProperty->IsNullable());
285 2 : poLayer->GetLayerDefn()->AddGeomFieldDefn(&oField);
286 : }
287 :
288 5 : if (poSRS)
289 0 : poSRS->Dereference();
290 :
291 5 : return poLayer;
292 : }
293 :
294 : /************************************************************************/
295 : /* GetLayer() */
296 : /************************************************************************/
297 :
298 4 : OGRLayer *OGRNASDataSource::GetLayer(int iLayer)
299 :
300 : {
301 4 : if (iLayer < 0 || iLayer >= nLayers)
302 0 : return nullptr;
303 :
304 4 : return papoLayers[iLayer];
305 : }
|