Skip to content

Commit

Permalink
Merge pull request #32322 from elpaso/server-wfs3-timefilter-dimensions
Browse files Browse the repository at this point in the history
[feature] Server wfs3 timefilter dimensions
  • Loading branch information
elpaso committed Oct 30, 2019
2 parents 6a7719f + e8fa8ed commit e689a65
Show file tree
Hide file tree
Showing 36 changed files with 2,257 additions and 90 deletions.
2 changes: 1 addition & 1 deletion python/core/auto_generated/qgsvectorlayer.sip.in
Expand Up @@ -567,7 +567,7 @@ which is used by the layer, so any changes are immediately applied.
%End


QgsVectorLayerServerProperties *serverProperties();
QgsVectorLayerServerProperties *serverProperties() const;
%Docstring
Returns QGIS Server Properties of the vector layer

Expand Down
Expand Up @@ -30,6 +30,7 @@ Manages QGIS Server properties for a vector layer
enum PredefinedWmsDimensionName
{
TIME,
DATE,
ELEVATION
};

Expand Down
62 changes: 62 additions & 0 deletions python/server/auto_generated/qgsserverapiutils.sip.in
Expand Up @@ -35,6 +35,68 @@ Parses a comma separated ``bbox`` into a (possibly empty) :py:class:`QgsRectangl
Z values (i.e. a 6 elements bbox) are silently discarded
%End

static QList< QgsVectorLayerServerProperties::WmsDimensionInfo > temporalDimensions( const QgsVectorLayer *layer );
%Docstring
Returns a list of temporal dimensions information for the given ``layer`` (either configured in wmsDimensions or the first date/datetime field)

.. versionadded:: 3.12
%End

static QgsDateRange parseTemporalDateInterval( const QString &interval ) throw( QgsServerApiBadRequestException );
%Docstring
Parses a date ``interval`` and returns a :py:class:`QgsDateRange`

:raises QgsServerApiBadRequestException: if interval cannot be parsed

.. versionadded:: 3.12
%End

static QgsDateTimeRange parseTemporalDateTimeInterval( const QString &interval ) throw( QgsServerApiBadRequestException );
%Docstring
Parses a datetime ``interval`` and returns a :py:class:`QgsDateTimeRange`

:raises QgsServerApiBadRequestException: if interval cannot be parsed

.. versionadded:: 3.12
%End



static QgsExpression temporalFilterExpression( const QgsVectorLayer *layer, const QString &interval );
%Docstring
Parses the ``interval`` and constructs a (possibly invalid) temporal filter expression for the given ``layer``

Interval syntax:

interval-closed = date-time "/" date-time
interval-open-start = [".."] "/" date-time
interval-open-end = date-time "/" [".."]
interval = interval-closed / interval-open-start / interval-open-end
datetime = date-time / interval

.. versionadded:: 3.12
%End



static QVariantList temporalExtentList( const QgsVectorLayer *layer ) /PyName=temporalExtent/;
%Docstring
temporalExtent returns a json array with an array of [min, max] temporal extent for the given ``layer``.
In case multiple temporal dimensions are available in the layer, a union of all dimensions is returned.

From specifications: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/extent.yaml

One or more time intervals that describe the temporal extent of the dataset.
The value `null` is supported and indicates an open time interval.

In the Core only a single time interval is supported. Extensions may support
multiple intervals. If multiple intervals are provided, the union of the
intervals describes the temporal extent.

:return: An array of intervals

.. versionadded:: 3.12
%End

static QgsCoordinateReferenceSystem parseCrs( const QString &bboxCrs );
%Docstring
Expand Down
40 changes: 37 additions & 3 deletions resources/server/api/ogc/schema.json
Expand Up @@ -188,6 +188,40 @@
"items" : {
"type" : "number"
}
},
"temporal" : {
"description" : "The temporal extent of the features in the collection.",
"type" : "object",
"properties" : {
"interval" : {
"description" : "One or more time intervals that describe the temporal extent of the dataset.\nThe value `null` is supported and indicates an open time interval.\nIn the Core only a single time interval is supported. Extensions may support multiple intervals. If multiple intervals are provided, the union of the intervals describes the temporal extent.",
"type": "array",
"minItems": 1,
"items" : {
"description" : "Begin and end times of the time interval. The timestamps\nare in the coordinate reference system specified in `trs`. By default\nthis is the Gregorian calendar.",
"type": "array",
"minItems": 2,
"maxItems": 2,
"items": {
"type" : "string",
"format" : "date-time",
"nullable" : true,
"example" : [
"2011-11-11T12:22:11Z",
null
],
"trs" : {
"description": "Coordinate reference system of the coordinates in the temporal extent\n(property `interval`). The default reference system is the Gregorian calendar.\nIn the Core this is the only supported temporal reference system.\nExtensions may support additional temporal reference systems and add\nadditional enum values.",
"type": "string",
"enum" : [
"http://www.opengis.net/def/uom/ISO-8601/0/Gregorian"
],
"default" : "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian"
}
}
}
}
}
}
}
},
Expand Down Expand Up @@ -306,10 +340,10 @@
}
}
},
"time" : {
"name" : "time",
"datetime" : {
"name" : "datetime",
"in" : "query",
"description" : "Either a date-time or a period string that adheres to RFC 3339. Examples:\n\n* A date-time: \"2018-02-12T23:20:50Z\"\n* A period: \"2018-02-12T00:00:00Z/2018-03-18T12:31:12Z\" or \"2018-02-12T00:00:00Z/P1M6DT12H31M12S\"\n\nOnly features that have a temporal property that intersects the value of\n`time` are selected.\n\nIf a feature has multiple temporal properties, it is the decision of the\nserver whether only a single temporal property is used to determine\nthe extent or all relevant temporal properties.",
"description" : "Either a date-time or an interval, open or closed. Date and time expressions\nadhere to RFC 3339. Open intervals are expressed using double-dots.\n\nExamples:\n * A date-time: \"2018-02-12T23:20:50Z\"\n * A closed interval: \"2018-02-12T00:00:00Z/2018-03-18T12:31:12Z\"\n * Open intervals: \"2018-02-12T00:00:00Z/..\" or \"../2018-03-18T12:31:12Z\"\nOnly features that have a temporal property that intersects the value of\n`datetime` are selected.\nIf a feature has multiple temporal properties, it is the decision of the\nserver whether only a single temporal property is used to determine\nthe extent or all relevant temporal properties.",
"required" : false,
"style" : "form",
"explode" : false,
Expand Down
19 changes: 19 additions & 0 deletions resources/server/api/ogc/static/style.css
Expand Up @@ -34,3 +34,22 @@ a { color: green; }
.top-buffer {
margin-top:20px;
}

table.table {
table-layout: fixed;
}

dl.row dt:first-child {
width: 20%;
}

table.table-params td:first-child, table.table-params th:first-child,
table.table-params td:last-child, table.table-params th:last-child
{
width: 15%;
}

table.table-params td.small
{
font-size: 90%;
}
39 changes: 21 additions & 18 deletions resources/server/api/ogc/templates/wfs3/getApiDescription.html
Expand Up @@ -46,44 +46,47 @@ <h5 class="mb-0">
data-parent="#accordion">
<div class="card-body">
<dl class="row">
<dt class="col-sm-3">OperationId</dt>
<dd class="col-sm-9">{{ method_data.operationId }}</dt>
<dt class="col-sm-3">Tags</dt>
<dd class="col-sm-9">{{ method_data.tags }}</dt>
<dt class="col-sm-3">Description</dt>
<dd class="col-sm-9">{{ method_data.description }}</dd>
<dt class="col-sm-2">OperationId</dt>
<dd class="col-sm-10">{{ method_data.operationId }}</dt>
<dt class="col-sm-2">Tags</dt>
<dd class="col-sm-10">{{ method_data.tags }}</dt>
<dt class="col-sm-2">Description</dt>
<dd class="col-sm-10">{{ method_data.description }}</dd>
{% if existsIn(method_data, "parameters") %}
<dt class="col-sm-3">Parameters</dt>
<dd class="col-sm-9">
<table class="table table-sm">
<dt class="col-sm-2">Parameters</dt>
<dd class="col-sm-10">
<table class="table table-sm table-params">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Description</th>
<th scope="col">Type</th>
</tr>
<th scope="col">Description</th>
<th scope="col">Type</th>
</tr>
</thead>
<tbody>
{% for param in method_data.parameters %}
{% if existsIn(param, "name") %}
<tr>
<td>{{ param.name }}</td>
<td>{{ param.description }}</td>
<td class="small">{{ param.description }}</td>
<td>{{ param.schema.type }}</td>
</tr>
{% else %}
{% for comp_param in component_parameter( param ) %}
<tr>
<td colspan=3 class="jref">
{{ param }}
</td>
<td>{{ comp_param.name }}</td>
<td class="small">{{ comp_param.description }}</td>
<td>{{ comp_param.schema.type }}</td>
</tr>
{% endfor %}
{% endif %}
{% endfor %}
</tbody>
</table>
</dt>
{% endif %}
<dt class="col-sm-3">Responses</dt>
<dd class="col-sm-9 jref">{{ method_data.responses}}</dd>
<dt class="col-sm-2">Responses</dt>
<dd class="col-sm-10 jref">{{ method_data.responses}}</dd>
</dl>
</div>
</div>
Expand Down
10 changes: 7 additions & 3 deletions src/app/qgssourcefieldsproperties.cpp
Expand Up @@ -60,8 +60,12 @@ QgsSourceFieldsProperties::QgsSourceFieldsProperties( QgsVectorLayer *layer, QWi
mFieldsList->setHorizontalHeaderItem( AttrLengthCol, new QTableWidgetItem( tr( "Length" ) ) );
mFieldsList->setHorizontalHeaderItem( AttrPrecCol, new QTableWidgetItem( tr( "Precision" ) ) );
mFieldsList->setHorizontalHeaderItem( AttrCommentCol, new QTableWidgetItem( tr( "Comment" ) ) );
mFieldsList->setHorizontalHeaderItem( AttrWMSCol, new QTableWidgetItem( QStringLiteral( "WMS" ) ) );
mFieldsList->setHorizontalHeaderItem( AttrWFSCol, new QTableWidgetItem( QStringLiteral( "WFS" ) ) );
const auto wmsWi = new QTableWidgetItem( QStringLiteral( "WMS" ) );
wmsWi->setToolTip( tr( "Defines if this field is available in QGIS Server WMS service" ) );
mFieldsList->setHorizontalHeaderItem( AttrWMSCol, wmsWi );
const auto wfsWi = new QTableWidgetItem( QStringLiteral( "WFS" ) );
wfsWi->setToolTip( tr( "Defines if this field is available in QGIS Server WFS (and OAPIF) service" ) );
mFieldsList->setHorizontalHeaderItem( AttrWFSCol, wfsWi );
mFieldsList->setHorizontalHeaderItem( AttrAliasCol, new QTableWidgetItem( tr( "Alias" ) ) );

mFieldsList->setSortingEnabled( true );
Expand Down Expand Up @@ -265,6 +269,7 @@ void QgsSourceFieldsProperties::setRow( int row, int idx, const QgsField &field
wfsAttrItem->setCheckState( mLayer->excludeAttributesWfs().contains( field.name() ) ? Qt::Unchecked : Qt::Checked );
wfsAttrItem->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable );
mFieldsList->setItem( row, AttrWFSCol, wfsAttrItem );

}

bool QgsSourceFieldsProperties::addAttribute( const QgsField &field )
Expand Down Expand Up @@ -302,7 +307,6 @@ void QgsSourceFieldsProperties::apply()

mLayer->setExcludeAttributesWms( excludeAttributesWMS );
mLayer->setExcludeAttributesWfs( excludeAttributesWFS );

}

//SLOTS
Expand Down
12 changes: 11 additions & 1 deletion src/app/qgswmsdimensiondialog.cpp
Expand Up @@ -43,7 +43,7 @@ QgsWmsDimensionDialog::QgsWmsDimensionDialog( QgsVectorLayer *layer, QStringList
connect( mFieldComboBox, &QgsFieldComboBox::fieldChanged, this, &QgsWmsDimensionDialog::fieldChanged );
connect( mEndFieldComboBox, &QgsFieldComboBox::fieldChanged, this, &QgsWmsDimensionDialog::fieldChanged );
connect( mNameComboBox, &QComboBox::editTextChanged, this, &QgsWmsDimensionDialog::nameChanged );
connect( mDefaultDisplayComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsWmsDimensionDialog::defaultDisplayChanged );
connect( mDefaultDisplayComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &QgsWmsDimensionDialog::defaultDisplayChanged );

// Set available names
const QMetaEnum pnMetaEnum( QMetaEnum::fromType<QgsVectorLayerServerProperties::PredefinedWmsDimensionName>() );
Expand Down Expand Up @@ -155,6 +155,16 @@ void QgsWmsDimensionDialog::nameChanged( const QString &name )
mUnitSymbolLabel->setEnabled( false );
mUnitSymbolLineEdit->setEnabled( false );
}
if ( data == QgsVectorLayerServerProperties::DATE )
{
mFieldComboBox->setFilters( QgsFieldProxyModel::String | QgsFieldProxyModel::Date );
mEndFieldComboBox->setFilters( QgsFieldProxyModel::String | QgsFieldProxyModel::Date );
mUnitsLineEdit->setText( QStringLiteral( "ISO8601" ) );
mUnitsLabel->setEnabled( false );
mUnitsLineEdit->setEnabled( false );
mUnitSymbolLabel->setEnabled( false );
mUnitSymbolLineEdit->setEnabled( false );
}
else if ( data == QgsVectorLayerServerProperties::ELEVATION )
{
mFieldComboBox->setFilters( QgsFieldProxyModel::Numeric );
Expand Down
11 changes: 11 additions & 0 deletions src/app/qgswmsdimensiondialog.h
Expand Up @@ -24,6 +24,17 @@

class QgsVectorLayer;

/**
* The QgsWmsDimensionDialog class provides an interface for WMS/OAPIF (WFS3) dimensions configuration
* Available pre-defined dimensions are
*
* - DATE (supported by OAPIF only)
* - TIME (supported by WMS and OAPIF)
* - ELEVATION (supported by WMS only)
*
* Dimensions can also be configured as ranges by defining an "end" field that contains the
* upper value of the range.
*/
class APP_EXPORT QgsWmsDimensionDialog: public QDialog, private Ui::QgsWmsDimensionDialogBase
{
Q_OBJECT
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsrange.h
Expand Up @@ -399,7 +399,7 @@ class QgsTemporalRange
* If \a other is empty the range is not changed.
* If the range is empty and \a other is not, the range is changed and set to \a other.
* \see isEmpty()
* \return TRUE if the range was extended
* \returns TRUE if the range was extended
* \since QGIS 3.12
*/
bool extend( const QgsTemporalRange<T> &other )
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsvectorlayer.h
Expand Up @@ -692,7 +692,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
* Returns QGIS Server Properties of the vector layer
* \since QGIS 3.10
*/
QgsVectorLayerServerProperties *serverProperties() { return mServerProperties.get(); }
QgsVectorLayerServerProperties *serverProperties() const { return mServerProperties.get(); }

/**
* Returns the number of features that are selected in this layer.
Expand Down
1 change: 1 addition & 0 deletions src/core/qgsvectorlayerserverproperties.h
Expand Up @@ -48,6 +48,7 @@ class CORE_EXPORT QgsVectorLayerServerProperties
enum PredefinedWmsDimensionName
{
TIME,
DATE,
ELEVATION
};
Q_ENUM( PredefinedWmsDimensionName )
Expand Down

0 comments on commit e689a65

Please sign in to comment.