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_mem_cache.h"
25 : #include "cpl_string.h"
26 : #include "ogr_api.h"
27 : #include "ogr_core.h"
28 : #include "ogr_p.h"
29 : #include "ogr_spatialref.h"
30 :
31 : /************************************************************************/
32 : /* GML_ExtractSrsNameFromGeometry() */
33 : /************************************************************************/
34 :
35 : const char *
36 2090 : GML_ExtractSrsNameFromGeometry(const CPLXMLNode *const *papsGeometry,
37 : std::string &osWork, bool bConsiderEPSGAsURN)
38 : {
39 2090 : if (papsGeometry[0] != nullptr && papsGeometry[1] == nullptr)
40 : {
41 : const char *pszSRSName =
42 2090 : CPLGetXMLValue(papsGeometry[0], "srsName", nullptr);
43 2090 : if (pszSRSName)
44 : {
45 553 : const int nLen = static_cast<int>(strlen(pszSRSName));
46 :
47 553 : if (STARTS_WITH(pszSRSName, "EPSG:") && bConsiderEPSGAsURN)
48 : {
49 36 : osWork.reserve(22 + nLen - 5);
50 36 : osWork.assign("urn:ogc:def:crs:EPSG::", 22);
51 36 : osWork.append(pszSRSName + 5, nLen - 5);
52 36 : return osWork.c_str();
53 : }
54 517 : else if (STARTS_WITH(pszSRSName,
55 : "http://www.opengis.net/gml/srs/epsg.xml#"))
56 : {
57 47 : osWork.reserve(5 + nLen - 40);
58 47 : osWork.assign("EPSG:", 5);
59 47 : osWork.append(pszSRSName + 40, nLen - 40);
60 47 : return osWork.c_str();
61 : }
62 : else
63 : {
64 470 : return pszSRSName;
65 : }
66 : }
67 : }
68 1537 : return nullptr;
69 : }
70 :
71 : /************************************************************************/
72 : /* GML_IsSRSLatLongOrder() */
73 : /************************************************************************/
74 :
75 248 : bool GML_IsSRSLatLongOrder(const char *pszSRSName)
76 : {
77 248 : if (pszSRSName == nullptr)
78 0 : return false;
79 :
80 248 : if (STARTS_WITH(pszSRSName, "urn:") &&
81 227 : strstr(pszSRSName, ":4326") != nullptr)
82 : {
83 : // Shortcut.
84 190 : return true;
85 : }
86 58 : else if (!EQUALN(pszSRSName, "EPSG:", 5))
87 : {
88 40 : OGRSpatialReference oSRS;
89 40 : if (oSRS.SetFromUserInput(
90 : pszSRSName,
91 40 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
92 : OGRERR_NONE)
93 : {
94 77 : if (oSRS.EPSGTreatsAsLatLong() ||
95 37 : oSRS.EPSGTreatsAsNorthingEasting())
96 5 : return true;
97 : }
98 : }
99 53 : return false;
100 : }
101 :
102 : /************************************************************************/
103 : /* OGRGML_SRSCacheEntry::~OGRGML_SRSCacheEntry() */
104 : /************************************************************************/
105 :
106 724 : OGRGML_SRSCacheEntry::~OGRGML_SRSCacheEntry()
107 : {
108 362 : if (poSRS)
109 362 : poSRS->Release();
110 362 : }
111 :
112 : /************************************************************************/
113 : /* OGRGML_SRSCache */
114 : /************************************************************************/
115 :
116 : class OGRGML_SRSCache
117 : {
118 : public:
119 : lru11::Cache<std::string, std::shared_ptr<OGRGML_SRSCacheEntry>>
120 : oSRSCache{};
121 : };
122 :
123 : /************************************************************************/
124 : /* OGRGML_SRSCache_Create() */
125 : /************************************************************************/
126 :
127 1951 : OGRGML_SRSCache *OGRGML_SRSCache_Create()
128 : {
129 1951 : return new OGRGML_SRSCache();
130 : }
131 :
132 : /************************************************************************/
133 : /* OGRGML_SRSCache_Destroy() */
134 : /************************************************************************/
135 :
136 1951 : void OGRGML_SRSCache_Destroy(OGRGML_SRSCache *hSRSCache)
137 : {
138 1951 : delete hSRSCache;
139 1951 : }
140 :
141 : /************************************************************************/
142 : /* OGRGML_SRSCache_GetInfo() */
143 : /************************************************************************/
144 :
145 : std::shared_ptr<OGRGML_SRSCacheEntry>
146 2099 : OGRGML_SRSCache_GetInfo(OGRGML_SRSCache *hSRSCache, const char *pszSRSName)
147 : {
148 2099 : std::shared_ptr<OGRGML_SRSCacheEntry> entry;
149 2099 : if (!hSRSCache->oSRSCache.tryGet(pszSRSName, entry))
150 : {
151 362 : entry = std::make_shared<OGRGML_SRSCacheEntry>();
152 362 : auto poSRS = new OGRSpatialReference();
153 362 : entry->poSRS = poSRS;
154 362 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
155 362 : if (poSRS->SetFromUserInput(
156 : pszSRSName,
157 362 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
158 : OGRERR_NONE)
159 : {
160 35 : return nullptr;
161 : }
162 327 : entry->nAxisCount = poSRS->GetAxesCount();
163 327 : entry->bIsGeographic = poSRS->IsGeographic();
164 327 : entry->bIsProjected = poSRS->IsProjected();
165 607 : entry->bInvertedAxisOrder = !STARTS_WITH(pszSRSName, "EPSG:") &&
166 280 : (poSRS->EPSGTreatsAsLatLong() ||
167 84 : poSRS->EPSGTreatsAsNorthingEasting());
168 327 : entry->dfSemiMajor = poSRS->GetSemiMajor();
169 327 : if (entry->bIsProjected)
170 104 : entry->dfLinearUnits = poSRS->GetLinearUnits(nullptr);
171 327 : if (entry->bIsGeographic)
172 : {
173 223 : entry->bAngularUnitIsDegree =
174 223 : fabs(poSRS->GetAngularUnits(nullptr) -
175 223 : CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8;
176 : }
177 327 : hSRSCache->oSRSCache.insert(pszSRSName, entry);
178 : }
179 2064 : return entry;
180 : }
181 :
182 : /************************************************************************/
183 : /* GML_BuildOGRGeometryFromList() */
184 : /************************************************************************/
185 :
186 1408 : OGRGeometry *GML_BuildOGRGeometryFromList(
187 : const CPLXMLNode *const *papsGeometry, bool bTryToMakeMultipolygons,
188 : bool bInvertAxisOrderIfLatLong, const char *pszDefaultSRSName,
189 : bool bConsiderEPSGAsURN, GMLSwapCoordinatesEnum eSwapCoordinates,
190 : int nPseudoBoolGetSecondaryGeometryOption, OGRGML_SRSCache *hSRSCache,
191 : bool bFaceHoleNegative)
192 : {
193 1408 : OGRGeometry *poGeom = nullptr;
194 1408 : OGRGeometryCollection *poCollection = nullptr;
195 : #ifndef WITHOUT_CPLDEBUG
196 : static const bool bDebugGML =
197 1408 : EQUAL(CPLGetConfigOption("CPL_DEBUG", ""), "GML");
198 : #endif
199 2816 : for (int i = 0; papsGeometry[i] != nullptr; i++)
200 : {
201 : #ifndef WITHOUT_CPLDEBUG
202 1408 : if (bDebugGML)
203 : {
204 0 : char *pszXML = CPLSerializeXMLTree(papsGeometry[i]);
205 0 : CPLDebug("GML", "Parsing: %s", pszXML);
206 0 : CPLFree(pszXML);
207 : }
208 : #endif
209 2816 : OGRGeometry *poSubGeom = GML2OGRGeometry_XMLNode(
210 1408 : papsGeometry[i], nPseudoBoolGetSecondaryGeometryOption, hSRSCache,
211 : 0, 0, false, true, bFaceHoleNegative);
212 1408 : if (poSubGeom)
213 : {
214 1401 : if (poGeom == nullptr)
215 : {
216 1401 : poGeom = poSubGeom;
217 : }
218 : else
219 : {
220 0 : if (poCollection == nullptr)
221 : {
222 0 : if (bTryToMakeMultipolygons &&
223 0 : wkbFlatten(poGeom->getGeometryType()) == wkbPolygon &&
224 0 : wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
225 : {
226 : OGRGeometryCollection *poGeomColl =
227 0 : new OGRMultiPolygon();
228 0 : poGeomColl->addGeometryDirectly(poGeom);
229 0 : poGeomColl->addGeometryDirectly(poSubGeom);
230 0 : poGeom = poGeomColl;
231 : }
232 0 : else if (bTryToMakeMultipolygons &&
233 0 : wkbFlatten(poGeom->getGeometryType()) ==
234 0 : wkbMultiPolygon &&
235 0 : wkbFlatten(poSubGeom->getGeometryType()) ==
236 : wkbPolygon)
237 : {
238 0 : OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
239 0 : poGeomColl->addGeometryDirectly(poSubGeom);
240 : }
241 0 : else if (bTryToMakeMultipolygons &&
242 0 : wkbFlatten(poGeom->getGeometryType()) ==
243 0 : wkbMultiPolygon &&
244 0 : wkbFlatten(poSubGeom->getGeometryType()) ==
245 : wkbMultiPolygon)
246 : {
247 0 : OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
248 0 : for (auto &&poMember : poSubGeom->toMultiPolygon())
249 : {
250 0 : poGeomColl->addGeometry(poMember);
251 : }
252 0 : delete poSubGeom;
253 : }
254 0 : else if (bTryToMakeMultipolygons &&
255 0 : wkbFlatten(poGeom->getGeometryType()) ==
256 : wkbMultiPolygon)
257 : {
258 0 : delete poGeom;
259 0 : delete poSubGeom;
260 0 : return GML_BuildOGRGeometryFromList(
261 : papsGeometry, false, bInvertAxisOrderIfLatLong,
262 : pszDefaultSRSName, bConsiderEPSGAsURN,
263 : eSwapCoordinates,
264 0 : nPseudoBoolGetSecondaryGeometryOption, hSRSCache);
265 : }
266 : else
267 : {
268 0 : poCollection = new OGRGeometryCollection();
269 0 : poCollection->addGeometryDirectly(poGeom);
270 0 : poGeom = poCollection;
271 : }
272 : }
273 0 : if (poCollection != nullptr)
274 : {
275 0 : poCollection->addGeometryDirectly(poSubGeom);
276 : }
277 : }
278 : }
279 : }
280 :
281 1408 : if (poGeom == nullptr)
282 7 : return nullptr;
283 :
284 1401 : std::string osWork;
285 1401 : const char *pszSRSName = GML_ExtractSrsNameFromGeometry(
286 : papsGeometry, osWork, bConsiderEPSGAsURN);
287 1401 : const char *pszNameLookup = pszSRSName;
288 1401 : if (pszNameLookup == nullptr)
289 993 : pszNameLookup = pszDefaultSRSName;
290 :
291 1401 : if (pszNameLookup != nullptr)
292 : {
293 870 : auto entry = OGRGML_SRSCache_GetInfo(hSRSCache, pszNameLookup);
294 435 : if (entry)
295 : {
296 435 : poGeom->assignSpatialReference(entry->poSRS);
297 866 : if ((eSwapCoordinates == GML_SWAP_AUTO &&
298 435 : entry->bInvertedAxisOrder && bInvertAxisOrderIfLatLong) ||
299 : eSwapCoordinates == GML_SWAP_YES)
300 : {
301 275 : poGeom->swapXY();
302 : }
303 : }
304 : }
305 966 : else if (eSwapCoordinates == GML_SWAP_YES)
306 : {
307 1 : poGeom->swapXY();
308 : }
309 :
310 1401 : return poGeom;
311 : }
312 :
313 : /************************************************************************/
314 : /* GML_GetSRSName() */
315 : /************************************************************************/
316 :
317 220 : char *GML_GetSRSName(const OGRSpatialReference *poSRS,
318 : OGRGMLSRSNameFormat eSRSNameFormat, bool *pbCoordSwap)
319 : {
320 220 : *pbCoordSwap = false;
321 220 : if (poSRS == nullptr)
322 116 : return CPLStrdup("");
323 :
324 104 : const auto &map = poSRS->GetDataAxisToSRSAxisMapping();
325 146 : if (eSRSNameFormat != SRSNAME_SHORT && map.size() >= 2 && map[0] == 2 &&
326 42 : map[1] == 1)
327 : {
328 42 : *pbCoordSwap = true;
329 : }
330 :
331 104 : const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
332 104 : const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
333 104 : if (nullptr != pszAuthName && nullptr != pszAuthCode)
334 : {
335 104 : if (eSRSNameFormat == SRSNAME_SHORT)
336 : {
337 9 : return CPLStrdup(
338 9 : CPLSPrintf(" srsName=\"%s:%s\"", pszAuthName, pszAuthCode));
339 : }
340 95 : else if (eSRSNameFormat == SRSNAME_OGC_URN)
341 : {
342 74 : return CPLStrdup(CPLSPrintf(" srsName=\"urn:ogc:def:crs:%s::%s\"",
343 74 : pszAuthName, pszAuthCode));
344 : }
345 21 : else if (eSRSNameFormat == SRSNAME_OGC_URL)
346 : {
347 21 : return CPLStrdup(CPLSPrintf(
348 : " srsName=\"http://www.opengis.net/def/crs/%s/0/%s\"",
349 21 : pszAuthName, pszAuthCode));
350 : }
351 : }
352 0 : return CPLStrdup("");
353 : }
354 :
355 : /************************************************************************/
356 : /* GML_IsLegitSRSName() */
357 : /************************************************************************/
358 :
359 98 : bool GML_IsLegitSRSName(const char *pszSRSName)
360 : {
361 :
362 98 : if (STARTS_WITH_CI(pszSRSName, "http"))
363 : {
364 6 : if (!(STARTS_WITH_CI(pszSRSName, "http://opengis.net/def/crs") ||
365 6 : STARTS_WITH_CI(pszSRSName, "http://www.opengis.net/def/crs")))
366 : {
367 0 : return false;
368 : }
369 : }
370 98 : return true;
371 : }
|