diff --git a/.github/workflows/docker-image-nightly.yml b/.github/workflows/docker-image-nightly.yml index 5e9701f..c370b3b 100644 --- a/.github/workflows/docker-image-nightly.yml +++ b/.github/workflows/docker-image-nightly.yml @@ -24,6 +24,6 @@ jobs: with: context: ./ # do not push for the moment, to avoid load - # push: true + push: true tags: ghcr.io/${{ github.repository }}:${{ github.head_ref }}-${{ github.sha }} target: prod diff --git a/Dockerfile b/Dockerfile index 7450550..9057460 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,21 @@ -FROM ghcr.io/opengisch/qwc2_minimal-qwc2-minimal-builder:v1.0.1 as builder +FROM ghcr.io/opengisch/qwc2_minimal-qwc2-minimal-builder:2024-lts.0 as builder COPY ./app/js/Help.jsx /app/js/Help.jsx COPY ./app/js/SearchProviders.js /app/js/SearchProviders.js COPY ./app/index.html /app/index.html +COPY ./app/static/translations/cs-CZ_overrides.json /app/static/translations/cs-CZ_overrides.json +COPY ./app/static/translations/de-CH_overrides.json /app/static/translations/de-CH_overrides.json +COPY ./app/static/translations/de-DE_overrides.json /app/static/translations/de-DE_overrides.json +COPY ./app/static/translations/en-US_overrides.json /app/static/translations/en-US_overrides.json +COPY ./app/static/translations/es-ES_overrides.json /app/static/translations/es-ES_overrides.json +COPY ./app/static/translations/fr-FR_overrides.json /app/static/translations/fr-FR_overrides.json +COPY ./app/static/translations/it-IT_overrides.json /app/static/translations/it-IT_overrides.json +COPY ./app/static/translations/pl-PL_overrides.json /app/static/translations/pl-PL_overrides.json +COPY ./app/static/translations/pt-BR_overrides.json /app/static/translations/pt-BR_overrides.json +COPY ./app/static/translations/ro-RO_overrides.json /app/static/translations/ro-RO_overrides.json +COPY ./app/static/translations/ru-RU_overrides.json /app/static/translations/ru-RU_overrides.json +COPY ./app/static/translations/sv-SE_overrides.json /app/static/translations/sv-SE_overrides.json +COPY ./app/static/translations/tr-TR_overrides.json /app/static/translations/tr-TR_overrides.json WORKDIR /app diff --git a/README.md b/README.md new file mode 100644 index 0000000..f167d27 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# WebGIS Thalwil + +## Local DEV setup +Run the docker compose file to fire up a local instance of the webGIS. It mounts the contents of +the app folder and also the configuration files copied from Thalwil TEST env (themes.json, config.json). + + +### Prevent CORS issues +To prevent CORS issues, the following automatic and manual steps are taken. + +- Search: A proxy service in the docker compose redirects search requests from localhost:8888 to the TEST env +- Map click / feature info: In themes.json, replace `"featureInfoUrl": "https://maps-test.thalwil.ch` with `"featureInfoUrl": "http://localhost:8888` to point at the proxy +- Theme and basemap thumbnails: These still suffer from CORS blocks, no solution yet diff --git a/app/index.html b/app/index.html index 698e538..4423483 100644 --- a/app/index.html +++ b/app/index.html @@ -8,6 +8,7 @@ Thalwil webGIS + @@ -49,12 +50,13 @@ } } + - +
diff --git a/app/js/Help.jsx b/app/js/Help.jsx index cd27196..7835311 100644 --- a/app/js/Help.jsx +++ b/app/js/Help.jsx @@ -16,8 +16,11 @@ export function renderHelp() {
QWC2 build {process.env.BuildDate}
- Verwendete QWC2 Version + Verwendete QWC2 Version +
+
+ Changelog
); -} \ No newline at end of file +} diff --git a/app/js/SearchProviders.js b/app/js/SearchProviders.js index e952ca9..3620108 100644 --- a/app/js/SearchProviders.js +++ b/app/js/SearchProviders.js @@ -1,81 +1,22 @@ /** - * Copyright 2016-2021 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. - */ +* Copyright 2016-2021 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. +*/ -/** - 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 yaml from 'js-yaml'; import CoordinatesUtils from 'qwc2/utils/CoordinatesUtils'; -import LocaleUtils from 'qwc2/utils/LocaleUtils'; +import IdentifyUtils from 'qwc2/utils/IdentifyUtils'; +import {SearchResultType} from 'qwc2/actions/search'; -function coordinatesSearch(text, requestId, searchOptions, dispatch) { - const displaycrs = searchOptions.displaycrs || "EPSG:4326"; +// When working locally, send requests to proxy at 8888 to prevent CORS issues +const DEV_PROXY_HOST = 'http://localhost:8888'; + +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) { @@ -126,239 +67,282 @@ 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("https://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) { - axios.get("//nominatim.openstreetmap.org/search", {params: { - 'q': text, - 'addressdetails': 1, - 'polygon_geojson': 1, - 'limit': 20, - 'format': 'json', - 'accept-language': LocaleUtils.lang() - }}).then(response => dispatch(nominatimSearchResults(response.data, requestId))); -} - -/** ************************************************************************ **/ - -function parametrizedSearch(cfg, text, requestId, searchOptions, dispatch) { - 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}))); + 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( + { + id: "qgis." + layername, + title: title + ": " + layername, + items: items + } + ); + }); + return {results}; + } + static getResultGeometry(resultItem, callback) { + callback({geometry: resultItem.geometry, crs: resultItem.crs}); + } } /** ************************************************************************ **/ -function layerSearch(text, requestId, searchOptions, dispatch) { - const results = []; - if (text === "bahnhof") { - const layer = { - sublayers: [ - { - 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 - ] - } - } - ] +function geoAdminLocationSearch(text, searchParams, callback, axios) { + const viewboxParams = {}; + if (searchParams.filterBBox) { + viewboxParams.bbox = window.qwc2.CoordinatesUtils.reprojectBbox(searchParams.filterBBox, searchParams.mapcrs, "EPSG:2056").map(x => Math.round(x)).join(","); + } + const params = { + searchText: text, + type: "locations", + limit: 20, + sr: 2056, + ...viewboxParams, + ...(searchParams.cfgParams || {}) + }; + const url = "https://api3.geo.admin.ch/rest/services/api/SearchServer"; + axios.get(url, {params}).then(response => { + const categoryMap = { + gg25: "Municipalities", + kantone: "Cantons", + district: "Districts", + sn25: "Places", + zipcode: "Zip Codes", + address: "Address", + gazetteer: "General place name directory" }; - results.push({ - id: "layers", - title: "Layers", - items: [{ - type: SearchResultType.THEMELAYER, - id: "bahnhof", - text: "Bahnhof", - layer: layer - }] + const parseItemBBox = (bboxstr) => { + try { + const matches = bboxstr.match(/^BOX\s*\(\s*(\d+\.?\d*)\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)\s*(\d+\.?\d*)\s*\)$/); + return matches.slice(1, 5).map(x => parseFloat(x)); + } catch (e) { + return null; + } + }; + const resultGroups = {}; + (response.data.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: [] + }; + } + const x = entry.attrs.y; + const y = entry.attrs.x; + resultGroups[entry.attrs.origin].items.push({ + id: entry.id, + text: entry.attrs.label, + x: x, + y: y, + crs: "EPSG:2056", + bbox: parseItemBBox(entry.attrs.geom_st_box2d) || [x, y, x, y] + }); }); - } - dispatch(addSearchResults({data: results, provider: "layers", reqId: requestId}, true)); + const results = Object.values(resultGroups); + callback({results: results}); + }); } /** ************************************************************************ **/ -function hydrantSearch(text, requestId, searchOptions, dispatch){ +function hydrantSearch(text, searchParams, callback, axios){ let results = []; // If it is a local installation (i.e. running with yarn development server) // I have to change the url to the actual QGIS server url. let host = ''; - if (window.location.host == 'localhost:8081') { - host = 'http://localhost:8080'; + if (window.location.host == 'localhost:8080') { + host = DEV_PROXY_HOST; } // I use format xml instead of json because json responses seem to be very bugged in QGIS server @@ -379,28 +363,28 @@ function hydrantSearch(text, requestId, searchOptions, dispatch){ id: 1, text: "Hydrant nr. " + text, label: "Hydrant Nr: " + text, - x: coordinates[0], - y: coordinates[1], + x: parseFloat(coordinates[0]), + y: parseFloat(coordinates[1]), crs: 'EPSG:2056', provider: 'hydrants' }] }); } - dispatch(addSearchResults({data: results, provider: "hydrants", reqId: requestId}, true)); + callback({results: results}); }); } /////////////////////////////////////////////////////////////////////////////// -function propertySearch(text, requestId, searchOptions, dispatch){ +function propertySearch(text, searchParams, callback, axios){ let results = []; // If it is a local installation (i.e. running with yarn development server) // I have to change the url to the actual QGIS server url. let host = ''; - if (window.location.host == 'localhost:8081') { - host = 'http://localhost:8080'; + if (window.location.host == 'localhost:8080') { + host = DEV_PROXY_HOST; } // I use format xml instead of json because json responses seem to be very bugged in QGIS server @@ -412,7 +396,6 @@ function propertySearch(text, requestId, searchOptions, dispatch){ let coordinates = xmlDoc.getElementsByTagName("coordinates")[0].childNodes[0].nodeValue.split(','); let number = xmlDoc.getElementsByTagName("qgs:nummer")[0].childNodes[0].nodeValue; let egrid = xmlDoc.getElementsByTagName("qgs:egris_egrid")[0].childNodes[0].nodeValue; - console.log(coordinates); results.push({ id: "property", @@ -422,28 +405,27 @@ function propertySearch(text, requestId, searchOptions, dispatch){ id: 1, text: "Nummer: " + number + ", EGRID: " + egrid, label: "Liegenschaft nr: " + number, - x: coordinates[0], - y: coordinates[1], - crs: 'EPSG:2056', - provider: 'properties' + x: parseFloat(coordinates[0]), + y: parseFloat(coordinates[1]), + crs: 'EPSG:2056' }] }); } - - dispatch(addSearchResults({data: results, provider: "properties", reqId: requestId}, true)); + + callback({results: results}); }); } /////////////////////////////////////////////////////////////////////////////// -function entranceSearch(text, requestId, searchOptions, dispatch){ +function entranceSearch(text, searchParams, callback, axios){ let results = []; // If it is a local installation (i.e. running with yarn development server) // I have to change the url to the actual QGIS server url. let host = ''; - if (window.location.host == 'localhost:8081') { - host = 'http://localhost:8080'; + if (window.location.host == 'localhost:8080') { + host = DEV_PROXY_HOST; } // I use format xml instead of json because json responses seem to be very bugged in QGIS server @@ -455,7 +437,6 @@ function entranceSearch(text, requestId, searchOptions, dispatch){ let coordinates = xmlDoc.getElementsByTagName("coordinates")[0].childNodes[0].nodeValue.split(','); let number = xmlDoc.getElementsByTagName("qgs:gebaeudenummer")[0].childNodes[0].nodeValue; let egid = xmlDoc.getElementsByTagName("qgs:gwr_egid")[0].childNodes[0].nodeValue; - console.log(coordinates); results.push({ id: "property", @@ -465,15 +446,14 @@ function entranceSearch(text, requestId, searchOptions, dispatch){ id: 1, text: "Nummer: " + number + ", egid: " + egid, label: "Gebäudeeingang nr: " + number, - x: coordinates[0], - y: coordinates[1], + x: parseFloat(coordinates[0]), + y: parseFloat(coordinates[1]), crs: 'EPSG:2056', - provider: 'entrances' }] }); } - - dispatch(addSearchResults({data: results, provider: "entrances", reqId: requestId}, true)); + + callback({results: results}); }); } @@ -484,47 +464,38 @@ function entranceSearch(text, requestId, searchOptions, dispatch){ export const SearchProviders = { coordinates: { labelmsgid: "search.coordinates", - onSearch: coordinatesSearch + onSearch: coordinatesSearch, + handlesGeomFilter: false }, 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 }, 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 }, hydrants: { - label: "Hydrants", - onSearch: hydrantSearch + label: "Hydranten", + onSearch: hydrantSearch, + handlesGeomFilter: false }, properties: { label: "Liegenschaft", - onSearch: propertySearch + onSearch: propertySearch, + handlesGeomFilter: false }, entrances: { label: "Gebäudeeingang", - onSearch: entranceSearch + onSearch: entranceSearch, + 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: