Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GML Utils
4 : * Purpose: GML reader
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "gmlutils.h"
15 :
16 : #include <cstdio>
17 : #include <cstdlib>
18 : #include <cstring>
19 : #include <map>
20 : #include <string>
21 : #include <utility>
22 :
23 : #include "cpl_conv.h"
24 : #include "cpl_string.h"
25 : #include "ogr_api.h"
26 : #include "ogr_core.h"
27 : #include "ogr_p.h"
28 : #include "ogr_spatialref.h"
29 :
30 : /************************************************************************/
31 : /* GML_ExtractSrsNameFromGeometry() */
32 : /************************************************************************/
33 :
34 : const char *
35 2073 : GML_ExtractSrsNameFromGeometry(const CPLXMLNode *const *papsGeometry,
36 : std::string &osWork, bool bConsiderEPSGAsURN)
37 : {
38 2073 : if (papsGeometry[0] != nullptr && papsGeometry[1] == nullptr)
39 : {
40 : const char *pszSRSName =
41 2073 : CPLGetXMLValue(papsGeometry[0], "srsName", nullptr);
42 2073 : if (pszSRSName)
43 : {
44 548 : const int nLen = static_cast<int>(strlen(pszSRSName));
45 :
46 548 : if (STARTS_WITH(pszSRSName, "EPSG:") && bConsiderEPSGAsURN)
47 : {
48 36 : osWork.reserve(22 + nLen - 5);
49 36 : osWork.assign("urn:ogc:def:crs:EPSG::", 22);
50 36 : osWork.append(pszSRSName + 5, nLen - 5);
51 36 : return osWork.c_str();
52 : }
53 512 : else if (STARTS_WITH(pszSRSName,
54 : "http://www.opengis.net/gml/srs/epsg.xml#"))
55 : {
56 47 : osWork.reserve(5 + nLen - 40);
57 47 : osWork.assign("EPSG:", 5);
58 47 : osWork.append(pszSRSName + 40, nLen - 40);
59 47 : return osWork.c_str();
60 : }
61 : else
62 : {
63 465 : return pszSRSName;
64 : }
65 : }
66 : }
67 1525 : return nullptr;
68 : }
69 :
70 : /************************************************************************/
71 : /* GML_IsSRSLatLongOrder() */
72 : /************************************************************************/
73 :
74 476 : bool GML_IsSRSLatLongOrder(const char *pszSRSName)
75 : {
76 476 : if (pszSRSName == nullptr)
77 0 : return false;
78 :
79 476 : if (STARTS_WITH(pszSRSName, "urn:") &&
80 404 : strstr(pszSRSName, ":4326") != nullptr)
81 : {
82 : // Shortcut.
83 337 : return true;
84 : }
85 139 : else if (!EQUALN(pszSRSName, "EPSG:", 5))
86 : {
87 87 : OGRSpatialReference oSRS;
88 87 : if (oSRS.SetFromUserInput(
89 : pszSRSName,
90 87 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
91 : OGRERR_NONE)
92 : {
93 159 : if (oSRS.EPSGTreatsAsLatLong() ||
94 72 : oSRS.EPSGTreatsAsNorthingEasting())
95 17 : return true;
96 : }
97 : }
98 122 : return false;
99 : }
100 :
101 : /************************************************************************/
102 : /* GML_BuildOGRGeometryFromList_CreateCache() */
103 : /************************************************************************/
104 :
105 : namespace
106 : {
107 : class SRSDesc
108 : {
109 : public:
110 : std::string osSRSName{};
111 : bool bAxisInvert = false;
112 : OGRSpatialReference *poSRS = nullptr;
113 :
114 458 : SRSDesc() = default;
115 :
116 229 : SRSDesc &operator=(SRSDesc &&other)
117 : {
118 229 : osSRSName = std::move(other.osSRSName);
119 229 : bAxisInvert = other.bAxisInvert;
120 229 : if (poSRS)
121 0 : poSRS->Release();
122 229 : poSRS = other.poSRS;
123 229 : other.poSRS = nullptr;
124 229 : return *this;
125 : }
126 :
127 458 : ~SRSDesc()
128 458 : {
129 458 : if (poSRS)
130 229 : poSRS->Release();
131 458 : }
132 :
133 : CPL_DISALLOW_COPY_ASSIGN(SRSDesc)
134 : };
135 :
136 : class SRSCache
137 : {
138 : std::map<std::string, SRSDesc> oMap{};
139 : SRSDesc *poLastDesc = nullptr;
140 :
141 : CPL_DISALLOW_COPY_ASSIGN(SRSCache)
142 :
143 : public:
144 827 : SRSCache() = default;
145 :
146 432 : const SRSDesc &Get(const std::string &osSRSName)
147 : {
148 432 : if (poLastDesc && osSRSName == poLastDesc->osSRSName)
149 203 : return *poLastDesc;
150 :
151 229 : std::map<std::string, SRSDesc>::iterator oIter = oMap.find(osSRSName);
152 229 : if (oIter != oMap.end())
153 : {
154 0 : poLastDesc = &(oIter->second);
155 0 : return *poLastDesc;
156 : }
157 :
158 229 : SRSDesc oDesc;
159 229 : oDesc.osSRSName = osSRSName;
160 229 : oDesc.bAxisInvert = GML_IsSRSLatLongOrder(osSRSName.c_str());
161 229 : oDesc.poSRS = new OGRSpatialReference();
162 229 : oDesc.poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
163 229 : if (oDesc.poSRS->SetFromUserInput(
164 : osSRSName.c_str(),
165 229 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
166 : OGRERR_NONE)
167 : {
168 0 : delete oDesc.poSRS;
169 0 : oDesc.poSRS = nullptr;
170 : }
171 229 : oMap[osSRSName] = std::move(oDesc);
172 229 : poLastDesc = &(oMap[osSRSName]);
173 229 : return *poLastDesc;
174 : }
175 : };
176 :
177 : } // namespace
178 :
179 827 : void *GML_BuildOGRGeometryFromList_CreateCache()
180 : {
181 827 : return new SRSCache();
182 : }
183 :
184 : /************************************************************************/
185 : /* GML_BuildOGRGeometryFromList_DestroyCache() */
186 : /************************************************************************/
187 :
188 827 : void GML_BuildOGRGeometryFromList_DestroyCache(void *hCacheSRS)
189 : {
190 827 : delete static_cast<SRSCache *>(hCacheSRS);
191 827 : }
192 :
193 : /************************************************************************/
194 : /* GML_BuildOGRGeometryFromList() */
195 : /************************************************************************/
196 :
197 1397 : OGRGeometry *GML_BuildOGRGeometryFromList(
198 : const CPLXMLNode *const *papsGeometry, bool bTryToMakeMultipolygons,
199 : bool bInvertAxisOrderIfLatLong, const char *pszDefaultSRSName,
200 : bool bConsiderEPSGAsURN, GMLSwapCoordinatesEnum eSwapCoordinates,
201 : int nPseudoBoolGetSecondaryGeometryOption, void *hCacheSRS,
202 : bool bFaceHoleNegative)
203 : {
204 1397 : OGRGeometry *poGeom = nullptr;
205 1397 : OGRGeometryCollection *poCollection = nullptr;
206 : #ifndef WITHOUT_CPLDEBUG
207 : static const bool bDebugGML =
208 1397 : EQUAL(CPLGetConfigOption("CPL_DEBUG", ""), "GML");
209 : #endif
210 2794 : for (int i = 0; papsGeometry[i] != nullptr; i++)
211 : {
212 : #ifndef WITHOUT_CPLDEBUG
213 1397 : if (bDebugGML)
214 : {
215 0 : char *pszXML = CPLSerializeXMLTree(papsGeometry[i]);
216 0 : CPLDebug("GML", "Parsing: %s", pszXML);
217 0 : CPLFree(pszXML);
218 : }
219 : #endif
220 2794 : OGRGeometry *poSubGeom = GML2OGRGeometry_XMLNode(
221 1397 : papsGeometry[i], nPseudoBoolGetSecondaryGeometryOption, 0, 0, false,
222 : true, bFaceHoleNegative);
223 1397 : if (poSubGeom)
224 : {
225 1390 : if (poGeom == nullptr)
226 : {
227 1390 : poGeom = poSubGeom;
228 : }
229 : else
230 : {
231 0 : if (poCollection == nullptr)
232 : {
233 0 : if (bTryToMakeMultipolygons &&
234 0 : wkbFlatten(poGeom->getGeometryType()) == wkbPolygon &&
235 0 : wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
236 : {
237 : OGRGeometryCollection *poGeomColl =
238 0 : new OGRMultiPolygon();
239 0 : poGeomColl->addGeometryDirectly(poGeom);
240 0 : poGeomColl->addGeometryDirectly(poSubGeom);
241 0 : poGeom = poGeomColl;
242 : }
243 0 : else if (bTryToMakeMultipolygons &&
244 0 : wkbFlatten(poGeom->getGeometryType()) ==
245 0 : wkbMultiPolygon &&
246 0 : wkbFlatten(poSubGeom->getGeometryType()) ==
247 : wkbPolygon)
248 : {
249 0 : OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
250 0 : poGeomColl->addGeometryDirectly(poSubGeom);
251 : }
252 0 : else if (bTryToMakeMultipolygons &&
253 0 : wkbFlatten(poGeom->getGeometryType()) ==
254 0 : wkbMultiPolygon &&
255 0 : wkbFlatten(poSubGeom->getGeometryType()) ==
256 : wkbMultiPolygon)
257 : {
258 0 : OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
259 0 : for (auto &&poMember : poSubGeom->toMultiPolygon())
260 : {
261 0 : poGeomColl->addGeometry(poMember);
262 : }
263 0 : delete poSubGeom;
264 : }
265 0 : else if (bTryToMakeMultipolygons &&
266 0 : wkbFlatten(poGeom->getGeometryType()) ==
267 : wkbMultiPolygon)
268 : {
269 0 : delete poGeom;
270 0 : delete poSubGeom;
271 0 : return GML_BuildOGRGeometryFromList(
272 : papsGeometry, false, bInvertAxisOrderIfLatLong,
273 : pszDefaultSRSName, bConsiderEPSGAsURN,
274 : eSwapCoordinates,
275 0 : nPseudoBoolGetSecondaryGeometryOption, hCacheSRS);
276 : }
277 : else
278 : {
279 0 : poCollection = new OGRGeometryCollection();
280 0 : poCollection->addGeometryDirectly(poGeom);
281 0 : poGeom = poCollection;
282 : }
283 : }
284 0 : if (poCollection != nullptr)
285 : {
286 0 : poCollection->addGeometryDirectly(poSubGeom);
287 : }
288 : }
289 : }
290 : }
291 :
292 1397 : if (poGeom == nullptr)
293 7 : return nullptr;
294 :
295 1390 : std::string osWork;
296 1390 : const char *pszSRSName = GML_ExtractSrsNameFromGeometry(
297 : papsGeometry, osWork, bConsiderEPSGAsURN);
298 1390 : const char *pszNameLookup = pszSRSName;
299 1390 : if (pszNameLookup == nullptr)
300 985 : pszNameLookup = pszDefaultSRSName;
301 :
302 1390 : if (pszNameLookup != nullptr)
303 : {
304 432 : SRSCache *poSRSCache = static_cast<SRSCache *>(hCacheSRS);
305 432 : const SRSDesc &oSRSDesc = poSRSCache->Get(pszNameLookup);
306 432 : poGeom->assignSpatialReference(oSRSDesc.poSRS);
307 432 : if ((eSwapCoordinates == GML_SWAP_AUTO && oSRSDesc.bAxisInvert &&
308 162 : bInvertAxisOrderIfLatLong) ||
309 : eSwapCoordinates == GML_SWAP_YES)
310 : {
311 272 : poGeom->swapXY();
312 : }
313 : }
314 958 : else if (eSwapCoordinates == GML_SWAP_YES)
315 : {
316 1 : poGeom->swapXY();
317 : }
318 :
319 1390 : return poGeom;
320 : }
321 :
322 : /************************************************************************/
323 : /* GML_GetSRSName() */
324 : /************************************************************************/
325 :
326 220 : char *GML_GetSRSName(const OGRSpatialReference *poSRS,
327 : OGRGMLSRSNameFormat eSRSNameFormat, bool *pbCoordSwap)
328 : {
329 220 : *pbCoordSwap = false;
330 220 : if (poSRS == nullptr)
331 116 : return CPLStrdup("");
332 :
333 104 : const auto &map = poSRS->GetDataAxisToSRSAxisMapping();
334 146 : if (eSRSNameFormat != SRSNAME_SHORT && map.size() >= 2 && map[0] == 2 &&
335 42 : map[1] == 1)
336 : {
337 42 : *pbCoordSwap = true;
338 : }
339 :
340 104 : const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
341 104 : const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
342 104 : if (nullptr != pszAuthName && nullptr != pszAuthCode)
343 : {
344 104 : if (eSRSNameFormat == SRSNAME_SHORT)
345 : {
346 9 : return CPLStrdup(
347 9 : CPLSPrintf(" srsName=\"%s:%s\"", pszAuthName, pszAuthCode));
348 : }
349 95 : else if (eSRSNameFormat == SRSNAME_OGC_URN)
350 : {
351 74 : return CPLStrdup(CPLSPrintf(" srsName=\"urn:ogc:def:crs:%s::%s\"",
352 74 : pszAuthName, pszAuthCode));
353 : }
354 21 : else if (eSRSNameFormat == SRSNAME_OGC_URL)
355 : {
356 21 : return CPLStrdup(CPLSPrintf(
357 : " srsName=\"http://www.opengis.net/def/crs/%s/0/%s\"",
358 21 : pszAuthName, pszAuthCode));
359 : }
360 : }
361 0 : return CPLStrdup("");
362 : }
363 :
364 : /************************************************************************/
365 : /* GML_IsLegitSRSName() */
366 : /************************************************************************/
367 :
368 98 : bool GML_IsLegitSRSName(const char *pszSRSName)
369 : {
370 :
371 98 : if (STARTS_WITH_CI(pszSRSName, "http"))
372 : {
373 6 : if (!(STARTS_WITH_CI(pszSRSName, "http://opengis.net/def/crs") ||
374 6 : STARTS_WITH_CI(pszSRSName, "http://www.opengis.net/def/crs")))
375 : {
376 0 : return false;
377 : }
378 : }
379 98 : return true;
380 : }
|