/src/gdal/ogr/ogrsf_frmts/ili/ili2reader.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: Interlis 2 Reader |
4 | | * Purpose: Implementation of ILI2Reader class. |
5 | | * Author: Markus Schnider, Sourcepole AG |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG |
9 | | * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "ili2readerp.h" |
15 | | #include "ogr_ili2.h" |
16 | | #include "cpl_conv.h" |
17 | | #include "cpl_string.h" |
18 | | |
19 | | #include "ili2reader.h" |
20 | | |
21 | | using namespace std; |
22 | | |
23 | | // |
24 | | // constants |
25 | | // |
26 | | static const char *const ILI2_TID = "TID"; |
27 | | static const XMLCh xmlch_ILI2_TID[] = {'T', 'I', 'D', '\0'}; |
28 | | static const XMLCh ILI2_REF[] = {'R', 'E', 'F', '\0'}; |
29 | | |
30 | | constexpr int ILI2_STRING_TYPE = 0; |
31 | | constexpr int ILI2_COORD_TYPE = 1; |
32 | | constexpr int ILI2_ARC_TYPE = 2; |
33 | | constexpr int ILI2_POLYLINE_TYPE = 4; |
34 | | constexpr int ILI2_BOUNDARY_TYPE = 8; |
35 | | constexpr int ILI2_AREA_TYPE = 16; // also SURFACE |
36 | | constexpr int ILI2_GEOMCOLL_TYPE = 32; |
37 | | |
38 | | static const char *const ILI2_COORD = "COORD"; |
39 | | static const char *const ILI2_ARC = "ARC"; |
40 | | static const char *const ILI2_POLYLINE = "POLYLINE"; |
41 | | static const char *const ILI2_BOUNDARY = "BOUNDARY"; |
42 | | static const char *const ILI2_AREA = "AREA"; |
43 | | static const char *const ILI2_SURFACE = "SURFACE"; |
44 | | |
45 | | namespace gdal |
46 | | { |
47 | | namespace ili2 |
48 | | { |
49 | | // |
50 | | // helper functions |
51 | | // |
52 | | int cmpStr(const string &s1, const string &s2) |
53 | 912k | { |
54 | 912k | string::const_iterator p1 = s1.begin(); |
55 | 912k | string::const_iterator p2 = s2.begin(); |
56 | | |
57 | 3.53M | while (p1 != s1.end() && p2 != s2.end()) |
58 | 3.50M | { |
59 | 3.50M | if (CPLToupper(static_cast<unsigned char>(*p1)) != |
60 | 3.50M | CPLToupper(static_cast<unsigned char>(*p2))) |
61 | 884k | return (CPLToupper(static_cast<unsigned char>(*p1)) < |
62 | 884k | CPLToupper(static_cast<unsigned char>(*p2))) |
63 | 884k | ? -1 |
64 | 884k | : 1; |
65 | 2.61M | ++p1; |
66 | 2.61M | ++p2; |
67 | 2.61M | } |
68 | | |
69 | 28.2k | return (s2.size() == s1.size()) ? 0 : (s1.size() < s2.size()) ? -1 : 1; |
70 | 912k | } |
71 | | |
72 | | string ltrim(const string &tmpstr) |
73 | 36.4k | { |
74 | 36.4k | size_t i = 0; |
75 | 46.0k | while (i < tmpstr.length() && (tmpstr[i] == ' ' || tmpstr[i] == '\t' || |
76 | 45.1k | tmpstr[i] == '\r' || tmpstr[i] == '\n')) |
77 | 9.63k | ++i; |
78 | 36.4k | return i > 0 ? tmpstr.substr(i, tmpstr.length() - i) : tmpstr; |
79 | 36.4k | } |
80 | | |
81 | | string rtrim(const string &tmpstr) |
82 | 36.4k | { |
83 | 36.4k | if (tmpstr.empty()) |
84 | 905 | return tmpstr; |
85 | 35.5k | size_t i = tmpstr.length() - 1U; |
86 | 35.9k | while (tmpstr[i] == ' ' || tmpstr[i] == '\t' || tmpstr[i] == '\r' || |
87 | 35.9k | tmpstr[i] == '\n') |
88 | 409 | --i; |
89 | 35.5k | return i < tmpstr.length() - 1 ? tmpstr.substr(0, i + 1) : tmpstr; |
90 | 36.4k | } |
91 | | |
92 | | string trim(const string &tmpstr) |
93 | 36.4k | { |
94 | 36.4k | auto ret = ltrim(tmpstr); |
95 | 36.4k | ret = rtrim(ret); |
96 | 36.4k | return ret; |
97 | 36.4k | } |
98 | | } // namespace ili2 |
99 | | } // namespace gdal |
100 | | |
101 | | using namespace gdal::ili2; |
102 | | |
103 | | static int getGeometryTypeOfElem(DOMElement *elem) |
104 | 123k | { |
105 | 123k | int type = ILI2_STRING_TYPE; |
106 | 123k | if (elem == nullptr) |
107 | 0 | return type; |
108 | 123k | char *pszTagName = XMLString::transcode(elem->getTagName()); |
109 | | |
110 | 123k | if (elem->getNodeType() == DOMNode::ELEMENT_NODE) |
111 | 123k | { |
112 | 123k | if (cmpStr(ILI2_COORD, pszTagName) == 0) |
113 | 0 | { |
114 | 0 | type = ILI2_COORD_TYPE; |
115 | 0 | } |
116 | 123k | else if (cmpStr(ILI2_ARC, pszTagName) == 0) |
117 | 0 | { |
118 | 0 | type = ILI2_ARC_TYPE; |
119 | 0 | } |
120 | 123k | else if (cmpStr(ILI2_POLYLINE, pszTagName) == 0) |
121 | 0 | { |
122 | 0 | type = ILI2_POLYLINE_TYPE; |
123 | 0 | } |
124 | 123k | else if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0) |
125 | 0 | { |
126 | 0 | type = ILI2_BOUNDARY_TYPE; |
127 | 0 | } |
128 | 123k | else if (cmpStr(ILI2_AREA, pszTagName) == 0) |
129 | 0 | { |
130 | 0 | type = ILI2_AREA_TYPE; |
131 | 0 | } |
132 | 123k | else if (cmpStr(ILI2_SURFACE, pszTagName) == 0) |
133 | 159 | { |
134 | 159 | type = ILI2_AREA_TYPE; |
135 | 159 | } |
136 | 123k | } |
137 | 123k | XMLString::release(&pszTagName); |
138 | 123k | return type; |
139 | 123k | } |
140 | | |
141 | | static char *getObjValue(DOMElement *elem) |
142 | 43.4k | { |
143 | 43.4k | DOMNode *child = elem->getFirstChild(); |
144 | 43.4k | if ((child != nullptr) && (child->getNodeType() == DOMNode::TEXT_NODE)) |
145 | 34.2k | { |
146 | 34.2k | return CPLStrdup(transcode(child->getNodeValue())); |
147 | 34.2k | } |
148 | | |
149 | 9.18k | return nullptr; |
150 | 43.4k | } |
151 | | |
152 | | static char *getREFValue(DOMElement *elem) |
153 | 9.18k | { |
154 | 9.18k | CPLString osREFValue(transcode(elem->getAttribute(ILI2_REF))); |
155 | 9.18k | return CPLStrdup(osREFValue); |
156 | 9.18k | } |
157 | | |
158 | | static OGRPoint *getPoint(DOMElement *elem) |
159 | 4.23k | { |
160 | | // elem -> COORD (or ARC) |
161 | 4.23k | DOMElement *coordElem = dynamic_cast<DOMElement *>(elem->getFirstChild()); |
162 | 4.23k | if (coordElem == nullptr) |
163 | 0 | return nullptr; |
164 | 4.23k | OGRPoint *pt = new OGRPoint(); |
165 | | |
166 | 12.6k | while (coordElem != nullptr) |
167 | 8.46k | { |
168 | 8.46k | char *pszTagName = XMLString::transcode(coordElem->getTagName()); |
169 | 8.46k | char *pszObjValue = getObjValue(coordElem); |
170 | 8.46k | if (pszObjValue) |
171 | 8.46k | { |
172 | 8.46k | if (cmpStr("C1", pszTagName) == 0) |
173 | 4.23k | pt->setX(CPLAtof(pszObjValue)); |
174 | 4.23k | else if (cmpStr("C2", pszTagName) == 0) |
175 | 4.23k | pt->setY(CPLAtof(pszObjValue)); |
176 | 0 | else if (cmpStr("C3", pszTagName) == 0) |
177 | 0 | pt->setZ(CPLAtof(pszObjValue)); |
178 | 8.46k | } |
179 | 8.46k | CPLFree(pszObjValue); |
180 | 8.46k | XMLString::release(&pszTagName); |
181 | 8.46k | coordElem = dynamic_cast<DOMElement *>(coordElem->getNextSibling()); |
182 | 8.46k | } |
183 | 4.23k | pt->flattenTo2D(); |
184 | 4.23k | return pt; |
185 | 4.23k | } |
186 | | |
187 | | OGRCircularString *ILI2Reader::getArc(DOMElement *elem) |
188 | 0 | { |
189 | | // previous point -> start point |
190 | 0 | auto elemPrev = dynamic_cast<DOMElement *>(elem->getPreviousSibling()); |
191 | 0 | if (elemPrev == nullptr) |
192 | 0 | return nullptr; |
193 | 0 | OGRPoint *ptStart = getPoint(elemPrev); // COORD or ARC |
194 | 0 | if (ptStart == nullptr) |
195 | 0 | return nullptr; |
196 | | |
197 | | // elem -> ARC |
198 | 0 | OGRCircularString *arc = new OGRCircularString(); |
199 | | // end point |
200 | 0 | OGRPoint *ptEnd = new OGRPoint(); |
201 | | // point on the arc |
202 | 0 | OGRPoint *ptOnArc = new OGRPoint(); |
203 | | // double radius = 0; // radius |
204 | |
|
205 | 0 | DOMElement *arcElem = dynamic_cast<DOMElement *>(elem->getFirstChild()); |
206 | 0 | while (arcElem != nullptr) |
207 | 0 | { |
208 | 0 | char *pszTagName = XMLString::transcode(arcElem->getTagName()); |
209 | 0 | char *pszObjValue = getObjValue(arcElem); |
210 | 0 | if (pszObjValue) |
211 | 0 | { |
212 | 0 | if (cmpStr("C1", pszTagName) == 0) |
213 | 0 | ptEnd->setX(CPLAtof(pszObjValue)); |
214 | 0 | else if (cmpStr("C2", pszTagName) == 0) |
215 | 0 | ptEnd->setY(CPLAtof(pszObjValue)); |
216 | 0 | else if (cmpStr("C3", pszTagName) == 0) |
217 | 0 | ptEnd->setZ(CPLAtof(pszObjValue)); |
218 | 0 | else if (cmpStr("A1", pszTagName) == 0) |
219 | 0 | ptOnArc->setX(CPLAtof(pszObjValue)); |
220 | 0 | else if (cmpStr("A2", pszTagName) == 0) |
221 | 0 | ptOnArc->setY(CPLAtof(pszObjValue)); |
222 | 0 | else if (cmpStr("A3", pszTagName) == 0) |
223 | 0 | ptOnArc->setZ(CPLAtof(pszObjValue)); |
224 | 0 | else if (cmpStr("R", pszTagName) == 0) |
225 | 0 | { |
226 | | // radius = CPLAtof(pszObjValue); |
227 | 0 | } |
228 | 0 | } |
229 | 0 | CPLFree(pszObjValue); |
230 | 0 | XMLString::release(&pszTagName); |
231 | 0 | arcElem = dynamic_cast<DOMElement *>(arcElem->getNextSibling()); |
232 | 0 | } |
233 | 0 | arc->addPoint(ptStart); |
234 | 0 | arc->addPoint(ptOnArc); |
235 | 0 | arc->addPoint(ptEnd); |
236 | 0 | delete ptStart; |
237 | 0 | delete ptOnArc; |
238 | 0 | delete ptEnd; |
239 | 0 | return arc; |
240 | 0 | } |
241 | | |
242 | | static OGRCompoundCurve *getPolyline(DOMElement *elem) |
243 | 80 | { |
244 | | // elem -> POLYLINE |
245 | 80 | OGRCompoundCurve *ogrCurve = new OGRCompoundCurve(); |
246 | 80 | OGRLineString *ls = new OGRLineString(); |
247 | | |
248 | 80 | DOMElement *lineElem = dynamic_cast<DOMElement *>(elem->getFirstChild()); |
249 | 4.31k | while (lineElem != nullptr) |
250 | 4.23k | { |
251 | 4.23k | char *pszTagName = XMLString::transcode(lineElem->getTagName()); |
252 | 4.23k | if (cmpStr(ILI2_COORD, pszTagName) == 0) |
253 | 4.23k | { |
254 | 4.23k | OGRPoint *poPoint = getPoint(lineElem); |
255 | 4.23k | if (poPoint) |
256 | 4.23k | { |
257 | 4.23k | ls->addPoint(poPoint); |
258 | 4.23k | delete poPoint; |
259 | 4.23k | } |
260 | 4.23k | } |
261 | 0 | else if (cmpStr(ILI2_ARC, pszTagName) == 0) |
262 | 0 | { |
263 | | // Finish line and start arc |
264 | 0 | if (ls->getNumPoints() > 1) |
265 | 0 | { |
266 | 0 | ogrCurve->addCurveDirectly(ls); |
267 | 0 | ls = new OGRLineString(); |
268 | 0 | } |
269 | 0 | else |
270 | 0 | { |
271 | 0 | ls->empty(); |
272 | 0 | } |
273 | 0 | OGRCircularString *arc = new OGRCircularString(); |
274 | | // end point |
275 | 0 | OGRPoint *ptEnd = new OGRPoint(); |
276 | | // point on the arc |
277 | 0 | OGRPoint *ptOnArc = new OGRPoint(); |
278 | | // radius |
279 | | // double radius = 0; |
280 | |
|
281 | 0 | DOMElement *arcElem = |
282 | 0 | dynamic_cast<DOMElement *>(lineElem->getFirstChild()); |
283 | 0 | while (arcElem != nullptr) |
284 | 0 | { |
285 | 0 | char *pszTagName2 = XMLString::transcode(arcElem->getTagName()); |
286 | 0 | char *pszObjValue = getObjValue(arcElem); |
287 | 0 | if (pszObjValue) |
288 | 0 | { |
289 | 0 | if (cmpStr("C1", pszTagName2) == 0) |
290 | 0 | ptEnd->setX(CPLAtof(pszObjValue)); |
291 | 0 | else if (cmpStr("C2", pszTagName2) == 0) |
292 | 0 | ptEnd->setY(CPLAtof(pszObjValue)); |
293 | 0 | else if (cmpStr("C3", pszTagName2) == 0) |
294 | 0 | ptEnd->setZ(CPLAtof(pszObjValue)); |
295 | 0 | else if (cmpStr("A1", pszTagName2) == 0) |
296 | 0 | ptOnArc->setX(CPLAtof(pszObjValue)); |
297 | 0 | else if (cmpStr("A2", pszTagName2) == 0) |
298 | 0 | ptOnArc->setY(CPLAtof(pszObjValue)); |
299 | 0 | else if (cmpStr("A3", pszTagName2) == 0) |
300 | 0 | ptOnArc->setZ(CPLAtof(pszObjValue)); |
301 | 0 | else if (cmpStr("R", pszTagName2) == 0) |
302 | 0 | { |
303 | | // radius = CPLAtof(pszObjValue); |
304 | 0 | } |
305 | 0 | } |
306 | 0 | CPLFree(pszObjValue); |
307 | 0 | XMLString::release(&pszTagName2); |
308 | |
|
309 | 0 | arcElem = dynamic_cast<DOMElement *>(arcElem->getNextSibling()); |
310 | 0 | } |
311 | |
|
312 | 0 | auto elemPrev = |
313 | 0 | dynamic_cast<DOMElement *>(lineElem->getPreviousSibling()); |
314 | 0 | if (elemPrev) |
315 | 0 | { |
316 | 0 | OGRPoint *ptStart = getPoint(elemPrev); // COORD or ARC |
317 | 0 | if (ptStart) |
318 | 0 | arc->addPoint(ptStart); |
319 | 0 | delete ptStart; |
320 | 0 | } |
321 | 0 | arc->addPoint(ptOnArc); |
322 | 0 | arc->addPoint(ptEnd); |
323 | 0 | ogrCurve->addCurveDirectly(arc); |
324 | | |
325 | | // Add arc endpoint as next start point, if COORD sequence follows. |
326 | 0 | DOMElement *nextElem = |
327 | 0 | dynamic_cast<DOMElement *>(lineElem->getNextSibling()); |
328 | 0 | if (nextElem) |
329 | 0 | { |
330 | 0 | char *nextTagName = |
331 | 0 | XMLString::transcode(nextElem->getTagName()); |
332 | 0 | if (cmpStr(ILI2_COORD, nextTagName) == 0) |
333 | 0 | { |
334 | 0 | ls->addPoint(ptEnd); |
335 | 0 | } |
336 | 0 | XMLString::release(&nextTagName); |
337 | 0 | } |
338 | |
|
339 | 0 | delete ptEnd; |
340 | 0 | delete ptOnArc; |
341 | 0 | } /* else { // TODO: StructureValue in Polyline not yet supported |
342 | | } */ |
343 | 4.23k | XMLString::release(&pszTagName); |
344 | | |
345 | 4.23k | lineElem = dynamic_cast<DOMElement *>(lineElem->getNextSibling()); |
346 | 4.23k | } |
347 | | |
348 | 80 | if (ls->getNumPoints() > 1) |
349 | 80 | { |
350 | 80 | ogrCurve->addCurveDirectly(ls); |
351 | 80 | } |
352 | 0 | else |
353 | 0 | { |
354 | 0 | delete ls; |
355 | 0 | } |
356 | 80 | return ogrCurve; |
357 | 80 | } |
358 | | |
359 | | static OGRCompoundCurve *getBoundary(DOMElement *elem) |
360 | 80 | { |
361 | | |
362 | 80 | DOMElement *lineElem = dynamic_cast<DOMElement *>(elem->getFirstChild()); |
363 | 80 | if (lineElem != nullptr) |
364 | 80 | { |
365 | 80 | char *pszTagName = XMLString::transcode(lineElem->getTagName()); |
366 | 80 | if (cmpStr(ILI2_POLYLINE, pszTagName) == 0) |
367 | 80 | { |
368 | 80 | XMLString::release(&pszTagName); |
369 | 80 | return getPolyline(lineElem); |
370 | 80 | } |
371 | 0 | XMLString::release(&pszTagName); |
372 | 0 | } |
373 | | |
374 | 0 | return new OGRCompoundCurve(); |
375 | 80 | } |
376 | | |
377 | | static OGRCurvePolygon *getPolygon(DOMElement *elem) |
378 | 78 | { |
379 | 78 | OGRCurvePolygon *pg = new OGRCurvePolygon(); |
380 | | |
381 | 78 | DOMElement *boundaryElem = |
382 | 78 | dynamic_cast<DOMElement *>(elem->getFirstChild()); // outer boundary |
383 | 158 | while (boundaryElem != nullptr) |
384 | 80 | { |
385 | 80 | char *pszTagName = XMLString::transcode(boundaryElem->getTagName()); |
386 | 80 | if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0) |
387 | 80 | { |
388 | 80 | OGRCompoundCurve *poCC = getBoundary(boundaryElem); |
389 | 80 | if (pg->addRingDirectly(poCC) != OGRERR_NONE) |
390 | 0 | { |
391 | 0 | delete poCC; |
392 | 0 | } |
393 | 80 | } |
394 | 80 | XMLString::release(&pszTagName); |
395 | 80 | boundaryElem = dynamic_cast<DOMElement *>( |
396 | 80 | boundaryElem->getNextSibling()); // inner boundaries |
397 | 80 | } |
398 | | |
399 | 78 | return pg; |
400 | 78 | } |
401 | | |
402 | | OGRGeometry *ILI2Reader::getGeometry(DOMElement *elem, int type) |
403 | 78 | { |
404 | 78 | OGRGeometryCollection *gm = new OGRGeometryCollection(); |
405 | | |
406 | 78 | DOMElement *childElem = elem; |
407 | 78 | while (childElem != nullptr) |
408 | 78 | { |
409 | 78 | char *pszTagName = XMLString::transcode(childElem->getTagName()); |
410 | 78 | switch (type) |
411 | 78 | { |
412 | 0 | case ILI2_COORD_TYPE: |
413 | 0 | if (cmpStr(ILI2_COORD, pszTagName) == 0) |
414 | 0 | { |
415 | 0 | delete gm; |
416 | 0 | XMLString::release(&pszTagName); |
417 | 0 | return getPoint(childElem); |
418 | 0 | } |
419 | 0 | break; |
420 | 0 | case ILI2_ARC_TYPE: |
421 | | // is it possible here? It have to be a ARC or COORD before |
422 | | // (getPreviousSibling) |
423 | 0 | if (cmpStr(ILI2_ARC, pszTagName) == 0) |
424 | 0 | { |
425 | 0 | delete gm; |
426 | 0 | XMLString::release(&pszTagName); |
427 | 0 | return getArc(childElem); |
428 | 0 | } |
429 | 0 | break; |
430 | 0 | case ILI2_POLYLINE_TYPE: |
431 | 0 | if (cmpStr(ILI2_POLYLINE, pszTagName) == 0) |
432 | 0 | { |
433 | 0 | delete gm; |
434 | 0 | XMLString::release(&pszTagName); |
435 | 0 | return getPolyline(childElem); |
436 | 0 | } |
437 | 0 | break; |
438 | 0 | case ILI2_BOUNDARY_TYPE: |
439 | 0 | if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0) |
440 | 0 | { |
441 | 0 | delete gm; |
442 | 0 | XMLString::release(&pszTagName); |
443 | 0 | return getPolyline(childElem); |
444 | 0 | } |
445 | 0 | break; |
446 | 78 | case ILI2_AREA_TYPE: |
447 | 78 | if ((cmpStr(ILI2_AREA, pszTagName) == 0) || |
448 | 78 | (cmpStr(ILI2_SURFACE, pszTagName) == 0)) |
449 | 78 | { |
450 | 78 | delete gm; |
451 | 78 | XMLString::release(&pszTagName); |
452 | 78 | return getPolygon(childElem); |
453 | 78 | } |
454 | 0 | break; |
455 | 0 | default: |
456 | 0 | if (type >= ILI2_GEOMCOLL_TYPE) |
457 | 0 | { |
458 | 0 | int subType = getGeometryTypeOfElem(childElem); //???? |
459 | 0 | OGRGeometry *poSubGeom = getGeometry(childElem, subType); |
460 | 0 | if (poSubGeom) |
461 | 0 | gm->addGeometryDirectly(poSubGeom); |
462 | 0 | } |
463 | 0 | break; |
464 | 78 | } |
465 | 0 | XMLString::release(&pszTagName); |
466 | | |
467 | | // GEOMCOLL |
468 | 0 | childElem = dynamic_cast<DOMElement *>(childElem->getNextSibling()); |
469 | 0 | } |
470 | | |
471 | 0 | return gm; |
472 | 78 | } |
473 | | |
474 | | int ILI2Reader::ReadModel(OGRILI2DataSource *poDS, ImdReader *poImdReader, |
475 | | const char *modelFilename) |
476 | 0 | { |
477 | 0 | poImdReader->ReadModel(modelFilename); |
478 | 0 | for (FeatureDefnInfos::const_iterator it = |
479 | 0 | poImdReader->featureDefnInfos.begin(); |
480 | 0 | it != poImdReader->featureDefnInfos.end(); ++it) |
481 | 0 | { |
482 | 0 | m_listLayer.push_back(std::make_unique<OGRILI2Layer>( |
483 | 0 | it->GetTableDefnRef(), it->poGeomFieldInfos, poDS)); |
484 | 0 | } |
485 | 0 | return 0; |
486 | 0 | } |
487 | | |
488 | | // Detect field name of value element |
489 | | static char *fieldName(DOMElement *elem) |
490 | 60.2k | { |
491 | 60.2k | DOMNode *node = elem; |
492 | 60.2k | if (getGeometryTypeOfElem(elem)) |
493 | 78 | { |
494 | 78 | int depth = 0; // Depth of value elem node |
495 | 468 | for (node = elem; node; node = node->getParentNode()) |
496 | 390 | ++depth; |
497 | | // Field name is on level 4 |
498 | 78 | node = elem; |
499 | 156 | for (int d = 0; d < depth - 4; ++d) |
500 | 78 | node = node->getParentNode(); |
501 | 78 | } |
502 | 60.2k | if (node == nullptr) |
503 | 0 | { |
504 | 0 | CPLError(CE_Failure, CPLE_AssertionFailed, "node == NULL"); |
505 | 0 | return CPLStrdup("***bug***"); |
506 | 0 | } |
507 | 60.2k | return CPLStrdup(transcode(node->getNodeName())); |
508 | 60.2k | } |
509 | | |
510 | | void ILI2Reader::setFieldDefn(OGRFeatureDefn *featureDef, DOMElement *elem) |
511 | 1.72k | { |
512 | 1.72k | int type = 0; |
513 | | // recursively search children |
514 | 1.72k | for (DOMNode *childNode = elem->getFirstChild(); |
515 | 4.94k | type == 0 && childNode && |
516 | 4.94k | childNode->getNodeType() == DOMNode::ELEMENT_NODE; |
517 | 3.22k | childNode = childNode->getNextSibling()) |
518 | 3.22k | { |
519 | 3.22k | DOMElement *childElem = dynamic_cast<DOMElement *>(childNode); |
520 | 3.22k | CPLAssert(childElem); |
521 | 3.22k | type = getGeometryTypeOfElem(childElem); |
522 | 3.22k | if (type == 0) |
523 | 3.21k | { |
524 | 3.21k | if (childElem->getFirstChild() && |
525 | 3.21k | childElem->getFirstChild()->getNodeType() == |
526 | 2.43k | DOMNode::ELEMENT_NODE) |
527 | 556 | { |
528 | 556 | setFieldDefn(featureDef, childElem); |
529 | 556 | } |
530 | 2.66k | else |
531 | 2.66k | { |
532 | 2.66k | char *fName = fieldName(childElem); |
533 | 2.66k | if (featureDef->GetFieldIndex(fName) == -1) |
534 | 2.58k | { |
535 | 2.58k | CPLDebug("OGR_ILI", "AddFieldDefn: %s", fName); |
536 | 2.58k | OGRFieldDefn oFieldDefn(fName, OFTString); |
537 | 2.58k | featureDef->AddFieldDefn(&oFieldDefn); |
538 | 2.58k | } |
539 | 2.66k | CPLFree(fName); |
540 | 2.66k | } |
541 | 3.21k | } |
542 | 3.22k | } |
543 | 1.72k | } |
544 | | |
545 | | void ILI2Reader::SetFieldValues(OGRFeature *feature, DOMElement *elem) |
546 | 16.4k | { |
547 | 16.4k | int type = 0; |
548 | | // recursively search children |
549 | 16.4k | for (DOMNode *childNode = elem->getFirstChild(); |
550 | 76.7k | type == 0 && childNode && |
551 | 76.7k | childNode->getNodeType() == DOMNode::ELEMENT_NODE; |
552 | 60.2k | childNode = childNode->getNextSibling()) |
553 | 60.2k | { |
554 | 60.2k | DOMElement *childElem = dynamic_cast<DOMElement *>(childNode); |
555 | 60.2k | CPLAssert(childElem); |
556 | 60.2k | type = getGeometryTypeOfElem(childElem); |
557 | 60.2k | if (type == 0) |
558 | 60.1k | { |
559 | 60.1k | if (childElem->getFirstChild() && |
560 | 60.1k | childElem->getFirstChild()->getNodeType() == |
561 | 28.5k | DOMNode::ELEMENT_NODE) |
562 | 2.58k | { |
563 | 2.58k | SetFieldValues(feature, childElem); |
564 | 2.58k | } |
565 | 57.5k | else |
566 | 57.5k | { |
567 | 57.5k | char *fName = fieldName(childElem); |
568 | 57.5k | int fIndex = feature->GetFieldIndex(fName); |
569 | 57.5k | if (fIndex != -1) |
570 | 34.9k | { |
571 | 34.9k | char *objVal = getObjValue(childElem); |
572 | 34.9k | if (objVal == nullptr) |
573 | 9.18k | objVal = getREFValue(childElem); // only to try |
574 | 34.9k | feature->SetField(fIndex, objVal); |
575 | 34.9k | CPLFree(objVal); |
576 | 34.9k | } |
577 | 22.6k | else |
578 | 22.6k | { |
579 | 22.6k | CPLDebug("OGR_ILI", "Attribute '%s' not found", fName); |
580 | 22.6k | m_missAttrs.push_back(fName); |
581 | 22.6k | } |
582 | 57.5k | CPLFree(fName); |
583 | 57.5k | } |
584 | 60.1k | } |
585 | 79 | else |
586 | 79 | { |
587 | 79 | char *fName = fieldName(childElem); |
588 | 79 | int fIndex = feature->GetGeomFieldIndex(fName); |
589 | 79 | OGRGeometry *geom = getGeometry(childElem, type); |
590 | 79 | if (geom) |
591 | 78 | { |
592 | 78 | if (fIndex == -1) |
593 | 78 | { // Unknown model |
594 | 78 | feature->SetGeometryDirectly(geom); |
595 | 78 | } |
596 | 0 | else |
597 | 0 | { |
598 | 0 | OGRwkbGeometryType geomType = |
599 | 0 | feature->GetGeomFieldDefnRef(fIndex)->GetType(); |
600 | 0 | if (geomType == wkbMultiLineString || |
601 | 0 | geomType == wkbPolygon) |
602 | 0 | { |
603 | 0 | feature->SetGeomFieldDirectly( |
604 | 0 | fIndex, geom->getLinearGeometry()); |
605 | 0 | delete geom; |
606 | 0 | } |
607 | 0 | else |
608 | 0 | { |
609 | 0 | feature->SetGeomFieldDirectly(fIndex, geom); |
610 | 0 | } |
611 | 0 | } |
612 | 78 | } |
613 | 79 | CPLFree(fName); |
614 | 79 | } |
615 | 60.2k | } |
616 | 16.4k | } |
617 | | |
618 | | // |
619 | | // ILI2Reader |
620 | | // |
621 | | IILI2Reader::~IILI2Reader() |
622 | 398 | { |
623 | 398 | } |
624 | | |
625 | | ILI2Reader::ILI2Reader() |
626 | 398 | : m_pszFilename(nullptr), m_poILI2Handler(nullptr), m_poSAXReader(nullptr), |
627 | 398 | m_bReadStarted(FALSE), m_bXercesInitialized(false) |
628 | 398 | { |
629 | 398 | SetupParser(); |
630 | 398 | } |
631 | | |
632 | | ILI2Reader::~ILI2Reader() |
633 | 398 | { |
634 | 398 | CPLFree(m_pszFilename); |
635 | | |
636 | 398 | CleanupParser(); |
637 | | |
638 | 398 | if (m_bXercesInitialized) |
639 | 398 | OGRDeinitializeXerces(); |
640 | 398 | } |
641 | | |
642 | | void ILI2Reader::SetSourceFile(const char *pszFilename) |
643 | 398 | { |
644 | 398 | CPLFree(m_pszFilename); |
645 | 398 | m_pszFilename = CPLStrdup(pszFilename); |
646 | 398 | } |
647 | | |
648 | | int ILI2Reader::SetupParser() |
649 | 398 | { |
650 | | |
651 | 398 | if (!m_bXercesInitialized) |
652 | 398 | { |
653 | 398 | if (!OGRInitializeXerces()) |
654 | 0 | return FALSE; |
655 | 398 | m_bXercesInitialized = true; |
656 | 398 | } |
657 | | |
658 | | // Cleanup any old parser. |
659 | 398 | if (m_poSAXReader != nullptr) |
660 | 0 | CleanupParser(); |
661 | | |
662 | | // Create and initialize parser. |
663 | 398 | m_poSAXReader = XMLReaderFactory::createXMLReader(); |
664 | | |
665 | 398 | m_poILI2Handler = new ILI2Handler(this); |
666 | | |
667 | 398 | m_poSAXReader->setContentHandler(m_poILI2Handler); |
668 | 398 | m_poSAXReader->setErrorHandler(m_poILI2Handler); |
669 | 398 | m_poSAXReader->setLexicalHandler(m_poILI2Handler); |
670 | 398 | m_poSAXReader->setEntityResolver(m_poILI2Handler); |
671 | 398 | m_poSAXReader->setDTDHandler(m_poILI2Handler); |
672 | 398 | m_poSAXReader->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, |
673 | 398 | true); |
674 | | |
675 | | /* No Validation |
676 | | #if (OGR_ILI2_VALIDATION) |
677 | | m_poSAXReader->setFeature( |
678 | | XMLString::transcode("http://xml.org/sax/features/validation"), |
679 | | true); m_poSAXReader->setFeature( |
680 | | XMLString::transcode("http://xml.org/sax/features/namespaces"), |
681 | | true); |
682 | | |
683 | | m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, true ); |
684 | | m_poSAXReader->setFeature( XMLUni::fgXercesSchema, true ); |
685 | | |
686 | | // m_poSAXReader->setDoSchema(true); |
687 | | // m_poSAXReader->setValidationSchemaFullChecking(true); |
688 | | #else |
689 | | */ |
690 | 398 | XMLCh *tmpCh = |
691 | 398 | XMLString::transcode("http://xml.org/sax/features/validation"); |
692 | 398 | m_poSAXReader->setFeature(tmpCh, false); |
693 | 398 | XMLString::release(&tmpCh); |
694 | 398 | tmpCh = XMLString::transcode("http://xml.org/sax/features/namespaces"); |
695 | 398 | m_poSAXReader->setFeature(tmpCh, false); |
696 | 398 | XMLString::release(&tmpCh); |
697 | | // #endif |
698 | | |
699 | 398 | m_bReadStarted = FALSE; |
700 | | |
701 | 398 | return TRUE; |
702 | 398 | } |
703 | | |
704 | | void ILI2Reader::CleanupParser() |
705 | 398 | { |
706 | 398 | if (m_poSAXReader == nullptr) |
707 | 0 | return; |
708 | | |
709 | 398 | delete m_poSAXReader; |
710 | 398 | m_poSAXReader = nullptr; |
711 | | |
712 | 398 | delete m_poILI2Handler; |
713 | 398 | m_poILI2Handler = nullptr; |
714 | | |
715 | 398 | m_bReadStarted = FALSE; |
716 | 398 | } |
717 | | |
718 | | int ILI2Reader::SaveClasses(const char *pszFile = nullptr) |
719 | 398 | { |
720 | | |
721 | | // Add logic later to determine reasonable default schema file. |
722 | 398 | if (pszFile == nullptr) |
723 | 0 | return FALSE; |
724 | | |
725 | 398 | VSILFILE *fp = VSIFOpenL(pszFile, "rb"); |
726 | 398 | if (fp == nullptr) |
727 | 0 | return FALSE; |
728 | | |
729 | 398 | InputSource *is = OGRCreateXercesInputSource(fp); |
730 | | |
731 | | // parse and create layers and features |
732 | 398 | try |
733 | 398 | { |
734 | 398 | CPLDebug("OGR_ILI", "Parsing %s", pszFile); |
735 | 398 | m_poSAXReader->parse(*is); |
736 | 398 | VSIFCloseL(fp); |
737 | 398 | OGRDestroyXercesInputSource(is); |
738 | 398 | } |
739 | 398 | catch (const DOMException &toCatch) |
740 | 398 | { |
741 | | // Can happen with createElement() in ILI2Handler::startElement() |
742 | 2 | CPLError(CE_Failure, CPLE_AppDefined, "DOMException: %s\n", |
743 | 2 | transcode(toCatch.getMessage()).c_str()); |
744 | 2 | VSIFCloseL(fp); |
745 | 2 | OGRDestroyXercesInputSource(is); |
746 | 2 | return FALSE; |
747 | 2 | } |
748 | 398 | catch (const SAXException &toCatch) |
749 | 398 | { |
750 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Parsing failed: %s\n", |
751 | 0 | transcode(toCatch.getMessage()).c_str()); |
752 | 0 | VSIFCloseL(fp); |
753 | 0 | OGRDestroyXercesInputSource(is); |
754 | 0 | return FALSE; |
755 | 0 | } |
756 | | |
757 | 396 | if (!m_missAttrs.empty()) |
758 | 134 | { |
759 | 134 | m_missAttrs.sort(); |
760 | 134 | m_missAttrs.unique(); |
761 | 134 | string attrs = ""; |
762 | 134 | list<string>::const_iterator it; |
763 | 2.58k | for (it = m_missAttrs.begin(); it != m_missAttrs.end(); ++it) |
764 | 2.44k | attrs += *it + ", "; |
765 | | |
766 | 134 | CPLError(CE_Warning, CPLE_NotSupported, |
767 | 134 | "Failed to add new definition to existing layers, attributes " |
768 | 134 | "not saved: %s", |
769 | 134 | attrs.c_str()); |
770 | 134 | } |
771 | | |
772 | 396 | return TRUE; |
773 | 398 | } |
774 | | |
775 | | std::vector<std::unique_ptr<OGRLayer>> &ILI2Reader::GetLayers() |
776 | 1.14k | { |
777 | 1.14k | return m_listLayer; |
778 | 1.14k | } |
779 | | |
780 | | int ILI2Reader::GetLayerCount() |
781 | 609 | { |
782 | 609 | return static_cast<int>(m_listLayer.size()); |
783 | 609 | } |
784 | | |
785 | | OGRLayer *ILI2Reader::GetLayer(const char *pszName) |
786 | 13.9k | { |
787 | 126k | for (auto it = m_listLayer.rbegin(); it != m_listLayer.rend(); ++it) |
788 | 124k | { |
789 | 124k | const OGRFeatureDefn *fDef = (*it)->GetLayerDefn(); |
790 | 124k | if (cmpStr(fDef->GetName(), pszName) == 0) |
791 | 12.7k | { |
792 | 12.7k | return it->get(); |
793 | 12.7k | } |
794 | 124k | } |
795 | 1.16k | return nullptr; |
796 | 13.9k | } |
797 | | |
798 | | int ILI2Reader::AddFeature(DOMElement *elem) |
799 | 13.9k | { |
800 | 13.9k | CPLString osName(transcode(elem->getTagName())); |
801 | | // CPLDebug( "OGR_ILI", "Reading layer: %s", osName.c_str() ); |
802 | | |
803 | | // test if this layer exist |
804 | 13.9k | OGRILI2Layer *curLayer = cpl::down_cast<OGRILI2Layer *>(GetLayer(osName)); |
805 | 13.9k | const bool needsNewLayer = (curLayer == nullptr); |
806 | 13.9k | std::unique_ptr<OGRILI2Layer> newLayer; |
807 | | |
808 | | // add a layer |
809 | 13.9k | if (needsNewLayer) |
810 | 1.16k | { |
811 | 1.16k | CPLDebug("OGR_ILI", "Adding layer: %s", osName.c_str()); |
812 | 1.16k | OGRFeatureDefn *poFeatureDefn = new OGRFeatureDefn(osName); |
813 | 1.16k | poFeatureDefn->SetGeomType(wkbUnknown); |
814 | 1.16k | GeomFieldInfos oGeomFieldInfos; |
815 | 1.16k | newLayer = std::make_unique<OGRILI2Layer>(poFeatureDefn, |
816 | 1.16k | oGeomFieldInfos, nullptr); |
817 | 1.16k | curLayer = newLayer.get(); |
818 | 1.16k | } |
819 | | |
820 | | // the feature and field definition |
821 | 13.9k | OGRFeatureDefn *featureDef = curLayer->GetLayerDefn(); |
822 | 13.9k | if (needsNewLayer) |
823 | 1.16k | { |
824 | | // add TID field |
825 | 1.16k | OGRFieldDefn ofieldDefn(ILI2_TID, OFTString); |
826 | 1.16k | featureDef->AddFieldDefn(&ofieldDefn); |
827 | | |
828 | 1.16k | setFieldDefn(featureDef, elem); |
829 | 1.16k | } |
830 | | |
831 | | // add the features |
832 | 13.9k | OGRFeature *feature = new OGRFeature(featureDef); |
833 | | |
834 | | // assign TID |
835 | 13.9k | int fIndex = feature->GetFieldIndex(ILI2_TID); |
836 | 13.9k | if (fIndex != -1) |
837 | 13.9k | { |
838 | 13.9k | feature->SetField( |
839 | 13.9k | fIndex, transcode(elem->getAttribute(xmlch_ILI2_TID)).c_str()); |
840 | 13.9k | } |
841 | 1 | else |
842 | 1 | { |
843 | 1 | CPLDebug("OGR_ILI", "'%s' not found", ILI2_TID); |
844 | 1 | } |
845 | | |
846 | 13.9k | SetFieldValues(feature, elem); |
847 | 13.9k | curLayer->AddFeature(feature); |
848 | | |
849 | 13.9k | if (needsNewLayer) |
850 | 1.16k | m_listLayer.push_back(std::move(newLayer)); |
851 | | |
852 | 13.9k | return 0; |
853 | 13.9k | } |
854 | | |
855 | | IILI2Reader *CreateILI2Reader() |
856 | 398 | { |
857 | 398 | return new ILI2Reader(); |
858 | 398 | } |
859 | | |
860 | | void DestroyILI2Reader(IILI2Reader *reader) |
861 | 9.40k | { |
862 | 9.40k | if (reader) |
863 | 398 | delete reader; |
864 | 9.40k | } |