Query on attribute properties

To find data based on one or more of its attribute values, we can use the query parameter of the search objects. This parameter takes a filter expression as its argument, based on the OGC filter expressions from the OWSLib library.

Available attribute fields

The list of available attributes (or: fields) depends on the dataset you want to query and can be retrieved with the get_fields method. This results in a dictionary with for each of the fields a new dictionary with more information about the field.

For each field, the following information is available:

name

The name of the field.

Example: 'methode'

definition

The definition of the field.

Example: 'De methode waarmee de boring uitgevoerd werd. Heeft als waarde 'onbekend' indien de methode niet gekend is.'

cost

The resource cost (in time) to request this field as part of the return fields. Is either ‘1’ (no additional cost) or ‘10’ (cost of an implicit XML download per result feature).

Example: 1

notnull

Whether the field is mandatory (True) or optional (False).

Example: True

query

Whether the field can be used in an attribute query.

Example: True

type

The datatype of the values of this field.

Example: 'string'

values

(Optional) In case the field has a list of possible values, they are listed here as a dictionary mapping the values to a definition (if available).

Example: {'Aa': 'Formatie van Aalter', 'AaBe': 'Lid van Beernem (Formatie van Aalter)', 'AaOe': 'Lid van Oedelem (Formatie van Aalter)'}

Example

To get the name and definition of all attributes that are available for use in a search query for boreholes (query is True), you’d use:

from pydov.search.boring import BoringSearch
boringsearch = BoringSearch()

fields = boringsearch.get_fields()
for f in fields:
    if fields[f]['query']:
        print(fields[f]['name'], '-', fields[f]['definition'])

Which would result in:

generated_id - Oplopend uniek volgnummer ter identificatie (intern en niet-stabiel).
id - Volgnummer ter identificatie (intern en niet-stabiel).
boornummer - Het boornummer (ook gekend als proefnummer) van de boring.
pkey_boring - Permanente URL die verwijst naar de gegevens van de boring op de website. Voeg '.xml' toe om een XML voorstelling van deze gegevens te verkrijgen.
rapport - URL die verwijst naar het rapport van de boring in PDF formaat.
diepte_boring_tot - Maximumdiepte van de boring ten opzichte van het aanvangspeil, in meter.
datum_aanvang - Datum waarop men de boring gestart is.
namen - Alternatieve benamingen voor de boring.
putnummer - Het GW_ID (ook gekend als putnummer) van de put waaraan de boring gekoppeld is. Wanneer dit veld leeg is is de boring niet gekoppeld aan een put.
x - De x-coördinaat van de boring in het Lambert72 coördinaatsysteem (in meter, EPSG:31370).
y - De y-coördinaat van de boring in het Lambert72 coördinaatsysteem (in meter, EPSG:31370).
start_boring_mtaw - De hoogte van het aanvangspeil van de boring in het TAW stelsel (in meter).
gemeente - De gemeente waarin de boring gelegen is.
uitvoerder - De organisatie die de boring uitvoerde. Heeft als waarde 'onbekend' indien de uitvoerder niet gekend is.
doel - Het doel van de boring.
methode - De methode waarmee de boring uitgevoerd werd. Heeft als waarde 'onbekend' indien de methode niet gekend is.
erkenning - In het kader van welke discipline de boring werd uitgevoerd. Deze codelijst bevat de verschillende disciplines van erkende boorbedrijven uit artikel 6, 7°, a) van VLAREL.
opdrachtgever - De organisatie die de opdracht gaf om de boring uit te voeren.  Heeft als waarde 'onbekend' indien de opdrachtgever niet gekend is.
informele_stratigrafie - Geeft aan of er aan de boring minstens één interpretatie van het type 'informele stratigrafie' gekoppeld is.
formele_stratigrafie - Geeft aan of er aan de boring minstens één interpretatie van het type 'formele stratigrafie' gekoppeld is.
lithologische_beschrijving - Geeft aan of er aan de boring minstens één interpretatie van het type 'lithologische beschrijving' gekoppeld is.
gecodeerde_lithologie - Geeft aan of er aan de boring minstens één interpretatie van het type 'gecodeerde lithologie' gekoppeld is.
hydrogeologische_stratigrafie - Geeft aan of er aan de boring minstens één interpretatie van het type 'hydrogeologische stratigrafie' gekoppeld is.
quartaire_stratigrafie - Geeft aan of er aan de boring minstens één interpretatie van het type 'quartaire stratigrafie' gekoppeld is.
geotechnische_codering - Geeft aan of er aan de boring minstens één interpretatie van het type 'geotechnische codering' gekoppeld is.
informele_hydrostratigrafie - Geeft aan of er aan de boring minstens één interpretatie van het type 'informele hydrostratigrafie' gekoppeld is.
doorheen_quartair - Geeft aan of de boring dieper ging dan de grens van het Quartair en het Neogeen/Paleogeen (Tertiair). Dit veld is enkel ingevuld indien er minstens één interpretatie van het type 'formele stratigrafie' gekoppeld is aan de boring én het Quartair geïnterpreteerd werd.
dikte_quartair - Dit veld geeft de dikte weer van het Quartair (in meter). Dit is een getal met twee decimalen, soms voorafgegaan door < of >= (bv. >= 10.00).
tertiair_onder_quartair - Indien 'doorheen_quartair' true is bevat dit veld de afkorting van de eerste lithostratigrafische eenheid van het Neogeen/Paleogeen (Tertiair) die voorkomt onder het Quartair.
opdrachten - De DOV-opdracht(en) waaraan de boring gekoppeld is.

Other information about each field includes its datatype (type), cost to use it as return field (cost) or whether it is mandatory (notnull):

fields['diepte_boring_tot']
{'cost': 1,
 'definition': 'Maximumdiepte van de boring ten opzichte van het aanvangspeil, in meter.',
 'name': 'diepte_boring_tot',
 'notnull': False,
 'type': 'float'}

Some fields additionally have a list of possible values (values):

fields['methode']['values'].keys()
['avegaarboring',
 'droge boring',
 'edelmanboring',
 'geen boring',
 'gestoken boring',
 'graafmachine',
 'handboring',
 'kernboring',
 'lansen',
 'lepelboring',
 'luchthamer',
 'luchthevelboren of air-lift boren',
 'meerdere technieken',
 'omgek. spoelboring',
 'onbekend',
 'pulsboring',
 'ramguts',
 'ramkernboring',
 'rollerbit',
 'slagboring',
 'spade',
 'spiraalboring',
 'spoelboring',
 'steenboring',
 'trilboring',
 'voorput',
 'zuigboring']

Sometimes the definition can be used as a (human readable) label for the (machine readable) code in the dataframe. This allows creating an extra column with the mapped labels:

from pydov.search.interpretaties import FormeleStratigrafieSearch
itp = FormeleStratigrafieSearch()

fields['lid1']
# {'cost': 10,
#  'definition': 'eerste eenheid van de laag formele stratigrafie',
#  'name': 'lid1',
#  'notnull': False,
#  'query': False,
#  'type': 'string',
#  'values': {'Aa': 'Formatie van Aalter',
#   'AaBe': 'Lid van Beernem (Formatie van Aalter)',
#   'AaOe': 'Lid van Oedelem (Formatie van Aalter)',
#   'Bb': 'Formatie van Bolderberg',
#   'BbGe': 'Lid van Genk (Formatie van Bolderberg)',
#   'BbHo': 'Lid van Houthalen (Formatie van Bolderberg)',
#   'BbOp': 'Lid van Opitter (Formatie van Bolderberg)',
#   'Bc': 'Formatie van Berchem',
#   ..}
# }

df['lid1_label'] = df['lid1'].map(fields['lid1']['values'])
df['lid2_label'] = df['lid2'].map(fields['lid2']['values'])

print(df[['diepte_laag_van', 'diepte_laag_tot', 'lid1',
     'lid1_label', 'relatie_lid1_lid2', 'lid2', 'lid2_label']].to_string())
#    diepte_laag_van  diepte_laag_tot lid1           lid1_label relatie_lid1_lid2 lid2            lid2_label
# 0              0.0             3.00    Q  Quartaire afzetting                 T    Q   Quartaire afzetting
# 1              3.0            14.05    U             Onbekend                 T   Bc  Formatie van Berchem

Using OGC filter expressions

An attribute query consists of an OGC filter predicate, a query field (propertyname) and a literal value (literal). pydov uses the OGC filter predicates from the OWSLib library, defined in the owslib.fes2 package.

Note that the literal value is always expressed as a string, even if the field that is being searched is of a numeric, date or boolean type (dates should be expressed in the ‘YYYY-mm-dd’ format).

The following OGC filters are relevant for string, numeric, date or boolean attributes:

PropertyIsEqualTo

Search for exact matches.

Example: PropertyIsEqualTo(propertyname='methode', literal='ramkernboring')

Example: PropertyIsEqualTo(propertyname='diepte_boring_tot', literal='10')

Example: PropertyIsEqualTo(propertyname='datum_aanvang', literal='2014-01-01')

Example: PropertyIsEqualTo(propertyname='quartaire_stratigrafie', literal='True')

PropertyIsNotEqualTo

Search for values different from a given literal. Does not include empty values.

Example: PropertyIsNotEqualTo(propertyname='methode', literal='onbekend')

PropertyIsNull

Search for empty values. This filter only requires a propertyname.

Example: PropertyIsNull(propertyname='gemeente')

The following OGC filters are relevant for string attributes:

PropertyIsLike

Search for fuzzy matches. You can use the ‘_’ wildcard to represent a single character and the ‘%’ wildcard to represent multiple characters.

Example: PropertyIsLike(propertyname='methode', literal='lucht%')

The following OGC filters are relevant for numeric or date attributes:

PropertyIsLessThan

Search for values strictly less than (or: before) the given literal.

Example: PropertyIsLessThan(propertyname='diepte_boring_tot', literal='10')

PropertyIsLessThanOrEqualTo

Search for values less than (or: before) or equal to the given literal.

Example: PropertyIsLessThanOrEqualTo(propertyname='datum_aanvang', literal='2014-12-31')

PropertyIsGreaterThan

Search for values strictly greater than (or: after) the given literal.

Example: PropertyIsGreaterThan(propertyname='diepte_boring_tot', literal='10')

PropertyIsGreaterThanOrEqualTo

Search for values greater than (or: after) or equal to the given literal.

Example: PropertyIsGreaterThanOrEqualTo(propertyname='datum_aanvang', literal='2015-01-01')

PropertyIsBetween

Search for values greater than (or: after) or equal to the lower boundary and less than (or: before) or equal to the upper boundary. This filter requires two literal values as the lower and upper boundaries. Boundaries are inclusive.

Example: PropertyIsBetween(propertyname='diepte_boring_tot', lower='20', upper='50')

Example: PropertyIsBetween(propertyname='datum_aanvang', lower='2014-01-01', upper='2014-12-31')

Logically combining filter expressions

You can combine different OGC filter expressions in one query by using the And, Or and Not predicates from the owslib.fes2 package.

Each of And, Or and Not take a list as argument, in the case of And and Or the list should consist of at least two items. Each item can be a simple OGC filter expression or another And, Or or Not expression, so you can nest different levels of filter expressions.

And

Return results that match all listed filters.

Example: And([PropertyIsLessThan(propertyname='diepte_boring_tot', literal='10'), PropertyIsGreaterThan(propertyname='datum_aanvang', literal='2014-12-31')])

Or

Return results that match one or more listed filters.

Example: Or([PropertyIsLessThan(propertyname='diepte_boring_tot', literal='10'), PropertyIsGreaterThan(propertyname='datum_aanvang', literal='2014-12-31')])

Not

Return results that do not match any of the listed filters.

Example: Not([PropertyIsLike(propertyname='methode', literal='lucht%')])

Example

An example of an advanced query using a nested combination of logical filter expressions:

from owslib.fes2 import And, Or, Not
from owslib.fes2 import PropertyIsEqualTo, PropertyIsLike, PropertyIsNull

from pydov.search.boring import BoringSearch
boringsearch = BoringSearch()

query = And([PropertyIsEqualTo(propertyname='gemeente',
                               literal='Antwerpen'),
             Or([Not([PropertyIsNull(propertyname='putnummer')]),
                PropertyIsLike(propertyname='doel',
                               literal='Grondwater%'),
                PropertyIsEqualTo(propertyname='erkenning',
                                  literal='2. Andere grondwaterwinningen')]
               )]
           )
df = boring.search(query=query)

Query using lists

pydov extends the default OGC filter expressions described above with a new expression PropertyInList that allows you to use lists (of strings) in search queries.

The PropertyInList internally translates to a PropertyIsEqualTo and is relevant for string, numeric, date or boolean attributes:

PropertyInList

Search for one of a list of exact matches.

Internally this is translated to Or([PropertyIsEqualTo(), PropertyIsEqualTo(), ...]).

Example: PropertyInList(propertyname='methode', list=['ramkernboring', 'spoelboring', 'spade'])

Join different searches

The Join expression allows you to join multiple searches together. This allows combining results from different datasets to get the results you’re looking for.

Join

Join searches together using a common attribute. Instead of a propertyname and a literal (or a list of literals), this expression takes a Pandas dataframe and one or two join columns.

You can either specify a single column (in the on parameter) which should exist both in the provided dataframe as in the datatype being searched. Alternatively, you can specify both the on column (which should exist in the queried datatype) as well as the using column (which should exists in the provided dataframe).

Example: Join(df_boringen, 'pkey_boring')

Example: Join(df_boringen, on='pkey_boring')

Example: Join(df_boringen, on='pkey_boring', using='boringfiche')

The following example returns all the lithological descriptions of boreholes that are at least 20 meters deep (note that this is different from ‘lithological descriptions with a depth of at least 20m’):

from pydov.util.query import Join

from pydov.search.boring import BoringSearch
from pydov.search.interpretaties import LithologischeBeschrijvingenSearch

bs = BoringSearch()
ls = LithologischeBeschrijvingenSearch()

boringen = bs.search(query=PropertyIsGreaterThan('diepte_tot_m', '20'),
                     return_fields=('pkey_boring',))

lithologische_beschrijvingen = ls.search(query=Join(boringen, 'pkey_boring'))

Join expressions can be logically combined with other filter expressions, for example to further restrict the resultset:

from owslib.fes2 import And
from owslib.fes2 import PropertyIsEqualTo

from pydov.util.query import Join

from pydov.search.boring import BoringSearch
from pydov.search.interpretaties import LithologischeBeschrijvingenSearch

bs = BoringSearch()
ls = LithologischeBeschrijvingenSearch()

boringen = bs.search(query=PropertyIsGreaterThan('diepte_tot_m', '20'),
                     return_fields=('pkey_boring',))

lithologische_beschrijvingen = ls.search(query=And([Join(boringen, 'pkey_boring'),
                                                    PropertyIsEqualTo('betrouwbaarheid_interpretatie', 'goed')]))

The following example gets borehole information based on a search for groundwater filters:

from pydov.util.query import Join

from pydov.search.boring import BoringSearch
from pydov.search.grondwaterfilter import GrondwaterFilterSearch

fs = GrondwaterFilterSearch()
bs = BoringSearch()

filters = fs.search(location=WithinDistance(Point(96540, 186900), 10, 'km'),
                    return_fields=('pkey_filter', 'boringfiche'))

print(filters.head())
#                                              pkey_filter                                            boringfiche
# 0  https://www.dov.vlaanderen.be/data/filter/1989-000092  https://www.dov.vlaanderen.be/data/boring/1989-021283
# 1  https://www.dov.vlaanderen.be/data/filter/2003-007671                                                    NaN
# 2  https://www.dov.vlaanderen.be/data/filter/1989-001026  https://www.dov.vlaanderen.be/data/boring/1989-065942

boringen = bs.search(query=Join(filters, on='pkey_boring', using='boringfiche'))

print(boringen.head())
#                                              pkey_boring      ...     boormethode
# 0  https://www.dov.vlaanderen.be/data/boring/1989-021283      ...        onbekend
# 1  https://www.dov.vlaanderen.be/data/boring/1989-065942      ...        onbekend