diff --git a/autotest/ogr/data/gmlas/gmlas_internal_xlink_href.xml b/autotest/ogr/data/gmlas/gmlas_internal_xlink_href.xml
new file mode 100644
index 000000000000..d2350e834627
--- /dev/null
+++ b/autotest/ogr/data/gmlas/gmlas_internal_xlink_href.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/autotest/ogr/data/gmlas/gmlas_internal_xlink_href.xsd b/autotest/ogr/data/gmlas/gmlas_internal_xlink_href.xsd
new file mode 100644
index 000000000000..05267a3d8f7e
--- /dev/null
+++ b/autotest/ogr/data/gmlas/gmlas_internal_xlink_href.xsd
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/autotest/ogr/data/gmlas/gmlas_test_targetelement.xml b/autotest/ogr/data/gmlas/gmlas_test_targetelement.xml
index 082110b536a5..c199ad05f803 100644
--- a/autotest/ogr/data/gmlas/gmlas_test_targetelement.xml
+++ b/autotest/ogr/data/gmlas/gmlas_test_targetelement.xml
@@ -1,5 +1,6 @@
-
+
+
+
diff --git a/autotest/ogr/data/gmlas/gmlas_test_targetelement.xsd b/autotest/ogr/data/gmlas/gmlas_test_targetelement.xsd
index 720362b43c44..2c31e28d3367 100644
--- a/autotest/ogr/data/gmlas/gmlas_test_targetelement.xsd
+++ b/autotest/ogr/data/gmlas/gmlas_test_targetelement.xsd
@@ -24,10 +24,18 @@
-
+
- other_ns:existing_target_elt
+ other_ns:target_elt_with_required_id
+
+
+
+
+
+
+
+ other_ns:target_elt_with_optional_id
diff --git a/autotest/ogr/data/gmlas/gmlas_test_targetelement_other_ns.xsd b/autotest/ogr/data/gmlas/gmlas_test_targetelement_other_ns.xsd
index 153f54c4aade..760647e6cf79 100644
--- a/autotest/ogr/data/gmlas/gmlas_test_targetelement_other_ns.xsd
+++ b/autotest/ogr/data/gmlas/gmlas_test_targetelement_other_ns.xsd
@@ -7,11 +7,21 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/autotest/ogr/data/gmlas/real_world/output/EUReg.example.txt b/autotest/ogr/data/gmlas/real_world/output/EUReg.example.txt
index 2cbada1215dc..bb657ded5b72 100644
--- a/autotest/ogr/data/gmlas/real_world/output/EUReg.example.txt
+++ b/autotest/ogr/data/gmlas/real_world/output/EUReg.example.txt
@@ -98,6 +98,7 @@ OGRFeature(productionfacility):1
beginlifespanversion (DateTime) = (null)
hostingsite_href (String) = #_123456789.SITE
hostingsite_owns (Integer(Boolean)) = 0
+ hostingsite_productionsite_eureg_productionsite_pkid (String) = _123456789.SITE
facilityname_owns (Integer(Boolean)) = 0
facilityname_featurename_nameoffeature (String) = EXAMPLE FACILITY 1
facilityname_featurename_confidentialityreason_owns (Integer(Boolean)) = 0
@@ -200,12 +201,14 @@ OGRFeature(productionfacility_groupedinstallation):1
parent_id (String) = _000000002.FACILITY
href (String) = #_010101011.INSTALLATION
owns (Integer(Boolean)) = 0
+ productioninstallation_eureg_productioninstallation_pkid (String) = _010101011.INSTALLATION
OGRFeature(productionfacility_groupedinstallation):2
ogr_pkid (String) = _000000002.FACILITY_groupedInstallation_2
parent_id (String) = _000000002.FACILITY
href (String) = #_010101012.INSTALLATION
owns (Integer(Boolean)) = 0
+ productioninstallation_eureg_productioninstallation_pkid (String) = _010101012.INSTALLATION
Layer name: productionfacility_competentauthorityeprtr
@@ -368,12 +371,14 @@ OGRFeature(eureg_productioninstallation_groupedinstallationpart):1
parent_id (String) = _010101011.INSTALLATION
href (String) = #_987654321.PART
owns (Integer(Boolean)) = 0
+ productioninstallatipart_eureg_productioninstallatipart_pkid (String) = _987654321.PART
OGRFeature(eureg_productioninstallation_groupedinstallationpart):2
ogr_pkid (String) = _010101012.INSTALLATION_groupedInstallationPart_2
parent_id (String) = _010101012.INSTALLATION
href (String) = #_987654322.PART
owns (Integer(Boolean)) = 0
+ productioninstallatipart_eureg_productioninstallatipart_pkid (String) = _987654322.PART
Layer name: eureg_productioninstallation_competentauthoritypermits
@@ -633,6 +638,7 @@ OGRFeature(eureg_productionsite):1
sitename_featurename_confidentialityreason_owns (Integer(Boolean)) = 0
reportdata_href (String) = #ES.RD.2017
reportdata_owns (Integer(Boolean)) = 0
+ reportdata_reportdata_pkid (String) = ES.RD.2017
eureg_location = POINT (2.104334 41.991925)
diff --git a/autotest/ogr/ogr_gmlas.py b/autotest/ogr/ogr_gmlas.py
index 6127133fef46..beff53de231d 100755
--- a/autotest/ogr/ogr_gmlas.py
+++ b/autotest/ogr/ogr_gmlas.py
@@ -359,15 +359,18 @@ def ogr_gmlas_gml_Reference():
return 'skip'
ds = ogr.Open('GMLAS:data/gmlas/gmlas_test_targetelement.xml')
- if ds.GetLayerCount() != 2:
+ if ds.GetLayerCount() != 3:
gdaltest.post_reason('fail')
print(ds.GetLayerCount())
return 'fail'
lyr = ds.GetLayerByName('main_elt')
- f = lyr.GetNextFeature()
- if f['reference_existing_target_elt_href'] != '#BAZ' or \
- f['reference_existing_target_elt_pkid'] != 'BAZ' or \
+ with gdaltest.error_handler():
+ f = lyr.GetNextFeature()
+ if f['reference_existing_target_elt_with_required_id_href'] != '#BAZ' or \
+ f['reference_existing_target_elt_with_required_id_pkid'] != 'BAZ' or \
+ f['reference_existing_target_elt_with_optional_id_href'] != '#BAZ2' or \
+ f['refe_exis_targ_elt_with_opti_id_targe_elt_with_optio_id_pkid'] != 'F36BAD21BD2F14DDCA8852DBF8C90DBC_target_elt_with_optional_id_1' or \
f['reference_existing_abstract_target_elt_href'] != '#BAW' or \
f.IsFieldSet('reference_existing_abstract_target_elt_nillable_href') or \
f['reference_existing_abstract_target_elt_nillable_nil'] != 1:
@@ -731,7 +734,7 @@ def ogr_gmlas_validate():
lyr.GetFeatureCount()
gdal.PopErrorHandler()
# Unexpected element with xpath=myns:main_elt/myns:bar (subxpath=myns:main_elt/myns:bar) found
- if len(myhandler.error_list) != 2:
+ if len(myhandler.error_list) < 2:
gdaltest.post_reason('fail')
print(myhandler.error_list)
return 'fail'
@@ -3335,7 +3338,7 @@ def ogr_gmlas_extra_eureg():
###############################################################################
# Test a schema that has nothing interesting in it but imports another
-# sceham
+# schema
def ogr_gmlas_no_element_in_first_choice_schema():
@@ -3352,6 +3355,160 @@ def ogr_gmlas_no_element_in_first_choice_schema():
return 'success'
+
+###############################################################################
+# Test cross-layer links with xlink:href="#my_id"
+
+def ogr_gmlas_internal_xlink_href():
+
+ if ogr.GetDriverByName('GMLAS') is None:
+ return 'skip'
+
+ with gdaltest.error_handler():
+ ds = gdal.OpenEx('GMLAS:data/gmlas/gmlas_internal_xlink_href.xml')
+ lyr = ds.GetLayerByName('main_elt')
+ f = lyr.GetNextFeature()
+ if f['link_to_second_or_third_elt_href'] != '#does_not_exist' or \
+ f.IsFieldSet('link_to_second_or_third_elt_second_elt_pkid') or \
+ f.IsFieldSet('link_to_second_or_third_elt_third_elt_pkid') or \
+ f.IsFieldSet('link_to_third_elt_third_elt_pkid'):
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ f = lyr.GetNextFeature()
+ if f['link_to_second_or_third_elt_href'] != '#id2' or \
+ f['link_to_second_or_third_elt_second_elt_pkid'] != 'id2' or \
+ f.IsFieldSet('link_to_second_or_third_elt_third_elt_pkid') or \
+ f.IsFieldSet('link_to_third_elt_third_elt_pkid'):
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ f = lyr.GetNextFeature()
+ if f['link_to_second_or_third_elt_href'] != '#id3' or \
+ f['link_to_second_or_third_elt_second_elt_pkid'] != 'id3' or \
+ f.IsFieldSet('link_to_second_or_third_elt_third_elt_pkid') or \
+ f.IsFieldSet('link_to_third_elt_third_elt_pkid'):
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ f = lyr.GetNextFeature()
+ if f['link_to_second_or_third_elt_href'] != '#id4' or \
+ f.IsFieldSet('link_to_second_or_third_elt_second_elt_pkid') or \
+ f['link_to_second_or_third_elt_third_elt_pkid'] != 'D1013B7E44F28C976B976A4314FA4A09_third_elt_1' or \
+ f.IsFieldSet('link_to_third_elt_third_elt_pkid'):
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ f = lyr.GetNextFeature()
+ if f['link_to_third_elt_href'] != '#id4' or \
+ f.IsFieldSet('link_to_second_or_third_elt_second_elt_pkid') or \
+ f.IsFieldSet('link_to_second_or_third_elt_third_elt_pkid') or \
+ f['link_to_third_elt_third_elt_pkid'] != 'D1013B7E44F28C976B976A4314FA4A09_third_elt_1':
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ lyr = ds.GetLayerByName('_ogr_fields_metadata')
+ f = lyr.GetNextFeature()
+ if f['layer_name'] != 'main_elt' or f['field_index'] != 1 or \
+ f['field_name'] != 'link_to_second_or_third_elt_href':
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ f = lyr.GetNextFeature()
+ if f['layer_name'] != 'main_elt' or f['field_index'] != 2 or \
+ f['field_name'] != 'link_to_second_or_third_elt_second_elt_pkid' or \
+ f['field_xpath'] != 'main_elt/link_to_second_or_third_elt/second_elt' or \
+ f['field_type'] != 'string' or \
+ f['field_is_list'] != 0 or \
+ f['field_min_occurs'] != 0 or \
+ f['field_max_occurs'] != 1 or \
+ f['field_category'] != 'PATH_TO_CHILD_ELEMENT_WITH_LINK' or \
+ f['field_related_layer'] != 'second_elt':
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ f = lyr.GetNextFeature()
+ if f['layer_name'] != 'main_elt' or f['field_index'] != 3 or \
+ f['field_name'] != 'link_to_second_or_third_elt_third_elt_pkid' or \
+ f['field_xpath'] != 'main_elt/link_to_second_or_third_elt/third_elt' or \
+ f['field_type'] != 'string' or \
+ f['field_is_list'] != 0 or \
+ f['field_min_occurs'] != 0 or \
+ f['field_max_occurs'] != 1 or \
+ f['field_category'] != 'PATH_TO_CHILD_ELEMENT_WITH_LINK' or \
+ f['field_related_layer'] != 'third_elt':
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ f = lyr.GetNextFeature()
+ if f['layer_name'] != 'main_elt' or f['field_index'] != 4 or \
+ f['field_name'] != 'link_to_second_or_third_elt':
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ f = lyr.GetNextFeature()
+ if f['layer_name'] != 'main_elt' or f['field_index'] != 5 or \
+ f['field_name'] != 'link_to_third_elt_href':
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ f = lyr.GetNextFeature()
+ if f['layer_name'] != 'main_elt' or f['field_index'] != 6 or \
+ f['field_name'] != 'link_to_third_elt_third_elt_pkid':
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ f = lyr.GetNextFeature()
+ if f['layer_name'] != 'third_elt' or f['field_index'] != 1 or \
+ f['field_name'] != 'id':
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ lyr = ds.GetLayerByName('_ogr_layer_relationships')
+ f = lyr.GetNextFeature()
+ if f['parent_layer'] != 'main_elt' or \
+ f['parent_pkid'] != 'ogr_pkid' or \
+ f['parent_element_name'] != 'link_to_third_elt_third_elt_pkid' or \
+ f['child_layer'] != 'third_elt' or \
+ f['child_pkid'] != 'ogr_pkid':
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ f = lyr.GetNextFeature()
+ if f['parent_layer'] != 'main_elt' or \
+ f['parent_pkid'] != 'ogr_pkid' or \
+ f['parent_element_name'] != 'link_to_second_or_third_elt_second_elt_pkid' or \
+ f['child_layer'] != 'second_elt' or \
+ f['child_pkid'] != 'id':
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ f = lyr.GetNextFeature()
+ if f['parent_layer'] != 'main_elt' or \
+ f['parent_pkid'] != 'ogr_pkid' or \
+ f['parent_element_name'] != 'link_to_second_or_third_elt_third_elt_pkid' or \
+ f['child_layer'] != 'third_elt' or \
+ f['child_pkid'] != 'ogr_pkid':
+ gdaltest.post_reason('fail')
+ f.DumpReadable()
+ return 'fail'
+
+ return 'success'
+
###############################################################################
# Cleanup
@@ -3426,6 +3583,7 @@ def ogr_gmlas_cleanup():
ogr_gmlas_aux_schema_without_namespace_prefix,
ogr_gmlas_geometry_as_substitutiongroup,
ogr_gmlas_no_element_in_first_choice_schema,
+ ogr_gmlas_internal_xlink_href,
ogr_gmlas_cleanup ]
# gdaltest_list = [ ogr_gmlas_basic, ogr_gmlas_aux_schema_without_namespace_prefix, ogr_gmlas_cleanup ]
diff --git a/gdal/data/gmlasconf.xml b/gdal/data/gmlasconf.xml
index 01ba5f096ec1..8fc6b67b9eac 100644
--- a/gdal/data/gmlasconf.xml
+++ b/gdal/data/gmlasconf.xml
@@ -119,6 +119,7 @@
-->
+ true
diff --git a/gdal/data/gmlasconf.xsd b/gdal/data/gmlasconf.xsd
index ef3122b2d084..3e67eacdd7cf 100644
--- a/gdal/data/gmlasconf.xsd
+++ b/gdal/data/gmlasconf.xsd
@@ -765,6 +765,21 @@
+
+
+
+
+ Whether xlink:href pointing to internal resources should be
+ resolved, so as to establish cross-layer relationships.
+ This options requires to keep in-memory xlink:href values
+ as well as feature ids, which in the case of really large
+ documents with many features and/or many cross-references
+ could consume a lot of RAM.
+ Default is true.
+
+
+
+
diff --git a/gdal/ogr/ogrsf_frmts/gmlas/GNUmakefile b/gdal/ogr/ogrsf_frmts/gmlas/GNUmakefile
index 7154e2094161..8f0a08a953bf 100644
--- a/gdal/ogr/ogrsf_frmts/gmlas/GNUmakefile
+++ b/gdal/ogr/ogrsf_frmts/gmlas/GNUmakefile
@@ -4,7 +4,7 @@ include ../../../GDALmake.opt
OBJ = ogrgmlasdriver.o ogrgmlasdatasource.o ogrgmlaslayer.o \
ogrgmlasreader.o ogrgmlasschemaanalyzer.o ogrgmlasfeatureclass.o \
ogrgmlasxsdcache.o ogrgmlasconf.o ogrgmlasxpatchmatcher.o \
- ogrgmlasxlinkresolver.o ogrgmlaswriter.o
+ ogrgmlasxlinkresolver.o ogrgmlaswriter.o ogrgmlasutils.o
CPPFLAGS := -I../mem -I../geojson $(JSON_INCLUDE) -I.. -I../.. -I../pgdump -DHAVE_XERCES=1 \
$(XERCES_INCLUDE) $(CPPFLAGS)
diff --git a/gdal/ogr/ogrsf_frmts/gmlas/makefile.vc b/gdal/ogr/ogrsf_frmts/gmlas/makefile.vc
index b8e5a5f6eef9..3d9bf33ed5ee 100644
--- a/gdal/ogr/ogrsf_frmts/gmlas/makefile.vc
+++ b/gdal/ogr/ogrsf_frmts/gmlas/makefile.vc
@@ -2,7 +2,7 @@
OBJ = ogrgmlasdriver.obj ogrgmlasdatasource.obj ogrgmlaslayer.obj \
ogrgmlasreader.obj ogrgmlasschemaanalyzer.obj ogrgmlasfeatureclass.obj \
ogrgmlasxsdcache.obj ogrgmlasconf.obj ogrgmlasxpatchmatcher.obj \
- ogrgmlasxlinkresolver.obj ogrgmlaswriter.obj
+ ogrgmlasxlinkresolver.obj ogrgmlaswriter.obj ogrgmlasutils.obj
GDAL_ROOT = ..\..\..
diff --git a/gdal/ogr/ogrsf_frmts/gmlas/ogr_gmlas.h b/gdal/ogr/ogrsf_frmts/gmlas/ogr_gmlas.h
index 2ac08caa6b76..1aa7bfeafed5 100644
--- a/gdal/ogr/ogrsf_frmts/gmlas/ogr_gmlas.h
+++ b/gdal/ogr/ogrsf_frmts/gmlas/ogr_gmlas.h
@@ -246,6 +246,8 @@ class GMLASXLinkResolutionConf
bool m_bDefaultCacheResults;
+ bool m_bResolveInternalXLinks;
+
class URLSpecificResolution
{
public:
@@ -882,6 +884,8 @@ class GMLASSchemaAnalyzer
* GML document */
std::map m_oMapDocNSURIToPrefix;
+ bool m_bAlwaysGenerateOGRId;
+
static bool IsSame( const XSModelGroup* poModelGroup1,
const XSModelGroup* poModelGroup2 );
XSModelGroupDefinition* GetGroupDefinition( const XSModelGroup* poModelGroup );
@@ -945,12 +949,6 @@ class GMLASSchemaAnalyzer
bool IsIgnoredXPath(const CPLString& osXPath);
- CPLString TruncateIdentifier(const CPLString& osName);
-
- CPLString AddSerialNumber(const CPLString& osNameIn,
- int iOccurrence,
- size_t nOccurrences);
-
void CollectClassesReferences(
GMLASFeatureClass& oClass,
std::vector& aoClasses );
@@ -980,6 +978,8 @@ class GMLASSchemaAnalyzer
{ m_nMaximumFieldsForFlattening = n; }
void SetMapDocNSURIToPrefix(const std::map& oMap)
{ m_oMapDocNSURIToPrefix = oMap; }
+ void SetAlwaysGenerateOGRId(bool b)
+ { m_bAlwaysGenerateOGRId = b; }
bool Analyze(GMLASXSDCache& oCache,
const CPLString& osBaseDirname,
@@ -1035,6 +1035,12 @@ class OGRGMLASDataSource: public GDALDataset
/** Map from geometry field definition to its expected SRSName */
std::map m_oMapGeomFieldDefnToSRSName;
+ /* map the ID attribute to its belonging layer, e.g foo.1 -> layer Foo */
+ std::map m_oMapElementIdToLayer;
+
+ /* map the ID attribute to the feature PKID (when different from itself) */
+ std::map m_oMapElementIdToPKID;
+
std::vector m_aoXSDsManuallyPassed;
GMLASConfiguration m_oConf;
@@ -1087,6 +1093,9 @@ class OGRGMLASDataSource: public GDALDataset
static std::vector BuildXSDVector(
const CPLString& osXSDFilenames);
+
+ void InitReaderWithFirstPassElements(GMLASReader* poReader);
+
public:
OGRGMLASDataSource();
virtual ~OGRGMLASDataSource();
@@ -1137,7 +1146,6 @@ class OGRGMLASDataSource: public GDALDataset
return m_oConf.m_oMapIgnoredXPathToWarn; }
const GMLASXPathMatcher& GetIgnoredXPathMatcher() const
{ return m_oIgnoredXPathMatcher; }
- const CPLString& GetHash() const { return m_osHash; }
const GMLASConfiguration& GetConf() const { return m_oConf; }
const std::vector& GetXSDsManuallyPassed() const {
@@ -1195,6 +1203,10 @@ class OGRGMLASLayer: public OGRLayer
void SetLayerDefnFinalized(bool bVal)
{ m_bLayerDefnFinalized = bVal; }
+ CPLString LaunderFieldName(const CPLString& osFieldName);
+
+ CPLString GetXPathFromOGRFieldIndex(int nIdx) const;
+
public:
OGRGMLASLayer(OGRGMLASDataSource* poDS,
const GMLASFeatureClass& oFC,
@@ -1239,6 +1251,12 @@ class OGRGMLASLayer: public OGRLayer
void InsertNewField( int nInsertPos,
OGRFieldDefn& oFieldDefn,
const CPLString& osXPath );
+
+ CPLString GetXPathOfFieldLinkForAttrToOtherLayer(
+ const CPLString& osFieldName,
+ const CPLString& osTargetLayerXPath );
+ CPLString CreateLinkForAttrToOtherLayer( const CPLString& osFieldName,
+ const CPLString& osTargetLayerXPath );
};
/************************************************************************/
@@ -1465,6 +1483,16 @@ class GMLASReader : public DefaultHandler
std::vector m_apoSWEDataArrayLayers;
int m_nSWEDataArrayLayerIdx;
+ /* Set of 3 maps used for xlink:href="#xxxx" internal links resolution */
+ /* 1) map the ID attribute to its belonging layer, e.g foo.1 -> layer Foo */
+ std::map m_oMapElementIdToLayer;
+ /* 2) map the ID attribute to the feature PKID (when different from itself) */
+ std::map m_oMapElementIdToPKID;
+ /* 3) map each (layer, field_xpath) to the list of ID it refers to */
+ /* e.g (layer Bar, field_xpath) -> [foo.1, foo.2] */
+ std::map,
+ std::vector > m_oMapFieldXPathToLinkValue;
+
void SetField( OGRFeature* poFeature,
OGRGMLASLayer* poLayer,
int nAttrIdx,
@@ -1490,7 +1518,8 @@ class GMLASReader : public DefaultHandler
void ProcessGeometry(CPLXMLNode* psRoot);
void ProcessAttributes(const Attributes& attrs);
- void ProcessXLinkHref( const CPLString& osAttrXPath,
+ void ProcessXLinkHref( int nAttrIdx,
+ const CPLString& osAttrXPath,
const CPLString& osAttrValue );
void ExploreXMLDoc( const CPLString& osAttrXPath,
const GMLASXLinkResolutionConf::URLSpecificResolution& oRule,
@@ -1509,6 +1538,9 @@ class GMLASReader : public DefaultHandler
bool FillTextContent() const { return !m_bInitialPass && m_nCurFieldIdx >=0; }
+ void ProcessInternalXLinkFirstPass(bool bRemoveUnusedFields,
+ std::map >&oMapUnusedFields);
+
public:
GMLASReader(GMLASXSDCache& oCache,
const GMLASXPathMatcher& oIgnoredXPathMatcher,
@@ -1544,6 +1576,16 @@ class GMLASReader : public DefaultHandler
void SetMapGeomFieldDefnToSRSName(const std::map& oMap )
{ m_oMapGeomFieldDefnToSRSName = oMap; }
+ const std::map& GetMapElementIdToLayer() const
+ { return m_oMapElementIdToLayer; }
+ void SetMapElementIdToLayer(std::map& oMap)
+ { m_oMapElementIdToLayer = oMap; }
+
+ const std::map& GetMapElementIdToPKID() const
+ { return m_oMapElementIdToPKID; }
+ void SetMapElementIdToPKID(const std::map& oMap )
+ { m_oMapElementIdToPKID = oMap; }
+
void SetHash(const CPLString& osHash) { m_osHash = osHash; }
void SetFileSize(vsi_l_offset nFileSize) { m_nFileSize = nFileSize; }
@@ -1592,4 +1634,12 @@ class GMLASReader : public DefaultHandler
{ return m_apoSWEDataArrayLayers; }
};
+CPLString OGRGMLASTruncateIdentifier(const CPLString& osName,
+ int nIdentMaxLength);
+
+CPLString OGRGMLASAddSerialNumber(const CPLString& osNameIn,
+ int iOccurrence,
+ size_t nOccurrences,
+ int nIdentMaxLength);
+
#endif // OGR_GMLAS_INCLUDED
diff --git a/gdal/ogr/ogrsf_frmts/gmlas/ogr_gmlas_consts.h b/gdal/ogr/ogrsf_frmts/gmlas/ogr_gmlas_consts.h
index 3d3f6599ad99..6b3992fcc376 100644
--- a/gdal/ogr/ogrsf_frmts/gmlas/ogr_gmlas_consts.h
+++ b/gdal/ogr/ogrsf_frmts/gmlas/ogr_gmlas_consts.h
@@ -28,8 +28,8 @@
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
-#ifndef OGR_GMLAS_CONSTS_INCLUDED_REFEFINABLE
-#define OGR_GMLAS_CONSTS_INCLUDED_REFEFINABLE
+#ifndef OGR_GMLAS_CONSTS_INCLUDED_REDEFINABLE
+#define OGR_GMLAS_CONSTS_INCLUDED_REDEFINABLE
#ifdef CONSTANT_DEFINITION
#define STRING_CONST(x,y) const char* const x = y
@@ -48,6 +48,7 @@ namespace GMLASConstants
BOOL_CONST(DEFAULT_RESOLUTION_ENABLED_DEFAULT, false);
BOOL_CONST(ALLOW_REMOTE_DOWNLOAD_DEFAULT, true);
BOOL_CONST(CACHE_RESULTS_DEFAULT, false);
+ BOOL_CONST(INTERNAL_XLINK_RESOLUTION_DEFAULT, false);
BOOL_CONST(ALLOW_REMOTE_SCHEMA_DOWNLOAD_DEFAULT, true);
BOOL_CONST(ALWAYS_GENERATE_OGR_ID_DEFAULT, false);
@@ -315,4 +316,4 @@ namespace GMLASConstants
using namespace GMLASConstants;
-#endif // OGR_GMLAS_CONSTS_INCLUDED_REFEFINABLE
+#endif // OGR_GMLAS_CONSTS_INCLUDED_REDEFINABLE
diff --git a/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasconf.cpp b/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasconf.cpp
index bead8c03353c..6ab97b6ce28e 100644
--- a/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasconf.cpp
+++ b/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasconf.cpp
@@ -32,7 +32,7 @@
#include "ogr_gmlas.h"
#define CONSTANT_DEFINITION
-#undef OGR_GMLAS_CONSTS_INCLUDED_REFEFINABLE
+#undef OGR_GMLAS_CONSTS_INCLUDED_REDEFINABLE
#include "ogr_gmlas_consts.h"
#include "cpl_minixml.h"
@@ -557,7 +557,8 @@ GMLASXLinkResolutionConf::GMLASXLinkResolutionConf() :
m_bDefaultAllowRemoteDownload(ALLOW_REMOTE_DOWNLOAD_DEFAULT),
m_eDefaultResolutionMode(RawContent),
m_nDefaultResolutionDepth(1),
- m_bDefaultCacheResults(CACHE_RESULTS_DEFAULT)
+ m_bDefaultCacheResults(CACHE_RESULTS_DEFAULT),
+ m_bResolveInternalXLinks(INTERNAL_XLINK_RESOLUTION_DEFAULT)
{
}
@@ -662,6 +663,10 @@ bool GMLASXLinkResolutionConf::LoadFromXML(CPLXMLNode* psRoot)
}
}
+ m_bResolveInternalXLinks = CPLGetXMLBoolValue( psRoot,
+ "ResolveInternalXLinks",
+ INTERNAL_XLINK_RESOLUTION_DEFAULT);
+
return true;
}
diff --git a/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasdatasource.cpp b/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasdatasource.cpp
index bb1feb1d5532..6311c99978bc 100644
--- a/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasdatasource.cpp
+++ b/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasdatasource.cpp
@@ -227,6 +227,7 @@ OGRLayer *OGRGMLASDataSource::GetLayer(int i)
const int nBaseLayers = static_cast(m_apoLayers.size());
if( i >= nBaseLayers )
{
+ RunFirstPassIfNeeded(NULL, NULL, NULL);
if( i - nBaseLayers < static_cast(m_apoRequestedMetadataLayers.size()) )
return m_apoRequestedMetadataLayers[i - nBaseLayers];
}
@@ -261,6 +262,7 @@ OGRLayer *OGRGMLASDataSource::GetLayerByName(const char* pszName)
{
m_apoRequestedMetadataLayers.push_back(apoLayers[i]);
}
+ RunFirstPassIfNeeded(NULL, NULL, NULL);
return apoLayers[i];
}
}
@@ -777,6 +779,7 @@ bool OGRGMLASDataSource::Open(GDALOpenInfo* poOpenInfo)
m_oConf.m_bCaseInsensitiveIdentifier);
oAnalyzer.SetPGIdentifierLaundering(m_oConf.m_bPGIdentifierLaundering);
oAnalyzer.SetMaximumFieldsForFlattening(m_oConf.m_nMaximumFieldsForFlattening);
+ oAnalyzer.SetAlwaysGenerateOGRId(m_oConf.m_bAlwaysGenerateOGRId);
m_osGMLFilename = STARTS_WITH_CI(poOpenInfo->pszFilename, szGMLAS_PREFIX) ?
CPLExpandTilde(poOpenInfo->pszFilename + strlen(szGMLAS_PREFIX)) :
@@ -1059,7 +1062,7 @@ GMLASReader* OGRGMLASDataSource::CreateReader( VSILFILE*& fpGML,
poReader->SetMapIgnoredXPathToWarn( GetMapIgnoredXPathToWarn());
- poReader->SetHash( GetHash() );
+ poReader->SetHash( m_osHash );
return poReader;
}
@@ -1247,6 +1250,23 @@ VSILFILE* OGRGMLASDataSource::PopUnusedGMLFilePointer()
return fpGML;
}
+/************************************************************************/
+/* InitReaderWithFirstPassElements() */
+/************************************************************************/
+
+void OGRGMLASDataSource::InitReaderWithFirstPassElements(GMLASReader* poReader)
+{
+ if( poReader != NULL )
+ {
+ poReader->SetMapSRSNameToInvertedAxis(m_oMapSRSNameToInvertedAxis);
+ poReader->SetMapGeomFieldDefnToSRSName(m_oMapGeomFieldDefnToSRSName);
+ poReader->SetProcessDataRecord(m_bFoundSWE && m_oConf.m_bSWEProcessDataRecord);
+ poReader->SetSWEDataArrayLayers(m_apoSWEDataArrayLayers);
+ poReader->SetMapElementIdToLayer(m_oMapElementIdToLayer);
+ poReader->SetMapElementIdToPKID(m_oMapElementIdToPKID);
+ }
+}
+
/************************************************************************/
/* RunFirstPassIfNeeded() */
/************************************************************************/
@@ -1257,13 +1277,7 @@ bool OGRGMLASDataSource::RunFirstPassIfNeeded( GMLASReader* poReader,
{
if( m_bFirstPassDone )
{
- if( poReader != NULL )
- {
- poReader->SetMapSRSNameToInvertedAxis(m_oMapSRSNameToInvertedAxis);
- poReader->SetMapGeomFieldDefnToSRSName(m_oMapGeomFieldDefnToSRSName);
- poReader->SetProcessDataRecord(m_bFoundSWE && m_oConf.m_bSWEProcessDataRecord);
- poReader->SetSWEDataArrayLayers(m_apoSWEDataArrayLayers);
- }
+ InitReaderWithFirstPassElements(poReader);
return true;
}
@@ -1282,10 +1296,12 @@ bool OGRGMLASDataSource::RunFirstPassIfNeeded( GMLASReader* poReader,
}
}
+ bool bSuccess = true;
const bool bHasURLSpecificRules =
!m_oXLinkResolver.GetConf().m_aoURLSpecificRules.empty();
if( bHasGeomFields || m_bValidate || m_bRemoveUnusedLayers ||
m_bRemoveUnusedFields || bHasURLSpecificRules ||
+ m_oXLinkResolver.GetConf().m_bResolveInternalXLinks ||
(m_bFoundSWE && (m_oConf.m_bSWEProcessDataRecord ||
m_oConf.m_bSWEProcessDataArray)) )
{
@@ -1322,11 +1338,14 @@ bool OGRGMLASDataSource::RunFirstPassIfNeeded( GMLASReader* poReader,
poReaderFirstPass->SetMapIgnoredXPathToWarn(
m_oConf.m_oMapIgnoredXPathToWarn);
+
+ poReaderFirstPass->SetHash( m_osHash );
+
// No need to warn afterwards
m_oConf.m_oMapIgnoredXPathToWarn.clear();
std::set aoSetRemovedLayerNames;
- m_bFirstPassDone = poReaderFirstPass->RunFirstPass(
+ bSuccess = poReaderFirstPass->RunFirstPass(
pfnProgress,
pProgressData,
m_bRemoveUnusedLayers,
@@ -1410,26 +1429,23 @@ bool OGRGMLASDataSource::RunFirstPassIfNeeded( GMLASReader* poReader,
m_poRelationshipsLayer->ResetReading();
}
- // Store 2 maps to reinject them in real readers
+ // Store maps to reinject them in real readers
m_oMapSRSNameToInvertedAxis =
poReaderFirstPass->GetMapSRSNameToInvertedAxis();
m_oMapGeomFieldDefnToSRSName =
poReaderFirstPass->GetMapGeomFieldDefnToSRSName();
+ m_oMapElementIdToLayer = poReaderFirstPass->GetMapElementIdToLayer();
+ m_oMapElementIdToPKID = poReaderFirstPass->GetMapElementIdToPKID();
+
delete poReaderFirstPass;
VSIFSeekL(fp, 0, SEEK_SET);
if( bJustOpenedFiled )
PushUnusedGMLFilePointer(fp);
- if( poReader != NULL )
- {
- poReader->SetMapSRSNameToInvertedAxis(m_oMapSRSNameToInvertedAxis);
- poReader->SetMapGeomFieldDefnToSRSName(m_oMapGeomFieldDefnToSRSName);
- poReader->SetProcessDataRecord(m_bFoundSWE && m_oConf.m_bSWEProcessDataRecord);
- poReader->SetSWEDataArrayLayers(m_apoSWEDataArrayLayers);
- }
+ InitReaderWithFirstPassElements(poReader);
}
- return m_bFirstPassDone;
+ return bSuccess;
}
diff --git a/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlaslayer.cpp b/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlaslayer.cpp
index 8c4c6856d867..01214922fc8f 100644
--- a/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlaslayer.cpp
+++ b/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlaslayer.cpp
@@ -30,6 +30,7 @@
// Must be first for DEBUG_BOOL case
#include "ogr_gmlas.h"
+#include "ogr_pgdump.h"
#include "cpl_minixml.h"
CPL_CVSID("$Id$")
@@ -1063,6 +1064,24 @@ bool OGRGMLASLayer::RemoveField( int nIdx )
m_oMapOGRFieldIdxtoFCFieldIdx = oMapOGRFieldIdxtoFCFieldIdx;
}
+ OGRLayer* poFieldsMetadataLayer = m_poDS->GetFieldsMetadataLayer();
+ OGRFeature* poFeature;
+ poFieldsMetadataLayer->ResetReading();
+ while( (poFeature = poFieldsMetadataLayer->GetNextFeature()) != NULL )
+ {
+ if( strcmp( poFeature->GetFieldAsString( szLAYER_NAME ), GetName() ) == 0 &&
+ poFeature->GetFieldAsInteger( szFIELD_INDEX ) == nIdx )
+ {
+ poFeature->SetField( szFIELD_INDEX, -1 );
+ CPL_IGNORE_RET_VAL(
+ poFieldsMetadataLayer->SetFeature( poFeature ) );
+ delete poFeature;
+ break;
+ }
+ delete poFeature;
+ }
+ poFieldsMetadataLayer->ResetReading();
+
return true;
}
@@ -1120,6 +1139,25 @@ void OGRGMLASLayer::InsertNewField( int nInsertPos,
}
m_oMapOGRFieldIdxtoFCFieldIdx = oMapOGRFieldIdxtoFCFieldIdx;
}
+
+ OGRLayer* poFieldsMetadataLayer = m_poDS->GetFieldsMetadataLayer();
+ OGRFeature* poFeature;
+ poFieldsMetadataLayer->ResetReading();
+ while( (poFeature = poFieldsMetadataLayer->GetNextFeature()) != NULL )
+ {
+ if( strcmp( poFeature->GetFieldAsString( szLAYER_NAME ), GetName() ) == 0 )
+ {
+ int nFieldIndex = poFeature->GetFieldAsInteger( szFIELD_INDEX );
+ if( nFieldIndex >= nInsertPos )
+ {
+ poFeature->SetField( szFIELD_INDEX, nFieldIndex + 1 );
+ CPL_IGNORE_RET_VAL(
+ poFieldsMetadataLayer->SetFeature( poFeature ) );
+ }
+ }
+ delete poFeature;
+ }
+ poFieldsMetadataLayer->ResetReading();
}
/************************************************************************/
@@ -1135,6 +1173,26 @@ int OGRGMLASLayer::GetOGRFieldIndexFromXPath(const CPLString& osXPath) const
return oIter->second;
}
+/************************************************************************/
+/* GetXPathFromOGRFieldIndex() */
+/************************************************************************/
+
+CPLString OGRGMLASLayer::GetXPathFromOGRFieldIndex(int nIdx) const
+{
+ const int nFCIdx = GetFCFieldIndexFromOGRFieldIdx(nIdx);
+ if( nFCIdx >= 0 )
+ return m_oFC.GetFields()[nFCIdx].GetXPath();
+
+ std::map::const_iterator oIter =
+ m_oMapFieldXPathToOGRFieldIdx.begin();
+ for( ; oIter != m_oMapFieldXPathToOGRFieldIdx.end(); ++oIter )
+ {
+ if( oIter->second == nIdx )
+ return oIter->first;
+ }
+ return CPLString();
+}
+
/************************************************************************/
/* GetOGRGeomFieldIndexFromXPath() */
/************************************************************************/
@@ -1187,6 +1245,223 @@ int OGRGMLASLayer::GetFCFieldIndexFromOGRGeomFieldIdx(int iOGRGeomFieldIdx) cons
return oIter->second;
}
+/************************************************************************/
+/* GetXPathOfFieldLinkForAttrToOtherLayer() */
+/************************************************************************/
+
+CPLString OGRGMLASLayer::GetXPathOfFieldLinkForAttrToOtherLayer(
+ const CPLString& osFieldName,
+ const CPLString& osTargetLayerXPath )
+{
+ const int nOGRFieldIdx = GetLayerDefn()->GetFieldIndex(osFieldName);
+ CPLAssert(nOGRFieldIdx >= 0);
+ const int nFCFieldIdx = GetFCFieldIndexFromOGRFieldIdx(nOGRFieldIdx);
+ CPLAssert(nFCFieldIdx >= 0);
+ CPLString osXPath(m_oFC.GetFields()[nFCFieldIdx].GetXPath());
+ size_t nPos = osXPath.find(szAT_XLINK_HREF);
+ CPLAssert(nPos != std::string::npos);
+ CPLAssert(nPos + strlen(szAT_XLINK_HREF) == osXPath.size());
+ CPLString osTargetFieldXPath(osXPath.substr(0, nPos) + osTargetLayerXPath);
+ return osTargetFieldXPath;
+}
+
+/************************************************************************/
+/* LaunderFieldName() */
+/************************************************************************/
+
+CPLString OGRGMLASLayer::LaunderFieldName(const CPLString& osFieldName)
+{
+ int nCounter = 1;
+ CPLString osLaunderedName(osFieldName);
+ while( m_poFeatureDefn->GetFieldIndex(osLaunderedName) >= 0 )
+ {
+ nCounter ++;
+ osLaunderedName = osFieldName + CPLSPrintf("%d", nCounter);
+ }
+
+ const int nIdentifierMaxLength = m_poDS->GetConf().m_nIdentifierMaxLength;
+ if( nIdentifierMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH &&
+ osLaunderedName.size() > static_cast(nIdentifierMaxLength) )
+ {
+ osLaunderedName = OGRGMLASTruncateIdentifier(osLaunderedName,
+ nIdentifierMaxLength);
+ }
+
+ if( m_poDS->GetConf().m_bPGIdentifierLaundering )
+ {
+ char* pszLaundered = OGRPGCommonLaunderName( osLaunderedName,
+ "GMLAS" );
+ osLaunderedName = pszLaundered;
+ CPLFree( pszLaundered );
+ }
+
+ if( m_poFeatureDefn->GetFieldIndex(osLaunderedName) >= 0 )
+ {
+ nCounter = 1;
+ CPLString osCandidate;
+ do
+ {
+ osCandidate = OGRGMLASAddSerialNumber( osLaunderedName,
+ nCounter,
+ nCounter + 1,
+ nIdentifierMaxLength );
+ } while( nCounter < 100 &&
+ m_poFeatureDefn->GetFieldIndex(osCandidate) >= 0 );
+ osLaunderedName = osCandidate;
+ }
+
+ return osLaunderedName;
+}
+
+/************************************************************************/
+/* CreateLinkForAttrToOtherLayer() */
+/************************************************************************/
+
+/* Create a new field to contain the PKID of the feature pointed by this */
+/* osFieldName (a xlink:href attribute), when it is an internal link to */
+/* another layer whose xpath is given by osTargetLayerXPath */
+
+CPLString OGRGMLASLayer::CreateLinkForAttrToOtherLayer(
+ const CPLString& osFieldName,
+ const CPLString& osTargetLayerXPath )
+{
+ CPLString osTargetFieldXPath = GetXPathOfFieldLinkForAttrToOtherLayer(
+ osFieldName, osTargetLayerXPath);
+ const int nExistingTgtOGRFieldIdx =
+ GetOGRFieldIndexFromXPath(osTargetFieldXPath);
+ if( nExistingTgtOGRFieldIdx >= 0 )
+ {
+ return GetLayerDefn()->GetFieldDefn(
+ nExistingTgtOGRFieldIdx)->GetNameRef();
+ }
+
+ const int nOGRFieldIdx = GetLayerDefn()->GetFieldIndex(osFieldName);
+ CPLAssert(nOGRFieldIdx >= 0);
+ const int nFCFieldIdx = GetFCFieldIndexFromOGRFieldIdx(nOGRFieldIdx);
+ CPLAssert(nFCFieldIdx >= 0);
+ CPLString osXPath(m_oFC.GetFields()[nFCFieldIdx].GetXPath());
+ size_t nPos = osXPath.find(szAT_XLINK_HREF);
+ CPLString osXPathStart( osXPath.substr(0, nPos) );
+
+ // Find at which position to insert the new field in the layer definition
+ // (we could happen at the end, but it will be nicer to insert close to
+ // the href field)
+ int nInsertPos = -1;
+ for( int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++ )
+ {
+ if( GetXPathFromOGRFieldIndex(i).find(osXPathStart) == 0 )
+ {
+ nInsertPos = i + 1;
+ }
+ else if( nInsertPos >= 0 )
+ break;
+ }
+
+ CPLString osNewFieldName(osFieldName);
+ nPos = osFieldName.find(szHREF_SUFFIX);
+ if( nPos != std::string::npos )
+ {
+ osNewFieldName.resize(nPos);
+ }
+ osNewFieldName += "_";
+ OGRGMLASLayer* poTargetLayer = m_poDS->GetLayerByXPath(osTargetLayerXPath);
+ CPLAssert(poTargetLayer);
+ osNewFieldName += poTargetLayer->GetName();
+ osNewFieldName += "_pkid";
+ osNewFieldName = LaunderFieldName(osNewFieldName);
+ OGRFieldDefn oFieldDefn( osNewFieldName, OFTString );
+ InsertNewField( nInsertPos, oFieldDefn, osTargetFieldXPath );
+
+
+ OGRLayer* poFieldsMetadataLayer = m_poDS->GetFieldsMetadataLayer();
+ OGRLayer* poRelationshipsLayer = m_poDS->GetRelationshipsLayer();
+
+ // Find a relevant location of the field metadata layer into which to
+ // insert the new feature (same as above, we could potentially insert just
+ // at the end)
+ GIntBig nFieldsMetadataIdxPos = -1;
+ poFieldsMetadataLayer->ResetReading();
+ OGRFeature* poFeature;
+ while( (poFeature = poFieldsMetadataLayer->GetNextFeature()) != NULL )
+ {
+ if( strcmp( poFeature->GetFieldAsString( szLAYER_NAME ), GetName() ) == 0 )
+ {
+ if (poFeature->GetFieldAsInteger( szFIELD_INDEX ) > nInsertPos )
+ {
+ delete poFeature;
+ break;
+ }
+ nFieldsMetadataIdxPos = poFeature->GetFID() + 1;
+ }
+ else if( nFieldsMetadataIdxPos >= 0 )
+ {
+ delete poFeature;
+ break;
+ }
+ delete poFeature;
+ }
+ poFieldsMetadataLayer->ResetReading();
+
+ // Move down all features beyond that insertion point
+ for(GIntBig nFID = poFieldsMetadataLayer->GetFeatureCount() - 1;
+ nFID >= nFieldsMetadataIdxPos; nFID-- )
+ {
+ poFeature = poFieldsMetadataLayer->GetFeature(nFID);
+ if( poFeature )
+ {
+ poFeature->SetFID(nFID+1);
+ CPL_IGNORE_RET_VAL( poFieldsMetadataLayer->SetFeature(poFeature) );
+ delete poFeature;
+ }
+ }
+ if( nFieldsMetadataIdxPos >= 0 )
+ {
+ CPL_IGNORE_RET_VAL(
+ poFieldsMetadataLayer->DeleteFeature(nFieldsMetadataIdxPos));
+ }
+
+ // Register field in _ogr_fields_metadata
+ OGRFeature* poFieldDescFeature =
+ new OGRFeature(poFieldsMetadataLayer->GetLayerDefn());
+ poFieldDescFeature->SetField( szLAYER_NAME, GetName() );
+ poFieldDescFeature->SetField( szFIELD_INDEX, nInsertPos );
+ poFieldDescFeature->SetField( szFIELD_XPATH, osTargetFieldXPath );
+ poFieldDescFeature->SetField( szFIELD_NAME,
+ oFieldDefn.GetNameRef() );
+ poFieldDescFeature->SetField( szFIELD_TYPE, szXS_STRING );
+ poFieldDescFeature->SetField( szFIELD_IS_LIST, 0 );
+ poFieldDescFeature->SetField( szFIELD_MIN_OCCURS, 0 );
+ poFieldDescFeature->SetField( szFIELD_MAX_OCCURS, 1 );
+ poFieldDescFeature->SetField( szFIELD_CATEGORY, szPATH_TO_CHILD_ELEMENT_WITH_LINK );
+ poFieldDescFeature->SetField( szFIELD_RELATED_LAYER,
+ poTargetLayer->GetName() );
+ if( nFieldsMetadataIdxPos >= 0 )
+ poFieldDescFeature->SetFID( nFieldsMetadataIdxPos );
+ CPL_IGNORE_RET_VAL(
+ poFieldsMetadataLayer->CreateFeature(poFieldDescFeature));
+ delete poFieldDescFeature;
+
+ // Register relationship in _ogr_layer_relationships
+ OGRFeature* poRelationshipsFeature =
+ new OGRFeature(poRelationshipsLayer->GetLayerDefn());
+ poRelationshipsFeature->SetField( szPARENT_LAYER, GetName() );
+ poRelationshipsFeature->SetField( szPARENT_PKID,
+ GetLayerDefn()->GetFieldDefn(
+ GetIDFieldIdx())->GetNameRef() );
+ poRelationshipsFeature->SetField( szPARENT_ELEMENT_NAME,
+ osNewFieldName );
+ poRelationshipsFeature->SetField(szCHILD_LAYER,
+ poTargetLayer->GetName() );
+ poRelationshipsFeature->SetField( szCHILD_PKID,
+ poTargetLayer->GetLayerDefn()->GetFieldDefn(
+ poTargetLayer->GetIDFieldIdx())->GetNameRef() );
+ CPL_IGNORE_RET_VAL(poRelationshipsLayer->CreateFeature(
+ poRelationshipsFeature));
+ delete poRelationshipsFeature;
+
+ return osNewFieldName;
+}
+
/************************************************************************/
/* GetLayerDefn() */
/************************************************************************/
@@ -1198,6 +1473,7 @@ OGRFeatureDefn* OGRGMLASLayer::GetLayerDefn()
// If we haven't yet determined the SRS of geometry columns, do it now
m_bLayerDefnFinalized = true;
if( m_poFeatureDefn->GetGeomFieldCount() > 0 ||
+ m_poDS->GetConf().m_oXLinkResolution.m_bResolveInternalXLinks ||
!m_poDS->GetConf().m_oXLinkResolution.m_aoURLSpecificRules.empty() )
{
if( m_poReader == NULL )
diff --git a/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasreader.cpp b/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasreader.cpp
index 99fbdf31a178..5b18a6fc42b8 100644
--- a/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasreader.cpp
+++ b/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasreader.cpp
@@ -1869,7 +1869,32 @@ void GMLASReader::ProcessAttributes(const Attributes& attrs)
osAttrLocalname == szHREF &&
!osAttrValue.empty() )
{
- ProcessXLinkHref( osAttrXPath, osAttrValue );
+ ProcessXLinkHref( nAttrIdx, osAttrXPath, osAttrValue );
+ }
+
+ if( m_oXLinkResolver.GetConf().m_bResolveInternalXLinks &&
+ m_bInitialPass )
+ {
+ nFCIdx = m_oCurCtxt.m_poLayer->
+ GetFCFieldIndexFromXPath(osAttrXPath);
+ if( nFCIdx >= 0 &&
+ m_oCurCtxt.m_poLayer->GetFeatureClass().
+ GetFields()[nFCIdx].GetType() == GMLAS_FT_ID )
+ {
+ // We don't check that there's no existing id in the map
+ // This is normally forbidden by the xs:ID rules
+ // If not respected by the document, this should not lead to
+ // crashes
+ m_oMapElementIdToLayer[ osAttrValue ] = m_oCurCtxt.m_poLayer;
+
+ if( m_oCurCtxt.m_poLayer->IsGeneratedIDField() )
+ {
+ CPLString osFeaturePKID(
+ m_oCurCtxt.m_poFeature->GetFieldAsString(
+ m_oCurCtxt.m_poLayer->GetIDFieldIdx() ));
+ m_oMapElementIdToPKID[ osAttrValue ] = osFeaturePKID;
+ }
+ }
}
}
@@ -2027,7 +2052,8 @@ void GMLASReader::ProcessAttributes(const Attributes& attrs)
/* ProcessXLinkHref() */
/************************************************************************/
-void GMLASReader::ProcessXLinkHref( const CPLString& osAttrXPath,
+void GMLASReader::ProcessXLinkHref( int nAttrIdx,
+ const CPLString& osAttrXPath,
const CPLString& osAttrValue )
{
// If we are a xlink:href attribute, and that the link value is
@@ -2039,12 +2065,52 @@ void GMLASReader::ProcessXLinkHref( const CPLString& osAttrXPath,
m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
GMLASField::MakePKIDFieldXPathFromXLinkHrefXPath(
osAttrXPath));
- if( nAttrIdx2 >= 0 )
+ if( nAttrIdx2 >= 0 )
{
SetField( m_oCurCtxt.m_poFeature,
m_oCurCtxt.m_poLayer,
nAttrIdx2, osAttrValue.substr(1) );
}
+ else if( m_oXLinkResolver.GetConf().m_bResolveInternalXLinks )
+ {
+ const CPLString osReferingField(
+ m_oCurCtxt.m_poLayer->GetLayerDefn()->
+ GetFieldDefn(nAttrIdx)->GetNameRef());
+ const CPLString osId(osAttrValue.substr(1));
+ if( m_bInitialPass )
+ {
+ std::pair oReferingPair(
+ m_oCurCtxt.m_poLayer, osReferingField);
+ m_oMapFieldXPathToLinkValue[oReferingPair].push_back(osId);
+ }
+ else
+ {
+ std::map::const_iterator oIter =
+ m_oMapElementIdToLayer.find(osId);
+ if( oIter != m_oMapElementIdToLayer.end() )
+ {
+ OGRGMLASLayer* poTargetLayer = oIter->second;
+ const CPLString osLinkFieldXPath =
+ m_oCurCtxt.m_poLayer->GetXPathOfFieldLinkForAttrToOtherLayer(
+ osReferingField,
+ poTargetLayer->GetFeatureClass().GetXPath());
+ const int nLinkFieldOGRId =
+ m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
+ osLinkFieldXPath);
+ std::map::const_iterator oIter2 =
+ m_oMapElementIdToPKID.find(osId);
+ if( oIter2 != m_oMapElementIdToPKID.end() )
+ {
+ m_oCurCtxt.m_poFeature->SetField(nLinkFieldOGRId,
+ oIter2->second);
+ }
+ else
+ {
+ m_oCurCtxt.m_poFeature->SetField(nLinkFieldOGRId, osId);
+ }
+ }
+ }
+ }
}
else
{
@@ -3177,7 +3243,7 @@ bool GMLASReader::RunFirstPass(GDALProgressFunc pfnProgress,
// Store in m_oSetGeomFieldsWithUnknownSRS the geometry fields
std::set oSetUnreferencedLayers;
- std::map > oMapUnusedFields;
+ std::map > oMapUnusedFields;
for(size_t i=0; i < m_papoLayers->size(); i++ )
{
OGRGMLASLayer* poLayer = (*m_papoLayers)[i];
@@ -3190,7 +3256,8 @@ bool GMLASReader::RunFirstPass(GDALProgressFunc pfnProgress,
}
for(int j=0; j< poFDefn->GetFieldCount(); j++ )
{
- oMapUnusedFields[poLayer].insert(j);
+ oMapUnusedFields[poLayer].insert(
+ poFDefn->GetFieldDefn(j)->GetNameRef());
}
}
@@ -3204,7 +3271,8 @@ bool GMLASReader::RunFirstPass(GDALProgressFunc pfnProgress,
bRemoveUnusedLayers ||
bRemoveUnusedFields ||
bHasURLSpecificRules ||
- bProcessSWEDataArray );
+ bProcessSWEDataArray ||
+ m_oXLinkResolver.GetConf().m_bResolveInternalXLinks);
// Loop on features until we have determined the SRS of all geometry
// columns, or potentially on the whole file for the above reasons
@@ -3217,13 +3285,13 @@ bool GMLASReader::RunFirstPass(GDALProgressFunc pfnProgress,
oSetUnreferencedLayers.erase( poLayer );
if( bRemoveUnusedFields )
{
- std::set& oSetUnusedFields = oMapUnusedFields[poLayer];
+ std::set& oSetUnusedFields = oMapUnusedFields[poLayer];
OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn();
int nFieldCount = poFDefn->GetFieldCount();
for(int j=0; j< nFieldCount; j++ )
{
if( poFeature->IsFieldSetAndNotNull(j) )
- oSetUnusedFields.erase(j);
+ oSetUnusedFields.erase(poFDefn->GetFieldDefn(j)->GetNameRef());
}
}
delete poFeature;
@@ -3231,6 +3299,8 @@ bool GMLASReader::RunFirstPass(GDALProgressFunc pfnProgress,
CPLDebug("GMLAS", "End of first pass");
+ ProcessInternalXLinkFirstPass(bRemoveUnusedFields, oMapUnusedFields);
+
if( bRemoveUnusedLayers )
{
std::vector apoNewLayers;
@@ -3255,15 +3325,12 @@ bool GMLASReader::RunFirstPass(GDALProgressFunc pfnProgress,
for(size_t i=0; i < m_papoLayers->size(); i++ )
{
poLayer = (*m_papoLayers)[i];
- std::set& oSetUnusedFields = oMapUnusedFields[poLayer];
- std::set::iterator oIter = oSetUnusedFields.begin();
- int nShiftIndex = 0;
+ std::set& oSetUnusedFields = oMapUnusedFields[poLayer];
+ std::set::iterator oIter = oSetUnusedFields.begin();
for( ; oIter != oSetUnusedFields.end(); ++oIter )
{
- if( poLayer->RemoveField( (*oIter) - nShiftIndex ) )
- {
- nShiftIndex ++;
- }
+ poLayer->RemoveField(
+ poLayer->GetLayerDefn()->GetFieldIndex(*oIter) );
}
// We need to run this again since we may have delete the
@@ -3286,6 +3353,54 @@ bool GMLASReader::RunFirstPass(GDALProgressFunc pfnProgress,
return !m_bInterrupted;
}
+/************************************************************************/
+/* ProcessInternalXLinkFirstPass() */
+/************************************************************************/
+
+void GMLASReader::ProcessInternalXLinkFirstPass(
+ bool bRemoveUnusedFields,
+ std::map >&oMapUnusedFields)
+{
+ std::map,
+ std::vector >::const_iterator
+ oIter = m_oMapFieldXPathToLinkValue.begin();
+ for( ; oIter != m_oMapFieldXPathToLinkValue.end(); ++oIter )
+ {
+ OGRGMLASLayer* poReferingLayer = oIter->first.first;
+ const CPLString& osReferingField = oIter->first.second;
+ const std::vector& aosLinks = oIter->second;
+ std::set oSetTargetLayers;
+ for( size_t i = 0; i < aosLinks.size(); i++ )
+ {
+ std::map::const_iterator oIter2 =
+ m_oMapElementIdToLayer.find(aosLinks[i]);
+ if( oIter2 == m_oMapElementIdToLayer.end() )
+ {
+ CPLError(CE_Warning, CPLE_AppDefined,
+ "%s:%s = '#%s' has no corresponding target "
+ "element in this document",
+ poReferingLayer->GetName(),
+ osReferingField.c_str(),
+ aosLinks[i].c_str());
+ }
+ else if( oSetTargetLayers.find(oIter2->second) ==
+ oSetTargetLayers.end() )
+ {
+ OGRGMLASLayer* poTargetLayer = oIter2->second;
+ oSetTargetLayers.insert(poTargetLayer);
+ CPLString osLinkFieldName =
+ poReferingLayer->CreateLinkForAttrToOtherLayer(
+ osReferingField,
+ poTargetLayer->GetFeatureClass().GetXPath());
+ if( bRemoveUnusedFields )
+ {
+ oMapUnusedFields[poReferingLayer].erase(osLinkFieldName);
+ }
+ }
+ }
+ }
+}
+
/************************************************************************/
/* CreateFieldsForURLSpecificRules() */
/************************************************************************/
diff --git a/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasschemaanalyzer.cpp b/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasschemaanalyzer.cpp
index f981939ca171..e83f91caa12f 100644
--- a/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasschemaanalyzer.cpp
+++ b/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasschemaanalyzer.cpp
@@ -257,6 +257,7 @@ GMLASSchemaAnalyzer::GMLASSchemaAnalyzer(
, m_bCaseInsensitiveIdentifier(CASE_INSENSITIVE_IDENTIFIER_DEFAULT)
, m_bPGIdentifierLaundering(PG_IDENTIFIER_LAUNDERING_DEFAULT)
, m_nMaximumFieldsForFlattening(MAXIMUM_FIELDS_FLATTENING_DEFAULT)
+ , m_bAlwaysGenerateOGRId(ALWAYS_GENERATE_OGR_ID_DEFAULT)
{
// A few hardcoded namespace uri->prefix mappings
m_oMapURIToPrefix[ szXMLNS_URI ] = szXMLNS_PREFIX;
@@ -382,7 +383,7 @@ void GMLASSchemaAnalyzer::LaunderFieldNames( GMLASFeatureClass& oClass )
{
const CPLString oClassNS =
GetNSOfLastXPathComponent(oClass.GetXPath());
- bool bHasDoneRemnamingForThatCase = false;
+ bool bHasDoneRenamingForThatCase = false;
for(size_t i=0; i(aoFields[i].GetName().size());
if( nNameSize > m_nIdentifierMaxLength )
{
- aoFields[i].SetName(TruncateIdentifier(aoFields[i].GetName()));
+ aoFields[i].SetName(
+ OGRGMLASTruncateIdentifier(aoFields[i].GetName(),
+ m_nIdentifierMaxLength));
}
}
}
@@ -484,9 +487,10 @@ void GMLASSchemaAnalyzer::LaunderFieldNames( GMLASFeatureClass& oClass )
for(size_t i=0; isecond[i]];
- oField.SetName( AddSerialNumber( oField.GetName(),
+ oField.SetName( OGRGMLASAddSerialNumber( oField.GetName(),
static_cast(i+1),
- nOccurrences) );
+ nOccurrences,
+ m_nIdentifierMaxLength) );
}
}
}
@@ -534,7 +538,9 @@ void GMLASSchemaAnalyzer::LaunderClassNames()
int nNameSize = static_cast(aoClasses[i]->GetName().size());
if( nNameSize > m_nIdentifierMaxLength )
{
- aoClasses[i]->SetName(TruncateIdentifier(aoClasses[i]->GetName()));
+ aoClasses[i]->SetName(OGRGMLASTruncateIdentifier(
+ aoClasses[i]->GetName(),
+ m_nIdentifierMaxLength));
}
}
}
@@ -575,170 +581,15 @@ void GMLASSchemaAnalyzer::LaunderClassNames()
for(size_t i=0; isecond[i]];
- poClass->SetName( AddSerialNumber(poClass->GetName(),
+ poClass->SetName( OGRGMLASAddSerialNumber(poClass->GetName(),
static_cast(i+1),
- nOccurrences) );
+ nOccurrences,
+ m_nIdentifierMaxLength) );
}
}
}
}
-/************************************************************************/
-/* AddSerialNumber() */
-/************************************************************************/
-
-CPLString GMLASSchemaAnalyzer::AddSerialNumber(const CPLString& osNameIn,
- int iOccurrence,
- size_t nOccurrences)
-{
- CPLString osName(osNameIn);
- const int nDigitsSize = (nOccurrences < 10) ? 1:
- (nOccurrences < 100) ? 2 : 3;
- char szDigits[4];
- snprintf(szDigits, sizeof(szDigits), "%0*d",
- nDigitsSize, iOccurrence);
- if( m_nIdentifierMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH )
- {
- if( static_cast(osName.size()) < m_nIdentifierMaxLength )
- {
- if( static_cast(osName.size()) + nDigitsSize <
- m_nIdentifierMaxLength )
- {
- osName += szDigits;
- }
- else
- {
- osName.resize(m_nIdentifierMaxLength - nDigitsSize);
- osName += szDigits;
- }
- }
- else
- {
- osName.resize(osName.size() - nDigitsSize);
- osName += szDigits;
- }
- }
- else
- {
- osName += szDigits;
- }
- return osName;
-}
-
-/************************************************************************/
-/* TruncateIdentifier() */
-/************************************************************************/
-
-CPLString GMLASSchemaAnalyzer::TruncateIdentifier(const CPLString& osName)
-{
- int nExtra = static_cast(osName.size()) - m_nIdentifierMaxLength;
- CPLAssert(nExtra > 0);
-
- // Decompose in tokens
- char** papszTokens = CSLTokenizeString2(osName, "_",
- CSLT_ALLOWEMPTYTOKENS );
- std::vector< char > achDelimiters;
- std::vector< CPLString > aosTokens;
- for( int j=0; papszTokens[j] != NULL; ++j )
- {
- const char* pszToken = papszTokens[j];
- bool bIsCamelCase = false;
- // Split parts like camelCase or CamelCase into several tokens
- if( pszToken[0] != '\0' && islower(pszToken[1]) )
- {
- bIsCamelCase = true;
- bool bLastIsLower = true;
- std::vector aoParts;
- CPLString osCurrentPart;
- osCurrentPart += pszToken[0];
- osCurrentPart += pszToken[1];
- for( int k=2; pszToken[k]; ++k)
- {
- if( isupper(pszToken[k]) )
- {
- if( !bLastIsLower )
- {
- bIsCamelCase = false;
- break;
- }
- aoParts.push_back(osCurrentPart);
- osCurrentPart.clear();
- bLastIsLower = false;
- }
- else
- {
- bLastIsLower = true;
- }
- osCurrentPart += pszToken[k];
- }
- if( bIsCamelCase )
- {
- if( !osCurrentPart.empty() )
- aoParts.push_back(osCurrentPart);
- for( size_t k=0; k 0 && k == 0) ? '_' : '\0' );
- aosTokens.push_back( aoParts[k] );
- }
- }
- }
- if( !bIsCamelCase )
- {
- achDelimiters.push_back( (j > 0) ? '_' : '\0' );
- aosTokens.push_back( pszToken );
- }
- }
- CSLDestroy(papszTokens);
-
- // Truncate identifier by removing last character of longest part
- bool bHasDoneSomething = true;
- while( nExtra > 0 && bHasDoneSomething )
- {
- bHasDoneSomething = false;
- int nMaxSize = 0;
- size_t nIdxMaxSize = 0;
- for( size_t j=0; j < aosTokens.size(); ++j )
- {
- int nTokenLen = static_cast(aosTokens[j].size());
- if( nTokenLen > nMaxSize )
- {
- // Avoid truncating last token unless it is excessively longer
- // than previous ones.
- if( j < aosTokens.size() - 1 ||
- nTokenLen > 2 * nMaxSize )
- {
- nMaxSize = nTokenLen;
- nIdxMaxSize = j;
- }
- }
- }
-
- if( nMaxSize > 1 )
- {
- aosTokens[nIdxMaxSize].resize( nMaxSize - 1 );
- bHasDoneSomething = true;
- nExtra --;
- }
- }
-
- // Reassemble truncated parts
- CPLString osNewName;
- for( size_t j=0; j < aosTokens.size(); ++j )
- {
- if( achDelimiters[j] )
- osNewName += achDelimiters[j];
- osNewName += aosTokens[j];
- }
-
- // If we are still longer than max allowed, truncate beginning of name
- if( nExtra > 0 )
- {
- osNewName = osNewName.substr(nExtra);
- }
- CPLAssert( static_cast(osNewName.size()) == m_nIdentifierMaxLength );
- return osNewName;
-}
-
/************************************************************************/
/* GMLASUniquePtr() */
/************************************************************************/
@@ -3276,35 +3127,60 @@ bool GMLASSchemaAnalyzer::ExploreModelGroup(
// handle substitutions
if( poTargetElt != NULL && !poTargetElt->getAbstract() )
{
- // If the element is nillable, then we
- // need an extra field to be able to distinguish between the
- // case of the missing element or the element with
- // xsi:nil="true"
- if( poElt->getNillable() && !m_bUseNullState )
+ bool bHasRequiredId = false;
+ XSComplexTypeDefinition* poTargetEltCT =
+ IsEltCompatibleOfFC(poTargetElt);
+ if( poTargetEltCT )
{
- GMLASField oFieldNil;
- oFieldNil.SetName( osPrefixedEltName + "_" + szNIL );
- oFieldNil.SetXPath( osElementXPath + "/" +
- szAT_XSI_NIL );
- oFieldNil.SetType( GMLAS_FT_BOOLEAN, "boolean" );
- oFieldNil.SetMinOccurs( 0 );
- oFieldNil.SetMaxOccurs( 1 );
- aoFields.push_back(oFieldNil);
+ XSAttributeUseList* poTargetEltAttrList =
+ poTargetEltCT->getAttributeUses();
+ const size_t nTEAttrListSize = (poTargetEltAttrList != NULL) ?
+ poTargetEltAttrList->size(): 0;
+ for(size_t j=0; j< nTEAttrListSize; ++j )
+ {
+ XSAttributeUse* poTEAttr = poTargetEltAttrList->elementAt(j);
+ XSAttributeDeclaration* poTEAttrDecl = poTEAttr->getAttrDeclaration();
+ XSSimpleTypeDefinition* poTEAttrType = poTEAttrDecl->getTypeDefinition();
+ if( transcode(poTEAttrType->getName()) == szXS_ID &&
+ poTEAttr->getRequired() )
+ {
+ bHasRequiredId = true;
+ break;
+ }
+ }
}
+ if( bHasRequiredId && !m_bAlwaysGenerateOGRId )
+ {
+ // If the element is nillable, then we
+ // need an extra field to be able to distinguish between the
+ // case of the missing element or the element with
+ // xsi:nil="true"
+ if( poElt->getNillable() && !m_bUseNullState )
+ {
+ GMLASField oFieldNil;
+ oFieldNil.SetName( osPrefixedEltName + "_" + szNIL );
+ oFieldNil.SetXPath( osElementXPath + "/" +
+ szAT_XSI_NIL );
+ oFieldNil.SetType( GMLAS_FT_BOOLEAN, "boolean" );
+ oFieldNil.SetMinOccurs( 0 );
+ oFieldNil.SetMaxOccurs( 1 );
+ aoFields.push_back(oFieldNil);
+ }
- GMLASField oField;
- // Fake xpath
- oField.SetXPath(
- GMLASField::MakePKIDFieldXPathFromXLinkHrefXPath(
- osElementXPath + "/" + szAT_XLINK_HREF));
- oField.SetName( osPrefixedEltName + szPKID_SUFFIX );
- oField.SetMinOccurs(0);
- oField.SetMaxOccurs(1);
- oField.SetType( GMLAS_FT_STRING, szXS_STRING );
- oField.SetCategory(
- GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK );
- oField.SetRelatedClassXPath(osTargetElement);
- aoFields.push_back( oField );
+ GMLASField oField;
+ // Fake xpath
+ oField.SetXPath(
+ GMLASField::MakePKIDFieldXPathFromXLinkHrefXPath(
+ osElementXPath + "/" + szAT_XLINK_HREF));
+ oField.SetName( osPrefixedEltName + szPKID_SUFFIX );
+ oField.SetMinOccurs(0);
+ oField.SetMaxOccurs(1);
+ oField.SetType( GMLAS_FT_STRING, szXS_STRING );
+ oField.SetCategory(
+ GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK );
+ oField.SetRelatedClassXPath(osTargetElement);
+ aoFields.push_back( oField );
+ }
}
else if( poTargetElt != NULL && poTargetElt->getAbstract() )
{
diff --git a/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasutils.cpp b/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasutils.cpp
new file mode 100644
index 000000000000..7bb2d66ce11c
--- /dev/null
+++ b/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasutils.cpp
@@ -0,0 +1,190 @@
+/******************************************************************************
+ * Project: OGR
+ * Purpose: OGRGMLASDriver implementation
+ * Author: Even Rouault,
+ *
+ * Initial development funded by the European Earth observation programme
+ * Copernicus
+ *
+ ******************************************************************************
+ * Copyright (c) 2016, Even Rouault,
+ *
+ * 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.
+ ****************************************************************************/
+
+#include "ogr_gmlas.h"
+
+/************************************************************************/
+/* OGRGMLASTruncateIdentifier() */
+/************************************************************************/
+
+CPLString OGRGMLASTruncateIdentifier(const CPLString& osName,
+ int nIdentMaxLength)
+{
+ int nExtra = static_cast(osName.size()) - nIdentMaxLength;
+ CPLAssert(nExtra > 0);
+
+ // Decompose in tokens
+ char** papszTokens = CSLTokenizeString2(osName, "_",
+ CSLT_ALLOWEMPTYTOKENS );
+ std::vector< char > achDelimiters;
+ std::vector< CPLString > aosTokens;
+ for( int j=0; papszTokens[j] != NULL; ++j )
+ {
+ const char* pszToken = papszTokens[j];
+ bool bIsCamelCase = false;
+ // Split parts like camelCase or CamelCase into several tokens
+ if( pszToken[0] != '\0' && islower(pszToken[1]) )
+ {
+ bIsCamelCase = true;
+ bool bLastIsLower = true;
+ std::vector aoParts;
+ CPLString osCurrentPart;
+ osCurrentPart += pszToken[0];
+ osCurrentPart += pszToken[1];
+ for( int k=2; pszToken[k]; ++k)
+ {
+ if( isupper(pszToken[k]) )
+ {
+ if( !bLastIsLower )
+ {
+ bIsCamelCase = false;
+ break;
+ }
+ aoParts.push_back(osCurrentPart);
+ osCurrentPart.clear();
+ bLastIsLower = false;
+ }
+ else
+ {
+ bLastIsLower = true;
+ }
+ osCurrentPart += pszToken[k];
+ }
+ if( bIsCamelCase )
+ {
+ if( !osCurrentPart.empty() )
+ aoParts.push_back(osCurrentPart);
+ for( size_t k=0; k 0 && k == 0) ? '_' : '\0' );
+ aosTokens.push_back( aoParts[k] );
+ }
+ }
+ }
+ if( !bIsCamelCase )
+ {
+ achDelimiters.push_back( (j > 0) ? '_' : '\0' );
+ aosTokens.push_back( pszToken );
+ }
+ }
+ CSLDestroy(papszTokens);
+
+ // Truncate identifier by removing last character of longest part
+ bool bHasDoneSomething = true;
+ while( nExtra > 0 && bHasDoneSomething )
+ {
+ bHasDoneSomething = false;
+ int nMaxSize = 0;
+ size_t nIdxMaxSize = 0;
+ for( size_t j=0; j < aosTokens.size(); ++j )
+ {
+ int nTokenLen = static_cast(aosTokens[j].size());
+ if( nTokenLen > nMaxSize )
+ {
+ // Avoid truncating last token unless it is excessively longer
+ // than previous ones.
+ if( j < aosTokens.size() - 1 ||
+ nTokenLen > 2 * nMaxSize )
+ {
+ nMaxSize = nTokenLen;
+ nIdxMaxSize = j;
+ }
+ }
+ }
+
+ if( nMaxSize > 1 )
+ {
+ aosTokens[nIdxMaxSize].resize( nMaxSize - 1 );
+ bHasDoneSomething = true;
+ nExtra --;
+ }
+ }
+
+ // Reassemble truncated parts
+ CPLString osNewName;
+ for( size_t j=0; j < aosTokens.size(); ++j )
+ {
+ if( achDelimiters[j] )
+ osNewName += achDelimiters[j];
+ osNewName += aosTokens[j];
+ }
+
+ // If we are still longer than max allowed, truncate beginning of name
+ if( nExtra > 0 )
+ {
+ osNewName = osNewName.substr(nExtra);
+ }
+ CPLAssert( static_cast(osNewName.size()) == nIdentMaxLength );
+ return osNewName;
+}
+
+
+/************************************************************************/
+/* OGRGMLASAddSerialNumber() */
+/************************************************************************/
+
+CPLString OGRGMLASAddSerialNumber(const CPLString& osNameIn,
+ int iOccurrence,
+ size_t nOccurrences,
+ int nIdentMaxLength)
+{
+ CPLString osName(osNameIn);
+ const int nDigitsSize = (nOccurrences < 10) ? 1:
+ (nOccurrences < 100) ? 2 : 3;
+ char szDigits[4];
+ snprintf(szDigits, sizeof(szDigits), "%0*d",
+ nDigitsSize, iOccurrence);
+ if( nIdentMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH )
+ {
+ if( static_cast(osName.size()) < nIdentMaxLength )
+ {
+ if( static_cast(osName.size()) + nDigitsSize <
+ nIdentMaxLength )
+ {
+ osName += szDigits;
+ }
+ else
+ {
+ osName.resize(nIdentMaxLength - nDigitsSize);
+ osName += szDigits;
+ }
+ }
+ else
+ {
+ osName.resize(osName.size() - nDigitsSize);
+ osName += szDigits;
+ }
+ }
+ else
+ {
+ osName += szDigits;
+ }
+ return osName;
+}