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