Server: datetime basic filter support for OAPIF

This commit is contained in:
Alessandro Pasotti 2019-10-18 14:50:49 +02:00
parent 6112da45f6
commit 595807171d
10 changed files with 970 additions and 50 deletions

View File

@ -49,11 +49,17 @@ Parses a comma separated ``bbox`` into a (possibly empty) :py:class:`QgsRectangl
static TemporalDateInterval parseTemporalDateInterval( const QString &interval ) throw( QgsServerApiBadRequestException );
%Docstring
Parse a date time ``interval`` and returns a TemporalInterval
Parse a date ``interval`` and returns a TemporalDateInterval
:raises QgsServerApiBadRequestException: if interval cannot be parsed
%End
static TemporalDateTimeInterval parseTemporalDateTimeInterval( const QString &interval ) throw( QgsServerApiBadRequestException );
%Docstring
Parse a datetime ``interval`` and returns a TemporalDateTimeInterval
:raises QgsServerApiBadRequestException: if interval cannot be parsed
%End

View File

@ -306,10 +306,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,

View File

@ -279,7 +279,7 @@ void QgsSourceFieldsProperties::setRow( int row, int idx, const QgsField &field
// ok, in theory, we could support any field type that
// can contain something convertible to a date/datetime, but
// let's keep it simple for now
if ( field.isDateOrTime() )
if ( field.isDateOrTime() || field.type() == QVariant::String )
{
oapifAttrItem->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable );
oapifAttrItem->setCheckState( mLayer->includeAttributesOapifTemporalFilters().contains( field.name() ) ? Qt::Checked : Qt::Unchecked );

View File

@ -83,6 +83,10 @@ template<typename T, class T2> T QgsServerApiUtils::parseTemporalInterval( const
}
};
const QStringList parts { interval.split( '/' ) };
if ( parts.length() != 2 )
{
throw QgsServerApiBadRequestException( QStringLiteral( "%1 is not a valid datetime interval." ).arg( interval ), QStringLiteral( "Server" ), Qgis::Critical );
}
return { parseDate( parts[0] ), parseDate( parts[1] ) };
}
@ -103,6 +107,25 @@ QgsExpression QgsServerApiUtils::temporalFilterExpression( const QgsVectorLayer
QgsExpression expression;
QStringList conditions;
QSet<QString> temporalFields { layer->includeAttributesOapifTemporalFilters() };
// Automatically pick any date/datetime fields if empty
if ( temporalFields.isEmpty() )
{
const auto constFields { layer->fields() };
for ( const auto &f : constFields )
{
if ( f.isDateOrTime() )
{
temporalFields.insert( f.name() );
}
}
}
if ( temporalFields.isEmpty() )
{
return expression;
}
// Is it an interval?
if ( interval.contains( '/' ) )
{
@ -110,7 +133,7 @@ QgsExpression QgsServerApiUtils::temporalFilterExpression( const QgsVectorLayer
try
{
TemporalDateInterval dateInterval { QgsServerApiUtils::parseTemporalDateInterval( interval ) };
for ( const auto &fieldName : layer->includeAttributesOapifTemporalFilters() )
for ( const auto &fieldName : temporalFields )
{
int fieldIdx { layer->fields().lookupField( fieldName ) };
if ( fieldIdx < 0 )
@ -155,7 +178,7 @@ QgsExpression QgsServerApiUtils::temporalFilterExpression( const QgsVectorLayer
catch ( QgsServerApiBadRequestException & ) // try datetime
{
TemporalDateTimeInterval dateTimeInterval { QgsServerApiUtils::parseTemporalDateTimeInterval( interval ) };
for ( const auto &fieldName : layer->includeAttributesOapifTemporalFilters() )
for ( const auto &fieldName : qgis::as_const( temporalFields ) )
{
int fieldIdx { layer->fields().lookupField( fieldName ) };
if ( fieldIdx < 0 )
@ -199,7 +222,7 @@ QgsExpression QgsServerApiUtils::temporalFilterExpression( const QgsVectorLayer
}
else // plain value
{
for ( const auto &fieldName : layer->includeAttributesOapifTemporalFilters() )
for ( const auto &fieldName : qgis::as_const( temporalFields ) )
{
int fieldIdx { layer->fields().lookupField( fieldName ) };
if ( fieldIdx < 0 )

View File

@ -59,12 +59,20 @@ class SERVER_EXPORT QgsServerApiUtils
*/
static QgsRectangle parseBbox( const QString &bbox );
/**
* A temporal date interval, if only one of "begin" or "end" are valid, the interval is open.
* If both "begin" and "end"a re invalid the interval is invalid.
*/
struct TemporalDateInterval
{
QDate begin;
QDate end;
};
/**
* A temporal datetime interval, if only one of "begin" or "end" are valid, the interval is open.
* If both "begin" and "end"a re invalid the interval is invalid.
*/
struct TemporalDateTimeInterval
{
QDateTime begin;
@ -72,14 +80,21 @@ class SERVER_EXPORT QgsServerApiUtils
};
/**
* Parse a date time \a interval and returns a TemporalInterval
* Parse a date \a interval and returns a TemporalDateInterval
*
* \throws QgsServerApiBadRequestException if interval cannot be parsed
*/
static TemporalDateInterval parseTemporalDateInterval( const QString &interval ) SIP_THROW( QgsServerApiBadRequestException );
/**
* Parse a datetime \a interval and returns a TemporalDateTimeInterval
*
* \throws QgsServerApiBadRequestException if interval cannot be parsed
*/
static TemporalDateTimeInterval parseTemporalDateTimeInterval( const QString &interval ) SIP_THROW( QgsServerApiBadRequestException );
///@cond PRIVATE
// T is TemporalDateInterval|TemporalDateTimeInterval, T2 is QDate|QdateTime
template<typename T, class T2> static T parseTemporalInterval( const QString &interval ) SIP_SKIP;
/// @endcond
@ -99,9 +114,6 @@ class SERVER_EXPORT QgsServerApiUtils
/**
* layerExtent returns json array with [xMin,yMin,xMax,yMax] CRS84 extent for the given \a layer
* FIXME: the OpenAPI swagger docs say that it is inverted axis order: West, north, east, south edges of the spatial extent.
* but current example implementations and GDAL assume it's not.
* TODO: maybe consider advertised extent instead?
*/
static json layerExtent( const QgsVectorLayer *layer ) SIP_SKIP;

View File

@ -30,6 +30,7 @@
#include "qgsserverinterface.h"
#include "qgsexpressioncontext.h"
#include "qgsexpressioncontextutils.h"
#include "qgslogger.h"
#ifdef HAVE_SERVER_PYTHON_PLUGINS
#include "qgsfilterrestorer.h"
@ -759,12 +760,38 @@ QList<QgsServerQueryStringParameter> QgsWfs3CollectionsItemsHandler::parameters(
// datetime
QgsServerQueryStringParameter datetime { QStringLiteral( "datetime" ), false,
QgsServerQueryStringParameter::Type::Integer,
QStringLiteral( "Date time filter" ),
QgsServerQueryStringParameter::Type::String,
QStringLiteral( "Datetime filter" ),
};
datetime.setCustomValidator( [ = ]( const QgsServerApiContext &, QVariant & value ) -> bool
datetime.setCustomValidator( [ ]( const QgsServerApiContext &, QVariant & value ) -> bool
{
// TODO
const QString stringValue { value.toString() };
if ( stringValue.contains( '/' ) )
{
try
{
QgsServerApiUtils::parseTemporalDateInterval( stringValue );
}
catch ( QgsServerException & )
{
try
{
QgsServerApiUtils::parseTemporalDateTimeInterval( stringValue );
}
catch ( QgsServerException & )
{
return false;
}
}
}
else
{
if ( ! QDate::fromString( stringValue, Qt::DateFormat::ISODate ).isValid( ) &&
! QDateTime::fromString( stringValue, Qt::DateFormat::ISODate ).isValid( ) )
{
return false;
}
}
return true;
} );
params.push_back( datetime );
@ -870,7 +897,7 @@ json QgsWfs3CollectionsItemsHandler::schema( const QgsServerApiContext &context
{ "$ref", "#/components/parameters/resultType" },
{ "$ref", "#/components/parameters/bbox" },
{ "$ref", "#/components/parameters/bbox-crs" },
{ "$ref", "#/components/parameters/time" },
{ "$ref", "#/components/parameters/datetime" },
}
};
@ -1044,7 +1071,7 @@ void QgsWfs3CollectionsItemsHandler::handleRequest( const QgsServerApiContext &c
QString filterExpression;
QStringList expressions;
/*/ datetime
// datetime
const QString datetime { context.request()->queryParameter( QStringLiteral( "datetime" ) ) };
if ( ! datetime.isEmpty() )
{
@ -1057,7 +1084,7 @@ void QgsWfs3CollectionsItemsHandler::handleRequest( const QgsServerApiContext &c
{
expressions.push_back( timeExpression.expression() );
}
}*/
}
// Inputs are valid, process request
QgsFeatureRequest featureRequest = filteredRequest( mapLayer, context );
@ -1092,8 +1119,13 @@ void QgsWfs3CollectionsItemsHandler::handleRequest( const QgsServerApiContext &c
}
// Join all expression filters
filterExpression = expressions.join( QStringLiteral( " AND " ) );
featureRequest.setFilterExpression( filterExpression );
if ( ! expressions.isEmpty() )
{
filterExpression = expressions.join( QStringLiteral( " AND " ) );
featureRequest.setFilterExpression( filterExpression );
QgsDebugMsgLevel( QStringLiteral( "Filter expression: %1" ).arg( featureRequest.filterExpression()->expression() ), 4 );
}
// WFS3 core specs only serves 4326
featureRequest.setDestinationCrs( crs, context.project()->transformContext() );

View File

@ -471,14 +471,19 @@ class QgsServerAPITest(QgsServerAPITestBase):
response = self.compareApi(request, project, 'test_wfs3_collections_items_layer1_with_short_name_eq_tw_star.json')
self.assertEqual(response.statusCode(), 200)
def test_wfs3_excluded_attributes(self):
"""Test excluded attributes"""
project = QgsProject()
project.read(unitTestDataPath('qgis_server') + '/test_project_api.qgs')
request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections/exclude_attribute/items/0.geojson')
response = self.compareApi(request, project, 'test_wfs3_collections_items_exclude_attribute_0.json')
self.assertEqual(response.statusCode(), 200)
def test_wfs3_time_filters(self):
"""Test datetime filters"""
project = QgsProject()
project.read(unitTestDataPath('qgis_server') + '/test_project_api_timefilters.qgs')
request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections/points/items?name=lus*')
response = self.compareApi(request, project, 'test_wfs3_collections_items_points_eq_lus.json')
self.assertEqual(response.statusCode(), 200)
# Prepare 3 projects with all three options: "created", "updated", both.
tmpDir = QtCore.QTemporaryDir()
@ -493,10 +498,14 @@ class QgsServerAPITest(QgsServerAPITestBase):
both_path = os.path.join(tmpDir.path(), 'test_project_api_timefilters_both.qgs')
project.write(both_path)
# Test data:
#wkt_geom fid name created updated
#Point (7.30355493642693343 44.82162158126364915) 2 bricherasio 2019-05-05 2020-05-05T05:05:05.000
#Point (7.2500747591236081 44.81342128741047048) 1 luserna 2019-01-01 2020-01-01T10:10:10.000
'''
Test data:
wkt_geom fid name created updated
Point (7.2500747591236081 44.81342128741047048) 1 luserna 2019-01-01 2020-01-01T10:10:10.000
Point (7.30355493642693343 44.82162158126364915) 2 bricherasio 2019-05-05 2020-05-05T05:05:05.000
Point (7.28848021144956881 44.79768920192042714) 3 bibiana
Point (7.22555186948937145 44.82015087638781381) 4 torre 2019-10-10 2019-10-10T10:10:10.000
'''
# What to test:
#interval-closed = date-time "/" date-time
@ -505,9 +514,9 @@ class QgsServerAPITest(QgsServerAPITestBase):
#interval = interval-closed / interval-open-start / interval-open-end
#datetime = date-time / interval
def _date_tester(project_path, expected, unexpected):
def _date_tester(project_path, datetime, expected, unexpected):
# Test "created" date field exact
request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections/points/items?datetime=2019-05-05')
request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections/points/items?datetime=%s' % datetime)
response = QgsBufferServerResponse()
project.read(project_path)
self.server.handleRequest(request, response, project)
@ -517,16 +526,89 @@ class QgsServerAPITest(QgsServerAPITestBase):
for unexp in unexpected:
self.assertFalse(unexp in body)
# Test "created" date field exact
_date_tester(created_path, ['bricherasio'], ['luserna'])
# Test single values
def test_wfs3_excluded_attributes(self):
"""Test excluded attributes"""
project = QgsProject()
project.read(unitTestDataPath('qgis_server') + '/test_project_api.qgs')
request = QgsBufferServerRequest('http://server.qgis.org/wfs3/collections/exclude_attribute/items/0.geojson')
response = self.compareApi(request, project, 'test_wfs3_collections_items_exclude_attribute_0.json')
self.assertEqual(response.statusCode(), 200)
##################################################################################
# Test "created" date field
# Test exact
_date_tester(created_path, '2019-05-05', ['bricherasio'], ['luserna', 'torre'])
# Test datetime field exact (test that we can use a time on a date type field)
_date_tester(created_path, '2019-05-05T01:01:01', ['bricherasio'], ['luserna', 'torre'])
# Test exact no match
_date_tester(created_path, '2019-05-06', [], ['luserna', 'bricherasio', 'torre'])
##################################################################################
# Test "updated" datetime field
# Test exact
_date_tester(updated_path, '2020-05-05T05:05:05', ['bricherasio'], ['luserna', 'torre'])
# Test date field exact (test that we can NOT use a date on a datetime type field)
_date_tester(updated_path, '2020-05-05', [], ['luserna', 'bricherasio', 'torre'])
# Test exact no match
_date_tester(updated_path, '2020-05-06T05:05:05', [], ['luserna', 'bricherasio', 'torre'])
##################################################################################
# Test both
# Test exact
_date_tester(both_path, '2019-10-10T10:10:10', ['torre'], ['bricherasio', 'luserna'])
# Test date field exact (test that we can NOT use a date on a datetime type field)
_date_tester(both_path, '2020-05-05', [], ['luserna', 'bricherasio', 'torre'])
# Test exact no match
_date_tester(both_path, '2020-05-06T05:05:05', [], ['luserna', 'bricherasio', 'torre'])
# Test intervals
##################################################################################
# Test "created" date field
_date_tester(created_path, '2019-05-04/2019-05-06', ['bricherasio'], ['luserna', 'torre'])
_date_tester(created_path, '2019-05-04/..', ['bricherasio', 'torre'], ['luserna'])
_date_tester(created_path, '2019-05-04/', ['bricherasio', 'torre'], ['luserna'])
_date_tester(created_path, '2100-05-04/', [], ['luserna', 'bricherasio', 'torre'])
_date_tester(created_path, '2100-05-04/..', [], ['luserna', 'bricherasio', 'torre'])
_date_tester(created_path, '/2019-05-06', ['bricherasio', 'luserna'], ['torre'])
_date_tester(created_path, '/2019-01-02', ['luserna'], ['torre', 'bricherasio'])
_date_tester(created_path, '../2019-05-06', ['bricherasio', 'luserna'], ['torre'])
_date_tester(created_path, '../2019-01-02', ['luserna'], ['torre', 'bricherasio'])
# Test datetimes on "created" date field
_date_tester(created_path, '2019-05-04T01:01:01/2019-05-06T01:01:01', ['bricherasio'], ['luserna', 'torre'])
_date_tester(created_path, '2019-05-04T01:01:01/..', ['bricherasio', 'torre'], ['luserna'])
_date_tester(created_path, '2019-05-04T01:01:01/', ['bricherasio', 'torre'], ['luserna'])
_date_tester(created_path, '2100-05-04T01:01:01/', [], ['luserna', 'bricherasio', 'torre'])
_date_tester(created_path, '2100-05-04T01:01:01/..', [], ['luserna', 'bricherasio', 'torre'])
_date_tester(created_path, '/2019-05-06T01:01:01', ['bricherasio', 'luserna'], ['torre'])
_date_tester(created_path, '/2019-01-02T01:01:01', ['luserna'], ['torre', 'bricherasio'])
_date_tester(created_path, '../2019-05-06T01:01:01', ['bricherasio', 'luserna'], ['torre'])
_date_tester(created_path, '../2019-01-02T01:01:01', ['luserna'], ['torre', 'bricherasio'])
##################################################################################
# Test "updated" date field
_date_tester(updated_path, '2020-05-04/2020-05-06', ['bricherasio'], ['luserna', 'torre'])
_date_tester(updated_path, '2020-05-04/..', ['bricherasio'], ['luserna', 'torre'])
_date_tester(updated_path, '2020-05-04/', ['bricherasio'], ['luserna', 'torre'])
_date_tester(updated_path, '2100-05-04/', [], ['luserna', 'bricherasio', 'torre'])
_date_tester(updated_path, '2100-05-04/..', [], ['luserna', 'bricherasio', 'torre'])
_date_tester(updated_path, '/2020-02-02', ['torre', 'luserna'], ['bricherasio'])
_date_tester(updated_path, '/2020-01-01', ['luserna', 'torre'], ['bricherasio'])
_date_tester(updated_path, '../2020-02-02', ['torre', 'luserna'], ['bricherasio'])
_date_tester(updated_path, '../2020-01-01', ['luserna', 'torre'], ['bricherasio'])
# Test datetimes on "updated" datetime field
_date_tester(updated_path, '2020-05-04/2020-05-06', ['bricherasio'], ['luserna', 'torre'])
_date_tester(updated_path, '2020-05-04/..', ['bricherasio'], ['luserna', 'torre'])
_date_tester(updated_path, '2020-05-04/', ['bricherasio'], ['luserna', 'torre'])
_date_tester(updated_path, '2100-05-04/', [], ['luserna', 'bricherasio', 'torre'])
_date_tester(updated_path, '2100-05-04/..', [], ['luserna', 'bricherasio', 'torre'])
_date_tester(updated_path, '/2020-02-02', ['torre', 'luserna'], ['bricherasio'])
_date_tester(updated_path, '/2020-01-01', ['luserna', 'torre'], ['bricherasio'])
_date_tester(updated_path, '../2020-02-02', ['torre', 'luserna'], ['bricherasio'])
_date_tester(updated_path, '../2020-01-01', ['luserna', 'torre'], ['bricherasio'])
##################################################################################
# Test both
_date_tester(both_path, '2019-10-09/2019-10-11', ['torre'], ['luserna', 'bricherasio'])
_date_tester(both_path, '2019-05-04/2020-05-06', ['torre', 'bricherasio'], ['luserna'])
_date_tester(both_path, '../2020-01-01', ['luserna', 'torre'], ['bricherasio'])
_date_tester(both_path, '/2020-01-01', ['luserna', 'torre'], ['bricherasio'])
class Handler1(QgsServerOgcApiHandler):

View File

@ -53,6 +53,17 @@ Content-Type: application/openapi+json;version=3.0
},
"style": "form"
},
"datetime": {
"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.",
"explode": false,
"in": "query",
"name": "datetime",
"required": false,
"schema": {
"type": "string"
},
"style": "form"
},
"featureId": {
"description": "Local identifier of a specific feature",
"in": "path",
@ -121,17 +132,6 @@ Content-Type: application/openapi+json;version=3.0
"type": "string"
},
"style": "form"
},
"time": {
"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.",
"explode": false,
"in": "query",
"name": "time",
"required": false,
"schema": {
"type": "string"
},
"style": "form"
}
},
"schemas": {

Binary file not shown.

View File

@ -0,0 +1,765 @@
<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
<qgis projectname="QGIS Test Project API" version="3.9.0-Master">
<homePath path=""/>
<title>QGIS Test Project API</title>
<autotransaction active="0"/>
<evaluateDefaultValues active="0"/>
<trust active="0"/>
<projectCrs>
<spatialrefsys>
<proj4>+proj=longlat +datum=WGS84 +no_defs</proj4>
<srsid>3452</srsid>
<srid>4326</srid>
<authid>EPSG:4326</authid>
<description>WGS 84</description>
<projectionacronym>longlat</projectionacronym>
<ellipsoidacronym>WGS84</ellipsoidacronym>
<geographicflag>true</geographicflag>
</spatialrefsys>
</projectCrs>
<layer-tree-group>
<customproperties/>
<layer-tree-layer providerKey="ogr" source="./test_project_api_timefilters.gpkg|layername=points" checked="Qt::Checked" expanded="1" id="points_47ad3bc8_35bd_4392_8994_2dc5ff04be60" name="points">
<customproperties/>
</layer-tree-layer>
<custom-order enabled="0">
<item>points_47ad3bc8_35bd_4392_8994_2dc5ff04be60</item>
</custom-order>
</layer-tree-group>
<snapping-settings mode="1" type="2" tolerance="0" unit="2" enabled="0" intersection-snapping="0">
<individual-layer-settings>
<layer-setting units="1" type="1" tolerance="12" enabled="0" id="points_47ad3bc8_35bd_4392_8994_2dc5ff04be60"/>
</individual-layer-settings>
</snapping-settings>
<relations/>
<mapcanvas annotationsVisible="1" name="theMapCanvas">
<units>degrees</units>
<extent>
<xmin>7.25418797910206159</xmin>
<ymin>44.7955945616427087</ymin>
<xmax>7.3179146610253607</xmax>
<ymax>44.85094654515160784</ymax>
</extent>
<rotation>0</rotation>
<destinationsrs>
<spatialrefsys>
<proj4>+proj=longlat +datum=WGS84 +no_defs</proj4>
<srsid>3452</srsid>
<srid>4326</srid>
<authid>EPSG:4326</authid>
<description>WGS 84</description>
<projectionacronym>longlat</projectionacronym>
<ellipsoidacronym>WGS84</ellipsoidacronym>
<geographicflag>true</geographicflag>
</spatialrefsys>
</destinationsrs>
<rendermaptile>0</rendermaptile>
<expressionContextScope/>
</mapcanvas>
<projectModels/>
<legend updateDrawingOrder="true">
<legendlayer drawingOrder="-1" checked="Qt::Checked" open="true" name="points" showFeatureCount="0">
<filegroup hidden="false" open="true">
<legendlayerfile isInOverview="0" visible="1" layerid="points_47ad3bc8_35bd_4392_8994_2dc5ff04be60"/>
</filegroup>
</legendlayer>
</legend>
<mapViewDocks/>
<projectlayers>
<maplayer simplifyDrawingTol="1.2" maxScale="0" refreshOnNotifyMessage="" styleCategories="AllStyleCategories" readOnly="0" geometry="Point" refreshOnNotifyEnabled="0" minScale="1e+08" autoRefreshTime="0" simplifyMaxScale="1" labelsEnabled="0" simplifyDrawingHints="0" autoRefreshEnabled="0" wkbType="Point" simplifyLocal="1" hasScaleBasedVisibilityFlag="0" simplifyAlgorithm="0" type="vector">
<extent>
<xmin>7.25007390975952148</xmin>
<ymin>44.79768753051757813</ymin>
<xmax>7.30355501174926758</xmax>
<ymax>44.82162857055664063</ymax>
</extent>
<id>points_47ad3bc8_35bd_4392_8994_2dc5ff04be60</id>
<datasource>./test_project_api_timefilters.gpkg|layername=points</datasource>
<keywordList>
<value></value>
</keywordList>
<layername>points</layername>
<srs>
<spatialrefsys>
<proj4>+proj=longlat +datum=WGS84 +no_defs</proj4>
<srsid>3452</srsid>
<srid>4326</srid>
<authid>EPSG:4326</authid>
<description>WGS 84</description>
<projectionacronym>longlat</projectionacronym>
<ellipsoidacronym>WGS84</ellipsoidacronym>
<geographicflag>true</geographicflag>
</spatialrefsys>
</srs>
<resourceMetadata>
<identifier></identifier>
<parentidentifier></parentidentifier>
<language></language>
<type>dataset</type>
<title></title>
<abstract></abstract>
<contact>
<name></name>
<organization></organization>
<position></position>
<voice></voice>
<fax></fax>
<email></email>
<role></role>
</contact>
<links/>
<fees></fees>
<encoding></encoding>
<crs>
<spatialrefsys>
<proj4></proj4>
<srsid>0</srsid>
<srid>0</srid>
<authid></authid>
<description></description>
<projectionacronym></projectionacronym>
<ellipsoidacronym></ellipsoidacronym>
<geographicflag>true</geographicflag>
</spatialrefsys>
</crs>
<extent>
<spatial maxz="0" miny="0" minz="0" maxx="0" dimensions="2" crs="" maxy="0" minx="0"/>
<temporal>
<period>
<start></start>
<end></end>
</period>
</temporal>
</extent>
</resourceMetadata>
<provider encoding="UTF-8">ogr</provider>
<vectorjoins/>
<layerDependencies/>
<dataDependencies/>
<legend type="default-vector"/>
<expressionfields/>
<map-layer-style-manager current="default">
<map-layer-style name="default"/>
</map-layer-style-manager>
<auxiliaryLayer/>
<flags>
<Identifiable>1</Identifiable>
<Removable>1</Removable>
<Searchable>1</Searchable>
</flags>
<renderer-v2 type="singleSymbol" symbollevels="0" enableorderby="0" forceraster="0">
<symbols>
<symbol clip_to_extent="1" type="marker" alpha="1" force_rhr="0" name="0">
<layer enabled="1" class="SimpleMarker" pass="0" locked="0">
<prop k="angle" v="0"/>
<prop k="color" v="232,113,141,255"/>
<prop k="horizontal_anchor_point" v="1"/>
<prop k="joinstyle" v="bevel"/>
<prop k="name" v="circle"/>
<prop k="offset" v="0,0"/>
<prop k="offset_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="offset_unit" v="MM"/>
<prop k="outline_color" v="35,35,35,255"/>
<prop k="outline_style" v="solid"/>
<prop k="outline_width" v="0"/>
<prop k="outline_width_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="outline_width_unit" v="MM"/>
<prop k="scale_method" v="diameter"/>
<prop k="size" v="2"/>
<prop k="size_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="size_unit" v="MM"/>
<prop k="vertical_anchor_point" v="1"/>
<data_defined_properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</data_defined_properties>
</layer>
</symbol>
</symbols>
<rotation/>
<sizescale/>
</renderer-v2>
<customproperties>
<property key="dualview/previewExpressions">
<value>name</value>
</property>
<property key="embeddedWidgets/count" value="0"/>
<property key="variableNames"/>
<property key="variableValues"/>
</customproperties>
<blendMode>0</blendMode>
<featureBlendMode>0</featureBlendMode>
<layerOpacity>1</layerOpacity>
<SingleCategoryDiagramRenderer diagramType="Histogram" attributeLegend="1">
<DiagramCategory minScaleDenominator="0" barWidth="5" labelPlacementMethod="XHeight" sizeScale="3x:0,0,0,0,0,0" rotationOffset="270" maxScaleDenominator="1e+08" enabled="0" diagramOrientation="Up" backgroundAlpha="255" lineSizeScale="3x:0,0,0,0,0,0" sizeType="MM" penAlpha="255" width="15" scaleBasedVisibility="0" backgroundColor="#ffffff" lineSizeType="MM" penColor="#000000" height="15" scaleDependency="Area" opacity="1" minimumSize="0" penWidth="0">
<fontProperties description="Noto Sans,10,-1,5,50,0,0,0,0,0,Regular" style="Regular"/>
<attribute field="" color="#000000" label=""/>
</DiagramCategory>
</SingleCategoryDiagramRenderer>
<DiagramLayerSettings zIndex="0" showAll="1" placement="0" priority="0" dist="0" linePlacementFlags="18" obstacle="0">
<properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</properties>
</DiagramLayerSettings>
<geometryOptions removeDuplicateNodes="0" geometryPrecision="0">
<activeChecks/>
<checkConfiguration/>
</geometryOptions>
<fieldConfiguration>
<field name="fid">
<editWidget type="TextEdit">
<config>
<Option/>
</config>
</editWidget>
</field>
<field name="name">
<editWidget type="TextEdit">
<config>
<Option/>
</config>
</editWidget>
</field>
<field name="created">
<editWidget type="DateTime">
<config>
<Option/>
</config>
</editWidget>
</field>
<field name="updated">
<editWidget type="DateTime">
<config>
<Option/>
</config>
</editWidget>
</field>
</fieldConfiguration>
<aliases>
<alias field="fid" index="0" name=""/>
<alias field="name" index="1" name=""/>
<alias field="created" index="2" name=""/>
<alias field="updated" index="3" name=""/>
</aliases>
<excludeAttributesWMS/>
<excludeAttributesWFS/>
<includeAttributesOapifTemporalFilters/>
<defaults>
<default applyOnUpdate="0" field="fid" expression=""/>
<default applyOnUpdate="0" field="name" expression=""/>
<default applyOnUpdate="0" field="created" expression=""/>
<default applyOnUpdate="0" field="updated" expression=""/>
</defaults>
<constraints>
<constraint exp_strength="0" field="fid" constraints="3" notnull_strength="1" unique_strength="1"/>
<constraint exp_strength="0" field="name" constraints="0" notnull_strength="0" unique_strength="0"/>
<constraint exp_strength="0" field="created" constraints="0" notnull_strength="0" unique_strength="0"/>
<constraint exp_strength="0" field="updated" constraints="0" notnull_strength="0" unique_strength="0"/>
</constraints>
<constraintExpressions>
<constraint field="fid" desc="" exp=""/>
<constraint field="name" desc="" exp=""/>
<constraint field="created" desc="" exp=""/>
<constraint field="updated" desc="" exp=""/>
</constraintExpressions>
<expressionfields/>
<attributeactions>
<defaultAction key="Canvas" value="{00000000-0000-0000-0000-000000000000}"/>
</attributeactions>
<attributetableconfig sortOrder="0" sortExpression="" actionWidgetStyle="dropDown">
<columns>
<column type="field" width="-1" hidden="0" name="fid"/>
<column type="field" width="-1" hidden="0" name="name"/>
<column type="actions" width="-1" hidden="1"/>
<column type="field" width="-1" hidden="0" name="created"/>
<column type="field" width="497" hidden="0" name="updated"/>
</columns>
</attributetableconfig>
<conditionalstyles>
<rowstyles/>
<fieldstyles/>
</conditionalstyles>
<storedexpressions/>
<editform tolerant="1"></editform>
<editforminit/>
<editforminitcodesource>0</editforminitcodesource>
<editforminitfilepath></editforminitfilepath>
<editforminitcode><![CDATA[# -*- coding: utf-8 -*-
"""
QGIS forms can have a Python function that is called when the form is
opened.
Use this function to add extra logic to your forms.
Enter the name of the function in the "Python Init function"
field.
An example follows:
"""
from qgis.PyQt.QtWidgets import QWidget
def my_form_open(dialog, layer, feature):
geom = feature.geometry()
control = dialog.findChild(QWidget, "MyLineEdit")
]]></editforminitcode>
<featformsuppress>0</featformsuppress>
<editorlayout>generatedlayout</editorlayout>
<editable>
<field editable="1" name="created"/>
<field editable="1" name="fid"/>
<field editable="1" name="name"/>
<field editable="1" name="updated"/>
</editable>
<labelOnTop>
<field name="created" labelOnTop="0"/>
<field name="fid" labelOnTop="0"/>
<field name="name" labelOnTop="0"/>
<field name="updated" labelOnTop="0"/>
</labelOnTop>
<widgets/>
<previewExpression>name</previewExpression>
<mapTip></mapTip>
</maplayer>
</projectlayers>
<layerorder>
<layer id="points_47ad3bc8_35bd_4392_8994_2dc5ff04be60"/>
</layerorder>
<properties>
<DefaultStyles>
<AlphaInt type="int">255</AlphaInt>
<ColorRamp type="QString"></ColorRamp>
<Fill type="QString"></Fill>
<Line type="QString"></Line>
<Marker type="QString"></Marker>
<Opacity type="double">1</Opacity>
<RandomColors type="bool">true</RandomColors>
</DefaultStyles>
<Digitizing>
<AvoidIntersectionsList type="QStringList"/>
<DefaultSnapTolerance type="double">0</DefaultSnapTolerance>
<DefaultSnapToleranceUnit type="int">2</DefaultSnapToleranceUnit>
<DefaultSnapType type="QString">off</DefaultSnapType>
<LayerSnapToList type="QStringList"/>
<LayerSnappingEnabledList type="QStringList"/>
<LayerSnappingList type="QStringList"/>
<LayerSnappingToleranceList type="QStringList"/>
<LayerSnappingToleranceUnitList type="QStringList"/>
<SnappingMode type="QString">current_layer</SnappingMode>
</Digitizing>
<Gui>
<CanvasColorBluePart type="int">255</CanvasColorBluePart>
<CanvasColorGreenPart type="int">255</CanvasColorGreenPart>
<CanvasColorRedPart type="int">255</CanvasColorRedPart>
<SelectionColorAlphaPart type="int">255</SelectionColorAlphaPart>
<SelectionColorBluePart type="int">0</SelectionColorBluePart>
<SelectionColorGreenPart type="int">255</SelectionColorGreenPart>
<SelectionColorRedPart type="int">255</SelectionColorRedPart>
</Gui>
<Identify>
<disabledLayers type="QStringList">
<value>testlayer_0b835118_a5d5_4255_b5dd_f42253c0a4a0</value>
</disabledLayers>
</Identify>
<Legend>
<filterByMap type="bool">false</filterByMap>
</Legend>
<Macros>
<pythonCode type="QString"></pythonCode>
</Macros>
<Measure>
<Ellipsoid type="QString">WGS84</Ellipsoid>
</Measure>
<Measurement>
<AreaUnits type="QString">m2</AreaUnits>
<DistanceUnits type="QString">meters</DistanceUnits>
</Measurement>
<PAL>
<CandidatesLine type="int">50</CandidatesLine>
<CandidatesPoint type="int">16</CandidatesPoint>
<CandidatesPolygon type="int">30</CandidatesPolygon>
<DrawOutlineLabels type="bool">true</DrawOutlineLabels>
<DrawRectOnly type="bool">false</DrawRectOnly>
<DrawUnplaced type="bool">false</DrawUnplaced>
<SearchMethod type="int">0</SearchMethod>
<ShowingAllLabels type="bool">false</ShowingAllLabels>
<ShowingCandidates type="bool">false</ShowingCandidates>
<ShowingPartialsLabels type="bool">true</ShowingPartialsLabels>
<TextFormat type="int">0</TextFormat>
<UnplacedColor type="QString">255,0,0,255</UnplacedColor>
</PAL>
<Paths>
<Absolute type="bool">false</Absolute>
</Paths>
<PositionPrecision>
<Automatic type="bool">true</Automatic>
<DecimalPlaces type="int">2</DecimalPlaces>
<DegreeFormat type="QString">D</DegreeFormat>
</PositionPrecision>
<RequiredLayers>
<Layers type="QStringList"/>
</RequiredLayers>
<SpatialRefSys>
<ProjectCRSID type="int">3452</ProjectCRSID>
<ProjectCRSProj4String type="QString">+proj=longlat +datum=WGS84 +no_defs</ProjectCRSProj4String>
<ProjectCrs type="QString">EPSG:4326</ProjectCrs>
<ProjectionsEnabled type="int">1</ProjectionsEnabled>
</SpatialRefSys>
<Variables>
<variableNames type="QStringList"/>
<variableValues type="QStringList"/>
</Variables>
<WCSLayers type="QStringList"/>
<WCSUrl type="QString"></WCSUrl>
<WFSLayers type="QStringList">
<value>points_47ad3bc8_35bd_4392_8994_2dc5ff04be60</value>
</WFSLayers>
<WFSLayersPrecision>
<points_47ad3bc8_35bd_4392_8994_2dc5ff04be60 type="int">8</points_47ad3bc8_35bd_4392_8994_2dc5ff04be60>
<testlayer20150528120452665 type="int">8</testlayer20150528120452665>
<testlayer_c0988fd7_97ca_451d_adbc_37ad6d10583a type="int">8</testlayer_c0988fd7_97ca_451d_adbc_37ad6d10583a>
<testlayer_èé_2_a5f61891_b949_43e3_ad30_84013fc922de type="int">8</testlayer_èé_2_a5f61891_b949_43e3_ad30_84013fc922de>
<testlayer_èé_cf86cf11_222f_4b62_929c_12cfc82b9774 type="int">8</testlayer_èé_cf86cf11_222f_4b62_929c_12cfc82b9774>
</WFSLayersPrecision>
<WFSTLayers>
<Delete type="QStringList"/>
<Insert type="QStringList"/>
<Update type="QStringList"/>
</WFSTLayers>
<WFSUrl type="QString"></WFSUrl>
<WMSAccessConstraints type="QString">None</WMSAccessConstraints>
<WMSAddWktGeometry type="bool">true</WMSAddWktGeometry>
<WMSContactMail type="QString">elpaso@itopen.it</WMSContactMail>
<WMSContactOrganization type="QString">QGIS dev team</WMSContactOrganization>
<WMSContactPerson type="QString">Alessandro Pasotti</WMSContactPerson>
<WMSContactPhone type="QString"></WMSContactPhone>
<WMSContactPosition type="QString"></WMSContactPosition>
<WMSDefaultMapUnitsPerMm type="double">1</WMSDefaultMapUnitsPerMm>
<WMSExtent type="QStringList">
<value>8.20315414376310059</value>
<value>44.901236559338642</value>
<value>8.204164917965862</value>
<value>44.90159838674664172</value>
</WMSExtent>
<WMSFees type="QString">conditions unknown</WMSFees>
<WMSImageQuality type="int">90</WMSImageQuality>
<WMSKeywordList type="QStringList">
<value></value>
</WMSKeywordList>
<WMSMaxAtlasFeatures type="int">1</WMSMaxAtlasFeatures>
<WMSOnlineResource type="QString"></WMSOnlineResource>
<WMSPrecision type="QString">4</WMSPrecision>
<WMSRequestDefinedDataSources type="bool">false</WMSRequestDefinedDataSources>
<WMSRestrictedComposers type="QStringList"/>
<WMSRestrictedLayers type="QStringList"/>
<WMSRootName type="QString"></WMSRootName>
<WMSSegmentizeFeatureInfoGeometry type="bool">false</WMSSegmentizeFeatureInfoGeometry>
<WMSServiceAbstract type="QString">Some UTF8 text èòù</WMSServiceAbstract>
<WMSServiceCapabilities type="bool">true</WMSServiceCapabilities>
<WMSServiceTitle type="QString">QGIS TestProject</WMSServiceTitle>
<WMSTileBuffer type="int">0</WMSTileBuffer>
<WMSUrl type="QString"></WMSUrl>
<WMSUseLayerIDs type="bool">false</WMSUseLayerIDs>
<WMTSGrids>
<CRS type="QStringList"/>
<Config type="QStringList"/>
</WMTSGrids>
<WMTSJpegLayers>
<Group type="QStringList"/>
<Layer type="QStringList"/>
<Project type="bool">false</Project>
</WMTSJpegLayers>
<WMTSLayers>
<Group type="QStringList"/>
<Layer type="QStringList"/>
<Project type="bool">false</Project>
</WMTSLayers>
<WMTSMinScale type="int">5000</WMTSMinScale>
<WMTSPngLayers>
<Group type="QStringList"/>
<Layer type="QStringList"/>
<Project type="bool">false</Project>
</WMTSPngLayers>
<WMTSUrl type="QString"></WMTSUrl>
</properties>
<visibility-presets/>
<transformContext/>
<projectMetadata>
<identifier></identifier>
<parentidentifier></parentidentifier>
<language></language>
<type></type>
<title>QGIS Test Project API</title>
<abstract></abstract>
<contact>
<name></name>
<organization></organization>
<position></position>
<voice></voice>
<fax></fax>
<email></email>
<role></role>
</contact>
<links/>
<author></author>
<creation>2000-01-01T00:00:00</creation>
</projectMetadata>
<Annotations/>
<Layouts>
<Layout units="mm" printResolution="300" worldFileMap="" name="mytemplate">
<Snapper snapToGuides="1" tolerance="5" snapToItems="1" snapToGrid="0"/>
<Grid resolution="10" offsetUnits="mm" offsetX="0" offsetY="0" resUnits="mm"/>
<PageCollection>
<symbol clip_to_extent="1" type="fill" alpha="1" force_rhr="0" name="">
<layer enabled="1" class="SimpleFill" pass="0" locked="0">
<prop k="border_width_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="color" v="255,255,255,255"/>
<prop k="joinstyle" v="miter"/>
<prop k="offset" v="0,0"/>
<prop k="offset_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="offset_unit" v="MM"/>
<prop k="outline_color" v="0,0,0,255"/>
<prop k="outline_style" v="no"/>
<prop k="outline_width" v="0.26"/>
<prop k="outline_width_unit" v="MM"/>
<prop k="style" v="solid"/>
<data_defined_properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</data_defined_properties>
</layer>
</symbol>
<LayoutItem referencePoint="0" groupUuid="" positionOnPage="0,0,mm" positionLock="false" background="true" visibility="1" excludeFromExports="0" id="" zValue="0" frameJoinStyle="miter" uuid="{45febe5f-bfdd-455a-aef5-c096b4677622}" size="297,210,mm" itemRotation="0" outlineWidthM="0.3,mm" position="0,0,mm" templateUuid="{45febe5f-bfdd-455a-aef5-c096b4677622}" blendMode="0" opacity="1" frame="false" type="65638">
<FrameColor blue="0" alpha="255" green="0" red="0"/>
<BackgroundColor blue="255" alpha="255" green="255" red="255"/>
<LayoutObject>
<dataDefinedProperties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</dataDefinedProperties>
<customproperties/>
</LayoutObject>
<symbol clip_to_extent="1" type="fill" alpha="1" force_rhr="0" name="">
<layer enabled="1" class="SimpleFill" pass="0" locked="0">
<prop k="border_width_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="color" v="255,255,255,255"/>
<prop k="joinstyle" v="miter"/>
<prop k="offset" v="0,0"/>
<prop k="offset_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="offset_unit" v="MM"/>
<prop k="outline_color" v="35,35,35,255"/>
<prop k="outline_style" v="no"/>
<prop k="outline_width" v="0.26"/>
<prop k="outline_width_unit" v="MM"/>
<prop k="style" v="solid"/>
<data_defined_properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</data_defined_properties>
</layer>
</symbol>
</LayoutItem>
<GuideCollection visible="1"/>
</PageCollection>
<LayoutItem referencePoint="0" followPreset="false" followPresetName="" groupUuid="" positionOnPage="126,143,mm" mapRotation="0" positionLock="false" background="true" visibility="1" excludeFromExports="0" mapFlags="1" id="" zValue="2" frameJoinStyle="miter" uuid="{5ed7a90f-8af2-4535-a15e-e18a7f6c5d1f}" size="61,26,mm" itemRotation="0" outlineWidthM="0.3,mm" position="126,143,mm" drawCanvasItems="true" labelMargin="0,mm" templateUuid="{5ed7a90f-8af2-4535-a15e-e18a7f6c5d1f}" blendMode="0" opacity="1" keepLayerSet="false" frame="false" type="65639">
<FrameColor blue="0" alpha="255" green="0" red="0"/>
<BackgroundColor blue="255" alpha="255" green="255" red="255"/>
<LayoutObject>
<dataDefinedProperties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</dataDefinedProperties>
<customproperties/>
</LayoutObject>
<Extent xmax="8.20679772684772146" ymin="44.90025542156142535" xmin="8.20128754650006186" ymax="44.90260402302108389"/>
<LayerSet/>
<ComposerMapGrid leftAnnotationPosition="1" name="Grille 1" topAnnotationDirection="0" unit="0" annotationFontColor="0,0,0,255" topAnnotationDisplay="0" rightAnnotationPosition="1" minimumIntervalWidth="50" annotationPrecision="3" gridFrameWidth="2" gridStyle="0" bottomAnnotationDirection="0" topFrameDivisions="0" show="0" leftFrameDivisions="0" annotationExpression="" position="3" bottomAnnotationPosition="1" offsetX="0" rightFrameDivisions="0" leftAnnotationDirection="0" frameFillColor1="255,255,255,255" bottomAnnotationDisplay="0" rightAnnotationDirection="0" annotationFormat="0" offsetY="0" gridFramePenColor="0,0,0,255" showAnnotation="0" topAnnotationPosition="1" uuid="{ca78854d-b53f-4d8b-97bd-6bbf0df72ad0}" frameFillColor2="0,0,0,255" intervalX="0" rightAnnotationDisplay="0" crossLength="3" intervalY="0" gridFrameStyle="0" leftAnnotationDisplay="0" blendMode="0" gridFramePenThickness="0.5" frameAnnotationDistance="1" gridFrameSideFlags="15" gridFrameMargin="0" maximumIntervalWidth="100" bottomFrameDivisions="0">
<lineStyle>
<symbol clip_to_extent="1" type="line" alpha="1" force_rhr="0" name="">
<layer enabled="1" class="SimpleLine" pass="0" locked="0">
<prop k="capstyle" v="square"/>
<prop k="customdash" v="5;2"/>
<prop k="customdash_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="customdash_unit" v="MM"/>
<prop k="draw_inside_polygon" v="0"/>
<prop k="joinstyle" v="bevel"/>
<prop k="line_color" v="0,0,0,255"/>
<prop k="line_style" v="solid"/>
<prop k="line_width" v="0"/>
<prop k="line_width_unit" v="MM"/>
<prop k="offset" v="0"/>
<prop k="offset_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="offset_unit" v="MM"/>
<prop k="ring_filter" v="0"/>
<prop k="use_custom_dash" v="0"/>
<prop k="width_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<data_defined_properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</data_defined_properties>
</layer>
</symbol>
</lineStyle>
<markerStyle>
<symbol clip_to_extent="1" type="marker" alpha="1" force_rhr="0" name="">
<layer enabled="1" class="SimpleMarker" pass="0" locked="0">
<prop k="angle" v="0"/>
<prop k="color" v="0,0,0,255"/>
<prop k="horizontal_anchor_point" v="1"/>
<prop k="joinstyle" v="bevel"/>
<prop k="name" v="circle"/>
<prop k="offset" v="0,0"/>
<prop k="offset_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="offset_unit" v="MM"/>
<prop k="outline_color" v="35,35,35,255"/>
<prop k="outline_style" v="solid"/>
<prop k="outline_width" v="0"/>
<prop k="outline_width_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="outline_width_unit" v="MM"/>
<prop k="scale_method" v="diameter"/>
<prop k="size" v="2"/>
<prop k="size_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="size_unit" v="MM"/>
<prop k="vertical_anchor_point" v="1"/>
<data_defined_properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</data_defined_properties>
</layer>
</symbol>
</markerStyle>
<annotationFontProperties description="Cantarell,11,-1,5,50,0,0,0,0,0" style=""/>
<LayoutObject>
<dataDefinedProperties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</dataDefinedProperties>
<customproperties/>
</LayoutObject>
</ComposerMapGrid>
<AtlasMap atlasDriven="0" scalingMode="2" margin="0.10000000000000001"/>
<labelBlockingItems/>
</LayoutItem>
<LayoutItem referencePoint="0" followPreset="false" followPresetName="" groupUuid="" positionOnPage="98.7716,20.1872,mm" mapRotation="0" positionLock="false" background="true" visibility="1" excludeFromExports="0" mapFlags="1" id="" zValue="1" frameJoinStyle="miter" uuid="{8fec18d6-8ba0-47d6-914e-3daffe8a8633}" size="87,103,mm" itemRotation="0" outlineWidthM="0.3,mm" position="98.7716,20.1872,mm" drawCanvasItems="true" labelMargin="0,mm" templateUuid="{8fec18d6-8ba0-47d6-914e-3daffe8a8633}" blendMode="0" opacity="1" keepLayerSet="false" frame="false" type="65639">
<FrameColor blue="0" alpha="255" green="0" red="0"/>
<BackgroundColor blue="255" alpha="255" green="255" red="255"/>
<LayoutObject>
<dataDefinedProperties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</dataDefinedProperties>
<customproperties/>
</LayoutObject>
<Extent xmax="8.20606418507941449" ymin="44.89903639486862374" xmin="8.20202108826836884" ymax="44.9038230497138855"/>
<LayerSet/>
<ComposerMapGrid leftAnnotationPosition="1" name="Grille 1" topAnnotationDirection="0" unit="0" annotationFontColor="0,0,0,255" topAnnotationDisplay="0" rightAnnotationPosition="1" minimumIntervalWidth="50" annotationPrecision="3" gridFrameWidth="2" gridStyle="0" bottomAnnotationDirection="0" topFrameDivisions="0" show="0" leftFrameDivisions="0" annotationExpression="" position="3" bottomAnnotationPosition="1" offsetX="0" rightFrameDivisions="0" leftAnnotationDirection="0" frameFillColor1="255,255,255,255" bottomAnnotationDisplay="0" rightAnnotationDirection="0" annotationFormat="0" offsetY="0" gridFramePenColor="0,0,0,255" showAnnotation="0" topAnnotationPosition="1" uuid="{94630841-1b07-4bc2-9cf7-1dce50a01a3e}" frameFillColor2="0,0,0,255" intervalX="0" rightAnnotationDisplay="0" crossLength="3" intervalY="0" gridFrameStyle="0" leftAnnotationDisplay="0" blendMode="0" gridFramePenThickness="0.5" frameAnnotationDistance="1" gridFrameSideFlags="15" gridFrameMargin="0" maximumIntervalWidth="100" bottomFrameDivisions="0">
<lineStyle>
<symbol clip_to_extent="1" type="line" alpha="1" force_rhr="0" name="">
<layer enabled="1" class="SimpleLine" pass="0" locked="0">
<prop k="capstyle" v="square"/>
<prop k="customdash" v="5;2"/>
<prop k="customdash_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="customdash_unit" v="MM"/>
<prop k="draw_inside_polygon" v="0"/>
<prop k="joinstyle" v="bevel"/>
<prop k="line_color" v="0,0,0,255"/>
<prop k="line_style" v="solid"/>
<prop k="line_width" v="0"/>
<prop k="line_width_unit" v="MM"/>
<prop k="offset" v="0"/>
<prop k="offset_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="offset_unit" v="MM"/>
<prop k="ring_filter" v="0"/>
<prop k="use_custom_dash" v="0"/>
<prop k="width_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<data_defined_properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</data_defined_properties>
</layer>
</symbol>
</lineStyle>
<markerStyle>
<symbol clip_to_extent="1" type="marker" alpha="1" force_rhr="0" name="">
<layer enabled="1" class="SimpleMarker" pass="0" locked="0">
<prop k="angle" v="0"/>
<prop k="color" v="0,0,0,255"/>
<prop k="horizontal_anchor_point" v="1"/>
<prop k="joinstyle" v="bevel"/>
<prop k="name" v="circle"/>
<prop k="offset" v="0,0"/>
<prop k="offset_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="offset_unit" v="MM"/>
<prop k="outline_color" v="35,35,35,255"/>
<prop k="outline_style" v="solid"/>
<prop k="outline_width" v="0"/>
<prop k="outline_width_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="outline_width_unit" v="MM"/>
<prop k="scale_method" v="diameter"/>
<prop k="size" v="2"/>
<prop k="size_map_unit_scale" v="3x:0,0,0,0,0,0"/>
<prop k="size_unit" v="MM"/>
<prop k="vertical_anchor_point" v="1"/>
<data_defined_properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</data_defined_properties>
</layer>
</symbol>
</markerStyle>
<annotationFontProperties description="Cantarell,11,-1,5,50,0,0,0,0,0" style=""/>
<LayoutObject>
<dataDefinedProperties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</dataDefinedProperties>
<customproperties/>
</LayoutObject>
</ComposerMapGrid>
<AtlasMap atlasDriven="0" scalingMode="2" margin="0.10000000000000001"/>
<labelBlockingItems/>
</LayoutItem>
<customproperties/>
<Atlas coverageLayer="" hideCoverage="0" sortFeatures="0" enabled="0" filenamePattern="'output_'||@atlas_featurenumber" pageNameExpression="" filterFeatures="0"/>
</Layout>
</Layouts>
<Bookmarks/>
</qgis>