/**
* @module modules/Search.js
* @name Search
* @copyright 2023 3Liz
* @license MPL-2.0
*/
import { mainLizmap } from '../modules/Globals.js';
import { transformExtent } from 'ol/proj.js';
/**
* @class
* @name Search
*/
export default class Search {
constructor() {
// Attributes
this._config = lizMap.config;
this._map = lizMap.map;
// Add or remove searches!
var configOptions = this._config.options;
if (('searches' in configOptions) && (configOptions.searches.length > 0)) {
this._addSearches();
}
else {
$('#nominatim-search').remove();
$('#lizmap-search, #lizmap-search-close').remove();
}
}
/**
*
*/
_startExternalSearch() {
if ($('#search-query').val().length != 0) {
$('#lizmap-search .items li > a').unbind('click');
$('#lizmap-search .items').html('<li class="start"><ul><li>' + lizDict['externalsearch.search'] + '</li></ul></li>');
$('#lizmap-search, #lizmap-search-close').addClass('open');
} else {
lizMap.addMessage(lizDict['externalsearch.noquery'], 'info', true).attr('id', 'lizmap-search-message');
}
}
/**
*
*/
_getHighlightRegEx() {
// Format answers to highlight searched keywords
var sqval = $('#search-query').val();
var sqvals = sqval.split(' ');
var sqvalsn = [];
var sqrex = '(';
for (var i in sqvals) {
var sqi = sqvals[i].trim();
if (sqi == '') {
continue;
}
sqvalsn.push(sqi);
if (sqi != lizMap.cleanName(sqi)) {
sqvalsn.push(lizMap.cleanName(sqi));
}
}
sqrex += sqvalsn.join('|');
sqrex += ')';
return new RegExp(sqrex, "ig");
}
/**
* PRIVATE method: addExternalSearch
* add external search capability
*
* Returns:
* {Boolean} external search is in the user interface
* @param searchConfig
*/
_addSearch(searchConfig) {
if (searchConfig.type == 'externalSearch') {
return false;
}
if (!'url' in searchConfig) {
return false;
}
// define max extent for searches
var wgs84 = new OpenLayers.Projection('EPSG:4326');
var extent = new OpenLayers.Bounds(this._map.maxExtent.toArray());
extent.transform(this._map.getProjection(), wgs84);
$('#nominatim-search').submit(() => {
this._startExternalSearch();
// Format answers to highlight searched keywords
var labrex = this._getHighlightRegEx();
$.get(searchConfig.url
, {
"repository": globalThis['lizUrls'].params.repository,
"project": globalThis['lizUrls'].params.project,
"query": $('#search-query').val(),
"bbox": extent.toBBOX()
}
, (results) => {
var text = '';
var count = 0;
// Loop through results
for (var ftsId in results) {
var ftsLayerResult = results[ftsId];
text += '<li><strong>' + ftsLayerResult.search_name + '</strong>';
text += '<ul>';
for (var i = 0, len = ftsLayerResult.features.length; i < len; i++) {
var ftsFeat = ftsLayerResult.features[i];
var ftsGeometry = OpenLayers.Geometry.fromWKT(ftsFeat.geometry);
if (ftsLayerResult.srid != 'EPSG:4326') {
ftsGeometry.transform(ftsLayerResult.srid, 'EPSG:4326');
}
var bbox = ftsGeometry.getBounds();
if (extent.intersectsBounds(bbox)) {
var lab = ftsFeat.label.replace(labrex, '<strong class="highlight">$1</strong>');
text += '<li><a href="#' + bbox.toBBOX() + '" data-wkt="' + ftsGeometry.toString() + '">' + lab + '</a></li>';
count++;
}
}
text += '</ul></li>';
}
if (count != 0 && text != '') {
this._updateExternalSearch(text);
}
else {
this._updateExternalSearch('<li><strong>' + lizDict['externalsearch.mapdata'] + '</strong><ul><li>' + lizDict['externalsearch.notfound'] + '</li></ul></li>');
}
}, 'json');
return false;
});
return true;
}
/**
* PRIVATE method: addExternalSearch
* add external search capability
*
* Returns:
* {Boolean} external search is in the user interface
* @param searchConfig
*/
_addExternalSearch(searchConfig) {
if (searchConfig.type != 'externalSearch') {
return false;
}
// define max extent for searches
var wgs84 = new OpenLayers.Projection('EPSG:4326');
var extent = new OpenLayers.Bounds(this._map.maxExtent.toArray());
extent.transform(this._map.getProjection(), wgs84);
// define external search service
var service = null;
switch (searchConfig.service) {
case 'nominatim':
if ('url' in searchConfig) {
service = OpenLayers.Util.urlAppend(searchConfig.url
, new URLSearchParams(globalThis['lizUrls'].params)
);
}
break;
case 'ign':
service = 'https://data.geopf.fr/geocodage/completion/';
break;
case 'google':
if (google && 'maps' in google && 'Geocoder' in google.maps) {
service = new google.maps.Geocoder();
}
break;
}
if (service == null) {
return false;
}
$('#nominatim-search').submit(() => {
this._startExternalSearch();
// Format answers to highlight searched keywords
var labrex = this._getHighlightRegEx();
const searchQuery = document.getElementById('search-query').value;
switch (searchConfig.service) {
case 'nominatim':
$.get(service
, { "query": searchQuery, "bbox": extent.toBBOX() }
, data => {
var text = '';
var count = 0;
for (const address of data) {
if (count > 9) {
return false;
}
if (!address.boundingbox) {
return true;
}
var bbox = [
address.boundingbox[2],
address.boundingbox[0],
address.boundingbox[3],
address.boundingbox[1]
];
bbox = new OpenLayers.Bounds(bbox);
if (extent.intersectsBounds(bbox)) {
var lab = address.display_name.replace(labrex, '<strong class="highlight">$1</strong>');
text += `<li><a href="#${bbox.toBBOX()}" data-wkt="POINT(${address.lon} ${address.lat})">${lab}</a></li>`;
count++;
}
}
if (count == 0 || text == '') {
text = '<li>' + lizDict['externalsearch.notfound'] + '</li>';
}
this._updateExternalSearch('<li><strong>OpenStreetMap</strong><ul>' + text + '</ul></li>');
}, 'json');
break;
case 'ign':
if (searchQuery.length < 3 || searchQuery.length > 200) {
lizMap.addMessage(lizDict['externalsearch.ignlimit'], 'warning', true);
break;
}
let mapExtent4326 = transformExtent(mainLizmap.map.getView().calculateExtent(), mainLizmap.projection, 'EPSG:4326');
let queryParam = '?text=' + searchQuery + '&type=StreetAddress&maximumResponses=10&bbox=' + mapExtent4326
$.getJSON(encodeURI(service + queryParam), data => {
let text = '';
let count = 0;
for (const result of data.results) {
var lab = result.fulltext.replace(labrex, '<strong class="highlight">$1</strong>');
text += `<li><a href="#${result.x},${result.y},${result.x},${result.y}" data-wkt="POINT(${result.x} ${result.y})">${lab}</a></li>`;
count++;
}
if (count == 0 || text == '') {
text = '<li>' + lizDict['externalsearch.notfound'] + '</li>';
}
this._updateExternalSearch('<li><strong>IGN</strong><ul>' + text + '</ul></li>');
});
break;
case 'google':
service.geocode({
'address': searchQuery,
'bounds': new google.maps.LatLngBounds(
new google.maps.LatLng(extent.top, extent.left),
new google.maps.LatLng(extent.bottom, extent.right)
)
}, (results, status) => {
if (status == google.maps.GeocoderStatus.OK) {
var text = '';
var count = 0;
for (const address of results) {
if (count > 9) {
return false;
}
var bbox = [];
if (address.geometry.viewport) {
bbox = [
address.geometry.viewport.getSouthWest().lng(),
address.geometry.viewport.getSouthWest().lat(),
address.geometry.viewport.getNorthEast().lng(),
address.geometry.viewport.getNorthEast().lat()
];
} else if (address.geometry.bounds) {
bbox = [
address.geometry.bounds.getSouthWest().lng(),
address.geometry.bounds.getSouthWest().lat(),
address.geometry.bounds.getNorthEast().lng(),
address.geometry.bounds.getNorthEast().lat()
];
}
if (bbox.length != 4) {
return false;
}
bbox = new OpenLayers.Bounds(bbox);
if (extent.intersectsBounds(bbox)) {
var lab = address.formatted_address.replace(labrex, '<strong class="highlight">$1</strong>');
text += '<li><a href="#' + bbox.toBBOX() + '">' + lab + '</a></li>';
count++;
}
}
if (count == 0 || text == '') {
text = '<li>' + lizDict['externalsearch.notfound'] + '</li>';
}
this._updateExternalSearch('<li><strong>Google</strong><ul>' + text + '</ul></li>');
} else {
this._updateExternalSearch('<li><strong>Google</strong><ul><li>' + lizDict['externalsearch.notfound'] + '</li></ul></li>');
}
});
break;
}
return false;
});
return true;
}
/**
* PRIVATE method: _addSearches
* add searches capability
*
* Returns:
* {Boolean} searches added to the user interface
*/
_addSearches() {
var configOptions = this._config.options;
if (!('searches' in configOptions) || (configOptions.searches.length == 0)) {
return;
}
var searchOptions = configOptions.searches;
var searchAdded = false;
for (var i = 0, len = searchOptions.length; i < len; i++) {
var searchOption = searchOptions[i];
var searchAddedResult = false;
if (searchOption.type == 'externalSearch') {
searchAddedResult = this._addExternalSearch(searchOption);
}
else {
searchAddedResult = this._addSearch(searchOption);
}
searchAdded = searchAdded || searchAddedResult;
}
if (!searchAdded) {
$('#nominatim-search').remove();
$('#lizmap-search, #lizmap-search-close').remove();
}
return searchAdded;
}
/**
*
* @param aHTML
*/
_updateExternalSearch(aHTML) {
if ($('#search-query').val().length != 0) {
var wgs84 = new OpenLayers.Projection('EPSG:4326');
$('#lizmap-search .items li > a').unbind('click');
if ($('#lizmap-search .items li.start').length != 0) {
$('#lizmap-search .items').html(aHTML);
}
else {
$('#lizmap-search .items').append(aHTML);
}
$('#lizmap-search, #lizmap-search-close').addClass('open');
document.querySelectorAll('#lizmap-search .items li > a').forEach(link => {
link.addEventListener('click', evt => {
evt.preventDefault();
const linkClicked = evt.currentTarget;
var bbox = linkClicked.getAttribute('href').replace('#', '');
var bbox = OpenLayers.Bounds.fromString(bbox);
bbox.transform(wgs84, this._map.getProjectionObject());
this._map.zoomToExtent(bbox);
var feat = new OpenLayers.Feature.Vector(bbox.toGeometry().getCentroid());
var geomWKT = linkClicked.dataset.wkt;
if (geomWKT) {
mainLizmap.map.setHighlightFeatures(geomWKT, "wkt", "EPSG:4326");
}
$('#lizmap-search, #lizmap-search-close').removeClass('open');
// trigger event containing selected feature
lizMap.events.triggerEvent('lizmapexternalsearchitemselected',
{
'feature': feat
}
);
return false;
});
});
$('#lizmap-search-close button').click(() => {
$('#lizmap-search, #lizmap-search-close').removeClass('open');
return false;
});
}
}
}