import GeoJSON from 'ol/format/GeoJSON'
import { bbox as bboxStrategy, tile as tileStrategy } from 'ol/loadingstrategy'
import VectorSource from 'ol/source/Vector'
import { createXYZ } from 'ol/tilegrid'
import * as MapviewerServices from '../tools/mapviewer-services'
import isEmpty from 'lodash/isEmpty'
/**
* @param {Object?} wfsOptions Option de chargement des données via un service wfs
* @param {String} wfsOptions.url Url du service wfs
* @param {string} wfsOptions.geometryName nom du champs geométrie du service (défaut geo)
* @param {Object} wfsOptions.params paramètre du service wfs
* @param {string} wfs.params.typename Nom de la couche du service WFS à exploiter
* @param {string} [wfsOptions.params.srsname=EPSG:3857] projection de retour des données
* @param {string} [wfsOptions.params.version=1.1.1] version du service WFS
* @param {string} wfsOptions.params.* autres paramètres du service WFS
* @param {string} [wfsOptions.projection=EPSG:3857] projection de la bbox pour interroger le service
* @param {string} [wfsOptions.geometryName=geometry] nom de la géométrie pour interroger la bbox
* @param {boolean} [wfsOptions.bboxStrategy=false] Utiliser une stratégie de chargement par bbox (true) ou par tuile (false). Par défaut false
* @param {boolean | Object} [wfsOptions.tileStrategy=false] Utiliser une stratégie de chargemen par tuile (false). Par défaut true
* @param {boolean | Object} [wfsOptions.tileStrategy.tileSize=256] Taille des tuiles pour la stratégie par tuile
* @param {string} wfsOptions.cqlFilter filtre cql
* @param {Object<string,(string|Array)>} wfsOptions.filters objet represant des filtres a générer (exemple {code_insee:[9420,8521]}). Ignoré si cqlFilter est défini
* @returns VectorSource | Null
*/
export function getWfsVectorSource (wfsOptions, otherSourceOptions = {}) {
if (!wfsOptions) {
return null
}
const options = {
projection: 'EPSG:3857', // format d'interrogation de la l'etendue des données
geometryName: 'geometry', // nom de la géométrie par défaut si rien n'est demandé
...wfsOptions,
params: {
version: '1.1.0', // 1.1.0 si rien n'est demandé
request: 'GetFeature', // GetFeature si rien n'est demandé
srsname: 'EPSG:3857', // format de retour des données
// projection: 'EPSG:3857', // format d'interrogation de la l'etendue des données
// geometryName: 'geometry', // nom de la géométrie par défaut si rien n'est demandé
...wfsOptions.params,
outputFormat: 'application/json',
},
}
if (isEmpty(options.geometryName)) {
throw new Error('paramètre options.wfs.geometryName manquant')
}
const wfsUrl = options.url + (options.url.indexOf('?') > -1 ? '&' : '?') +
Object.entries(options.params).map(([param, value]) => `${param}=${value}`).join('&')
// Gestion des filtres
let filters = options?.cqlFilter
? options.cqlFilter
: options.filters
? Object.entries(options.filters || {}).map(([property, value]) => {
if (Array.isArray(value)) {
// in
value = value.map(val => (typeof val === 'string' ? `'${val}'` : val)).join(',')
return `${property} in (${value})`
} else {
// Equal
value = typeof value === 'string' ? `'${value}'` : value
return `${property}=${value}`
}
}).join(' AND ')
: null
filters = filters !== null && !isEmpty(filters) ? ` AND ${filters}` : ''
// pour le wfs, si on a certaine options on passe par un loader personnalisé, sinon undefined donne le loader par défaut
function getWfsLoader () {
// actuellement prend en compte uniquement les headers
if (!options?.headers) {
return undefined
}
const headers = new Headers(options?.headers || {})
return async function (extent, resolution, projection, success, failure) {
const url = typeof this.getUrl() === 'function' ? this.getUrl()(extent, resolution, projection) : this.getUrl()
try {
const response = await fetch(url, { headers })
if (!response.ok) {
throw new Error(`Response status: ${response.status}`)
}
const json = await response.json()
// le wfs service renvoi du geojson, on a juste à utiliser le service afin de les convertir en features
const features = MapviewerServices.geoJsonToFeature(json, options.params.srsname, projection.getCode())
this.addFeatures(features)
success(features)
} catch (error) {
console.error('[datalayer-getWfsLoader]', error)
this.removeLoadedExtent(extent)
// si on est pas dans le scope on peut faire ceci:
failure()
}
}
}
return new VectorSource({
format: new GeoJSON(),
url: function (extent, resolution, projection) {
const dataProjection = options.projection
const extentTransformed = MapviewerServices.convertExtent(extent, projection.getCode(), dataProjection)
return `${wfsUrl}&cql_filter=BBOX(${options.geometryName},${extentTransformed.join(',')},'${dataProjection}')${filters}`
/* 'https://wxs.ign.fr/parcellaire/geoportail/wfs?SERVICE=WFS&' +
'VERSION=2.0.0&request=GetFeature&typename=CADASTRALPARCELS.PARCELLAIRE_EXPRESS:parcelle&' +
'outputFormat=application/json&srsname=EPSG:3857&' +
'bbox=' +
extent.join(',') +
',EPSG:3857' */
},
strategy: options?.bboxStrategy ? bboxStrategy : tileStrategy(createXYZ({ tileSize: options?.tileStrategy?.tileSize || 256 })),
loader: getWfsLoader(),
...otherSourceOptions,
})
}