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 1579 : GML_ExtractSrsNameFromGeometry(const CPLXMLNode *const *papsGeometry,
36 : std::string &osWork, bool bConsiderEPSGAsURN)
37 : {
38 1579 : if (papsGeometry[0] != nullptr && papsGeometry[1] == nullptr)
39 : {
40 : const char *pszSRSName =
41 1579 : CPLGetXMLValue(papsGeometry[0], "srsName", nullptr);
42 1579 : if (pszSRSName)
43 : {
44 531 : const int nLen = static_cast<int>(strlen(pszSRSName));
45 :
46 531 : 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 495 : 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 448 : return pszSRSName;
64 : }
65 : }
66 : }
67 1048 : return nullptr;
68 : }
69 :
70 : /************************************************************************/
71 : /* GML_IsSRSLatLongOrder() */
72 : /************************************************************************/
73 :
74 456 : bool GML_IsSRSLatLongOrder(const char *pszSRSName)
75 : {
76 456 : if (pszSRSName == nullptr)
77 0 : return false;
78 :
79 456 : if (STARTS_WITH(pszSRSName, "urn:") &&
80 385 : strstr(pszSRSName, ":4326") != nullptr)
81 : {
82 : // Shortcut.
83 327 : 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 430 : SRSDesc() = default;
121 :
122 215 : SRSDesc &operator=(SRSDesc &&other)
123 : {
124 215 : osSRSName = std::move(other.osSRSName);
125 215 : bAxisInvert = other.bAxisInvert;
126 215 : if (poSRS)
127 0 : poSRS->Release();
128 215 : poSRS = other.poSRS;
129 215 : other.poSRS = nullptr;
130 215 : return *this;
131 : }
132 :
133 430 : ~SRSDesc()
134 430 : {
135 430 : if (poSRS)
136 213 : poSRS->Release();
137 430 : }
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 758 : SRSCache() = default;
151 :
152 414 : const SRSDesc &Get(const std::string &osSRSName)
153 : {
154 414 : if (poLastDesc && osSRSName == poLastDesc->osSRSName)
155 199 : return *poLastDesc;
156 :
157 215 : std::map<std::string, SRSDesc>::iterator oIter = oMap.find(osSRSName);
158 215 : if (oIter != oMap.end())
159 : {
160 0 : poLastDesc = &(oIter->second);
161 0 : return *poLastDesc;
162 : }
163 :
164 215 : SRSDesc oDesc;
165 215 : oDesc.osSRSName = osSRSName;
166 215 : oDesc.bAxisInvert = GML_IsSRSLatLongOrder(osSRSName.c_str());
167 215 : oDesc.poSRS = new OGRSpatialReference();
168 215 : oDesc.poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
169 215 : if (oDesc.poSRS->SetFromUserInput(
170 : osSRSName.c_str(),
171 215 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
172 : OGRERR_NONE)
173 : {
174 2 : delete oDesc.poSRS;
175 2 : oDesc.poSRS = nullptr;
176 : }
177 215 : oMap[osSRSName] = std::move(oDesc);
178 215 : poLastDesc = &(oMap[osSRSName]);
179 215 : return *poLastDesc;
180 : }
181 : };
182 :
183 : } // namespace
184 :
185 758 : void *GML_BuildOGRGeometryFromList_CreateCache()
186 : {
187 758 : return new SRSCache();
188 : }
189 :
190 : /************************************************************************/
191 : /* GML_BuildOGRGeometryFromList_DestroyCache() */
192 : /************************************************************************/
193 :
194 758 : void GML_BuildOGRGeometryFromList_DestroyCache(void *hCacheSRS)
195 : {
196 758 : delete static_cast<SRSCache *>(hCacheSRS);
197 758 : }
198 :
199 : /************************************************************************/
200 : /* GML_BuildOGRGeometryFromList() */
201 : /************************************************************************/
202 :
203 1122 : 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 1122 : OGRGeometry *poGeom = nullptr;
211 1122 : OGRGeometryCollection *poCollection = nullptr;
212 2244 : for (int i = 0; papsGeometry[i] != nullptr; i++)
213 : {
214 2244 : OGRGeometry *poSubGeom = GML2OGRGeometry_XMLNode(
215 1122 : papsGeometry[i], nPseudoBoolGetSecondaryGeometryOption, 0, 0, false,
216 : true, bFaceHoleNegative);
217 1122 : if (poSubGeom)
218 : {
219 1120 : if (poGeom == nullptr)
220 : {
221 1120 : poGeom = poSubGeom;
222 : }
223 : else
224 : {
225 0 : if (poCollection == nullptr)
226 : {
227 0 : if (bTryToMakeMultipolygons &&
228 0 : wkbFlatten(poGeom->getGeometryType()) == wkbPolygon &&
229 0 : wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
230 : {
231 : OGRGeometryCollection *poGeomColl =
232 0 : new OGRMultiPolygon();
233 0 : poGeomColl->addGeometryDirectly(poGeom);
234 0 : poGeomColl->addGeometryDirectly(poSubGeom);
235 0 : poGeom = poGeomColl;
236 : }
237 0 : else if (bTryToMakeMultipolygons &&
238 0 : wkbFlatten(poGeom->getGeometryType()) ==
239 0 : wkbMultiPolygon &&
240 0 : wkbFlatten(poSubGeom->getGeometryType()) ==
241 : wkbPolygon)
242 : {
243 0 : OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
244 0 : poGeomColl->addGeometryDirectly(poSubGeom);
245 : }
246 0 : else if (bTryToMakeMultipolygons &&
247 0 : wkbFlatten(poGeom->getGeometryType()) ==
248 0 : wkbMultiPolygon &&
249 0 : wkbFlatten(poSubGeom->getGeometryType()) ==
250 : wkbMultiPolygon)
251 : {
252 0 : OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
253 0 : for (auto &&poMember : poSubGeom->toMultiPolygon())
254 : {
255 0 : poGeomColl->addGeometry(poMember);
256 : }
257 0 : delete poSubGeom;
258 : }
259 0 : else if (bTryToMakeMultipolygons &&
260 0 : wkbFlatten(poGeom->getGeometryType()) ==
261 : wkbMultiPolygon)
262 : {
263 0 : delete poGeom;
264 0 : delete poSubGeom;
265 0 : return GML_BuildOGRGeometryFromList(
266 : papsGeometry, false, bInvertAxisOrderIfLatLong,
267 : pszDefaultSRSName, bConsiderEPSGAsURN,
268 : eSwapCoordinates,
269 0 : nPseudoBoolGetSecondaryGeometryOption, hCacheSRS);
270 : }
271 : else
272 : {
273 0 : poCollection = new OGRGeometryCollection();
274 0 : poCollection->addGeometryDirectly(poGeom);
275 0 : poGeom = poCollection;
276 : }
277 : }
278 0 : if (poCollection != nullptr)
279 : {
280 0 : poCollection->addGeometryDirectly(poSubGeom);
281 : }
282 : }
283 : }
284 : }
285 :
286 1122 : if (poGeom == nullptr)
287 2 : return nullptr;
288 :
289 1120 : std::string osWork;
290 1120 : const char *pszSRSName = GML_ExtractSrsNameFromGeometry(
291 : papsGeometry, osWork, bConsiderEPSGAsURN);
292 1120 : const char *pszNameLookup = pszSRSName;
293 1120 : if (pszNameLookup == nullptr)
294 727 : pszNameLookup = pszDefaultSRSName;
295 :
296 1120 : if (pszNameLookup != nullptr)
297 : {
298 414 : SRSCache *poSRSCache = static_cast<SRSCache *>(hCacheSRS);
299 414 : const SRSDesc &oSRSDesc = poSRSCache->Get(pszNameLookup);
300 414 : poGeom->assignSpatialReference(oSRSDesc.poSRS);
301 414 : if ((eSwapCoordinates == GML_SWAP_AUTO && oSRSDesc.bAxisInvert &&
302 156 : bInvertAxisOrderIfLatLong) ||
303 : eSwapCoordinates == GML_SWAP_YES)
304 : {
305 260 : poGeom->swapXY();
306 : }
307 : }
308 :
309 1120 : return poGeom;
310 : }
311 :
312 : /************************************************************************/
313 : /* GML_GetSRSName() */
314 : /************************************************************************/
315 :
316 220 : char *GML_GetSRSName(const OGRSpatialReference *poSRS,
317 : OGRGMLSRSNameFormat eSRSNameFormat, bool *pbCoordSwap)
318 : {
319 220 : *pbCoordSwap = false;
320 220 : if (poSRS == nullptr)
321 116 : return CPLStrdup("");
322 :
323 104 : const auto &map = poSRS->GetDataAxisToSRSAxisMapping();
324 146 : if (eSRSNameFormat != SRSNAME_SHORT && map.size() >= 2 && map[0] == 2 &&
325 42 : map[1] == 1)
326 : {
327 42 : *pbCoordSwap = true;
328 : }
329 :
330 104 : const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
331 104 : const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
332 104 : if (nullptr != pszAuthName && nullptr != pszAuthCode)
333 : {
334 104 : if (eSRSNameFormat == SRSNAME_SHORT)
335 : {
336 9 : return CPLStrdup(
337 9 : CPLSPrintf(" srsName=\"%s:%s\"", pszAuthName, pszAuthCode));
338 : }
339 95 : else if (eSRSNameFormat == SRSNAME_OGC_URN)
340 : {
341 74 : return CPLStrdup(CPLSPrintf(" srsName=\"urn:ogc:def:crs:%s::%s\"",
342 74 : pszAuthName, pszAuthCode));
343 : }
344 21 : else if (eSRSNameFormat == SRSNAME_OGC_URL)
345 : {
346 21 : return CPLStrdup(CPLSPrintf(
347 : " srsName=\"http://www.opengis.net/def/crs/%s/0/%s\"",
348 21 : pszAuthName, pszAuthCode));
349 : }
350 : }
351 0 : return CPLStrdup("");
352 : }
353 :
354 : /************************************************************************/
355 : /* GML_IsLegitSRSName() */
356 : /************************************************************************/
357 :
358 85 : bool GML_IsLegitSRSName(const char *pszSRSName)
359 : {
360 :
361 85 : if (STARTS_WITH_CI(pszSRSName, "http"))
362 : {
363 6 : if (!(STARTS_WITH_CI(pszSRSName, "http://opengis.net/def/crs") ||
364 6 : STARTS_WITH_CI(pszSRSName, "http://www.opengis.net/def/crs")))
365 : {
366 0 : return false;
367 : }
368 : }
369 85 : return true;
370 : }
|