From ccb73fbe98ccc2f54197372ff8f047b535a77bc8 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 27 Sep 2023 15:20:16 +0200 Subject: Get Observations from ArtDataBanken. --- gis-observation-map | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 167 insertions(+), 1 deletion(-) diff --git a/gis-observation-map b/gis-observation-map index 4fe06cf..551a8af 100755 --- a/gis-observation-map +++ b/gis-observation-map @@ -322,6 +322,78 @@ artDataBankenHeaders = { # https://github.com/biodiversitydata-se/SOS/blob/master/Docs/Authentication.md 'Ocp-Apim-Subscription-Key': config['ArtDataBanken']['Subscription-Key'] } + +def getObservations(taxonLists, taxonRedlistCategories, data): + # https://api-portal.artdatabanken.se/docs/services/sos-api-v1/operations/Exports_DownloadGeoJson + headers = artDataBankenHeaders.copy() + headers['Content-Type'] = 'application/json' + headers['Authorization'] = config['ArtDataBanken']['Authorization'] + params = { + 'cultureCode': 'sv-SE', + 'flat': 'true', + 'excludeNullValues': 'true', + 'gzip': 'false', + #'sensitiveObservations': 'true', + 'validateSearchFilter': 'true', + } + resp = requests.post( + artDataBankenURL + '/species-observation-system/v1/Exports/Download/GeoJson', + headers=headers, + params=params, + data=json.dumps(data) + ) + + resp.raise_for_status() + obs = resp.json() + + # https://www.rfc-editor.org/rfc/rfc7946 + if obs is None or type(obs) != dict or 'type' not in obs.keys(): + raise Exception('invalid GeoJSON output') + if 'crs' not in obs.keys(): + print('WARN: GeoJSON output lacks CRS', file=sys.stderr) + + if obs['type'] == 'FeatureCollection' and 'features' in obs.keys() and type(obs['features']) == list: + for feat in obs['features']: + if (type(feat) != dict or 'type' not in feat.keys() or feat['type'] != 'Feature' + or 'properties' not in feat.keys() or type(feat['properties']) != dict): + print('WARN: Invalid feature in GeoJSON output', file=sys.stderr) + continue + properties = feat['properties'] + if 'DyntaxaTaxonId' not in properties.keys() or properties['DyntaxaTaxonId'] is None: + print('WARN: Feature lacks taxon ID', file=sys.stderr) + continue + + tid = properties['DyntaxaTaxonId'] + for k, taxonList in taxonLists.items(): + v = (tid in taxonList) + if k in properties.keys() and properties[k] != v: + print(f'WARN: #{tid} {k}: {properties[k]} → {v}', file=sys.stderr) + properties[k] = v + + if tid in taxonRedlistCategories.keys(): + k = 'RedlistCategory' + v = taxonRedlistCategories[tid] + if k in properties.keys() and properties[k] != v: + print(f'WARN: #{tid} {k}: {properties[k]} → {v}', file=sys.stderr) + properties[k] = v + + # TODO generate GPKG instead and reproject to EPSG:3006 + path = projectHome.joinpath('fynd.geojson') + with path.open(mode='w') as fp: + json.dump(obs, fp, indent=2, ensure_ascii=False) + + if projectInstance is not None: + layer = QgsVectorLayer(path.as_posix() + '''|subset="Kingdom" IN ('Fungi', 'Plantae') AND "IsPositiveObservation" AND "IsNaturalOccurrence" AND "CoordinateUncertaintyInMeters" <= 250''', path.stem, 'ogr') + if not layer.isValid(): + raise Exception(f'ERROR: {path}: failed to load in QGIS') + if 'QGIS' in config.keys() and 'style' in config['QGIS']: + style = Path(config['QGIS']['style']).expanduser() + layer.loadNamedStyle(style.as_posix()) + layer.setReadOnly(True) + layer.setFlags(QgsMapLayer.Identifiable | QgsMapLayer.Searchable) + projectInstance.addMapLayer(layer) + layerTreeRoot.findLayer(layer.id()).setItemVisibilityChecked(True) + def getTaxonLists(): resp = requests.get( artDataBankenURL + '/species-observation-system/v1/TaxonLists?cultureCode=en-US', @@ -442,7 +514,101 @@ geograficsFilter = { 'considerDisturbanceRadius': True } taxonLists, taxonRedlistCategories = getTaxonLists() -exit() + +# https://github.com/biodiversitydata-se/SOS/blob/master/Docs/SearchFilter.md +searchFilter = { + 'geographics': geograficsFilter, + 'determinationFilter': 'NoFilter', + 'notRecoveredFilter': 'NoFilter', + 'occurrenceStatus': 'BothPresentAndAbsent', + 'verificationStatus': 'BothVerifiedAndNotVerified', + 'output': { + 'fields': [ + # https://github.com/biodiversitydata-se/SOS/blob/master/Docs/SearchFilter.md#fields + # https://github.com/biodiversitydata-se/SOS/blob/master/Docs/Observation.md + 'dataProviderId', + 'datasetName', + #'basisOfRecord', + 'rightsHolder', + + 'modified', + 'sensitive', + 'measurementOrFacts', + 'projects', + + 'occurrence.occurrenceId', + 'occurrence.occurrenceRemarks', + 'occurrence.recordedBy', + 'occurrence.reportedBy', + 'occurrence.reportedDate', + 'occurrence.occurrenceStatus', + 'occurrence.activity', + 'occurrence.behavior', + 'occurrence.biotope', + 'occurrence.biotopeDescription', + 'occurrence.lifeStage', + 'occurrence.reproductiveCondition', + 'occurrence.sex', + 'occurrence.associatedMedia', + 'occurrence.associatedReferences', + 'occurrence.individualCount', + 'occurrence.organismQuantityInt', + 'occurrence.organismQuantityUnit', + 'occurrence.sensitivityCategory', + 'occurrence.isNaturalOccurrence', + 'occurrence.isNeverFoundObservation', + 'occurrence.isNotRediscoveredObservation', + 'occurrence.isPositiveObservation', + 'occurrence.substrate.description', + 'occurrence.substrate.name', + 'occurrence.length', + 'occurrence.weight', + 'occurrence.url', + + 'event.startDate', + 'event.endDate', + 'event.habitat', + 'event.eventRemarks', + 'event.discoveryMethod', + 'event.measurementOrFacts', + 'event.fieldNotes', + + 'identification.verified', + 'identification.uncertainIdentification', + 'identification.verificationStatus', + 'identification.confirmedBy', + 'identification.confirmedDate', + 'identification.identifiedBy', + 'identification.dateIdentified', + 'identification.verifiedBy', + 'identification.determinationMethod', + 'identification.identificationRemarks', + + 'location.locality', + 'location.county', + 'location.municipality', + 'location.coordinateUncertaintyInMeters', + + 'taxon.id', + 'taxon.scientificName', + 'taxon.scientificNameAuthorship', + 'taxon.vernacularName', + 'taxon.genus', + 'taxon.family', + 'taxon.order', + 'taxon.class', + 'taxon.phylum', + 'taxon.kingdom', + + 'taxon.attributes.taxonCategory', + 'taxon.attributes.organismGroup', + + 'taxon.attributes.redlistCategory', + ] + } +} + +getObservations(taxonLists, taxonRedlistCategories, searchFilter) topo_maps = ['Topografi 10', 'Topografi 50', 'Topografi 100'] if projectInstance is not None: -- cgit v1.2.3