Skip to content

Commit

Permalink
feature: add search locations through nominatim (#157)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: mab <benjamin.marguin@makina-corpus.com>
  • Loading branch information
Cédric Farcy and mabhub authored Oct 5, 2023
1 parent bacd426 commit 894eb00
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 35 deletions.
47 changes: 12 additions & 35 deletions src/views/Visualizer/View/View.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,14 @@ import {
} from '@terralego/core/modules/Visualizer/services/layersTreeUtils';
import classnames from 'classnames';
import debounce from 'debounce';
import turfCenter from '@turf/center';
import turfBbox from '@turf/bbox';
import memoize from 'memoize-one';
import { connectSettings } from '../../Main/Provider/context';

import DataTable from './DataTable';
import Widgets from './Widgets';
import { generateClusterList } from './interactions';
import BoundingBoxObserver from '../../../components/BoundingBoxObserver';
import searchInMap from './search';

export const INTERACTION_DISPLAY_DETAILS = 'displayDetails';

Expand Down Expand Up @@ -600,38 +599,6 @@ export class Visualizer extends React.Component {
details.hide = () => removeHighlight({ layerId, featureId });
};

searchInMap = async query => {
const { activeAndSearchableLayers: layers } = this;

if (!layers.length) return undefined;

const { responses } = await searchService.msearch(
layers.map(([{ filters: { layer }, baseEsQuery }]) => ({
query,
index: layer,
baseQuery: baseEsQuery,
size: 6,
})),
);

const results = layers.map(([{
label, layers: resultsLayers, filters: { mainField },
}], index) => ({
group: label,
total: responses[index].hits.total.value,
results: responses[index].hits.hits.map(({ _id: id, _source: source }) => ({
label: source[mainField] || id,
id,
...source,
center: turfCenter(source.geom).geometry.coordinates,
bounds: turfBbox(source.geom),
layers: resultsLayers,
})),
}));

return results;
}

onPrintToggle = printIsOpened => {
this.hideDetails();
this.setState({ printIsOpened });
Expand Down Expand Up @@ -891,6 +858,10 @@ export class Visualizer extends React.Component {
enable: measureControl,
styles: measureDrawStyles,
} = {},
searchInLocations: {
enable: locationsEnable,
searchProvider,
} = {},
} = {},
theme: {
logo,
Expand Down Expand Up @@ -948,7 +919,13 @@ export class Visualizer extends React.Component {

if (displaySearchInMap) {
const search = controls.find(({ control }) => control === CONTROL_SEARCH);
search.onSearch = this.searchInMap;
search.onSearch = searchInMap({
language,
searchProvider,
locationsEnable,
translate: t,
layers: activeAndSearchableLayers,
});
search.onSearchResultClick = this.searchResultClick;
}

Expand Down
98 changes: 98 additions & 0 deletions src/views/Visualizer/View/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import turfCenter from '@turf/center';
import turfBbox from '@turf/bbox';

import searchService from '@terralego/core/modules/Visualizer/services/search';

export const fetchNominatim = async ({
query,
translate,
baseUrl,
language = 'en',
options: { viewbox = [] } = {},
}) => {
const url = new URL(baseUrl);
url.searchParams.set('q', query);
url.searchParams.set('q', query);
url.searchParams.set('format', 'geojson');
url.searchParams.set('accept-language', language);
if (viewbox.length) {
url.searchParams.set('viewbox', viewbox);
url.searchParams.set('polygon_geojson', 1);
url.searchParams.set('bounded', 1);
}

const headers = new Headers([['Content-Type', 'application/json']]);
let results;
try {
results = await fetch(url, {
headers,
}).then(response => response.json());
} catch (e) {
return [];
}
// Filter to avoid duplicates location
const filteredFeatures = results.features.reduce((list, result) => {
if (!list.some(item => item.properties.display_name === result.properties.display_name)) {
list.push(result);
}
return list;
}, []);
const data = [
{
total: filteredFeatures.length,
group: translate('terralego.map.search_results.locations'),
results: filteredFeatures.map(
({ bbox, properties: { osm_id: id, display_name: label } }) => ({
label,
id,
bounds: bbox,
}),
),
},
];
return data;
};


const searchInMap = ({
searchProvider: { provider, baseUrl, options = {} },
layers,
translate,
locationsEnable,
language = 'en',
}) => async query => {
let locations = [];
if (locationsEnable && provider === 'Nominatim') {
locations = await fetchNominatim({ query, language, translate, baseUrl, options });
}

if (!layers.length && !locations.length) return undefined;

const { responses } = await searchService.msearch(
layers.map(([{ filters: { layer }, baseEsQuery }]) => ({
query,
index: layer,
baseQuery: baseEsQuery,
size: 6,
})),
);

const results = layers.map(([{
label, layers: resultsLayers, filters: { mainField },
}], index) => ({
group: label,
total: responses[index].hits.total.value,
results: responses[index].hits.hits.map(({ _id: id, _source: source }) => ({
label: source[mainField] || id,
id,
...source,
center: turfCenter(source.geom).geometry.coordinates,
bounds: turfBbox(source.geom),
layers: resultsLayers,
})),
}));

return [...results, ...locations];
};

export default searchInMap;
Loading

0 comments on commit 894eb00

Please sign in to comment.