EVOLUTION-MANAGER
Edit File: imdreader.cpp
/****************************************************************************** * $Id$ * * Project: Interlis 1/2 Translator * Purpose: IlisMeta model reader. * Author: Pirmin Kalberer, Sourcepole AG * ****************************************************************************** * Copyright (c) 2014, Pirmin Kalberer, Sourcepole AG * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ // IlisMeta model: http://www.interlis.ch/models/core/IlisMeta07-20111222.ili #include "imdreader.h" #include "cpl_minixml.h" #include <set> #include <vector> CPL_CVSID("$Id$"); typedef std::map<CPLString,CPLXMLNode*> StrNodeMap; typedef std::vector<CPLXMLNode*> NodeVector; typedef std::map<CPLXMLNode*,int> NodeCountMap; class IliClass; typedef std::map<CPLXMLNode*,IliClass*> ClassesMap; /* all classes with XML node for lookup */ /* Helper class for collection class infos */ class IliClass { public: CPLXMLNode* node; int iliVersion; OGRFeatureDefn* poTableDefn; StrNodeMap& oTidLookup; ClassesMap& oClasses; NodeCountMap& oAxisCount; GeomFieldInfos poGeomFieldInfos; StructFieldInfos poStructFieldInfos; NodeVector oFields; bool isAssocClass; bool hasDerivedClasses; IliClass(CPLXMLNode* node_, const char* name, int iliVersion_, StrNodeMap& oTidLookup_, ClassesMap& oClasses_, NodeCountMap& oAxisCount_) : node(node_), iliVersion(iliVersion_), oTidLookup(oTidLookup_), oClasses(oClasses_), oAxisCount(oAxisCount_), poGeomFieldInfos(), poStructFieldInfos(), oFields(), isAssocClass(false), hasDerivedClasses(false) { char* layerName = LayerName(name); poTableDefn = new OGRFeatureDefn(layerName); CPLFree(layerName); }; ~IliClass() { delete poTableDefn; }; const char* GetName() { return poTableDefn->GetName(); } char* LayerName(const char* psClassTID) { if (iliVersion == 1) { //Skip topic and replace . with __ char **papszTokens = CSLTokenizeString2( psClassTID, ".", CSLT_ALLOWEMPTYTOKENS ); CPLString layername; for(int i = 1; papszTokens != NULL && papszTokens[i] != NULL; i++) { if (i>1) layername += "__"; layername += papszTokens[i]; } CSLDestroy( papszTokens ); return CPLStrdup(layername); } else { return CPLStrdup(psClassTID); } }; void AddFieldNode(CPLXMLNode* node, int iOrderPos) { if (iOrderPos >= (int)oFields.size()) oFields.resize(iOrderPos+1); //CPLDebug( "OGR_ILI", "Register field with OrderPos %d to Class %s", iOrderPos, GetName()); oFields[iOrderPos] = node; } void AddRoleNode(CPLXMLNode* node, int iOrderPos) { isAssocClass = true; AddFieldNode(node, iOrderPos); } bool isEmbedded() { if (isAssocClass) for (NodeVector::const_iterator it = oFields.begin(); it != oFields.end(); ++it) { if (*it == NULL) continue; if (CSLTestBoolean(CPLGetXMLValue( *it, "EmbeddedTransfer", "FALSE" ))) return true; } return false; } // Add additional Geometry table for Interlis 1 void AddGeomTable(CPLString layerName, const char* psFieldName, OGRwkbGeometryType eType) { OGRFeatureDefn* poGeomTableDefn = new OGRFeatureDefn(layerName); OGRFieldDefn fieldDef("_TID", OFTString); poGeomTableDefn->AddFieldDefn(&fieldDef); if (eType == wkbPolygon) { OGRFieldDefn fieldDefRef("_RefTID", OFTString); poGeomTableDefn->AddFieldDefn(&fieldDefRef); } poGeomTableDefn->DeleteGeomFieldDefn(0); OGRGeomFieldDefn fieldDefGeom(psFieldName, eType); poGeomTableDefn->AddGeomFieldDefn(&fieldDefGeom); CPLDebug( "OGR_ILI", "Adding geometry table %s for field %s", poGeomTableDefn->GetName(), psFieldName); poGeomFieldInfos[psFieldName].geomTable = poGeomTableDefn; } void AddField(const char* psName, OGRFieldType fieldType) { OGRFieldDefn fieldDef(psName, fieldType); poTableDefn->AddFieldDefn(&fieldDef); CPLDebug( "OGR_ILI", "Adding field '%s' to Class %s", psName, GetName()); } void AddGeomField(const char* psName, OGRwkbGeometryType geomType) { OGRGeomFieldDefn fieldDef(psName, geomType); //oGFld.SetSpatialRef(geomlayer->GetSpatialRef()); poTableDefn->AddGeomFieldDefn(&fieldDef); CPLDebug( "OGR_ILI", "Adding geometry field '%s' to Class %s", psName, GetName()); } void AddCoord(const char* psName, CPLXMLNode* psTypeNode) { int dim = oAxisCount[psTypeNode]; if (dim == 0) dim = 2; //Area center points have no Axis spec if (iliVersion == 1) { for (int i=0; i<dim; i++) { AddField(CPLSPrintf("%s_%d", psName, i), OFTReal); } } OGRwkbGeometryType geomType = (dim > 2) ? wkbPoint25D : wkbPoint; AddGeomField(psName, geomType); } OGRFieldType GetFormattedType(CPLXMLNode* node) { const char* psRefSuper = CPLGetXMLValue( node, "Super.REF", NULL ); if (psRefSuper) return GetFormattedType(oTidLookup[psRefSuper]); else return OFTString; //TODO: Time, Date, etc. if possible } void InitFieldDefinitions() { // Delete default geometry field poTableDefn->DeleteGeomFieldDefn(0); const char* psKind = CPLGetXMLValue( node, "Kind", NULL ); //CPLDebug( "OGR_ILI", "InitFieldDefinitions of '%s' kind: %s", GetName(), psKind); if (EQUAL(psKind, "Structure")) { // add foreign_key field OGRFieldDefn ofieldDefn1("REF_NAME", OFTString); poTableDefn->AddFieldDefn(&ofieldDefn1); OGRFieldDefn ofieldDefn2("REF_ID", OFTString); poTableDefn->AddFieldDefn(&ofieldDefn2); } else { // Class // add TID field const char* psTidColName = (iliVersion == 1) ? "_TID" : "TID"; OGRFieldDefn ofieldDefn(psTidColName, OFTString); poTableDefn->AddFieldDefn(&ofieldDefn); } if (CSLTestBoolean(CPLGetXMLValue( node, "Abstract", "FALSE" ))) hasDerivedClasses = true; } void AddFieldDefinitions() { for (NodeVector::const_iterator it = oFields.begin(); it != oFields.end(); ++it) { if (*it == NULL) continue; const char* psName = CPLGetXMLValue( *it, "Name", NULL ); const char* psTypeRef = CPLGetXMLValue( *it, "Type.REF", NULL ); if (psTypeRef == NULL) //Assoc Role AddField(psName, OFTString); //FIXME: numeric? else { CPLXMLNode* psElementNode = oTidLookup[psTypeRef]; const char* typeName = psElementNode->pszValue; if (EQUAL(typeName, "IlisMeta07.ModelData.TextType")) { //Kind Text,MText AddField(psName, OFTString); } else if (EQUAL(typeName, "IlisMeta07.ModelData.EnumType")) { AddField(psName, (iliVersion == 1) ? OFTInteger : OFTString); } else if (EQUAL(typeName, "IlisMeta07.ModelData.BooleanType")) { AddField(psName, OFTString); //?? } else if (EQUAL(typeName, "IlisMeta07.ModelData.NumType")) { //// Unit INTERLIS.ANYUNIT, INTERLIS.TIME, INTERLIS.h, INTERLIS.min, INTERLIS.s, INTERLIS.M, INTERLIS.d AddField(psName, OFTReal); } else if (EQUAL(typeName, "IlisMeta07.ModelData.BlackboxType")) { AddField(psName, OFTString); } else if (EQUAL(typeName, "IlisMeta07.ModelData.FormattedType")) { AddField(psName, GetFormattedType(*it)); } else if (EQUAL(typeName, "IlisMeta07.ModelData.MultiValue")) { //min -> Multiplicity/IlisMeta07.ModelData.Multiplicity/Min //max -> Multiplicity/IlisMeta07.ModelData.Multiplicity/Max const char* psClassRef = CPLGetXMLValue( psElementNode, "BaseType.REF", NULL ); if (psClassRef) { IliClass* psParentClass = oClasses[oTidLookup[psClassRef]]; poStructFieldInfos[psName] = psParentClass->GetName(); CPLDebug( "OGR_ILI", "Register table %s for struct field '%s'", poStructFieldInfos[psName].c_str(), psName); /* Option: Embed fields if max == 1 CPLDebug( "OGR_ILI", "Adding embedded struct members of MultiValue field '%s' from Class %s", psName, psClassRef); AddFieldDefinitions(psParentClass->oFields); */ } } else if (EQUAL(typeName, "IlisMeta07.ModelData.CoordType")) { AddCoord(psName, psElementNode); } else if (EQUAL(typeName, "IlisMeta07.ModelData.LineType")) { const char* psKind = CPLGetXMLValue( psElementNode, "Kind", NULL ); poGeomFieldInfos[psName].iliGeomType = psKind; if (iliVersion == 1) { if (EQUAL(psKind, "Area")) { CPLString areaPointGeomName = psName + CPLString("__Point"); AddCoord(areaPointGeomName, psElementNode); CPLString lineLayerName = GetName() + CPLString("_") + psName; AddGeomTable(lineLayerName, psName, wkbMultiLineString); //Add geometry field for polygonized areas AddGeomField(psName, wkbPolygon); } else if (EQUAL(psKind, "Surface")) { CPLString geomLayerName = GetName() + CPLString("_") + psName; AddGeomTable(geomLayerName, psName, wkbPolygon); AddGeomField(psName, wkbPolygon); } else { // Polyline, DirectedPolyline AddGeomField(psName, wkbMultiLineString); } } else { if (EQUAL(psKind, "Area") || EQUAL(psKind, "Surface")) { AddGeomField(psName, wkbPolygon); } else { // Polyline, DirectedPolyline AddGeomField(psName, wkbMultiLineString); } } } else { //ClassRefType CPLError(CE_Warning, CPLE_NotSupported, "Field '%s' of class %s has unsupported type %s", psName, GetName(), typeName); } } } } FeatureDefnInfo tableDefs() { FeatureDefnInfo poLayerInfo; poLayerInfo.poTableDefn = NULL; if (!hasDerivedClasses && !isEmbedded()) { poLayerInfo.poTableDefn = poTableDefn; poLayerInfo.poGeomFieldInfos = poGeomFieldInfos; } return poLayerInfo; } }; ImdReader::ImdReader(int iliVersionIn) : iliVersion(iliVersionIn), modelInfos() { mainModelName = "OGR"; mainTopicName = "OGR"; codeBlank = '_'; codeUndefined = '@'; codeContinue = '\\'; } ImdReader::~ImdReader() { } void ImdReader::ReadModel(const char *pszFilename) { CPLDebug( "OGR_ILI", "Reading model '%s'", pszFilename); CPLXMLNode* psRootNode = CPLParseXMLFile(pszFilename); if( psRootNode == NULL ) return; CPLXMLNode *psSectionNode = CPLGetXMLNode( psRootNode, "=TRANSFER.DATASECTION" ); if( psSectionNode == NULL ) return; StrNodeMap oTidLookup; /* for fast lookup of REF relations */ ClassesMap oClasses; NodeCountMap oAxisCount; const char *modelName; /* Fill TID lookup map and IliClasses lookup map */ CPLXMLNode* psModel = psSectionNode->psChild; while( psModel != NULL ) { modelName = CPLGetXMLValue( psModel, "BID", NULL ); //CPLDebug( "OGR_ILI", "Model: '%s'", modelName); CPLXMLNode* psEntry = psModel->psChild; while( psEntry != NULL ) { if (psEntry->eType != CXT_Attribute) //ignore BID { //CPLDebug( "OGR_ILI", "Node tag: '%s'", psEntry->pszValue); const char* psTID = CPLGetXMLValue( psEntry, "TID", NULL ); if( psTID != NULL ) oTidLookup[psTID] = psEntry; if( EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.Model") && !EQUAL(modelName, "MODEL.INTERLIS")) { IliModelInfo modelInfo; modelInfo.name = CPLGetXMLValue( psEntry, "Name", "OGR" ); modelInfo.version = CPLGetXMLValue( psEntry, "Version", "" ); modelInfo.uri = CPLGetXMLValue( psEntry, "At", "" ); modelInfos.push_back(modelInfo); mainModelName = modelInfo.name; //FIXME: check model inheritance //version = CPLGetXMLValue(psEntry, "iliVersion", "0"); //1 or 2.3 CPLXMLNode *psFormatNode = CPLGetXMLNode( psEntry, "ili1Format" ); if (psFormatNode != NULL) { psFormatNode = psFormatNode->psChild; codeBlank = atoi(CPLGetXMLValue(psFormatNode, "blankCode", "95")); codeUndefined = atoi(CPLGetXMLValue(psFormatNode, "undefinedCode", "64")); codeContinue = atoi(CPLGetXMLValue(psFormatNode, "continueCode", "92")); } } else if( EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.SubModel") && !EQUAL(modelName, "MODEL.INTERLIS")) { mainBasketName = CPLGetXMLValue(psEntry, "TID", "OGR"); mainTopicName = CPLGetXMLValue(psEntry, "Name", "OGR"); } else if( EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.Class") && !EQUAL(modelName, "MODEL.INTERLIS")) { CPLDebug( "OGR_ILI", "Class name: '%s'", psTID); oClasses[psEntry] = new IliClass(psEntry, psTID, iliVersion, oTidLookup, oClasses, oAxisCount); } } psEntry = psEntry->psNext; } // 2nd pass: add fields via TransferElement entries & role associations psEntry = psModel->psChild; while( psEntry != NULL ) { if (psEntry->eType != CXT_Attribute) //ignore BID { //CPLDebug( "OGR_ILI", "Node tag: '%s'", psEntry->pszValue); if( EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.TransferElement") && !EQUAL(modelName, "MODEL.INTERLIS")) { const char* psClassRef = CPLGetXMLValue( psEntry, "TransferClass.REF", NULL ); const char* psElementRef = CPLGetXMLValue( psEntry, "TransferElement.REF", NULL ); int iOrderPos = atoi(CPLGetXMLValue( psEntry, "TransferElement.ORDER_POS", "0" ))-1; IliClass* psParentClass = oClasses[oTidLookup[psClassRef]]; CPLXMLNode* psElementNode = oTidLookup[psElementRef]; psParentClass->AddFieldNode(psElementNode, iOrderPos); } else if( EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.Role") && !EQUAL(modelName, "MODEL.INTERLIS")) { const char* psRefParent = CPLGetXMLValue( psEntry, "Association.REF", NULL ); int iOrderPos = atoi(CPLGetXMLValue( psEntry, "Association.ORDER_POS", "0" ))-1; IliClass* psParentClass = oClasses[oTidLookup[psRefParent]]; if (psParentClass) psParentClass->AddRoleNode(psEntry, iOrderPos); } else if( EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.AxisSpec") && !EQUAL(modelName, "MODEL.INTERLIS")) { const char* psClassRef = CPLGetXMLValue( psEntry, "CoordType.REF", NULL ); //int iOrderPos = atoi(CPLGetXMLValue( psEntry, "Axis.ORDER_POS", "0" ))-1; CPLXMLNode* psCoordTypeNode = oTidLookup[psClassRef]; oAxisCount[psCoordTypeNode] += 1; } } psEntry = psEntry->psNext; } psModel = psModel->psNext; } /* Analyze class inheritance & add fields to class table defn */ for (ClassesMap::const_iterator it = oClasses.begin(); it != oClasses.end(); ++it) { //CPLDebug( "OGR_ILI", "Class: '%s'", it->second->GetName()); const char* psRefSuper = CPLGetXMLValue( it->first, "Super.REF", NULL ); if (psRefSuper) oClasses[oTidLookup[psRefSuper]]->hasDerivedClasses = true; it->second->InitFieldDefinitions(); it->second->AddFieldDefinitions(); } /* Filter relevant classes */ for (ClassesMap::const_iterator it = oClasses.begin(); it != oClasses.end(); ++it) { FeatureDefnInfo oClassInfo = it->second->tableDefs(); if (oClassInfo.poTableDefn) featureDefnInfos.push_back(oClassInfo); } CPLDestroyXMLNode(psRootNode); } FeatureDefnInfo ImdReader::GetFeatureDefnInfo(const char *pszLayerName) { FeatureDefnInfo featureDefnInfo; for (FeatureDefnInfos::const_iterator it = featureDefnInfos.begin(); it != featureDefnInfos.end(); ++it) { OGRFeatureDefn* fdefn = it->poTableDefn; if (EQUAL(fdefn->GetName(), pszLayerName)) featureDefnInfo = *it; } return featureDefnInfo; }