Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions autotest/ogr/data/gml/datetime.gml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
gml:id="aFeatureCollection"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ datetime.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml/3.2">
<ogr:featureMember>
<ogr:test gml:id="test.0">
<ogr:time>23:59:60</ogr:time>
<ogr:date>9999-12-31</ogr:date>
<ogr:datetime>9999-12-31T23:59:60.999</ogr:datetime>
<ogr:dtInTimePosition><gml:timePosition>9999-12-31T23:59:60.999</gml:timePosition></ogr:dtInTimePosition>
</ogr:test>
</ogr:featureMember>
<ogr:featureMember>
<ogr:test gml:id="test.1">
<ogr:time>23:59:60.999</ogr:time>
<ogr:date>9999-12-31</ogr:date>
<ogr:datetime>9999-12-31T23:59:60Z</ogr:datetime>
<ogr:dtInTimePosition><gml:timePosition>9999-12-31T23:59:60+12:30</gml:timePosition></ogr:dtInTimePosition>
</ogr:test>
</ogr:featureMember>
</ogr:FeatureCollection>
54 changes: 54 additions & 0 deletions autotest/ogr/data/gml/datetime.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
targetNamespace="http://ogr.maptools.org/"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:gml="http://www.opengis.net/gml/3.2"
xmlns:gmlsf="http://www.opengis.net/gmlsf/2.0"
elementFormDefault="qualified"
version="1.0">
<xs:annotation>
<xs:appinfo source="http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd">
<gmlsf:ComplianceLevel>0</gmlsf:ComplianceLevel>
</xs:appinfo>
</xs:annotation>
<xs:import namespace="http://www.opengis.net/gml/3.2" schemaLocation="http://schemas.opengis.net/gml/3.2.1/gml.xsd"/>
<xs:import namespace="http://www.opengis.net/gmlsf/2.0" schemaLocation="http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:AbstractFeature"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="featureMember">
<xs:complexType>
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureMemberType">
<xs:sequence>
<xs:element ref="gml:AbstractFeature"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="test" type="ogr:test_Type" substitutionGroup="gml:AbstractFeature"/>
<xs:complexType name="test_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="time" nillable="true" minOccurs="0" maxOccurs="1" type="xs:time">
</xs:element>
<xs:element name="date" nillable="true" minOccurs="0" maxOccurs="1" type="xs:date">
</xs:element>
<xs:element name="datetime" nillable="true" minOccurs="0" maxOccurs="1" type="xs:dateTime">
</xs:element>
<xs:element name="dtInTimePosition" nillable="true" minOccurs="0" maxOccurs="1" type="gml:TimeInstantType">
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
35 changes: 35 additions & 0 deletions autotest/ogr/ogr_gml.py
Original file line number Diff line number Diff line change
Expand Up @@ -5214,3 +5214,38 @@ def test_ogr_gml_citygml3(tmp_vsimem, skip_resolve_as_open_option):
f.GetGeometryRef().ExportToWkt()
== "POLYHEDRALSURFACE Z (((0 0 0,1 1 1,1 0 2,0 0 0)))"
)


###############################################################################


def test_ogr_gml_datetime(tmp_vsimem):

ds = ogr.Open("data/gml/datetime.gml")
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
assert lyr.GetLayerDefn().GetFieldDefn(1).GetType() == ogr.OFTTime
assert f["time"] == "23:59:60"
assert lyr.GetLayerDefn().GetFieldDefn(2).GetType() == ogr.OFTDate
assert f["date"] == "9999/12/31"
assert lyr.GetLayerDefn().GetFieldDefn(3).GetType() == ogr.OFTDateTime
assert f["datetime"] == "9999/12/31 23:59:60.999"
assert lyr.GetLayerDefn().GetFieldDefn(4).GetType() == ogr.OFTDateTime
assert f["dtInTimePosition"] == "9999/12/31 23:59:60.999"

tmp_filename = tmp_vsimem / "out.gml"
with gdal.VSIFile("data/gml/datetime.gml", "rb") as fin:
with gdal.VSIFile(tmp_filename, "wb") as fout:
fout.write(fin.read())

ds = ogr.Open(tmp_filename)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
assert lyr.GetLayerDefn().GetFieldDefn(1).GetType() == ogr.OFTTime
assert f["time"] == "23:59:60"
assert lyr.GetLayerDefn().GetFieldDefn(2).GetType() == ogr.OFTDate
assert f["date"] == "9999/12/31"
assert lyr.GetLayerDefn().GetFieldDefn(3).GetType() == ogr.OFTDateTime
assert f["datetime"] == "9999/12/31 23:59:60.999"
assert lyr.GetLayerDefn().GetFieldDefn(4).GetType() == ogr.OFTDateTime
assert f["timePosition"] == "9999/12/31 23:59:60.999"
32 changes: 32 additions & 0 deletions autotest/ogr/ogr_gmlas.py
Original file line number Diff line number Diff line change
Expand Up @@ -3534,3 +3534,35 @@ def test_ogr_gmlas_citygml_lod2_no_schema_location():
pytest.skip(f"cannot open {url}")

assert ds.GetLayerCount() == 1537


###############################################################################


@pytest.mark.require_curl()
def test_ogr_gmlas_datetime(tmp_vsimem):

ds = ogr.Open("GMLAS:data/gml/datetime.gml")
lyr = ds.GetLayer("test")
f = lyr.GetNextFeature()
lyr_defn = lyr.GetLayerDefn()
assert (
lyr_defn.GetFieldDefn(lyr_defn.GetFieldIndex("time")).GetType() == ogr.OFTTime
)
assert f["time"] == "23:59:60"
assert (
lyr_defn.GetFieldDefn(lyr_defn.GetFieldIndex("date")).GetType() == ogr.OFTDate
)
assert f["date"] == "9999/12/31"
assert (
lyr_defn.GetFieldDefn(lyr_defn.GetFieldIndex("datetime")).GetType()
== ogr.OFTDateTime
)
assert f["datetime"] == "9999/12/31 23:59:60.999"
assert (
lyr_defn.GetFieldDefn(
lyr_defn.GetFieldIndex("dtintimeposition_timeposition")
).GetType()
== ogr.OFTDateTime
)
assert f["dtintimeposition_timeposition"] == "9999/12/31 23:59:60.999"
87 changes: 86 additions & 1 deletion autotest/ogr/ogr_wfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3455,7 +3455,7 @@ def test_ogr_wfs_vsimem_wfs110_schema_not_understood(with_and_without_streaming)
or f.int != 123456789
or f.float != 1.2
or f.double != 1.23
or f.dt != "2015-04-17T12:34:56Z"
or f.dt != "2015/04/17 12:34:56+00"
or f.GetGeometryRef().ExportToWkt() != "POINT (2 49)"
)

Expand Down Expand Up @@ -5342,3 +5342,88 @@ def test_ogr_wfs_no_gml_driver(
lyr.GetNextFeature()
finally:
gml_drv.Register()


###############################################################################
# Test fix for https://github.com/OSGeo/gdal/issues/13120


def test_ogr_wfs_does_not_understand_schema():

getfeatures_response = """<wfs:FeatureCollection xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:foo="http://foo"
xmlns:wfs="http://www.opengis.net/wfs/2.0"
xmlns:gml="http://www.opengis.net/gml/3.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
numberMatched="unknown" numberReturned="1" timeStamp="2015-01-01T00:00:00.000Z"
xsi:schemaLocation="http://www.opengis.net/gml/3.2 http://schemas.opengis.net/gml/3.2.1/gml.xsd
http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd
http://foo /vsimem/test_ogr_wfs_does_not_understand_schema?SERVICE=WFS&amp;VERSION=2.0.0&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=foo:lyr">
<wfs:member>
<foo:lyr gml:id="lyr-101">
<foo:str>foo</foo:str>
<foo:shape>
<gml:Point srsDimension="2" srsName="urn:ogc:def:crs:EPSG::4326">
<gml:pos>41.5 0.5</gml:pos>
</gml:Point>
</foo:shape>
</foo:lyr>
</wfs:member>
</wfs:FeatureCollection>"""

schema = """<xsd:schema xmlns:foo="http://foo" xmlns:gml="http://www.opengis.net/gml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://foo">
<xsd:import namespace="http://www.opengis.net/gml" schemaLocation="http://foo/schemas/gml/3.2.1/base/gml.xsd"/>
<xsd:complexType name="lyrType">
<xsd:complexContent>
<xsd:extension base="gml:AbstractFeatureType">
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="0" name="str" nillable="true" type="i_do_not_know_this_type"/>
<xsd:element maxOccurs="1" minOccurs="0" name="shape" nillable="true" type="gml:PointPropertyType"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="lyr" substitutionGroup="gml:_Feature" type="foo:lyr1Type"/>
</xsd:schema>
"""

with gdaltest.tempfile(
"/vsimem/test_ogr_wfs_does_not_understand_schema?SERVICE=WFS&REQUEST=GetCapabilities",
"""<WFS_Capabilities version="2.0.0">
<FeatureTypeList>
<FeatureType>
<Name>foo:lyr</Name>
<DefaultSRS>urn:ogc:def:crs:EPSG::4326</DefaultSRS>
<ows:WGS84BoundingBox>
<ows:LowerCorner>-10 40</ows:LowerCorner>
<ows:UpperCorner>15 50</ows:UpperCorner>
</ows:WGS84BoundingBox>
</FeatureType>
</FeatureTypeList>
</WFS_Capabilities>
""",
), gdaltest.tempfile(
"/vsimem/test_ogr_wfs_does_not_understand_schema?SERVICE=WFS&VERSION=2.0.0&REQUEST=DescribeFeatureType&TYPENAME=foo:lyr",
schema,
), gdaltest.tempfile(
"/vsimem/test_ogr_wfs_does_not_understand_schema?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&TYPENAMES=foo:lyr&COUNT=1",
getfeatures_response,
), gdaltest.tempfile(
"/vsimem/test_ogr_wfs_does_not_understand_schema?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&TYPENAMES=foo:lyr",
getfeatures_response,
):
ds = ogr.Open("WFS:/vsimem/test_ogr_wfs_does_not_understand_schema")
lyr = ds.GetLayer(0)
assert lyr.GetGeometryColumn() == "shape"

lyr.SetSpatialFilterRect(0, 41, 1, 42)

with gdaltest.tempfile(
"/vsimem/test_ogr_wfs_does_not_understand_schema?SERVICE=WFS&VERSION=2.0.0&REQUEST=DescribeFeatureType&TYPENAME=foo:lyr",
schema,
), gdaltest.tempfile(
"/vsimem/test_ogr_wfs_does_not_understand_schema?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&TYPENAMES=foo:lyr&FILTER=%3CFilter%20xmlns%3D%22http:%2F%2Fwww.opengis.net%2Ffes%2F2.0%22%20xmlns:gml%3D%22http:%2F%2Fwww.opengis.net%2Fgml%2F3.2%22%3E%3CBBOX%3E%3CValueReference%3Eshape%3C%2FValueReference%3E%3Cgml:Envelope%3E%3Cgml:lowerCorner%3E41.0000000000000000%200.0000000000000000%3C%2Fgml:lowerCorner%3E%3Cgml:upperCorner%3E42.0000000000000000%201.0000000000000000%3C%2Fgml:upperCorner%3E%3C%2Fgml:Envelope%3E%3C%2FBBOX%3E%3C%2FFilter%3E",
getfeatures_response,
):
f = lyr.GetNextFeature()
assert f
12 changes: 10 additions & 2 deletions ogr/ogrsf_frmts/gml/gmlhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1085,11 +1085,19 @@ int GMLHandler::FindRealPropertyByCheckingConditions(int nIdx, void *attr)
OGRErr GMLHandler::startElementFeatureAttribute(const char *pszName,
int nLenName, void *attr)
{
/* Reset flag */
m_bInCurField = false;

GMLReadState *poState = m_poReader->GetState();

if (m_bInCurField && m_nAttributeIndex >= 0 &&
std::string_view(pszName, nLenName) == "timePosition")
{
poState->PushPath(pszName, nLenName);
return OGRERR_NONE;
}

/* Reset flag */
m_bInCurField = false;

/* -------------------------------------------------------------------- */
/* If we are collecting geometry, or if we determine this is a */
/* geometry element then append to the geometry info. */
Expand Down
1 change: 1 addition & 0 deletions ogr/ogrsf_frmts/gmlas/ogr_gmlas_consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ STRING_CONST(szXS_GYEAR, "gYear");
STRING_CONST(szXS_GYEAR_MONTH, "gYearMonth");
STRING_CONST(szXS_TIME, "time");
STRING_CONST(szXS_DATETIME, "dateTime");
STRING_CONST(szXS_TIME_INSTANT_TYPE, "TimeInstantType");
STRING_CONST(szXS_ANY_URI, "anyURI");
STRING_CONST(szXS_ANY_TYPE, "anyType");
STRING_CONST(szXS_ANY_SIMPLE_TYPE, "anySimpleType");
Expand Down
11 changes: 11 additions & 0 deletions ogr/ogrsf_frmts/gmlas/ogrgmlasschemaanalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3344,9 +3344,19 @@ bool GMLASSchemaAnalyzer::ExploreModelGroup(

const std::vector<GMLASField> &osNestedClassFields =
oNestedClass.GetFields();
const bool bIsTimeInstantType =
transcode(poTypeDef->getName()) ==
szXS_TIME_INSTANT_TYPE;
for (size_t j = 0; j < osNestedClassFields.size(); j++)
{
GMLASField oField(osNestedClassFields[j]);
if (bIsTimeInstantType &&
oField.GetType() == GMLAS_FT_ANYSIMPLETYPE &&
oField.GetName() == "timePosition")
{
oField.SetType(GMLAS_FT_DATETIME,
szXS_DATETIME);
}
oField.SetName(osPrefixedEltName + "_" +
oField.GetName());
if (nMinOccurs == 0 ||
Expand All @@ -3356,6 +3366,7 @@ bool GMLASSchemaAnalyzer::ExploreModelGroup(
oField.SetMinOccurs(0);
oField.SetNotNullable(false);
}

aoFields.push_back(std::move(oField));
}

Expand Down
57 changes: 56 additions & 1 deletion ogr/ogrsf_frmts/gmlutils/gmlpropertydefn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,62 @@ void GMLPropertyDefn::AnalysePropertyValue(const GMLProperty *psGMLProperty,
}
else
{
m_eType = GMLPT_String;
const auto IsDigitLowerOrEqual = [](char c, int max)
{ return c >= '0' && c <= '0' + max; };

if ((m_eType == GMLPT_Untyped || m_eType == GMLPT_DateTime ||
m_eType == GMLPT_Date) &&
IsDigitLowerOrEqual(pszValue[0], 9) &&
IsDigitLowerOrEqual(pszValue[1], 9) &&
IsDigitLowerOrEqual(pszValue[2], 9) &&
IsDigitLowerOrEqual(pszValue[3], 9) && pszValue[4] == '-' &&
IsDigitLowerOrEqual(pszValue[5], 1) &&
IsDigitLowerOrEqual(pszValue[6], 9) && pszValue[7] == '-' &&
IsDigitLowerOrEqual(pszValue[8], 3) &&
IsDigitLowerOrEqual(pszValue[9], 9))
{
if (pszValue[10] == 'T' &&
IsDigitLowerOrEqual(pszValue[11], 2) &&
IsDigitLowerOrEqual(pszValue[12], 9) &&
pszValue[13] == ':' &&
IsDigitLowerOrEqual(pszValue[14], 5) &&
IsDigitLowerOrEqual(pszValue[15], 9) &&
pszValue[16] == ':' &&
IsDigitLowerOrEqual(pszValue[17], 6) &&
IsDigitLowerOrEqual(pszValue[18], 9) &&
(pszValue[19] == '\0' || pszValue[19] == '.' ||
pszValue[19] == 'Z' || pszValue[19] == '+' ||
pszValue[19] == '-'))
{
m_eType = GMLPT_DateTime;
}
else if (pszValue[10] == '\0')
{
if (m_eType != GMLPT_DateTime)
m_eType = GMLPT_Date;
}
else
{
m_eType = GMLPT_String;
}
}
else if ((m_eType == GMLPT_Untyped || m_eType == GMLPT_Time) &&
IsDigitLowerOrEqual(pszValue[0], 2) &&
IsDigitLowerOrEqual(pszValue[1], 9) &&
pszValue[2] == ':' &&
IsDigitLowerOrEqual(pszValue[3], 5) &&
IsDigitLowerOrEqual(pszValue[4], 9) &&
pszValue[5] == ':' &&
IsDigitLowerOrEqual(pszValue[6], 6) &&
IsDigitLowerOrEqual(pszValue[7], 9) &&
(pszValue[8] == '\0' || pszValue[8] == '.'))
{
m_eType = GMLPT_Time;
}
else
{
m_eType = GMLPT_String;
}
}
}
else
Expand Down
5 changes: 3 additions & 2 deletions ogr/ogrsf_frmts/gmlutils/parsexsd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ static bool GetSimpleTypeProperties(CPLXMLNode *psTypeNode,
return true;
}

else if (EQUAL(pszBase, "dateTime"))
else if (EQUAL(pszBase, "dateTime") || EQUAL(pszBase, "TimeInstantType"))
{
*pGMLType = GMLPT_DateTime;
return true;
Expand Down Expand Up @@ -425,7 +425,8 @@ static GMLFeatureClass *GMLParseFeatureType(CPLXMLNode *psSchemaNode,
gmlType = GMLPT_Date;
else if (EQUAL(pszStrippedNSType, "time"))
gmlType = GMLPT_Time;
else if (EQUAL(pszStrippedNSType, "dateTime"))
else if (EQUAL(pszStrippedNSType, "dateTime") ||
EQUAL(pszStrippedNSType, "TimeInstantType"))
gmlType = GMLPT_DateTime;
else if (EQUAL(pszStrippedNSType, "real") ||
EQUAL(pszStrippedNSType, "double") ||
Expand Down
Loading
Loading