UNPKG

qwc2

Version:
451 lines (449 loc) 21.7 kB
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /** * Copyright 2017-2024 Sourcepole AG * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ /** * NOTE: This sample editing interface is designed to work with the counterpart at * https://github.com/qwc-services/qwc-data-service */ import axios from 'axios'; import isEmpty from 'lodash.isempty'; import ConfigUtils from './ConfigUtils'; import CoordinatesUtils from './CoordinatesUtils'; import { computeExpressionFields } from './EditingUtils'; import LocaleUtils from './LocaleUtils'; var EditingInterface = { buildErrMsg: function buildErrMsg(err) { var message = LocaleUtils.tr("editing.commitfailed"); if (err.response && err.response.data && err.response.data.message) { message = err.response.data.message; if (!isEmpty(err.response.data.geometry_errors)) { message += ":\n"; message += err.response.data.geometry_errors.map(function (entry) { var entrymsg = " - " + entry.reason; if (entry.location) { entrymsg += " at " + entry.location; } return entrymsg; }); } if (!isEmpty(err.response.data.data_errors)) { message += ":\n - " + err.response.data.data_errors.join("\n - "); } if (!isEmpty(err.response.data.validation_errors)) { message += ":\n - " + err.response.data.validation_errors.join("\n - "); } if (!isEmpty(err.response.data.attachment_errors)) { message += ":\n - " + err.response.data.attachment_errors.join("\n - "); } } else if (err.response && err.response.statusText) { message += ": " + err.response.statusText; } return message; }, replacer: function replacer(mapCrs) { if (mapCrs === "EPSG:4326") { return function (key, val) { return val.toFixed ? Number(val.toFixed(5)) : val; }; } else { return function (key, val) { return val.toFixed ? Number(val.toFixed(2)) : val; }; } }, /** * Gets features at the specified map position. * * @param editConfig The edit config of the dataset to query features from * @param mapPos The [x, y] map position * @param mapCrs The CRS of the map, as an EPSG code * @param mapScale The scale denominator, used to compute the pick tolerance * @param dpi The screen dpi, used to compute the pick tolerance * @param callback Callback invoked with the picked features, taking `{features: [...]}` on success and `null` on failure * @param filter An optional feature attribute filter expression * @param filterGeom An optional filter geometry */ getFeature: function getFeature(editConfig, mapPos, mapCrs, mapScale, dpi, callback) { var filter = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null; var filterGeom = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null; var editServiceUrl = ConfigUtils.getConfigProp("editServiceUrl").replace(/\/$/, ''); var requestUrl = editServiceUrl + '/' + editConfig.editDataset + '/'; // 10px tolerance var bbox = null; var metersPerPixel = 1 / dpi * 0.0254 * mapScale; if (CoordinatesUtils.getUnits(mapCrs) === 'degrees') { var tolLat = 10 * metersPerPixel / 111320.0; // meters per degree latitude var tolLon = 10 * metersPerPixel / (111320.0 * Math.cos(mapPos[1] * Math.PI / 180)); bbox = [mapPos[0] - tolLon, mapPos[1] - tolLat, mapPos[0] + tolLon, mapPos[1] + tolLat].join(","); } else { var tol = 10 * metersPerPixel; bbox = mapPos[0] - tol + "," + (mapPos[1] - tol) + "," + (mapPos[0] + tol) + "," + (mapPos[1] + tol); } var params = { bbox: bbox, crs: mapCrs, filter: filter ? JSON.stringify(filter) : undefined, filter_geom: filterGeom ? JSON.stringify(_objectSpread(_objectSpread({}, filterGeom), {}, { crs: { type: "name", properties: { name: mapCrs } } }), this.replacer(mapCrs)) : undefined }; var headers = { "Accept-Language": LocaleUtils.lang() }; axios.get(requestUrl, { headers: headers, params: params }).then(function (response) { var _response$data; if (!isEmpty(response === null || response === void 0 || (_response$data = response.data) === null || _response$data === void 0 ? void 0 : _response$data.features)) { var version = +new Date(); var promises = response.data.features.map(function (feature) { return new Promise(function (resolve) { computeExpressionFields(editConfig, feature, EditingInterface, mapCrs, function (newfeature) { return resolve(_objectSpread(_objectSpread({}, newfeature), {}, { __version__: version })); }); }); }); Promise.all(promises).then(function (features) { return callback({ features: features }); }); } else { callback(null); } })["catch"](function () { callback(null); }); }, /** * Queries a feature by id * * @param editConfig The edit config of the dataset to query the feature from * @param featureId The feature id * @param mapCrs The CRS of the map, as an EPSG code * @param callback Callback invoked with the picked feature, taking `{<feature>}` on success and `null` on failure */ getFeatureById: function getFeatureById(editConfig, featureId, mapCrs, callback) { var editServiceUrl = ConfigUtils.getConfigProp("editServiceUrl").replace(/\/$/, ''); var requestUrl = editServiceUrl + '/' + editConfig.editDataset + '/' + featureId; var params = { crs: mapCrs }; var headers = { "Accept-Language": LocaleUtils.lang() }; axios.get(requestUrl, { headers: headers, params: params }).then(function (response) { computeExpressionFields(editConfig, response.data, EditingInterface, mapCrs, function (newfeature) { return callback(_objectSpread(_objectSpread({}, newfeature), {}, { __version__: +new Date() })); }); })["catch"](function () { callback(null); }); }, /** * Gets the dataset features * * @param editConfig The edit config of the dataset to query features from * @param mapCrs The CRS of the map, as an EPSG code * @param callback Callback invoked with the picked features, taking `{features: [...]}` on success and `null` on failure * @param filter An optional feature attribute filter expression * @param filterGeom An optional filter geometry * @param fields An optional list of fields to query, if unspecified all fields are queried */ getFeatures: function getFeatures(editConfig, mapCrs, callback) { var bbox = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; var filter = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null; var filterGeom = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : null; var fields = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null; var editServiceUrl = ConfigUtils.getConfigProp("editServiceUrl").replace(/\/$/, ''); var requestUrl = editServiceUrl + '/' + editConfig.editDataset + '/'; var params = { crs: mapCrs, bbox: bbox ? bbox.join(",") : undefined, filter: filter ? JSON.stringify(filter, this.replacer(mapCrs)) : undefined, filter_geom: filterGeom ? JSON.stringify(_objectSpread(_objectSpread({}, filterGeom), {}, { crs: { type: "name", properties: { name: mapCrs } } }), this.replacer(mapCrs)) : undefined, fields: fields ? fields.join(",") : undefined }; var headers = { "Accept-Language": LocaleUtils.lang() }; axios.get(requestUrl, { headers: headers, params: params }).then(function (response) { var _response$data2; if (Array.isArray(response === null || response === void 0 || (_response$data2 = response.data) === null || _response$data2 === void 0 ? void 0 : _response$data2.features)) { var version = +new Date(); var promises = response.data.features.map(function (feature) { return new Promise(function (resolve) { computeExpressionFields(editConfig, feature, EditingInterface, mapCrs, function (newfeature) { return resolve(_objectSpread(_objectSpread({}, newfeature), {}, { __version__: version })); }); }); }); Promise.all(promises).then(function (features) { return callback({ features: features }); }); } else { callback(null); } })["catch"](function () { return callback(null); }); }, /** * Query the extent of the dataset features * @param editConfig The edit config of the dataset to query features from * @param mapCrs The CRS of the map, as an EPSG code * @param callback Callback invoked with the feature extent, taking `{bbox: [xmin, ymin, xmax, ymax]}` on success and `null` on failure * @param filter An optional feature attribute filter expression * @param filterGeom An optional filter geometry */ getExtent: function getExtent(editConfig, mapCrs, callback) { var filter = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; var filterGeom = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null; var editServiceUrl = ConfigUtils.getConfigProp("editServiceUrl").replace(/\/$/, ''); var requestUrl = editServiceUrl + '/' + editConfig.editDataset + "/extent"; var params = { crs: mapCrs, filter: filter ? JSON.stringify(filter) : undefined, filter_geom: filterGeom ? JSON.stringify(_objectSpread(_objectSpread({}, filterGeom), {}, { crs: { type: "name", properties: { name: mapCrs } } }), this.replacer(mapCrs)) : undefined }; var headers = { "Accept-Language": LocaleUtils.lang() }; axios.get(requestUrl, { headers: headers, params: params }).then(function (response) { callback(response.data); })["catch"](function () { callback(null); }); }, /** * Adds a feature to the dataset * @param editConfig The edit config of the dataset to add the feature to * @param mapCrs The CRS of the map, as an EPSG code * @param featureData A FormData instance, with: * - A 'feature' entry containing the GeoJSON serialized feature * - Zero or more 'file:' prefixed file upload entries * - Zero or more 'relfile:' prefixed file upload entries * - Optionally a 'g-recaptcha-response' entry with the captcha response * @param callback Callback invoked with the added feature, taking `(true, {<feature>}` on success and `(false, <Error Message>}` on failure */ addFeatureMultipart: function addFeatureMultipart(editConfig, mapCrs, featureData, callback) { var editServiceUrl = ConfigUtils.getConfigProp("editServiceUrl").replace(/\/$/, ''); var requestUrl = editServiceUrl + '/' + editConfig.editDataset + '/multipart'; var headers = { "Content-Type": "multipart/form-data", "Accept-Language": LocaleUtils.lang() }; axios.post(requestUrl, featureData, { headers: headers }).then(function (response) { computeExpressionFields(editConfig, response.data, EditingInterface, mapCrs, function (newfeature) { return callback(true, _objectSpread(_objectSpread({}, newfeature), {}, { __version__: +new Date() })); }); })["catch"](function (err) { callback(false, EditingInterface.buildErrMsg(err)); }); }, /** * Edits a feature of the dataset * @param editConfig The edit config of the edited dataset * @param mapCrs The CRS of the map, as an EPSG code * @param featureId The ID of the edited feature * @param featureData A FormData instance, with: * - A 'feature' entry containing the GeoJSON serialized feature * - Zero or more 'file:' prefixed file upload entries * - Zero or more 'relfile:' prefixed file upload entries * - Optionally a 'g-recaptcha-response' entry with the captcha response * @param callback Callback invoked with the edited feature, taking `(true, {<feature>}` on success and `(false, <Error Message>}` on failure */ editFeatureMultipart: function editFeatureMultipart(editConfig, mapCrs, featureId, featureData, callback) { var editServiceUrl = ConfigUtils.getConfigProp("editServiceUrl").replace(/\/$/, ''); var requestUrl = editServiceUrl + '/' + editConfig.editDataset + '/multipart/' + featureId; var headers = { "Content-Type": "multipart/form-data", "Accept-Language": LocaleUtils.lang() }; axios.put(requestUrl, featureData, { headers: headers }).then(function (response) { computeExpressionFields(editConfig, response.data, EditingInterface, mapCrs, function (newfeature) { return callback(true, _objectSpread(_objectSpread({}, newfeature), {}, { __version__: +new Date() })); }); })["catch"](function (err) { callback(false, EditingInterface.buildErrMsg(err)); }); }, /* layerId: The edit layer id featureId: The id of the feature to delete callback: function(success, result), if success = true, result is null, if success = false, result is an error message recaptchaResponse: Optional, captcha challenge response */ /** * Deletes a feature from the dataset * @param editConfig The edit config of the dataset from which to delete the feature * @param featureId The ID of the edited feature * @param callback Callback invoked with the id of the deleted feature, taking `(true, <feature_id>` on success and `(false, <Error Message>}` on failure * @param recaptchaResponse Optional captcha challenge response */ deleteFeature: function deleteFeature(editConfig, featureId, callback) { var recaptchaResponse = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; var editServiceUrl = ConfigUtils.getConfigProp("editServiceUrl").replace(/\/$/, ''); var req = editServiceUrl + '/' + editConfig.editDataset + '/' + featureId; var headers = { "Accept-Language": LocaleUtils.lang() }; var data = {}; if (recaptchaResponse) { data['g-recaptcha-response'] = recaptchaResponse; } axios["delete"](req, { headers: headers, data: data }).then(function () { callback(true, featureId); })["catch"](function (err) { callback(false, EditingInterface.buildErrMsg(err)); }); }, /** * Queries relation values of a feature * @param editConfig The edit config of the feature dataset * @param featureId The feature ID * @param mapCrs The CRS of the map, as an EPSG code * @param tables Comma separated string of relation table names * @param editConfigs The theme editConfig block, containing all theme dataset edit configs * @param callback Callback invoked with the relation values, taking `{<tablename>: {<relation_values>}}` on success and `{}` on failure */ getRelations: function getRelations(editConfig, featureId, mapCrs, tables, editConfigs, callback) { var editServiceUrl = ConfigUtils.getConfigProp("editServiceUrl").replace(/\/$/, ''); var req = editServiceUrl + '/' + editConfig.editDataset + '/' + featureId + "/relations"; var params = { tables: tables, crs: mapCrs }; var headers = { "Accept-Language": LocaleUtils.lang() }; axios.get(req, { headers: headers, params: params }).then(function (response) { Promise.all(Object.entries(response.data).map(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), reldataset = _ref2[0], relvalues = _ref2[1]; return new Promise(function (resolveTable) { Promise.all(relvalues.features.map(function (feature) { return new Promise(function (resolveFeature) { var relEditConfig = Object.values(editConfigs).find(function (entry) { return entry.editDataset === reldataset; }); computeExpressionFields(relEditConfig, feature, EditingInterface, mapCrs, resolveFeature); }); })).then(function (newfeatures) { return resolveTable([reldataset, _objectSpread(_objectSpread({}, relvalues), {}, { features: newfeatures })]); }); }); })).then(function (entries) { return callback(Object.fromEntries(entries)); }); })["catch"](function () { return callback({}); }); }, /** * Query key-value-pairs of a key-value-relation * @param keyvalues The keyval string `<keyvaldataset>:<keyfield>:<valuefield>` * @param callback Callback invoked with the key-value pairs, taking `{keyvalues: {<keyvaldataset>: [{key: <key>, value: <value>}]}}` on success and `{}` on failure * @param filter An optional filter expression, as `[[["<name>", "<op>", <value>],"and|or",["<name>","<op>",<value>],...]]` (one filter expr per keyvalue entry) */ getKeyValues: function getKeyValues(keyvalues, callback) { var filter = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; var editServiceUrl = ConfigUtils.getConfigProp("editServiceUrl").replace(/\/$/, ''); var req = editServiceUrl + '/' + "keyvals?tables=" + keyvalues; var params = { filter: filter ? JSON.stringify(filter) : undefined }; var headers = { "Accept-Language": LocaleUtils.lang() }; axios.get(req, { headers: headers, params: params }).then(function (response) { callback(response.data); })["catch"](function () { return callback({}); }); }, /** * Resolve an attachment value to a full URL * * @param dataset The dataset name * @param fileValue The attachment value */ resolveAttachmentUrl: function resolveAttachmentUrl(dataset, fileValue) { var editServiceUrl = ConfigUtils.getConfigProp("editServiceUrl").replace(/\/$/, ''); return editServiceUrl + '/' + dataset + "/attachment?file=" + encodeURIComponent(fileValue); } }; export default EditingInterface;