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 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "cpl_port.h"
30 : #include "gmlutils.h"
31 :
32 : #include <cstdio>
33 : #include <cstdlib>
34 : #include <cstring>
35 : #include <map>
36 : #include <string>
37 : #include <utility>
38 :
39 : #include "cpl_conv.h"
40 : #include "cpl_string.h"
41 : #include "ogr_api.h"
42 : #include "ogr_core.h"
43 : #include "ogr_p.h"
44 : #include "ogr_spatialref.h"
45 :
46 : /************************************************************************/
47 : /* GML_ExtractSrsNameFromGeometry() */
48 : /************************************************************************/
49 :
50 : const char *
51 1567 : GML_ExtractSrsNameFromGeometry(const CPLXMLNode *const *papsGeometry,
52 : std::string &osWork, bool bConsiderEPSGAsURN)
53 : {
54 1567 : if (papsGeometry[0] != nullptr && papsGeometry[1] == nullptr)
55 : {
56 : const char *pszSRSName =
57 1567 : CPLGetXMLValue(papsGeometry[0], "srsName", nullptr);
58 1567 : if (pszSRSName)
59 : {
60 526 : const int nLen = static_cast<int>(strlen(pszSRSName));
61 :
62 526 : if (STARTS_WITH(pszSRSName, "EPSG:") && bConsiderEPSGAsURN)
63 : {
64 36 : osWork.reserve(22 + nLen - 5);
65 36 : osWork.assign("urn:ogc:def:crs:EPSG::", 22);
66 36 : osWork.append(pszSRSName + 5, nLen - 5);
67 36 : return osWork.c_str();
68 : }
69 490 : else if (STARTS_WITH(pszSRSName,
70 : "http://www.opengis.net/gml/srs/epsg.xml#"))
71 : {
72 47 : osWork.reserve(5 + nLen - 40);
73 47 : osWork.assign("EPSG:", 5);
74 47 : osWork.append(pszSRSName + 40, nLen - 40);
75 47 : return osWork.c_str();
76 : }
77 : else
78 : {
79 443 : return pszSRSName;
80 : }
81 : }
82 : }
83 1041 : return nullptr;
84 : }
85 :
86 : /************************************************************************/
87 : /* GML_IsSRSLatLongOrder() */
88 : /************************************************************************/
89 :
90 443 : bool GML_IsSRSLatLongOrder(const char *pszSRSName)
91 : {
92 443 : if (pszSRSName == nullptr)
93 0 : return false;
94 :
95 443 : if (STARTS_WITH(pszSRSName, "urn:") &&
96 383 : strstr(pszSRSName, ":4326") != nullptr)
97 : {
98 : // Shortcut.
99 327 : return true;
100 : }
101 : /* fguuid:jgd20??.bl (Japanese FGD GML v4) */
102 116 : else if (EQUALN(pszSRSName, "fguuid:jgd2011.bl", 17) ||
103 114 : EQUALN(pszSRSName, "fguuid:jgd2001.bl", 17))
104 : {
105 2 : return true;
106 : }
107 114 : else if (!EQUALN(pszSRSName, "EPSG:", 5))
108 : {
109 73 : OGRSpatialReference oSRS;
110 73 : if (oSRS.SetFromUserInput(
111 : pszSRSName,
112 73 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
113 : OGRERR_NONE)
114 : {
115 133 : if (oSRS.EPSGTreatsAsLatLong() ||
116 60 : oSRS.EPSGTreatsAsNorthingEasting())
117 15 : return true;
118 : }
119 : }
120 99 : return false;
121 : }
122 :
123 : /************************************************************************/
124 : /* GML_BuildOGRGeometryFromList_CreateCache() */
125 : /************************************************************************/
126 :
127 : class SRSDesc
128 : {
129 : public:
130 : std::string osSRSName;
131 : bool bAxisInvert;
132 : OGRSpatialReference *poSRS;
133 :
134 940 : SRSDesc() : bAxisInvert(false), poSRS(nullptr)
135 : {
136 940 : }
137 : };
138 :
139 : class SRSCache
140 : {
141 : std::map<std::string, SRSDesc> oMap;
142 : SRSDesc oLastDesc;
143 :
144 : public:
145 730 : SRSCache()
146 730 : {
147 730 : }
148 :
149 730 : ~SRSCache()
150 730 : {
151 730 : std::map<std::string, SRSDesc>::iterator oIter;
152 940 : for (oIter = oMap.begin(); oIter != oMap.end(); ++oIter)
153 : {
154 210 : if (oIter->second.poSRS != nullptr)
155 208 : oIter->second.poSRS->Release();
156 : }
157 730 : }
158 :
159 409 : SRSDesc &Get(const std::string &osSRSName)
160 : {
161 409 : if (osSRSName == oLastDesc.osSRSName)
162 199 : return oLastDesc;
163 :
164 210 : std::map<std::string, SRSDesc>::iterator oIter = oMap.find(osSRSName);
165 210 : if (oIter != oMap.end())
166 : {
167 0 : oLastDesc = oIter->second;
168 0 : return oLastDesc;
169 : }
170 :
171 210 : oLastDesc.osSRSName = osSRSName;
172 210 : oLastDesc.bAxisInvert = GML_IsSRSLatLongOrder(osSRSName.c_str());
173 210 : oLastDesc.poSRS = new OGRSpatialReference();
174 210 : oLastDesc.poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
175 210 : if (oLastDesc.poSRS->SetFromUserInput(
176 : osSRSName.c_str(),
177 210 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
178 : OGRERR_NONE)
179 : {
180 2 : delete oLastDesc.poSRS;
181 2 : oLastDesc.poSRS = nullptr;
182 : }
183 210 : oMap[osSRSName] = oLastDesc;
184 210 : return oLastDesc;
185 : }
186 : };
187 :
188 730 : void *GML_BuildOGRGeometryFromList_CreateCache()
189 : {
190 730 : return new SRSCache();
191 : }
192 :
193 : /************************************************************************/
194 : /* GML_BuildOGRGeometryFromList_DestroyCache() */
195 : /************************************************************************/
196 :
197 730 : void GML_BuildOGRGeometryFromList_DestroyCache(void *hCacheSRS)
198 : {
199 730 : delete static_cast<SRSCache *>(hCacheSRS);
200 730 : }
201 :
202 : /************************************************************************/
203 : /* GML_BuildOGRGeometryFromList() */
204 : /************************************************************************/
205 :
206 1112 : OGRGeometry *GML_BuildOGRGeometryFromList(
207 : const CPLXMLNode *const *papsGeometry, bool bTryToMakeMultipolygons,
208 : bool bInvertAxisOrderIfLatLong, const char *pszDefaultSRSName,
209 : bool bConsiderEPSGAsURN, GMLSwapCoordinatesEnum eSwapCoordinates,
210 : int nPseudoBoolGetSecondaryGeometryOption, void *hCacheSRS,
211 : bool bFaceHoleNegative)
212 : {
213 1112 : OGRGeometry *poGeom = nullptr;
214 1112 : OGRGeometryCollection *poCollection = nullptr;
215 2224 : for (int i = 0; papsGeometry[i] != nullptr; i++)
216 : {
217 2224 : OGRGeometry *poSubGeom = GML2OGRGeometry_XMLNode(
218 1112 : papsGeometry[i], nPseudoBoolGetSecondaryGeometryOption, 0, 0, false,
219 : true, bFaceHoleNegative);
220 1112 : if (poSubGeom)
221 : {
222 1111 : if (poGeom == nullptr)
223 : {
224 1111 : poGeom = poSubGeom;
225 : }
226 : else
227 : {
228 0 : if (poCollection == nullptr)
229 : {
230 0 : if (bTryToMakeMultipolygons &&
231 0 : wkbFlatten(poGeom->getGeometryType()) == wkbPolygon &&
232 0 : wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon)
233 : {
234 : OGRGeometryCollection *poGeomColl =
235 0 : new OGRMultiPolygon();
236 0 : poGeomColl->addGeometryDirectly(poGeom);
237 0 : poGeomColl->addGeometryDirectly(poSubGeom);
238 0 : poGeom = poGeomColl;
239 : }
240 0 : else if (bTryToMakeMultipolygons &&
241 0 : wkbFlatten(poGeom->getGeometryType()) ==
242 0 : wkbMultiPolygon &&
243 0 : wkbFlatten(poSubGeom->getGeometryType()) ==
244 : wkbPolygon)
245 : {
246 0 : OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
247 0 : poGeomColl->addGeometryDirectly(poSubGeom);
248 : }
249 0 : else if (bTryToMakeMultipolygons &&
250 0 : wkbFlatten(poGeom->getGeometryType()) ==
251 0 : wkbMultiPolygon &&
252 0 : wkbFlatten(poSubGeom->getGeometryType()) ==
253 : wkbMultiPolygon)
254 : {
255 0 : OGRMultiPolygon *poGeomColl = poGeom->toMultiPolygon();
256 0 : for (auto &&poMember : poSubGeom->toMultiPolygon())
257 : {
258 0 : poGeomColl->addGeometry(poMember);
259 : }
260 0 : delete poSubGeom;
261 : }
262 0 : else if (bTryToMakeMultipolygons &&
263 0 : wkbFlatten(poGeom->getGeometryType()) ==
264 : wkbMultiPolygon)
265 : {
266 0 : delete poGeom;
267 0 : delete poSubGeom;
268 0 : return GML_BuildOGRGeometryFromList(
269 : papsGeometry, false, bInvertAxisOrderIfLatLong,
270 : pszDefaultSRSName, bConsiderEPSGAsURN,
271 : eSwapCoordinates,
272 0 : nPseudoBoolGetSecondaryGeometryOption, hCacheSRS);
273 : }
274 : else
275 : {
276 0 : poCollection = new OGRGeometryCollection();
277 0 : poCollection->addGeometryDirectly(poGeom);
278 0 : poGeom = poCollection;
279 : }
280 : }
281 0 : if (poCollection != nullptr)
282 : {
283 0 : poCollection->addGeometryDirectly(poSubGeom);
284 : }
285 : }
286 : }
287 : }
288 :
289 1112 : if (poGeom == nullptr)
290 1 : return nullptr;
291 :
292 1111 : std::string osWork;
293 1111 : const char *pszSRSName = GML_ExtractSrsNameFromGeometry(
294 : papsGeometry, osWork, bConsiderEPSGAsURN);
295 1111 : const char *pszNameLookup = pszSRSName;
296 1111 : if (pszNameLookup == nullptr)
297 723 : pszNameLookup = pszDefaultSRSName;
298 :
299 1111 : if (pszNameLookup != nullptr)
300 : {
301 409 : SRSCache *poSRSCache = static_cast<SRSCache *>(hCacheSRS);
302 409 : const SRSDesc &oSRSDesc = poSRSCache->Get(pszNameLookup);
303 409 : poGeom->assignSpatialReference(oSRSDesc.poSRS);
304 409 : if ((eSwapCoordinates == GML_SWAP_AUTO && oSRSDesc.bAxisInvert &&
305 151 : bInvertAxisOrderIfLatLong) ||
306 : eSwapCoordinates == GML_SWAP_YES)
307 : {
308 260 : poGeom->swapXY();
309 : }
310 : }
311 :
312 1111 : return poGeom;
313 : }
314 :
315 : /************************************************************************/
316 : /* GML_GetSRSName() */
317 : /************************************************************************/
318 :
319 201 : char *GML_GetSRSName(const OGRSpatialReference *poSRS,
320 : OGRGMLSRSNameFormat eSRSNameFormat, bool *pbCoordSwap)
321 : {
322 201 : *pbCoordSwap = false;
323 201 : if (poSRS == nullptr)
324 110 : return CPLStrdup("");
325 :
326 91 : const auto &map = poSRS->GetDataAxisToSRSAxisMapping();
327 121 : if (eSRSNameFormat != SRSNAME_SHORT && map.size() >= 2 && map[0] == 2 &&
328 30 : map[1] == 1)
329 : {
330 30 : *pbCoordSwap = true;
331 : }
332 :
333 91 : const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
334 91 : const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
335 91 : if (nullptr != pszAuthName && nullptr != pszAuthCode)
336 : {
337 91 : if (eSRSNameFormat == SRSNAME_SHORT)
338 : {
339 9 : return CPLStrdup(
340 9 : CPLSPrintf(" srsName=\"%s:%s\"", pszAuthName, pszAuthCode));
341 : }
342 82 : else if (eSRSNameFormat == SRSNAME_OGC_URN)
343 : {
344 61 : return CPLStrdup(CPLSPrintf(" srsName=\"urn:ogc:def:crs:%s::%s\"",
345 61 : pszAuthName, pszAuthCode));
346 : }
347 21 : else if (eSRSNameFormat == SRSNAME_OGC_URL)
348 : {
349 21 : return CPLStrdup(CPLSPrintf(
350 : " srsName=\"http://www.opengis.net/def/crs/%s/0/%s\"",
351 21 : pszAuthName, pszAuthCode));
352 : }
353 : }
354 0 : return CPLStrdup("");
355 : }
356 :
357 : /************************************************************************/
358 : /* GML_IsLegitSRSName() */
359 : /************************************************************************/
360 :
361 80 : bool GML_IsLegitSRSName(const char *pszSRSName)
362 : {
363 :
364 80 : if (STARTS_WITH_CI(pszSRSName, "http"))
365 : {
366 6 : if (!(STARTS_WITH_CI(pszSRSName, "http://opengis.net/def/crs") ||
367 6 : STARTS_WITH_CI(pszSRSName, "http://www.opengis.net/def/crs")))
368 : {
369 0 : return false;
370 : }
371 : }
372 80 : return true;
373 : }
|