Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Interlis 1/2 Translator
4 : * Purpose: IlisMeta model reader.
5 : * Author: Pirmin Kalberer, Sourcepole AG
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2014, Pirmin Kalberer, Sourcepole AG
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : // IlisMeta model: http://www.interlis.ch/models/core/IlisMeta07-20111222.ili
14 :
15 : #include "cpl_minixml.h"
16 : #include "imdreader.h"
17 :
18 : #include <set>
19 : #include <vector>
20 : #include <algorithm>
21 :
22 : typedef std::map<CPLString, CPLXMLNode *> StrNodeMap;
23 : typedef std::vector<CPLXMLNode *> NodeVector;
24 : typedef std::map<const CPLXMLNode *, int> NodeCountMap;
25 : class IliClass;
26 : // All classes with XML node for lookup.
27 : typedef std::map<const CPLXMLNode *, IliClass *> ClassesMap;
28 :
29 : /* Helper class for collection class infos */
30 : class IliClass
31 : {
32 : public:
33 : CPLXMLNode *node;
34 : int iliVersion;
35 : CPLString modelVersion;
36 : OGRFeatureDefn *poTableDefn;
37 : StrNodeMap &oTidLookup;
38 : ClassesMap &oClasses;
39 : NodeCountMap &oAxisCount;
40 : GeomFieldInfos poGeomFieldInfos;
41 : StructFieldInfos poStructFieldInfos;
42 : NodeVector oFields;
43 : bool isAssocClass;
44 : bool hasDerivedClasses;
45 :
46 521 : IliClass(CPLXMLNode *node_, int iliVersion_, const CPLString &modelVersion_,
47 : StrNodeMap &oTidLookup_, ClassesMap &oClasses_,
48 : NodeCountMap &oAxisCount_)
49 521 : : node(node_), iliVersion(iliVersion_), modelVersion(modelVersion_),
50 : oTidLookup(oTidLookup_), oClasses(oClasses_), oAxisCount(oAxisCount_),
51 : poGeomFieldInfos(), poStructFieldInfos(), oFields(),
52 521 : isAssocClass(false), hasDerivedClasses(false)
53 : {
54 521 : char *layerName = LayerName();
55 521 : poTableDefn = new OGRFeatureDefn(layerName);
56 521 : poTableDefn->Reference();
57 521 : CPLFree(layerName);
58 521 : }
59 :
60 521 : ~IliClass()
61 521 : {
62 521 : poTableDefn->Release();
63 521 : }
64 :
65 1171 : const char *GetName() const
66 : {
67 1171 : return poTableDefn->GetName();
68 : }
69 :
70 1042 : const char *GetIliName()
71 : {
72 1042 : return CPLGetXMLValue(node, "TID",
73 2084 : CPLGetXMLValue(node, "ili:tid", nullptr));
74 : }
75 :
76 521 : char *LayerName()
77 : {
78 521 : const char *psClassTID = GetIliName();
79 521 : if (iliVersion == 1)
80 : {
81 : // Skip topic and replace . with __
82 : char **papszTokens =
83 323 : CSLTokenizeString2(psClassTID, ".", CSLT_ALLOWEMPTYTOKENS);
84 :
85 646 : CPLString layername;
86 709 : for (int i = 1; papszTokens != nullptr && papszTokens[i] != nullptr;
87 : i++)
88 : {
89 386 : if (i > 1)
90 63 : layername += "__";
91 386 : layername += papszTokens[i];
92 : }
93 323 : CSLDestroy(papszTokens);
94 323 : return CPLStrdup(layername);
95 : }
96 198 : else if (EQUAL(modelVersion, "2.4"))
97 : {
98 : // Remove namespace
99 : const CPLStringList aosTokens(
100 172 : CSLTokenizeString2(psClassTID, ".", 0));
101 86 : return CPLStrdup(aosTokens[aosTokens.size() - 1]);
102 : }
103 : else
104 : {
105 112 : return CPLStrdup(psClassTID);
106 : }
107 : }
108 :
109 1199 : void AddFieldNode(CPLXMLNode *nodeIn, int iOrderPos)
110 : {
111 1199 : if (iOrderPos < 0 || iOrderPos > 100000)
112 : {
113 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid order pos = %d",
114 : iOrderPos);
115 0 : return;
116 : }
117 1199 : if (iOrderPos >= (int)oFields.size())
118 1021 : oFields.resize(iOrderPos + 1);
119 : #ifdef DEBUG_VERBOSE
120 : CPLDebug("OGR_ILI", "Register field with OrderPos %d to Class %s",
121 : iOrderPos, GetName());
122 : #endif
123 1199 : oFields[iOrderPos] = nodeIn;
124 : }
125 :
126 86 : void AddRoleNode(CPLXMLNode *nodeIn, int iOrderPos)
127 : {
128 86 : isAssocClass = true;
129 86 : AddFieldNode(nodeIn, iOrderPos);
130 86 : }
131 :
132 354 : bool isEmbedded()
133 : {
134 354 : if (isAssocClass)
135 89 : for (NodeVector::const_iterator it = oFields.begin();
136 138 : it != oFields.end(); ++it)
137 : {
138 77 : if (*it == nullptr)
139 0 : continue;
140 154 : if (CPLTestBool(CPLGetXMLValue(
141 77 : *it, "EmbeddedTransfer",
142 77 : CPLGetXMLValue(*it, "IlisMeta16:EmbeddedTransfer",
143 : "FALSE"))))
144 28 : return true;
145 : }
146 326 : return false;
147 : }
148 :
149 : // Add additional Geometry table for Interlis 1
150 14 : void AddGeomTable(const CPLString &layerName, const char *psFieldName,
151 : OGRwkbGeometryType eType, bool bRefTIDField = false)
152 : {
153 14 : OGRFeatureDefn *poGeomTableDefn = new OGRFeatureDefn(layerName);
154 28 : OGRFieldDefn fieldDef("_TID", OFTString);
155 14 : poGeomTableDefn->AddFieldDefn(&fieldDef);
156 14 : if (bRefTIDField)
157 : {
158 18 : OGRFieldDefn fieldDefRef("_RefTID", OFTString);
159 9 : poGeomTableDefn->AddFieldDefn(&fieldDefRef);
160 : }
161 14 : poGeomTableDefn->DeleteGeomFieldDefn(0);
162 14 : OGRGeomFieldDefn fieldDefGeom(psFieldName, eType);
163 14 : poGeomTableDefn->AddGeomFieldDefn(&fieldDefGeom);
164 14 : CPLDebug("OGR_ILI", "Adding geometry table %s for field %s",
165 14 : poGeomTableDefn->GetName(), psFieldName);
166 14 : poGeomFieldInfos[psFieldName].SetGeomTableDefn(poGeomTableDefn);
167 14 : }
168 :
169 930 : void AddField(const char *psName, OGRFieldType fieldType) const
170 : {
171 1860 : OGRFieldDefn fieldDef(psName, fieldType);
172 930 : poTableDefn->AddFieldDefn(&fieldDef);
173 930 : CPLDebug("OGR_ILI", "Adding field '%s' to Class %s", psName, GetName());
174 930 : }
175 :
176 123 : void AddGeomField(const char *psName, OGRwkbGeometryType geomType) const
177 : {
178 246 : OGRGeomFieldDefn fieldDef(psName, geomType);
179 : // oGFld.SetSpatialRef(geomlayer->GetSpatialRef());
180 123 : poTableDefn->AddGeomFieldDefn(&fieldDef);
181 123 : CPLDebug("OGR_ILI", "Adding geometry field '%s' to Class %s", psName,
182 : GetName());
183 123 : }
184 :
185 82 : void AddCoord(const char *psName, const CPLXMLNode *psTypeNode) const
186 : {
187 82 : auto oIter = oAxisCount.find(psTypeNode);
188 82 : int dim = (oIter == oAxisCount.end()) ? 0 : oIter->second;
189 82 : if (dim == 0)
190 5 : dim = 2; // Area center points have no Axis spec
191 82 : if (iliVersion == 1)
192 : {
193 196 : for (int i = 0; i < dim; i++)
194 : {
195 131 : AddField(CPLSPrintf("%s_%d", psName, i), OFTReal);
196 : }
197 : }
198 82 : OGRwkbGeometryType geomType = (dim > 2) ? wkbPoint25D : wkbPoint;
199 82 : AddGeomField(psName, geomType);
200 82 : }
201 :
202 36 : OGRFieldType GetFormattedType(CPLXMLNode *nodeIn)
203 : {
204 36 : const char *psRefSuper = CPLGetXMLValue(
205 : nodeIn, "Super.REF",
206 : CPLGetXMLValue(nodeIn, "IlisMeta16:Super.ili:ref", nullptr));
207 36 : if (psRefSuper)
208 0 : return GetFormattedType(oTidLookup[psRefSuper]);
209 :
210 36 : return OFTString; // TODO: Time, Date, etc. if possible
211 : }
212 :
213 521 : void InitFieldDefinitions()
214 : {
215 : // Delete default geometry field
216 521 : poTableDefn->DeleteGeomFieldDefn(0);
217 :
218 1563 : const char *psKind = CPLGetXMLValue(
219 521 : node, "Kind", CPLGetXMLValue(node, "IlisMeta16:Kind", ""));
220 : #ifdef DEBUG_VERBOSE
221 : CPLDebug("OGR_ILI", "InitFieldDefinitions of '%s' kind: %s", GetName(),
222 : psKind);
223 : #endif
224 521 : if (EQUAL(psKind, "Structure"))
225 : {
226 : // add foreign_key field
227 506 : OGRFieldDefn ofieldDefn1("REF_NAME", OFTString);
228 253 : poTableDefn->AddFieldDefn(&ofieldDefn1);
229 506 : OGRFieldDefn ofieldDefn2("REF_ID", OFTString);
230 253 : poTableDefn->AddFieldDefn(&ofieldDefn2);
231 : }
232 : else
233 : { // Class
234 : // add TID field
235 268 : const char *psTidColName = (iliVersion == 1) ? "_TID" : "TID";
236 536 : OGRFieldDefn ofieldDefn(psTidColName, OFTString);
237 268 : poTableDefn->AddFieldDefn(&ofieldDefn);
238 : }
239 521 : if (CPLTestBool(CPLGetXMLValue(node, "Abstract", "FALSE")))
240 121 : hasDerivedClasses = true;
241 521 : }
242 :
243 884 : const CPLXMLNode *TidLookup(const char *pszKey) const
244 : {
245 884 : if (pszKey == nullptr)
246 : {
247 0 : CPLError(CE_Failure, CPLE_AppDefined,
248 : "Null key passed to TidLookup");
249 0 : return nullptr;
250 : }
251 884 : auto oIter = oTidLookup.find(pszKey);
252 884 : if (oIter == oTidLookup.end())
253 : {
254 0 : CPLError(CE_Failure, CPLE_AppDefined,
255 : "Unknown key %s passed to TidLookup", pszKey);
256 0 : return nullptr;
257 : }
258 884 : return oIter->second;
259 : }
260 :
261 521 : void AddFieldDefinitions(NodeVector oArcLineTypes)
262 : {
263 1542 : for (NodeVector::const_iterator it = oFields.begin();
264 2563 : it != oFields.end(); ++it)
265 : {
266 1021 : if (*it == nullptr)
267 0 : continue;
268 2042 : const char *psName = CPLGetXMLValue(
269 2042 : *it, "Name", CPLGetXMLValue(*it, "IlisMeta16:Name", nullptr));
270 1021 : if (psName == nullptr)
271 0 : continue;
272 2042 : const char *psTypeRef = CPLGetXMLValue(
273 1021 : *it, "Type.REF",
274 1021 : CPLGetXMLValue(*it, "IlisMeta16:Type.ili:ref", nullptr));
275 1021 : if (psTypeRef == nullptr) // Assoc Role
276 137 : AddField(psName, OFTString); // TODO: numeric?
277 : else
278 : {
279 884 : const CPLXMLNode *psElementNode = TidLookup(psTypeRef);
280 884 : if (psElementNode == nullptr)
281 0 : continue;
282 884 : const char *typeName = psElementNode->pszValue;
283 884 : CPLDebug("OGR_ILI", "AddFieldDefinitions typename '%s'",
284 : typeName);
285 884 : if (EQUAL(typeName, "IlisMeta07.ModelData.TextType") ||
286 712 : EQUAL(typeName, "IlisMeta16:TextType"))
287 : { // Kind Text,MText
288 236 : AddField(psName, OFTString);
289 : }
290 648 : else if (EQUAL(typeName, "IlisMeta07.ModelData.EnumType") ||
291 616 : EQUAL(typeName, "IlisMeta16:EnumType"))
292 : {
293 62 : AddField(psName,
294 62 : (iliVersion == 1) ? OFTInteger : OFTString);
295 : }
296 586 : else if (EQUAL(typeName, "IlisMeta07.ModelData.BooleanType") ||
297 586 : EQUAL(typeName, "IlisMeta16:BooleanType"))
298 : {
299 1 : AddField(psName, OFTString); //??
300 : }
301 585 : else if (EQUAL(typeName, "IlisMeta07.ModelData.NumType") ||
302 315 : EQUAL(typeName, "IlisMeta16:NumType"))
303 : { //// Unit INTERLIS.ANYUNIT, INTERLIS.TIME, INTERLIS.h,
304 : /// INTERLIS.min, INTERLIS.s, INTERLIS.M, INTERLIS.d
305 321 : AddField(psName, OFTReal);
306 : }
307 264 : else if (EQUAL(typeName, "IlisMeta07.ModelData.BlackboxType") ||
308 262 : EQUAL(typeName, "IlisMeta16:BlackboxType"))
309 : {
310 6 : AddField(psName, OFTString);
311 : }
312 258 : else if (EQUAL(typeName,
313 251 : "IlisMeta07.ModelData.FormattedType") ||
314 251 : EQUAL(typeName, "IlisMeta16:FormattedType"))
315 : {
316 36 : AddField(psName, GetFormattedType(*it));
317 : }
318 222 : else if (EQUAL(typeName, "IlisMeta07.ModelData.MultiValue") ||
319 147 : EQUAL(typeName, "IlisMeta16:MultiValue"))
320 : {
321 : // min -> Multiplicity/IlisMeta07.ModelData.Multiplicity/Min
322 : // max -> Multiplicity/IlisMeta07.ModelData.Multiplicity/Max
323 104 : const char *psClassRef = CPLGetXMLValue(
324 : psElementNode, "BaseType.REF",
325 : CPLGetXMLValue(psElementNode,
326 : "IlisMeta16:BaseType.ili:ref", nullptr));
327 104 : if (psClassRef)
328 : {
329 : IliClass *psParentClass =
330 104 : oClasses[oTidLookup[psClassRef]];
331 104 : poStructFieldInfos[psName] = psParentClass->GetName();
332 104 : CPLDebug("OGR_ILI",
333 : "Register table %s for struct field '%s'",
334 208 : poStructFieldInfos[psName].c_str(), psName);
335 : /* Option: Embed fields if max == 1
336 : CPLDebug( "OGR_ILI", "Adding embedded struct members of
337 : MultiValue field '%s' from Class %s", psName,
338 : psClassRef);
339 : AddFieldDefinitions(psParentClass->oFields);
340 : */
341 104 : }
342 : }
343 118 : else if (EQUAL(typeName, "IlisMeta07.ModelData.CoordType") ||
344 46 : EQUAL(typeName, "IlisMeta16:CoordType"))
345 : {
346 77 : AddCoord(psName, psElementNode);
347 : }
348 41 : else if (EQUAL(typeName, "IlisMeta07.ModelData.LineType") ||
349 1 : EQUAL(typeName, "IlisMeta16:LineType"))
350 : {
351 41 : const char *psKind = CPLGetXMLValue(
352 : psElementNode, "Kind",
353 : CPLGetXMLValue(psElementNode, "IlisMeta16:Kind", ""));
354 41 : poGeomFieldInfos[psName].iliGeomType = psKind;
355 : bool isLinearType =
356 41 : (std::find(oArcLineTypes.begin(), oArcLineTypes.end(),
357 41 : psElementNode) == oArcLineTypes.end());
358 : bool linearGeom =
359 41 : isLinearType || CPLTestBool(CPLGetConfigOption(
360 41 : "OGR_STROKE_CURVE", "FALSE"));
361 41 : OGRwkbGeometryType multiLineType =
362 41 : linearGeom ? wkbMultiLineString : wkbMultiCurve;
363 41 : OGRwkbGeometryType polyType =
364 41 : linearGeom ? wkbPolygon : wkbCurvePolygon;
365 41 : if (iliVersion == 1)
366 : {
367 33 : if (EQUAL(psKind, "Area"))
368 : {
369 : CPLString lineLayerName =
370 15 : GetName() + CPLString("_") + psName;
371 5 : AddGeomTable(lineLayerName, psName, multiLineType);
372 :
373 : // Add geometry field for polygonized areas
374 5 : AddGeomField(psName, wkbPolygon);
375 :
376 : // We add the area helper point geometry after
377 : // polygon for better behavior of clients with
378 : // limited multi geometry support
379 : CPLString areaPointGeomName =
380 15 : psName + CPLString("__Point");
381 5 : AddCoord(areaPointGeomName, psElementNode);
382 : }
383 28 : else if (EQUAL(psKind, "Surface"))
384 : {
385 : CPLString geomLayerName =
386 27 : GetName() + CPLString("_") + psName;
387 9 : AddGeomTable(geomLayerName, psName, multiLineType,
388 : true);
389 9 : AddGeomField(psName, polyType);
390 : }
391 : else
392 : { // Polyline, DirectedPolyline
393 19 : AddGeomField(psName, multiLineType);
394 : }
395 : }
396 : else
397 : {
398 8 : if (EQUAL(psKind, "Area") || EQUAL(psKind, "Surface"))
399 : {
400 2 : AddGeomField(psName, polyType);
401 : }
402 : else
403 : { // Polyline, DirectedPolyline
404 6 : AddGeomField(psName, multiLineType);
405 : }
406 41 : }
407 : }
408 : else
409 : {
410 : // ClassRefType
411 0 : CPLError(CE_Warning, CPLE_NotSupported,
412 : "Field '%s' of class %s has unsupported type %s",
413 : psName, GetName(), typeName);
414 : }
415 : }
416 : }
417 521 : }
418 :
419 521 : FeatureDefnInfo tableDefs()
420 : {
421 521 : FeatureDefnInfo poLayerInfo;
422 521 : if (!hasDerivedClasses && !isEmbedded())
423 : {
424 326 : poLayerInfo.SetTableDefn(poTableDefn);
425 326 : poLayerInfo.poGeomFieldInfos = poGeomFieldInfos;
426 : }
427 521 : return poLayerInfo;
428 : }
429 :
430 : private:
431 : CPL_DISALLOW_COPY_ASSIGN(IliClass)
432 : };
433 :
434 26402 : ImdReader::ImdReader(int iliVersionIn)
435 : : iliVersion(iliVersionIn), mainTopicName("OGR"), codeBlank('_'),
436 26402 : codeUndefined('@'), codeContinue('\\')
437 : {
438 26402 : }
439 :
440 26402 : ImdReader::~ImdReader()
441 : {
442 26402 : }
443 :
444 16 : void ImdReader::ReadModel(const char *pszFilename)
445 : {
446 16 : CPLDebug("OGR_ILI", "Reading model '%s'", pszFilename);
447 :
448 16 : CPLXMLNode *psRootNode = CPLParseXMLFile(pszFilename);
449 16 : if (psRootNode == nullptr)
450 0 : return;
451 : CPLXMLNode *psSectionNode =
452 16 : CPLGetXMLNode(psRootNode, "=TRANSFER.DATASECTION");
453 16 : if (psSectionNode == nullptr)
454 : psSectionNode =
455 1 : CPLGetXMLNode(psRootNode, "=ili:transfer.ili:datasection");
456 16 : if (psSectionNode == nullptr)
457 : {
458 0 : CPLDestroyXMLNode(psRootNode);
459 0 : return;
460 : }
461 :
462 32 : StrNodeMap oTidLookup; /* for fast lookup of REF relations */
463 32 : ClassesMap oClasses;
464 32 : NodeCountMap oAxisCount;
465 32 : NodeVector oArcLineTypes;
466 :
467 7500 : const auto TidLookup = [&oTidLookup](const char *pszKey)
468 : {
469 2500 : if (pszKey == nullptr)
470 : {
471 0 : CPLError(CE_Failure, CPLE_AppDefined,
472 : "Null key passed to TidLookup");
473 0 : return static_cast<CPLXMLNode *>(nullptr);
474 : }
475 2500 : auto oIter = oTidLookup.find(pszKey);
476 2500 : if (oIter == oTidLookup.end())
477 : {
478 0 : CPLError(CE_Failure, CPLE_AppDefined,
479 : "Unknown key %s passed to TidLookup", pszKey);
480 0 : return static_cast<CPLXMLNode *>(nullptr);
481 : }
482 2500 : return static_cast<CPLXMLNode *>(oIter->second);
483 16 : };
484 :
485 : /* Fill TID lookup map and IliClasses lookup map */
486 16 : CPLXMLNode *psModel = psSectionNode->psChild;
487 67 : while (psModel != nullptr)
488 : {
489 : #ifdef DEBUG_VERBOSE
490 : const char *modelName = CPLGetXMLValue(
491 : psModel, "BID", CPLGetXMLValue(psModel, "ili:bid", nullptr));
492 : CPLDebug("OGR_ILI", "Model: '%s'", modelName);
493 : #endif
494 :
495 9124 : for (CPLXMLNode *psEntry = psModel->psChild; psEntry != nullptr;
496 9073 : psEntry = psEntry->psNext)
497 : {
498 9073 : if (psEntry->eType != CXT_Attribute) // ignore BID
499 : {
500 : #ifdef DEBUG_VERBOSE
501 : CPLDebug("OGR_ILI", "Node tag: '%s'", psEntry->pszValue);
502 : #endif
503 : const char *psTID =
504 9022 : CPLGetXMLValue(psEntry, "TID",
505 : CPLGetXMLValue(psEntry, "ili:tid", nullptr));
506 9022 : if (psTID != nullptr)
507 6952 : oTidLookup[psTID] = psEntry;
508 :
509 9022 : if (EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.Model") ||
510 8986 : EQUAL(psEntry->pszValue, "IlisMeta16:Model"))
511 : {
512 102 : IliModelInfo modelInfo;
513 : modelInfo.name = CPLGetXMLValue(
514 : psEntry, "Name",
515 51 : CPLGetXMLValue(psEntry, "IlisMeta16:Name", "OGR"));
516 : modelInfo.version = CPLGetXMLValue(
517 : psEntry, "Version",
518 51 : CPLGetXMLValue(psEntry, "IlisMeta16:iliVersion", ""));
519 : // "1", "2.3", "2.4"
520 51 : modelInfo.uri = CPLGetXMLValue(psEntry, "At", "");
521 51 : modelInfos.push_back(std::move(modelInfo));
522 :
523 : CPLXMLNode *psFormatNode =
524 51 : CPLGetXMLNode(psEntry, "ili1Format");
525 51 : if (psFormatNode != nullptr)
526 : {
527 13 : psFormatNode = psFormatNode->psChild;
528 13 : codeBlank = static_cast<char>(atoi(
529 : CPLGetXMLValue(psFormatNode, "blankCode", "95")));
530 13 : codeUndefined = static_cast<char>(atoi(CPLGetXMLValue(
531 : psFormatNode, "undefinedCode", "64")));
532 13 : codeContinue = static_cast<char>(atoi(CPLGetXMLValue(
533 : psFormatNode, "continueCode", "92")));
534 51 : }
535 : }
536 8971 : else if (EQUAL(psEntry->pszValue,
537 8938 : "IlisMeta07.ModelData.SubModel") ||
538 8938 : EQUAL(psEntry->pszValue, "IlisMeta16:SubModel"))
539 : {
540 : mainBasketName = CPLGetXMLValue(
541 : psEntry, "TID",
542 47 : CPLGetXMLValue(psEntry, "ili:tid", "OGR"));
543 47 : mainTopicName = CPLGetXMLValue(
544 : psEntry, "Name",
545 47 : CPLGetXMLValue(psEntry, "IlisMeta16:Name", "OGR"));
546 : }
547 8924 : else if (EQUAL(psEntry->pszValue,
548 8511 : "IlisMeta07.ModelData.Class") ||
549 8511 : EQUAL(psEntry->pszValue, "IlisMeta16:Class"))
550 : {
551 521 : CPLDebug("OGR_ILI", "Class name: '%s'", psTID);
552 521 : const auto &modelVersion = modelInfos.back().version;
553 521 : oClasses[psEntry] =
554 : new IliClass(psEntry, iliVersion, modelVersion,
555 521 : oTidLookup, oClasses, oAxisCount);
556 : }
557 : }
558 : }
559 :
560 : // 2nd pass: add fields via TransferElement entries & role associations
561 9124 : for (CPLXMLNode *psEntry = psModel->psChild; psEntry != nullptr;
562 9073 : psEntry = psEntry->psNext)
563 : {
564 9073 : if (psEntry->eType != CXT_Attribute) // ignore BID
565 : {
566 : #ifdef DEBUG_VERBOSE
567 : CPLDebug("OGR_ILI", "Node tag: '%s'", psEntry->pszValue);
568 : #endif
569 9022 : if (iliVersion == 1 &&
570 3925 : EQUAL(psEntry->pszValue,
571 : "IlisMeta07.ModelData.Ili1TransferElement"))
572 : {
573 150 : const char *psClassRef = CPLGetXMLValue(
574 : psEntry, "Ili1TransferClass.REF", nullptr);
575 : const char *psElementRef =
576 150 : CPLGetXMLValue(psEntry, "Ili1RefAttr.REF", nullptr);
577 150 : if (psClassRef == nullptr || psElementRef == nullptr)
578 0 : continue;
579 : int iOrderPos =
580 150 : atoi(CPLGetXMLValue(psEntry, "Ili1RefAttr.ORDER_POS",
581 : "0")) -
582 150 : 1;
583 150 : auto tidClassRef = TidLookup(psClassRef);
584 150 : if (tidClassRef == nullptr)
585 0 : continue;
586 150 : auto classesIter = oClasses.find(tidClassRef);
587 150 : if (classesIter == oClasses.end())
588 0 : continue;
589 150 : IliClass *psParentClass = classesIter->second;
590 150 : CPLXMLNode *psElementNode = TidLookup(psElementRef);
591 150 : if (psElementNode == nullptr)
592 0 : continue;
593 150 : psParentClass->AddFieldNode(psElementNode, iOrderPos);
594 : }
595 8872 : else if (EQUAL(psEntry->pszValue,
596 8178 : "IlisMeta07.ModelData.TransferElement") ||
597 8178 : EQUAL(psEntry->pszValue, "IlisMeta16:TransferElement"))
598 : {
599 963 : const char *psClassRef = CPLGetXMLValue(
600 : psEntry, "TransferClass.REF",
601 : CPLGetXMLValue(psEntry,
602 : "IlisMeta16:TransferClass.ili:ref",
603 : nullptr));
604 963 : const char *psElementRef = CPLGetXMLValue(
605 : psEntry, "TransferElement.REF",
606 : CPLGetXMLValue(psEntry,
607 : "IlisMeta16:TransferElement.ili:ref",
608 : nullptr));
609 963 : if (psClassRef == nullptr || psElementRef == nullptr)
610 0 : continue;
611 : int iOrderPos =
612 963 : atoi(CPLGetXMLValue(
613 : psEntry, "TransferElement.ORDER_POS",
614 : CPLGetXMLValue(
615 : psEntry,
616 : "IlisMeta16:TransferElement.ili:order_pos",
617 : "0"))) -
618 963 : 1;
619 963 : auto tidClassRef = TidLookup(psClassRef);
620 963 : if (tidClassRef == nullptr)
621 0 : continue;
622 963 : auto classesIter = oClasses.find(tidClassRef);
623 963 : if (classesIter == oClasses.end())
624 0 : continue;
625 963 : IliClass *psParentClass = classesIter->second;
626 963 : CPLXMLNode *psElementNode = TidLookup(psElementRef);
627 963 : if (psElementNode == nullptr)
628 0 : continue;
629 963 : psParentClass->AddFieldNode(psElementNode, iOrderPos);
630 : }
631 7909 : else if (EQUAL(psEntry->pszValue,
632 7873 : "IlisMeta07.ModelData.Role") ||
633 7873 : EQUAL(psEntry->pszValue, "IlisMeta16:Role"))
634 : {
635 86 : const char *psRefParent = CPLGetXMLValue(
636 : psEntry, "Association.REF",
637 : CPLGetXMLValue(psEntry,
638 : "IlisMeta16:Association.ili:ref",
639 : nullptr));
640 86 : if (psRefParent == nullptr)
641 0 : continue;
642 : int iOrderPos =
643 86 : atoi(CPLGetXMLValue(
644 : psEntry, "Association.ORDER_POS",
645 : CPLGetXMLValue(
646 : psEntry, "IlisMeta16:Association.ili:order_pos",
647 : "0"))) -
648 86 : 1;
649 86 : auto tidClassRef = TidLookup(psRefParent);
650 86 : if (tidClassRef == nullptr)
651 0 : continue;
652 86 : auto classesIter = oClasses.find(tidClassRef);
653 86 : if (classesIter == oClasses.end())
654 0 : continue;
655 86 : IliClass *psParentClass = classesIter->second;
656 86 : if (psParentClass)
657 86 : psParentClass->AddRoleNode(psEntry, iOrderPos);
658 : }
659 7823 : else if (EQUAL(psEntry->pszValue,
660 7715 : "IlisMeta07.ModelData.AxisSpec") ||
661 7715 : EQUAL(psEntry->pszValue, "IlisMeta16:AxisSpec"))
662 : {
663 142 : const char *psClassRef = CPLGetXMLValue(
664 : psEntry, "CoordType.REF",
665 : CPLGetXMLValue(psEntry, "IlisMeta16:CoordType.ili:ref",
666 : nullptr));
667 142 : if (psClassRef == nullptr)
668 0 : continue;
669 : // int iOrderPos = atoi(
670 : // CPLGetXMLValue( psEntry, "Axis.ORDER_POS", "0" ))-1;
671 142 : CPLXMLNode *psCoordTypeNode = TidLookup(psClassRef);
672 142 : if (psCoordTypeNode == nullptr)
673 0 : continue;
674 142 : oAxisCount[psCoordTypeNode] += 1;
675 : }
676 7681 : else if (EQUAL(psEntry->pszValue,
677 7635 : "IlisMeta07.ModelData.LinesForm") ||
678 7635 : EQUAL(psEntry->pszValue, "IlisMeta16:LinesForm"))
679 : {
680 118 : const char *psLineForm = CPLGetXMLValue(
681 : psEntry, "LineForm.REF",
682 : CPLGetXMLValue(psEntry, "IlisMeta16:LineForm.ili:ref",
683 : nullptr));
684 118 : if (psLineForm != nullptr &&
685 118 : EQUAL(psLineForm, "INTERLIS.ARCS"))
686 : {
687 46 : const char *psElementRef = CPLGetXMLValue(
688 : psEntry, "LineType.REF",
689 : CPLGetXMLValue(psEntry,
690 : "IlisMeta16:LineType.ili:ref",
691 : nullptr));
692 46 : CPLXMLNode *psElementNode = TidLookup(psElementRef);
693 46 : if (psElementNode == nullptr)
694 0 : continue;
695 46 : oArcLineTypes.push_back(psElementNode);
696 : }
697 : }
698 : }
699 : }
700 :
701 51 : psModel = psModel->psNext;
702 : }
703 :
704 : // Last model is main model
705 32 : const CPLString mainModelName = modelInfos.back().name;
706 32 : const CPLString modelVersion = modelInfos.back().version;
707 16 : CPLDebug("OGR_ILI", "mainModelName: '%s' version: '%s'",
708 : mainModelName.c_str(), modelVersion.c_str());
709 :
710 : /* Analyze class inheritance & add fields to class table defn */
711 537 : for (ClassesMap::const_iterator it = oClasses.begin(); it != oClasses.end();
712 521 : ++it)
713 : {
714 : #ifdef DEBUG_VERBOSE
715 : CPLDebug("OGR_ILI", "Class: '%s'", it->second->GetName());
716 : #endif
717 2084 : const char *psRefSuper = CPLGetXMLValue(
718 521 : it->first, "Super.REF",
719 521 : CPLGetXMLValue(it->first, "IlisMeta16:Super.ili:ref", nullptr));
720 521 : if (psRefSuper)
721 : {
722 675 : if (oTidLookup.find(psRefSuper) != oTidLookup.end() &&
723 450 : oClasses.find(oTidLookup[psRefSuper]) != oClasses.end())
724 : {
725 225 : oClasses[oTidLookup[psRefSuper]]->hasDerivedClasses = true;
726 : }
727 : else
728 : {
729 0 : CPLError(CE_Warning, CPLE_AppDefined,
730 : "Couldn't reference super class '%s'", psRefSuper);
731 : }
732 : }
733 521 : it->second->InitFieldDefinitions();
734 521 : it->second->AddFieldDefinitions(oArcLineTypes);
735 : }
736 :
737 : /* Filter relevant classes */
738 537 : for (ClassesMap::const_iterator it = oClasses.begin(); it != oClasses.end();
739 521 : ++it)
740 : {
741 521 : const char *className = it->second->GetIliName();
742 1042 : FeatureDefnInfo oClassInfo = it->second->tableDefs();
743 521 : bool include = EQUAL(modelVersion, "2.4")
744 521 : ? STARTS_WITH(className, mainModelName.c_str())
745 413 : : !STARTS_WITH_CI(className, "INTERLIS.");
746 521 : if (include && oClassInfo.GetTableDefnRef())
747 : {
748 71 : featureDefnInfos.push_back(oClassInfo);
749 : }
750 : }
751 :
752 537 : for (ClassesMap::iterator it = oClasses.begin(); it != oClasses.end(); ++it)
753 : {
754 521 : delete it->second;
755 : }
756 :
757 16 : CPLDestroyXMLNode(psRootNode);
758 : }
759 :
760 0 : FeatureDefnInfo ImdReader::GetFeatureDefnInfo(const char *pszLayerName)
761 : {
762 0 : FeatureDefnInfo featureDefnInfo;
763 0 : for (FeatureDefnInfos::const_iterator it = featureDefnInfos.begin();
764 0 : it != featureDefnInfos.end(); ++it)
765 : {
766 0 : OGRFeatureDefn *fdefn = it->GetTableDefnRef();
767 0 : if (EQUAL(fdefn->GetName(), pszLayerName))
768 0 : featureDefnInfo = *it;
769 : }
770 0 : return featureDefnInfo;
771 : }
|