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 1676 : GML_ExtractSrsNameFromGeometry(const CPLXMLNode *const *papsGeometry,
36 : std::string &osWork, bool bConsiderEPSGAsURN)
37 : {
38 1676 : if (papsGeometry[0] != nullptr && papsGeometry[1] == nullptr)
39 : {
40 : const char *pszSRSName =
41 1676 : CPLGetXMLValue(papsGeometry[0], "srsName", nullptr);
42 1676 : if (pszSRSName)
43 : {
44 543 : const int nLen = static_cast<int>(strlen(pszSRSName));
45 :
46 543 : 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 507 : 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 460 : return pszSRSName;
64 : }
65 : }
66 : }
67 1133 : return nullptr;
68 : }
69 :
70 : /************************************************************************/
71 : /* GML_IsSRSLatLongOrder() */
72 : /************************************************************************/
73 :
74 461 : bool GML_IsSRSLatLongOrder(const char *pszSRSName)
75 : {
76 461 : if (pszSRSName == nullptr)
77 0 : return false;
78 :
79 461 : if (STARTS_WITH(pszSRSName, "urn:") &&
80 390 : strstr(pszSRSName, ":4326") != nullptr)
81 : {
82 : // Shortcut.
83 332 : return true;
84 : }
85 : /* fguuid:jgd20??.bl (Japanese FGD GML v4) */
86 129 : else if (EQUALN(pszSRSName, "fguuid:jgd2011.bl", 17) ||
87 127 : EQUALN(pszSRSName, "fguuid:jgd2001.bl", 17))
88 : {
89 2 : return true;
90 : }
91 127 : else if (!EQUALN(pszSRSName, "EPSG:", 5))
92 : {
93 75 : OGRSpatialReference oSRS;
94 75 : if (oSRS.SetFromUserInput(
95 : pszSRSName,
96 75 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
97 : OGRERR_NONE)
98 : {
99 137 : if (oSRS.EPSGTreatsAsLatLong() ||
100 62 : oSRS.EPSGTreatsAsNorthingEasting())
101 15 : return true;
102 : }
103 : }
104 112 : return false;
105 : }
106 :
107 : /************************************************************************/
108 : /* GML_BuildOGRGeometryFromList_CreateCache() */
109 : /************************************************************************/
110 :
111 : namespace
112 : {
113 : class SRSDesc
114 : {
115 : public:
116 : std::string osSRSName{};
117 : bool bAxisInvert = false;
118 : OGRSpatialReference *poSRS = nullptr;
119 :
120 440 : SRSDesc() = default;
121 :
122 220 : SRSDesc &operator=(SRSDesc &&other)
123 : {
124 220 : osSRSName = std::move(other.osSRSName);
125 220 : bAxisInvert = other.bAxisInvert;
126 220 : if (poSRS)
127 0 : poSRS->Release();
128 220 : poSRS = other.poSRS;
129 220 : other.poSRS = nullptr;
130 220 : return *this;
131 : }
132 :
133 440 : ~SRSDesc()
134 440 : {
135 440 : if (poSRS)
136 218 : poSRS->Release();
137 440 : }
138 :
139 : CPL_DISALLOW_COPY_ASSIGN(SRSDesc)
140 : };
141 :
142 : class SRSCache
143 : {
144 : std::map<std::string, SRSDesc> oMap{};
145 : SRSDesc *poLastDesc = nullptr;
146 :
147 : CPL_DISALLOW_COPY_ASSIGN(SRSCache)
148 :
149 : public:
150 794 : SRSCache() = default;
151 :
152 422 : const SRSDesc &Get(const std::string &osSRSName)
153 : {
154 422 : if (poLastDesc && osSRSName == poLastDesc->osSRSName)
155 202 : return *poLastDesc;
156 :
157 220 : std::map<std::string, SRSDesc>::iterator oIter = oMap.find(osSRSName);
158 220 : if (oIter != oMap.end())
159 : {
160 0 : poLastDesc = &(oIter->second);
161 0 : return *poLastDesc;
162 : }
163 :
164 220 : SRSDesc oDesc;
165 220 : oDesc.osSRSName = osSRSName;
166 220 : oDesc.bAxisInvert = GML_IsSRSLatLongOrder(osSRSName.c_str());
167 220 : oDesc.poSRS = new OGRSpatialReference();
168 220 : oDesc.poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
169 220 : if (oDesc.poSRS->SetFromUserInput(
170 : osSRSName.c_str(),
171 220 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
172 : OGRERR_NONE)
173 : {
174 2 : delete oDesc.poSRS;
175 2 : oDesc.poSRS = nullptr;
176 : }
177 220 : oMap[osSRSName] = std::move(oDesc);
178 220 : poLastDesc = &(oMap[osSRSName]);
179 220 : return *poLastDesc;
180 : }
181 : };
182 :
183 : } // namespace
184 :
185 794 : void *GML_BuildOGRGeometryFromList_CreateCache()
186 : {
187 794 : return new SRSCache();
188 : }
189 :
190 : /************************************************************************/
191 : /* GML_BuildOGRGeometryFromList_DestroyCache() */
192 : /************************************************************************/
193 :
194 794 : void GML_BuildOGRGeometryFromList_DestroyCache(void *hCacheSRS)
195 : {
196 794 : delete static_cast<SRSCache *>(hCacheSRS);
197 794 : }
198 :
199 : /************************************************************************/
200 : /* GML_BuildOGRGeometryFromList() */
201 : /************************************************************************/
202 :
203 1184 : OGRGeometry *GML_BuildOGRGeometryFromList(
204 : const CPLXMLNode *const *papsGeometry, bool bTryToMakeMultipolygons,
205 : bool bInvertAxisOrderIfLatLong, const char *pszDefaultSRSName,
206 : bool bConsiderEPSGAsURN, GMLSwapCoordinatesEnum eSwapCoordinates,
207 : int nPseudoBoolGetSecondaryGeometryOption, void *hCacheSRS,
208 : bool bFaceHoleNegative)
209 : {
210 1184 : OGRGeometry *poGeom = nullptr;
211 1184 : OGRGeometryCollection *poCollection = nullptr;
212 : #ifndef WITHOUT_CPLDEBUG
213 : static const bool bDebugGML =
214 1184 : EQUAL(CPLGetConfigOption("CPL_DEBUG", ""), "GML");
215 : #endif
216 2368 : for (int i = 0; papsGeometry[i] != nullptr; i++)
217 : {
218 : #ifndef WITHOUT_CPLDEBUG
219 1184 : if (bDebugGML)
220 : {
221 0 : char *pszXML = CPLSerializeXMLTree(papsGeometry[i]);
222 0 : CPLDebug("GML", "Parsing: %s", pszXML);
223 0 : CPLFree(pszXML);
224 : }
225 : #endif
226 2368 : OGRGeometry *poSubGeom = GML2OGRGeometry_XMLNode(
227 1184 : papsGeometry[i], nPseudoBoolGetSecondaryGeometryOption, 0, 0, false,
228 : true, bFaceHoleNegative);
229 1184 : if (poSubGeom)
230 : {
231 1182 : if (poGeom == nullptr)
232 : {
233 1182 : poGeom = poSubGeom;
234 : }
235 : else
236 : {
237 0 : if (poCollection == nullptr)
238 : {
239 0 : if (bTryToMakeMultipolygons &&
240 0 : wkbFlatten(poGeom->getGeometryType()) == wkbPolygon &&
241 0 : wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
242 : {
243 : OGRGeometryCollection *poGeomColl =
244 0 : new OGRMultiPolygon();
245 0 : poGeomColl->addGeometryDirectly(poGeom);
246 0 : poGeomColl->addGeometryDirectly(poSubGeom);
247 0 : poGeom = poGeomColl;
248 : }
249 0 : else if (bTryToMakeMultipolygons &&
250 0 : wkbFlatten(poGeom->getGeometryType()) ==
251 0 : wkbMultiPolygon &&
252 0 : wkbFlatten(poSubGeom->getGeometryType()) ==
253 : wkbPolygon)
254 : {
255 0 : OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
256 0 : poGeomColl->addGeometryDirectly(poSubGeom);
257 : }
258 0 : else if (bTryToMakeMultipolygons &&
259 0 : wkbFlatten(poGeom->getGeometryType()) ==
260 0 : wkbMultiPolygon &&
261 0 : wkbFlatten(poSubGeom->getGeometryType()) ==
262 : wkbMultiPolygon)
263 : {
264 0 : OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
265 0 : for (auto &&poMember : poSubGeom->toMultiPolygon())
266 : {
267 0 : poGeomColl->addGeometry(poMember);
268 : }
269 0 : delete poSubGeom;
270 : }
271 0 : else if (bTryToMakeMultipolygons &&
272 0 : wkbFlatten(poGeom->getGeometryType()) ==
273 : wkbMultiPolygon)
274 : {
275 0 : delete poGeom;
276 0 : delete poSubGeom;
277 0 : return GML_BuildOGRGeometryFromList(
278 : papsGeometry, false, bInvertAxisOrderIfLatLong,
279 : pszDefaultSRSName, bConsiderEPSGAsURN,
280 : eSwapCoordinates,
281 0 : nPseudoBoolGetSecondaryGeometryOption, hCacheSRS);
282 : }
283 : else
284 : {
285 0 : poCollection = new OGRGeometryCollection();
286 0 : poCollection->addGeometryDirectly(poGeom);
287 0 : poGeom = poCollection;
288 : }
289 : }
290 0 : if (poCollection != nullptr)
291 : {
292 0 : poCollection->addGeometryDirectly(poSubGeom);
293 : }
294 : }
295 : }
296 : }
297 :
298 1184 : if (poGeom == nullptr)
299 2 : return nullptr;
300 :
301 1182 : std::string osWork;
302 1182 : const char *pszSRSName = GML_ExtractSrsNameFromGeometry(
303 : papsGeometry, osWork, bConsiderEPSGAsURN);
304 1182 : const char *pszNameLookup = pszSRSName;
305 1182 : if (pszNameLookup == nullptr)
306 781 : pszNameLookup = pszDefaultSRSName;
307 :
308 1182 : if (pszNameLookup != nullptr)
309 : {
310 422 : SRSCache *poSRSCache = static_cast<SRSCache *>(hCacheSRS);
311 422 : const SRSDesc &oSRSDesc = poSRSCache->Get(pszNameLookup);
312 422 : poGeom->assignSpatialReference(oSRSDesc.poSRS);
313 422 : if ((eSwapCoordinates == GML_SWAP_AUTO && oSRSDesc.bAxisInvert &&
314 156 : bInvertAxisOrderIfLatLong) ||
315 : eSwapCoordinates == GML_SWAP_YES)
316 : {
317 268 : poGeom->swapXY();
318 : }
319 : }
320 760 : else if (eSwapCoordinates == GML_SWAP_YES)
321 : {
322 1 : poGeom->swapXY();
323 : }
324 :
325 1182 : return poGeom;
326 : }
327 :
328 : /************************************************************************/
329 : /* GML_GetSRSName() */
330 : /************************************************************************/
331 :
332 220 : char *GML_GetSRSName(const OGRSpatialReference *poSRS,
333 : OGRGMLSRSNameFormat eSRSNameFormat, bool *pbCoordSwap)
334 : {
335 220 : *pbCoordSwap = false;
336 220 : if (poSRS == nullptr)
337 116 : return CPLStrdup("");
338 :
339 104 : const auto &map = poSRS->GetDataAxisToSRSAxisMapping();
340 146 : if (eSRSNameFormat != SRSNAME_SHORT && map.size() >= 2 && map[0] == 2 &&
341 42 : map[1] == 1)
342 : {
343 42 : *pbCoordSwap = true;
344 : }
345 :
346 104 : const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
347 104 : const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
348 104 : if (nullptr != pszAuthName && nullptr != pszAuthCode)
349 : {
350 104 : if (eSRSNameFormat == SRSNAME_SHORT)
351 : {
352 9 : return CPLStrdup(
353 9 : CPLSPrintf(" srsName=\"%s:%s\"", pszAuthName, pszAuthCode));
354 : }
355 95 : else if (eSRSNameFormat == SRSNAME_OGC_URN)
356 : {
357 74 : return CPLStrdup(CPLSPrintf(" srsName=\"urn:ogc:def:crs:%s::%s\"",
358 74 : pszAuthName, pszAuthCode));
359 : }
360 21 : else if (eSRSNameFormat == SRSNAME_OGC_URL)
361 : {
362 21 : return CPLStrdup(CPLSPrintf(
363 : " srsName=\"http://www.opengis.net/def/crs/%s/0/%s\"",
364 21 : pszAuthName, pszAuthCode));
365 : }
366 : }
367 0 : return CPLStrdup("");
368 : }
369 :
370 : /************************************************************************/
371 : /* GML_IsLegitSRSName() */
372 : /************************************************************************/
373 :
374 85 : bool GML_IsLegitSRSName(const char *pszSRSName)
375 : {
376 :
377 85 : if (STARTS_WITH_CI(pszSRSName, "http"))
378 : {
379 6 : if (!(STARTS_WITH_CI(pszSRSName, "http://opengis.net/def/crs") ||
380 6 : STARTS_WITH_CI(pszSRSName, "http://www.opengis.net/def/crs")))
381 : {
382 0 : return false;
383 : }
384 : }
385 85 : return true;
386 : }
|