import { L } from "../../leaflet/index.js";
import { ServiceLayer } from "../ServiceLayer/ServiceLayer.js";
import { WFSLayerSchema } from "./WFSLayer.schema.js";
import { GeoJSONLayerProxy } from "../GeoJSONLayer/GeoJSONLayer.proxy.js";
import { GeoJSONLayerProxyLeaflet } from "../GeoJSONLayer/GeoJSONLayer.proxy.leaflet.js";
import { NETWORK_LOADING_ERROR } from "../../utils/messageTemplates.js";
/**
* WFS Layer Implementation
*
* @author rhess <robin.hess@awi.de>
* @author sjaswal <shahzeib.jaswal@awi.de>
*
* @memberof vef.map.layer
*/
class WFSLayer extends ServiceLayer {
/**
* Set all properties for the layer based on the options object.
*
* @param {object} config
* @param {object} cache
* @param {string} id
*/
constructor(config, cache, id) {
// calling parent constructor
super(config, cache, id);
// init default values and define getters and setters
this.setSchema_(WFSLayerSchema.getSchema());
// ignore availableCrs and always use the default value
if ("availableCrs" in this.config) delete this.config.availableCrs;
if ("availableCrs" in this.cache) delete this.cache.availableCrs;
// currently active geoJSON response
this.geoJSON = null;
// current url and requests for the data
this.activeUrl_ = null;
this.activeRequest_ = null;
// framework specific map implementations
this.layerProxies_.leaflet = GeoJSONLayerProxyLeaflet;
this.activeLayer_ = new GeoJSONLayerProxy();
this.previousActiveLayer_ = null;
}
/**
* Creates URL for getting wfs layer
*/
createUrl_() {
const url = this.serviceUrl;
const software = this.serviceSoftware.toLowerCase();
let params = {
service: this.type,
version: (software == "esri") ? '2.0.0' : this.serviceVersion,
request: 'GetFeature',
typeName: this.getRequestName(),
outputFormat: (software == "esri") ? 'GEOJSON' : 'application/json',
SrsName: 'EPSG:4326'
};
// apply the filter
if (this.filter) {
const filterParams = this.createFilterParams_(this.filter);
Object.assign(params, filterParams, this.queryParams);
}
return url + L.Util.getParamString(params);
}
/**
* fix invalid GeoJson as a workaround for invalid data from the WFS
*
* @private
*/
fixGeoJson_(text) {
// Workaround for ESRI LineString syntax in 10.7
// Also remove features with coordinates as nan
// by replacing nan with null and then removing them in the next step
// ToDo: Remove after updating to 10.8
if (this.serviceSoftware.toLowerCase() == "esri") {
text = text.replaceAll('"LineString"', '"MultiLineString"');
text = text.replace(/\bnan\b/g, null);
let parsedJson = JSON.parse(text);
for (let i = parsedJson.features.length - 1; i >= 0; i--) {
if (parsedJson.features[i].geometry.coordinates.includes(null)) {
parsedJson.features.splice(i, 1);
}
}
return parsedJson;
} else return JSON.parse(text);
}
/**
* create a map layer based on the service options
*
* @param {string} type e.g. "leaflet"
* @private
*/
createMapLayer_(type, options) {
const proxy = super.createMapLayer_(type, this);
if (proxy) {
proxy.on("layerproxy_click", (e) => this.fire("layer_click", e));
proxy.on("layerproxy_mouseover", (e) => this.fire("layer_mouseover", e));
proxy.on("layerproxy_mouseout", (e) => this.fire("layer_mouseout", e));
}
return proxy;
}
/**
* Helper method to update the mapLayer according to
* the current state. Might be necessary after switching the map type
*
* @private
*/
updateMapLayer_(type) {
const url = this.createUrl_();
if (this.activeUrl_ != url) {
this.loadGeojson_(url);
} else if (this.activeLayer_ !== this.previousActiveLayer_) {
this.activeLayer_.setData(this.geoJSON);
}
this.previousActiveLayer_ = this.activeLayer_;
}
/**
* load the geojson content of the layer and insert it
*
* @private
*/
loadGeojson_(url) {
if (this.activeRequest_) {
this.activeRequest_.abort();
this.activeRequest_ = null;
this.fire("layer_loaded");
}
this.fire("layer_loading");
const xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = () => {
if (xhttp.readyState == 4) {
if (xhttp.status == 200) {
const json = this.fixGeoJson_(xhttp.responseText)
this.geoJSON = json;
this.activeUrl_ = url;
this.activeLayer_.setData(this.geoJSON);
} else {
this.addMessage(NETWORK_LOADING_ERROR);
}
this.activeRequest_ = null;
this.fire("layer_loaded");
}
};
xhttp.open("GET", url, true);
xhttp.send();
this.activeRequest_ = xhttp;
}
/**
* Apply a filter on the layer. Uses WMS filter string
*/
applyFilter() {
// only apply filter if the layer is active
if (this.active) {
const url = this.createUrl_();
if (this.activeUrl_ != url) {
this.loadGeojson_(url);
}
}
}
/**
* Get Info of the last clicked features
* @returns {Promise} feature info
*/
getFeatureInfo() {
return new Promise(resolve => {
resolve(this.activeLayer_.getClickedFeatures());
});
}
/**
* Set the transparency between 0 and 1
* @override
* @param {number} opacity
*/
setOpacity(opacity) {
this.opacity = opacity;
this.activeLayer_.updateStyle();
}
/**
* Get the bounding box as an object
*
* @returns {object} { min: {x, y}, max: {x, y}, crs: "CRS:84" }
*/
getBounds() {
const bounds = this.activeLayer_.getBounds();
return (bounds) ? bounds : super.getBounds();
}
}
export { WFSLayer };