import { CsvLayer } from "../../layer/CsvLayer/CsvLayer.js";
import { GeoJSONLayer } from "../../layer/GeoJSONLayer/GeoJSONLayer.js";
import { Layer } from "../../layer/Layer/Layer.js";
import { Folder } from "../../layer/Folder/Folder.js";
import { createLayerTreeStructure } from "../utils.js";
import { OgcBinder } from "./OgcBinder.js";
/**
* load file based layers (gejson, csv)
* @memberof vef.map.importer.binder
*/
class FileBinder {
/**
* Creates layers from a QGis project
*
* @param {string} text xml text
*
* @returns {Promise} {structure, layers}
*/
static loadQGisProject(text) {
const structure = {};
const layers = {}
function getLayerCapabilities() {
return new Promise(resolve => {
const promises = [];
const services = { "wms": {}, "wfs": {} };
for (let id in layers) {
if (layers[id] instanceof Layer) continue;
if (!["wms", "wfs"].includes(layers[id].type)) {
layers[id] = new Folder({ title: layers[id].title });
continue;
}
if (!(layers[id].url in services[layers[id].type])) {
services[layers[id].type][layers[id].url] = "loading";
const promise = OgcBinder.getCapabilities(null, layers[id].url, layers[id].type);
promise.then(result => { services[layers[id].type][layers[id].url] = result; });
promises.push(promise);
}
}
Promise.all(promises).then(data => {
for (let id1 in layers) {
if (layers[id1] instanceof Layer) continue;
const service = services[layers[id1].type][layers[id1].url];
for (let index = 0; index < service.length; ++index) {
for (let id2 in service[index].layers) {
const layer = service[index].layers[id2];
const name = layers[id1].name.toString().split(",")[0].trim()
if (name == layer.name) {
layer.uniqueId = id1;
layer.name = layers[id1].name;
layer.title = layers[id1].title;
layer.active = layers[id1].active;
if (layers[id1].styles) layer.queryParams.styles = layers[id1].styles;
layers[id1] = layer;
break;
}
}
}
if ((!layers[id1] instanceof Layer)) layers[id1] = new Folder(layers[id1]);
}
resolve()
});
})
}
function addLayer(layer, parent) {
if (!(parent in structure)) structure[parent] = [];
structure[parent].push(layer.uniqueId);
layers[layer.uniqueId] = layer;
}
function parseLayerTreeGroup(group, parent) {
const nodeName = group.nodeName.toLowerCase();
switch (nodeName) {
case "layer-tree-group":
const folder = new Folder({
title: group.getAttribute("name"),
active: (group.getAttribute("checked") == "Qt::Checked") ? true : false
});
addLayer(folder, parent);
for (let i = 0; i < group.childNodes.length; ++i) {
parseLayerTreeGroup(group.childNodes[i], folder.uniqueId);
}
break;
case "layer-tree-layer":
const params = group.getAttribute("source").split("&").reduce((accumulator, entry) => {
const parts = entry.split("=");
if (parts[0] in accumulator) {
accumulator[parts[0]] += (parts.length > 1) ? ("," + parts[1]) : "";
} else {
accumulator[parts[0]] = (parts.length > 1) ? parts[1] : "";
}
return accumulator;
}, {});
const layer = {
uniqueId: group.getAttribute("id"),
type: group.getAttribute("providerKey").toLowerCase(),
title: group.getAttribute("name"),
active: (group.getAttribute("checked") == "Qt::Checked") ? true : false,
name: params.layers,
url: params.url,
styles: params.styles
};
if (layer) addLayer(layer, parent);
break;
}
}
return new Promise(resolve => {
try {
const parser = new DOMParser();
const xml = parser.parseFromString(text, "text/xml");
const layerTree = xml.querySelector("qgis > layer-tree-group");
for (let i = 0; i < layerTree.childNodes.length; ++i) {
parseLayerTreeGroup(layerTree.childNodes[i], "#");
}
} catch (error) {
console.warn(error);
}
getLayerCapabilities().then(() => {
resolve({ structure: structure, layers: layers });
});
})
}
/**
* Creates a Layer from a GeoJson source that is
* already parsed into a JavaScript object.
*
* @param {object | string} json geojson object
* @param {string} title layer title (optional)
*
* @returns {object} {structure, layers}
*/
static loadGeoJsonLayer(json, title) {
json = (typeof json == "string") ? JSON.parse(json) : json;
if (json.features) {
const layers = [];
for (let i = 0; i < json.features.length; ++i) {
const feature = json.features[i];
layers.push(new GeoJSONLayer({
title: (feature && feature.properties && feature.properties.title) ? feature.properties.title : (title + " [" + i + "]"),
geoJSON: feature,
active: true
}));
}
return createLayerTreeStructure(layers, title);
} else {
return createLayerTreeStructure([new GeoJSONLayer({
title: (json && json.properties && json.properties.title) ? json.properties.title : title,
geoJSON: json,
active: true
})]);
}
}
/**
* Creates a GeoJsonLayer from a DShip JSOn export
*
* @param {object | string} json geojson object
* @param {string} title layer title (optional)
*
* @returns {object} {structure, layers}
*/
static loadDshipLayer(json, title) {
json = (typeof json == "string") ? JSON.parse(json) : json;
const GeoJson = {
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: []
}
};
let iLat = -1;
let iLon = -1;
let iTime = -1;
for (let i = 0; i < json.header.columns.length; ++i) {
const col = json.header.columns[i];
if (col.shortname.toLowerCase().includes("lat")) iLat = i;
if (col.shortname.toLowerCase().includes("lon")) iLon = i;
if (col.shortname.toLowerCase().includes("time")) iTime = i;
}
if ((iLat < 0) || (iLon < 0)) return null;
for (let i = 0; i < json.data.length; ++i) {
const data = json.data[i];
if ((i == 0) && (iTime >= 0)) GeoJson.properties["Start Date"] = data[iTime];
if ((i == (json.data.length - 1)) && (iTime >= 0)) GeoJson.properties["End Date"] = data[iTime];
GeoJson.geometry.coordinates.push([data[iLon], data[iLat]]);
}
return FileBinder.loadGeoJsonLayer(GeoJson, title);
}
/**
* @param {File | String} file file or URL
* @returns {Promise}
*/
static getFileContents(file) {
return new Promise(resolve => {
if (file instanceof File) {
const reader = new FileReader();
reader.onload = e => resolve(e.target.result);
reader.readAsText(file);
} else if (typeof file == "string") {
fetch(file).then(res => res.text()).then(text => resolve(text));
} else {
resolve(null);
}
})
}
/**
* Creates a Layer from csv or geojson.
* Automatically tries to set the type based on the filename
*
* @param {File | string} file File object or a path to a file
* @param {object} options
* @param {string} mode geojson or csv
*
* @returns {MeasurementsLayer | MeasurementsLayer[]} layers
*/
static loadFileLayer(file, options, mode) {
return new Promise(resolve => {
const name = (typeof file == "string") ? file : file.name;
mode = mode || FileBinder.guessFileType(name);
switch (mode) {
case "csv":
CsvLayer.createLayers(file, options).then(data => resolve(data));
break;
case "geojson":
FileBinder.getFileContents(file).then(text => {
resolve(FileBinder.loadGeoJsonLayer(text, name))
});
break;
case "qgis":
FileBinder.getFileContents(file).then(text => {
FileBinder.loadQGisProject(text).then(layers => resolve(layers))
});
break;
case "dship":
FileBinder.getFileContents(file).then(text => {
resolve(FileBinder.loadDshipLayer(text, name));
});
break;
default:
resolve(null);
break;
}
});
}
/**
* Guess file type based on the filename
* @param {string} filename
* @returns {string} type
*/
static guessFileType(filename) {
if (typeof filename != "string") return null;
filename = filename.toLowerCase().trim();
if (filename.endsWith(".tab") || filename.endsWith(".tsv") || filename.endsWith(".csv") || filename.endsWith("format=textfile")) {
return "csv";
}
if (filename.endsWith(".dship.json")) {
return "dship";
}
if (filename.endsWith(".geojson") || filename.endsWith(".json")) {
return "geojson";
}
if (filename.endsWith(".qgs")) {
return "qgis";
}
return null;
}
}
export { FileBinder };