From a88f34459cea0901fe6452d842b8e644461cab4c Mon Sep 17 00:00:00 2001 From: Pi Moll Date: Fri, 15 Nov 2024 11:27:34 +0100 Subject: [PATCH 01/11] Update app data to 2024 lts --- app/.babelrc.json | 11 +- app/.eslintrc | 54 +- app/index.html | 1 + ...teCalculator.jsx => IdentifyExtensions.js} | 20 + app/js/SearchProviders.js | 515 +- app/js/app.jsx | 9 +- app/js/appConfig.js | 302 +- app/package.json | 36 +- app/static/api_examples.js | 25 +- app/static/assets/css/colorschemes.css | 122 + app/static/assets/img/qwc-logo.svg | 76 +- .../assets/templates/heightprofileprint.html | 11 + app/static/translations/cs-CZ.json | 457 +- app/static/translations/de-CH.json | 227 +- app/static/translations/de-DE.json | 227 +- app/static/translations/en-US.json | 215 +- app/static/translations/es-ES.json | 217 +- app/static/translations/fr-FR.json | 217 +- app/static/translations/it-IT.json | 219 +- app/static/translations/pl-PL.json | 219 +- app/static/translations/pt-BR.json | 517 +- app/static/translations/pt-PT.json | 621 +- app/static/translations/ro-RO.json | 479 +- app/static/translations/ru-RU.json | 217 +- app/static/translations/sv-SE.json | 213 +- app/static/translations/tr-TR.json | 215 +- app/static/translations/tsconfig.json | 13 +- app/styleConfig.js | 2 + app/themesConfig.json | 10 +- app/webpack.config.js | 9 +- app/yarn.lock | 7206 +++++++++-------- 31 files changed, 7622 insertions(+), 5060 deletions(-) rename app/js/{CustomAttributeCalculator.jsx => IdentifyExtensions.js} (58%) create mode 100644 app/static/assets/css/colorschemes.css create mode 100644 app/static/assets/templates/heightprofileprint.html diff --git a/app/.babelrc.json b/app/.babelrc.json index 5b8591d..728ed82 100644 --- a/app/.babelrc.json +++ b/app/.babelrc.json @@ -1,7 +1,7 @@ { "plugins": [ - "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-object-rest-spread" + "@babel/plugin-transform-class-properties", + "@babel/plugin-transform-object-rest-spread" ], "presets": [ [ @@ -11,5 +11,10 @@ } ], "@babel/preset-react" - ] + ], + "env": { + "production": { + "plugins": ["transform-react-remove-prop-types"] + } + } } diff --git a/app/.eslintrc b/app/.eslintrc index 745c2a1..4aa5b0e 100644 --- a/app/.eslintrc +++ b/app/.eslintrc @@ -46,21 +46,21 @@ "jsx": true }, "rules": { -/** - * Strict mode - */ + /** + * Strict mode + */ // babel inserts "use strict"; for us "strict": [2, "never"], // http://eslint.org/docs/rules/strict -/** - * ES6 - */ + /** + * ES6 + */ "no-var": 2, // http://eslint.org/docs/rules/no-var "prefer-const": 2, // http://eslint.org/docs/rules/prefer-const -/** - * Variables - */ + /** + * Variables + */ "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars @@ -69,9 +69,9 @@ }], "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define -/** - * Possible errors - */ + /** + * Possible errors + */ "comma-dangle": [2, "never"], // http://eslint.org/docs/rules/comma-dangle "no-console": 1, // http://eslint.org/docs/rules/no-console "no-alert": 1, // http://eslint.org/docs/rules/no-alert @@ -79,9 +79,9 @@ "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var -/** - * Best practices - */ + /** + * Best practices + */ "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly "default-case": 2, // http://eslint.org/docs/rules/default-case @@ -95,6 +95,7 @@ "no-eval": 2, // http://eslint.org/docs/rules/no-eval "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind + "no-extra-semi": "warn", "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks @@ -116,14 +117,14 @@ "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife "yoda": 2, // http://eslint.org/docs/rules/yoda -/** - * Style - */ + /** + * Style + */ "indent": [2, 4], // http://eslint.org/docs/rules/indent "brace-style": [2, // http://eslint.org/docs/rules/brace-style "1tbs", { - "allowSingleLine": true - }], + "allowSingleLine": true + }], "quotes": [ 0, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes ], @@ -138,8 +139,8 @@ "eol-last": 2, // http://eslint.org/docs/rules/eol-last "func-names": 0, // http://eslint.org/docs/rules/func-names "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing - "beforeColon": false, - "afterColon": true + "beforeColon": false, + "afterColon": true }], "new-cap": [2, { // http://eslint.org/docs/rules/new-cap "newIsCap": true @@ -166,14 +167,15 @@ "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops "spaced-comment": 2, // http://eslint.org/docs/rules/spaced-comment -/** - * JSX style - */ + /** + * JSX style + */ "react/jsx-boolean-value": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md "react/jsx-sort-props": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md "react/sort-prop-types": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-prop-types.md "react/no-did-mount-set-state": [2, "disallow-in-func"], // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md "react/self-closing-comp": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md - "react/jsx-wrap-multilines": 2 // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-wrap-multilines.md + "react/jsx-wrap-multilines": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-wrap-multilines.md + "react/no-access-state-in-setstate": "error" } } diff --git a/app/index.html b/app/index.html index 55ec534..3d0af95 100644 --- a/app/index.html +++ b/app/index.html @@ -8,6 +8,7 @@ QGIS Web Client 2 + diff --git a/app/js/CustomAttributeCalculator.jsx b/app/js/IdentifyExtensions.js similarity index 58% rename from app/js/CustomAttributeCalculator.jsx rename to app/js/IdentifyExtensions.js index 799161b..e91e984 100644 --- a/app/js/CustomAttributeCalculator.jsx +++ b/app/js/IdentifyExtensions.js @@ -19,3 +19,23 @@ export function customAttributeCalculator(layer, feature) { // )]; return []; } + +export function attributeTransform(name, value, layer, feature) { + // Here you can transform the attribute value. + return value; +} +export const customExporters = [ + /* + { + id: "myexport", + title: "My Format", + allowClipboard: true, + export: (features, callback) => { + const data = convertToMyFormat(features); + callback({ + data: data, type: "mime/type", filename: "export.ext" + }); + } + } + */ +]; diff --git a/app/js/SearchProviders.js b/app/js/SearchProviders.js index 02069e3..7584d2c 100644 --- a/app/js/SearchProviders.js +++ b/app/js/SearchProviders.js @@ -6,76 +6,13 @@ * LICENSE file in the root directory of this source tree. */ -/** -Search provider interface: --------------------------- - - onSearch: function(text, requestId, searchOptions, dispatch, state) { - let results = [ ... ]; // See below - return addSearchResults({data: results, provider: providerId, reqId: requestId}, true); - // or - return dispatch( (...) => { - return addSearchResults({data: results, provider: providerId, reqId: requestId}, true); - }); - } - - getResultGeometry: function(resultItem, callback) { - // ... - callback(resultItem, geometryWktString); - } - - getMoreResults: function(moreItem, text, requestId, dispatch) { - // Same return object as onSearch - } - - -Format of search results: -------------------------- - - results = [ - { - id: categoryid, // Unique category ID - title: display_title, // Text to display as group title in the search results - priority: priority_nr, // Optional search result group priority. Groups with higher priority are displayed first in the list. - items: [ - { // Location search result: - type: SearchResultType.PLACE, // Specifies that this is a location search result - id: itemid, // Unique item ID - text: display_text, // Text to display as search result - label: map_label_text, // Optional, text to show next to the position marker on the map instead of - x: x, // X coordinate of result - y: y, // Y coordinate of result - crs: crs, // CRS of result coordinates and bbox - bbox: [xmin, ymin, xmax, ymax], // Bounding box of result (if non-empty, map will zoom to this extent when selecting result) - provider: providerid // The ID of the provider which generated this result. Required if `getResultGeometry` is to be called. - }, - { // Theme layer search result (advanced): - type: SearchResultType.THEMELAYER, // Specifies that this is a theme layer search result - id: itemid, // Unique item ID - text: display_text, // Text to display as search result - layer: {} // Layer definition, in the same format as a "sublayers" entry in themes.json. - }, - { // Optional entry to request more results: - id: itemid, // Unique item ID - more: true, // Specifies that this entry is a "More..." entry - provider: providerid // The ID of the provider which generated this result. - } - ] - }, - { - ... - } - ] - -*/ -import axios from 'axios'; -import {addSearchResults, SearchResultType} from "qwc2/actions/search"; -import CoordinatesUtils from 'qwc2/utils/CoordinatesUtils'; -import LocaleUtils from 'qwc2/utils/LocaleUtils'; +import yaml from 'js-yaml'; +import CoordinatesUtils from '../qwc2/utils/CoordinatesUtils'; +import IdentifyUtils from '../qwc2/utils/IdentifyUtils'; -function coordinatesSearch(text, requestId, searchOptions, dispatch) { - const displaycrs = searchOptions.displaycrs || "EPSG:4326"; +function coordinatesSearch(text, searchParams, callback) { + const displaycrs = searchParams.displaycrs || "EPSG:4326"; const matches = text.match(/^\s*([+-]?\d+\.?\d*)[,\s]\s*([+-]?\d+\.?\d*)\s*$/); const items = []; if (matches && matches.length >= 3) { @@ -93,7 +30,7 @@ function coordinatesSearch(text, requestId, searchOptions, dispatch) { } if (x >= -180 && x <= 180 && y >= -90 && y <= 90) { const title = Math.abs(x) + (x >= 0 ? "°E" : "°W") + ", " - + Math.abs(y) + (y >= 0 ? "°N" : "°S"); + + Math.abs(y) + (y >= 0 ? "°N" : "°S"); items.push({ id: "coord" + items.length, text: title, @@ -105,7 +42,7 @@ function coordinatesSearch(text, requestId, searchOptions, dispatch) { } if (x >= -90 && x <= 90 && y >= -180 && y <= 180 && x !== y) { const title = Math.abs(y) + (y >= 0 ? "°E" : "°W") + ", " - + Math.abs(x) + (x >= 0 ? "°N" : "°S"); + + Math.abs(x) + (x >= 0 ? "°N" : "°S"); items.push({ id: "coord" + items.length, text: title, @@ -126,228 +63,211 @@ function coordinatesSearch(text, requestId, searchOptions, dispatch) { } ); } - dispatch(addSearchResults({data: results, provider: "coordinates", reqId: requestId}, true)); + callback({results: results}); } /** ************************************************************************ **/ -function geoAdminLocationSearch(text, requestId, searchOptions, dispatch) { - axios.get("http://api3.geo.admin.ch/rest/services/api/SearchServer?searchText=" + encodeURIComponent(text) + "&type=locations&limit=20") - .then(response => dispatch(geoAdminLocationSearchResults(response.data, requestId))); -} - -function parseItemBBox(bboxstr) { - if (bboxstr === undefined) { - return null; - } - const matches = bboxstr.match(/^BOX\s*\(\s*(\d+\.?\d*)\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)\s*(\d+\.?\d*)\s*\)$/); - if (matches && matches.length < 5) { - return null; - } - const xmin = parseFloat(matches[1]); - const ymin = parseFloat(matches[2]); - const xmax = parseFloat(matches[3]); - const ymax = parseFloat(matches[4]); - return CoordinatesUtils.reprojectBbox([xmin, ymin, xmax, ymax], "EPSG:21781", "EPSG:4326"); -} - -function geoAdminLocationSearchResults(obj, requestId) { - const categoryMap = { - gg25: "Municipalities", - kantone: "Cantons", - district: "Districts", - sn25: "Places", - zipcode: "Zip Codes", - address: "Address", - gazetteer: "General place name directory" - }; - const resultGroups = {}; - (obj.results || []).map(entry => { - if (resultGroups[entry.attrs.origin] === undefined) { - resultGroups[entry.attrs.origin] = { - id: entry.attrs.origin, - title: categoryMap[entry.attrs.origin] || entry.attrs.origin, - items: [] - }; +class NominatimSearch { + static TRANSLATIONS = {}; + + static search(text, searchParams, callback, axios) { + const viewboxParams = {}; + if (searchParams.filterBBox) { + viewboxParams.viewbox = CoordinatesUtils.reprojectBbox(searchParams.filterBBox, searchParams.mapcrs, "EPSG:4326").join(","); + viewboxParams.bounded = 1; } - const x = entry.attrs.lon; - const y = entry.attrs.lat; - resultGroups[entry.attrs.origin].items.push({ - id: entry.id, - text: entry.attrs.label, - x: x, - y: y, - crs: "EPSG:4326", - bbox: parseItemBBox(entry.attrs.geom_st_box2d) || [x, y, x, y], - provider: "geoadmin" + axios.get("https://nominatim.openstreetmap.org/search", {params: { + 'q': text, + 'addressdetails': 1, + 'polygon_geojson': 1, + 'limit': 20, + 'format': 'json', + 'accept-language': searchParams.lang, + ...viewboxParams, + ...(searchParams.cfgParams || {}) + }}).then(response => { + const locale = searchParams.lang; + if (NominatimSearch.TRANSLATIONS[locale] === undefined) { + NominatimSearch.TRANSLATIONS[locale] = {promise: NominatimSearch.loadLocale(locale, axios)}; + NominatimSearch.TRANSLATIONS[locale].promise.then(() => { + NominatimSearch.parseResults(response.data, NominatimSearch.TRANSLATIONS[locale].strings, callback); + }); + } else if (NominatimSearch.TRANSLATIONS[locale].promise) { + NominatimSearch.TRANSLATIONS[locale].promise.then(() => { + NominatimSearch.parseResults(response.data, NominatimSearch.TRANSLATIONS[locale].strings, callback); + }); + } else if (NominatimSearch.TRANSLATIONS[locale].strings) { + NominatimSearch.parseResults(response.data, NominatimSearch.TRANSLATIONS[locale].strings, callback); + } }); - }); - const results = Object.values(resultGroups); - return addSearchResults({data: results, provider: "geoadmin", reqId: requestId}, true); -} - -/** ************************************************************************ **/ - -function usterSearch(text, requestId, searchOptions, dispatch) { - axios.get("https://webgis.uster.ch/wsgi/search.wsgi?&searchtables=&query=" + encodeURIComponent(text)) - .then(response => dispatch(usterSearchResults(response.data, requestId))); -} - -function usterSearchResults(obj, requestId) { - const results = []; - let currentgroup = null; - let groupcounter = 0; - let counter = 0; - (obj.results || []).map(entry => { - if (!entry.bbox) { - // Is group - currentgroup = { - id: "ustergroup" + (groupcounter++), - title: entry.displaytext, - items: [] - }; - results.push(currentgroup); - } else if (currentgroup) { - currentgroup.items.push({ - id: "usterresult" + (counter++), - text: entry.displaytext, - searchtable: entry.searchtable, - bbox: entry.bbox.slice(0), - x: 0.5 * (entry.bbox[0] + entry.bbox[2]), - y: 0.5 * (entry.bbox[1] + entry.bbox[3]), - crs: "EPSG:21781", - provider: "uster" + } + static parseResults(obj, translations, callback) { + const results = []; + const groups = {}; + let groupcounter = 0; + + (obj || []).map(entry => { + if (!(entry.class in groups)) { + let title = entry.type; + try { + title = translations[entry.class][entry.type]; + } catch (e) { + /* pass */ + } + groups[entry.class] = { + id: "nominatimgroup" + (groupcounter++), + // capitalize class + title: title, + items: [] + }; + results.push(groups[entry.class]); + } + + // shorten display_name + let text = entry.display_name.split(', ').slice(0, 3).join(', '); + // map label + const label = text; + + // collect address fields + const address = []; + if (entry.address.town) { + address.push(entry.address.town); + } + if (entry.address.city) { + address.push(entry.address.city); + } + if (entry.address.state) { + address.push(entry.address.state); + } + if (entry.address.country) { + address.push(entry.address.country); + } + if (address.length > 0) { + text += "
" + address.join(', ') + ""; + } + + // reorder coords from [miny, maxy, minx, maxx] to [minx, miny, maxx, maxy] + const b = entry.boundingbox.map(coord => parseFloat(coord)); + const bbox = [b[2], b[0], b[3], b[1]]; + + groups[entry.class].items.push({ + id: entry.place_id, + // shorten display_name + text: text, + label: label, + bbox: bbox, + geometry: entry.geojson, + x: 0.5 * (bbox[0] + bbox[2]), + y: 0.5 * (bbox[1] + bbox[3]), + crs: "EPSG:4326", + provider: "nominatim" + }); + }); + callback({results: results}); + } + static loadLocale(locale, axios) { + return new Promise((resolve) => { + axios.get('https://raw.githubusercontent.com/openstreetmap/openstreetmap-website/master/config/locales/' + locale + '.yml') + .then(resp2 => { + NominatimSearch.TRANSLATIONS[locale] = {strings: NominatimSearch.parseLocale(resp2.data, locale)}; + resolve(true); + }).catch(() => { + NominatimSearch.TRANSLATIONS[locale] = { + promise: axios.get('https://raw.githubusercontent.com/openstreetmap/openstreetmap-website/master/config/locales/' + locale.slice(0, 2) + '.yml') + .then(resp3 => { + NominatimSearch.TRANSLATIONS[locale] = {strings: NominatimSearch.parseLocale(resp3.data, locale.slice(0, 2))}; + resolve(true); + }).catch(() => { + NominatimSearch.TRANSLATIONS[locale] = {strings: {}}; + resolve(true); + }) + }; }); + }); + } + static parseLocale(data, locale) { + const doc = yaml.load(data, {json: true}); + try { + return doc[locale].geocoder.search_osm_nominatim.prefix; + } catch (e) { + return {}; } - }); - return addSearchResults({data: results, provider: "uster", reqId: requestId}, true); -} - -function usterResultGeometry(resultItem, callback) { - axios.get("https://webgis.uster.ch/wsgi/getSearchGeom.wsgi?searchtable=" + encodeURIComponent(resultItem.searchtable) + "&displaytext=" + encodeURIComponent(resultItem.text)) - .then(response => callback(resultItem, response.data, "EPSG:21781")); + } } /** ************************************************************************ **/ -function nominatimSearchResults(obj, requestId) { - const results = []; - const groups = {}; - let groupcounter = 0; - - (obj || []).map(entry => { - if (!(entry.class in groups)) { - groups[entry.class] = { - id: "nominatimgroup" + (groupcounter++), - // capitalize class - title: LocaleUtils.trWithFallback("search.nominatim." + entry.class, entry.class.charAt(0).toUpperCase() + entry.class.slice(1)), - items: [] - }; - results.push(groups[entry.class]); - } - - // shorten display_name - let text = entry.display_name.split(', ').slice(0, 3).join(', '); - // map label - const label = text; - - // collect address fields - const address = []; - if (entry.address.town) { - address.push(entry.address.town); - } - if (entry.address.city) { - address.push(entry.address.city); - } - if (entry.address.state) { - address.push(entry.address.state); - } - if (entry.address.country) { - address.push(entry.address.country); - } - if (address.length > 0) { - text += "
" + address.join(', ') + ""; - } - - // reorder coords from [miny, maxy, minx, maxx] to [minx, miny, maxx, maxy] - const b = entry.boundingbox.map(coord => parseFloat(coord)); - const bbox = [b[2], b[0], b[3], b[1]]; - - groups[entry.class].items.push({ - id: entry.place_id, - // shorten display_name - text: text, - label: label, - bbox: bbox, - geometry: entry.geojson, - x: 0.5 * (bbox[0] + bbox[2]), - y: 0.5 * (bbox[1] + bbox[3]), - crs: "EPSG:4326", - provider: "nominatim" +class QgisSearch { + + static search(text, searchParams, callback, axios) { + + const filter = {...searchParams.cfgParams.expression}; + const values = {TEXT: text}; + const params = { + SERVICE: 'WMS', + VERSION: searchParams.theme.version, + REQUEST: 'GetFeatureInfo', + CRS: searchParams.theme.mapCrs, + WIDTH: 100, + HEIGHT: 100, + LAYERS: [], + FILTER: [], + WITH_MAPTIP: false, + WITH_GEOMETRY: true, + feature_count: searchParams.cfgParams.featureCount || 100, + info_format: 'text/xml' + }; + Object.keys(filter).forEach(layer => { + Object.entries(values).forEach(([key, value]) => { + filter[layer] = filter[layer].replaceAll(`$${key}$`, value.replace("'", "\\'")); + }); + params.LAYERS.push(layer); + params.FILTER.push(layer + ":" + filter[layer]); }); - }); - return addSearchResults({data: results, provider: "nominatim", reqId: requestId}, true); -} - -function nominatimSearch(text, requestId, searchOptions, dispatch, cfg = {}) { - axios.get("//nominatim.openstreetmap.org/search", {params: { - 'q': text, - 'addressdetails': 1, - 'polygon_geojson': 1, - 'limit': 20, - 'format': 'json', - 'accept-language': LocaleUtils.lang(), - ...(cfg.params || {}) - }}).then(response => dispatch(nominatimSearchResults(response.data, requestId))); -} - -/** ************************************************************************ **/ - -function parametrizedSearch(text, requestId, searchOptions, dispatch, cfg) { - const SEARCH_URL = ""; // ... - axios.get(SEARCH_URL + "?param=" + cfg.param + "&searchtext=" + encodeURIComponent(text)) - .then(response => dispatch(addSearchResults({data: response.data, provider: cfg.key, reqId: requestId}))) - .catch(() => dispatch(addSearchResults({data: [], provider: cfg.key, reqId: requestId}))); -} - -/** ************************************************************************ **/ - -function layerSearch(text, requestId, searchOptions, dispatch) { - const results = []; - if (text === "bahnhof") { - const layer = { - sublayers: [ + params.QUERY_LAYERS = params.LAYERS = params.LAYERS.join(","); + params.FILTER = params.FILTER.join(";"); + axios.get(searchParams.theme.featureInfoUrl, {params}).then(response => { + callback(QgisSearch.searchResults( + IdentifyUtils.parseResponse(response.data, searchParams.theme, 'text/xml', null, searchParams.mapcrs), + searchParams.cfgParams.title, searchParams.cfgParams.resultTitle + )); + }).catch(() => { + callback({results: []}); + }); + } + static searchResults(features, title, resultTitle) { + const results = []; + Object.entries(features).forEach(([layername, layerfeatures]) => { + const items = layerfeatures.map(feature => { + const values = { + ...feature.properties, + id: feature.id, + layername: layername + }; + return { + id: "qgis." + layername + "." + feature.id, + text: resultTitle ? resultTitle.replace(/{([^}]+)}/g, match => values[match.slice(1, -1)]) : feature.displayname, + x: 0.5 * (feature.bbox[0] + feature.bbox[2]), + y: 0.5 * (feature.bbox[1] + feature.bbox[3]), + crs: feature.crs, + bbox: feature.bbox, + geometry: feature.geometry + }; + }); + results.push( { - name: "a", - title: "a", - visibility: true, - queryable: true, - displayField: "maptip", - opacity: 255, - bbox: { - crs: "EPSG:4326", - bounds: [ - 8.53289, - 47.3768, - 8.54141, - 47.3803 - ] - } + id: "qgis." + layername, + title: title + ": " + layername, + items: items } - ] - }; - results.push({ - id: "layers", - title: "Layers", - items: [{ - type: SearchResultType.THEMELAYER, - id: "bahnhof", - text: "Bahnhof", - layer: layer - }] + ); }); + return {results}; + } + static getResultGeometry(resultItem, callback) { + callback({geometry: resultItem.geometry, crs: resultItem.crs}); } - dispatch(addSearchResults({data: results, provider: "layers", reqId: requestId}, true)); } /** ************************************************************************ **/ @@ -355,45 +275,18 @@ function layerSearch(text, requestId, searchOptions, dispatch) { export const SearchProviders = { coordinates: { labelmsgid: "search.coordinates", - onSearch: coordinatesSearch - }, - geoadmin: { - label: "Swisstopo", - onSearch: geoAdminLocationSearch, - requiresLayer: "a" // Make provider availability depend on the presence of a theme WMS layer - }, - uster: { - label: "Uster", - onSearch: usterSearch, - getResultGeometry: usterResultGeometry + onSearch: coordinatesSearch, + handlesGeomFilter: false }, nominatim: { label: "OpenStreetMap", - onSearch: nominatimSearch + onSearch: NominatimSearch.search, + handlesGeomFilter: false }, - layers: { - label: "Layers", - onSearch: layerSearch + qgis: { + label: "QGIS", + onSearch: QgisSearch.search, + getResultGeometry: QgisSearch.getResultGeometry, + handlesGeomFilter: false } }; - -export function searchProviderFactory(cfg) { - // Note: cfg corresponds to an entry of the theme searchProviders array in themesConfig.json, in this case - // { key: , label: