diff --git a/assets/src/components/FeatureToolbar.js b/assets/src/components/FeatureToolbar.js index 8b7ee8aa15..b1597485cf 100644 --- a/assets/src/components/FeatureToolbar.js +++ b/assets/src/components/FeatureToolbar.js @@ -15,6 +15,8 @@ import { getCenter } from 'ol/extent.js'; import GeoJSON from 'ol/format/GeoJSON.js'; import GPX from 'ol/format/GPX.js'; import KML from 'ol/format/KML.js'; +import Point from 'ol/geom/Point.js'; +import {fromExtent} from 'ol/geom/Polygon.js'; import '../images/svg/map-print.svg'; @@ -397,12 +399,6 @@ export default class FeatureToolbar extends HTMLElement { } zoom() { - // FIXME: necessary? - // Remove map popup to avoid confusion - if (lizMap.map.popups.length != 0){ - lizMap.map.removePopup(lizMap.map.popups[0]); - } - if (this.getAttribute('crs')){ const featureExtent = [ parseFloat(this.getAttribute('bbox-minx')), @@ -415,9 +411,18 @@ export default class FeatureToolbar extends HTMLElement { this.getAttribute('crs'), lizMap.mainLizmap.projection ); - lizMap.mainLizmap.extent = targetMapExtent; - }else{ - lizMap.zoomToFeature(this.featureType, this.fid, 'zoom'); + + let geom; + // The geom is a Point + if (targetMapExtent[0] == targetMapExtent[2] && targetMapExtent[1] == targetMapExtent[3]) { + geom = new Point([targetMapExtent[0], targetMapExtent[1]]) + } else { + geom = fromExtent(targetMapExtent); + } + + mainLizmap.map.zoomToGeometryOrExtent(geom); + } else { + mainLizmap.map.zoomToFid(this.featureType + '.' + this.fid); } } diff --git a/assets/src/legacy/atlas.js b/assets/src/legacy/atlas.js index 09d0babd50..864fb75243 100644 --- a/assets/src/legacy/atlas.js +++ b/assets/src/legacy/atlas.js @@ -6,6 +6,8 @@ */ import DOMPurify from 'dompurify'; +import GeoJSON from 'ol/format/GeoJSON.js'; +import { getCenter } from 'ol/extent.js'; (function () { @@ -531,26 +533,18 @@ import DOMPurify from 'dompurify'; * @param feature */ function runAtlasItem(feature) { - - // Use OL tools to reproject feature geometry - var format = new OpenLayers.Format.GeoJSON({ - ignoreExtraDims: true - }); - var feat = format.read(feature)[0]; - var f = feat.clone(); - var proj = lizMap.config.layers[lizAtlasConfig.layername]['featureCrs']; - f.geometry.transform(proj, lizMap.map.getProjection()); + const olFeature = (new GeoJSON()).readFeature(feature); // Zoom to feature if (lizAtlasConfig['zoom']) { if (lizAtlasConfig['zoom'].toLowerCase() == 'center') { // center - var lonlat = f.geometry.getBounds().getCenterLonLat(); - lizMap.map.setCenter(lonlat); + const center = getCenter(olFeature.getGeometry().getExtent()); + lizMap.map.setCenter(center); } else { // zoom - lizMap.map.zoomToExtent(f.geometry.getBounds()); + lizMap.mainLizmap.map.zoomToGeometryOrExtent(olFeature.getGeometry()); } } diff --git a/assets/src/legacy/filter.js b/assets/src/legacy/filter.js index f3f7b64ead..f87a680252 100644 --- a/assets/src/legacy/filter.js +++ b/assets/src/legacy/filter.js @@ -1462,8 +1462,7 @@ var lizLayerFilterTool = function () { if (!bounds || abounds.length != 4) { return false; } - var extent = new OpenLayers.Bounds(abounds[0], abounds[1], abounds[2], abounds[3]); - lizMap.map.zoomToExtent(extent); + lizMap.mainLizmap.map.zoomToGeometryOrExtent(abounds); return false; } diff --git a/assets/src/legacy/map.js b/assets/src/legacy/map.js index 31f6da366b..83440acb7d 100644 --- a/assets/src/legacy/map.js +++ b/assets/src/legacy/map.js @@ -13,7 +13,6 @@ import { extend } from 'ol/extent.js'; import WFS from '../modules/WFS.js'; import WMS from '../modules/WMS.js'; import Utils from '../modules/Utils.js'; -import DOMPurify from 'dompurify'; window.lizMap = function() { /** @@ -772,59 +771,6 @@ window.lizMap = function() { window.addEventListener('resize', updateContentSize); } - /** - * Get features for locate by layer tool - * @param aName - */ - function updateLocateFeatureList(aName) { - var locate = config.locateByLayer[aName]; - // clone features reference - var features = {}; - for ( var fid in locate.features ) { - features[fid] = locate.features[fid]; - } - // filter by filter field name - if ('filterFieldName' in locate) { - var filterValue = $('#locate-layer-'+cleanName(aName)+'-'+locate.filterFieldName).val(); - if ( filterValue != '-1' ) { - for (var fid in features) { - var feat = features[fid]; - if (feat.properties[locate.filterFieldName] != filterValue) - delete features[fid]; - } - } else - features = {} - } - // filter by vector joins - if ( 'vectorjoins' in locate && locate.vectorjoins.length != 0 ) { - var vectorjoins = locate.vectorjoins; - for ( var i=0, len =vectorjoins.length; i< len; i++) { - var vectorjoin = vectorjoins[i]; - var jName = vectorjoin.joinLayer; - if ( jName in config.locateByLayer ) { - var jLocate = config.locateByLayer[jName]; - var jVal = $('#locate-layer-'+cleanName(jName)).val(); - if ( jVal == '-1' ) continue; - var jFeat = jLocate.features[jVal]; - for (var fid in features) { - var feat = features[fid]; - if ( feat.properties[vectorjoin.targetFieldName] != jFeat.properties[vectorjoin.joinFieldName] ) - delete features[fid]; - } - } - } - } - // create the option list - const placeHolder = config.layers[aName].title; - var options = ''; - for (var fid in features) { - var feat = features[fid]; - options += ''; - } - // add option list - $('#locate-layer-'+cleanName(aName)).html(options); - } - /** * * @param layer_name @@ -837,401 +783,6 @@ window.lizMap = function() { layer[0].destroyFeatures(); } - /** - * Zoom to locate feature - * @param aName - */ - function zoomToLocateFeature(aName) { - // clear highlight layer - lizMap.mainLizmap.map.clearHighlightFeatures(); - - // get locate by layer val - var locate = config.locateByLayer[aName]; - var layerName = cleanName(aName); - var proj = new OpenLayers.Projection(locate.crs); - var val = $('#locate-layer-'+layerName).val(); - if (val == '-1') { - - // Trigger event - lizMap.events.triggerEvent('lizmaplocatefeaturecanceled', - { - 'featureType': aName - } - ); - } else { - // zoom to val - var feat = locate.features[val]; - var format = new OpenLayers.Format.GeoJSON({ - ignoreExtraDims: true - }); - feat = format.read(feat)[0]; - - if( feat.geometry != null){ - feat.geometry.transform(proj, map.getProjection()); - // Show geometry if asked - if (locate.displayGeom == 'True') { - var getFeatureUrlData = getVectorLayerWfsUrl( aName, null, null, null ); - getFeatureUrlData['options']['PROPERTYNAME'] = ['geometry',locate.fieldName].join(','); - getFeatureUrlData['options']['FEATUREID'] = val; - // Get data - $.post( getFeatureUrlData['url'], getFeatureUrlData['options'], function(data) { - if ( !data.features ){ - data = JSON.parse(data); - } - lizMap.mainLizmap.map.setHighlightFeatures(data.features[0], "geojson"); - }).fail(function(){ - lizMap.mainLizmap.map.setHighlightFeatures(feat, "geojson"); - }); - } - // zoom to extent - map.zoomToExtent(feat.geometry.getBounds()); - } - - var fid = val.split('.')[1]; - - // Trigger event - lizMap.events.triggerEvent('lizmaplocatefeaturechanged', - { - 'featureType': aName, - 'featureId': fid - } - ); - } - } - - /** - * Get features for locate by layer tool - * @param aName - */ - function getLocateFeature(aName) { - var locate = config.locateByLayer[aName]; - - // get fields to retrieve - var fields = ['geometry',locate.fieldName]; - // if a filter field is defined - if ('filterFieldName' in locate) - fields.push( locate.filterFieldName ); - // check for join fields - if ( 'filterjoins' in locate ) { - var filterjoins = locate.filterjoins; - for ( var i=0, len=filterjoins.length; i b - } - } else { - if (isNaN(bProperty)) { // a number and b string - return -1; // a < b - } else { // a and b are numbers - return parseFloat(aProperty) - parseFloat(bProperty); - } - } - }); - var filterPlaceHolder = ''; - if ( 'filterFieldAlias' in locate && locate.filterFieldAlias!='') - filterPlaceHolder += locate.filterFieldAlias+' '; - else - filterPlaceHolder += locate.filterFieldName; - filterPlaceHolder +=' ('+ lConfig.title + ')'; - var fOptions = ''; - var fValue = '-1'; - for (var i=0, len=features.length; i'+fValue+''; - } - } - - - // add filter values list - $('#locate-layer-'+layerName).parent().before('

'); - // listen to filter select changes - $('#locate-layer-'+layerName+'-'+locate.filterFieldName).change(function(){ - var filterValue = $(this).children(':selected').val(); - updateLocateFeatureList( aName ); - if (filterValue == '-1') - $('#locate-layer-'+layerName+'-'+locate.filterFieldName+' ~ span > input').val(''); - $('#locate-layer-'+layerName+' ~ span > input').val(''); - $('#locate-layer-'+layerName).val('-1'); - zoomToLocateFeature(aName); - }); - // add combobox to the filter select - $('#locate-layer-'+layerName+'-'+locate.filterFieldName).combobox({ - position: { my : "right top", at: "right bottom" }, - "selected": function(evt, ui){ - if ( ui.item ) { - var self = $(this); - var uiItem = $(ui.item); - window.setTimeout(function(){ - self.val(uiItem.val()).change(); - }, 1); - } - } - }); - - // add place holder to the filter combobox input - $('#locate-layer-'+layerName+'-'+locate.filterFieldName+' ~ span > input').attr('placeholder', filterPlaceHolder).val(''); - $('#locate-layer-'+layerName+'-'+locate.filterFieldName+' ~ span > input').autocomplete('close'); - } - - // create combobox for the layer - features.sort(function(a, b) { - var aProperty = a.properties[locate.fieldName]; - var bProperty = b.properties[locate.fieldName]; - if (isNaN(aProperty)) { - if (isNaN(bProperty)) { // a and b are strings - return aProperty.localeCompare(bProperty); - } else { // a string and b number - return 1; // a > b - } - } else { - if (isNaN(bProperty)) { // a number and b string - return -1; // a < b - } else { // a and b are numbers - return parseFloat(aProperty) - parseFloat(bProperty); - } - } - }); - var placeHolder = ''; - if ('filterFieldName' in locate) { - if ( 'fieldAlias' in locate && locate.fieldAlias!='' ) - placeHolder += locate.fieldAlias+' '; - else - placeHolder += locate.fieldName+' '; - placeHolder += '('+lConfig.title+')'; - } else { - placeHolder = lConfig.title; - } - var options = ''; - for (var i=0, len=features.length; i' + DOMPurify.sanitize(feat.properties[locate.fieldName]) + ''; - } - // listen to select changes - $('#locate-layer-'+layerName).html(options).change(function() { - var val = $(this).children(':selected').val(); - if (val == '-1') { - $('#locate-layer-'+layerName+' ~ span > input').val(''); - // update to join layer - if ( 'filterjoins' in locate && locate.filterjoins.length != 0 ) { - var filterjoins = locate.filterjoins; - for (var i=0, len=filterjoins.length; i input').val(''); - } - } - } - } - $(this).blur(); - return; - }); - $('#locate-layer-'+layerName).combobox({ - "minLength": ('minLength' in locate) ? locate.minLength : 0, - "position": { my : "right top", at: "right bottom" }, - "selected": function(evt, ui){ - if ( ui.item ) { - var self = $(this); - var uiItem = $(ui.item); - window.setTimeout(function(){ - self.val(uiItem.val()).change(); - }, 1); - } - } - }); - $('#locate-layer-'+layerName+' ~ span > input').attr('placeholder', placeHolder).val(''); - $('#locate-layer-'+layerName+' option[value=-1]').attr('label', placeHolder); - $('#locate-layer-'+layerName+' ~ span > input').autocomplete('close'); - if ( ('minLength' in locate) && locate.minLength > 0 ) - $('#locate-layer-'+layerName).parent().addClass('no-toggle'); - if(mCheckMobile()){ - // autocompletion items for locatebylayer feature - $('div.locate-layer select').show(); - $('span.custom-combobox').hide(); - } - },'json'); - } - - function addLocateByLayer(){ - var locateByLayerList = []; - for (var lname in config.locateByLayer) { - if ('order' in config.locateByLayer[lname]) - locateByLayerList[config.locateByLayer[lname].order] = lname; - else - locateByLayerList.push(lname); - } - var locateContent = []; - for (var l in locateByLayerList) { - var lname = locateByLayerList[l]; - var lConfig = config.layers[lname]; - var html = '
'; - html += ''; - html += '
'; - //constructing the select - locateContent.push(html); - } - $('#locate .menu-content').html(locateContent.join('
')); - - var featureTypes = lizMap.mainLizmap.initialConfig.vectorLayerFeatureTypeList; - if (featureTypes.length == 0) { - config.locateByLayer = {}; - $('#button-locate').parent().remove(); - $('#locate-menu').remove(); - } else { - for (const featureType of featureTypes) { - var typeName = featureType.Name; - var lname = lizMap.getNameByTypeName(typeName); - if (!lname) { - if (typeName in config.locateByLayer) - lname = typeName - else if ((typeName in shortNameMap) && (shortNameMap[typeName] in config.locateByLayer)) - lname = shortNameMap[typeName]; - else { - for (var lbl in config.locateByLayer) { - if (lbl.split(' ').join('_') == typeName) { - lname = lbl; - break; - } - } - } - } - - if (!(lname in config.locateByLayer)) - continue; - - var locate = config.locateByLayer[lname]; - locate['crs'] = featureType.SRS; - loadProjDefinition(locate.crs, function () { - new OpenLayers.Projection(locate.crs); - }); - locate['bbox'] = featureType.LatLongBoundingBox; - } - - // get joins - for (var lName in config.locateByLayer) { - var locate = config.locateByLayer[lName]; - if ('vectorjoins' in locate && locate['vectorjoins'].length != 0) { - var vectorjoin = locate['vectorjoins'][0]; - locate['joinFieldName'] = vectorjoin['targetFieldName']; - for (var jName in config.locateByLayer) { - var jLocate = config.locateByLayer[jName]; - if (jLocate.layerId == vectorjoin.joinLayerId) { - vectorjoin['joinLayer'] = jName; - locate['joinLayer'] = jName; - jLocate['joinFieldName'] = vectorjoin['joinFieldName']; - jLocate['joinLayer'] = lName; - jLocate['filterjoins'] = [{ - 'targetFieldName': vectorjoin['joinFieldName'], - 'joinFieldName': vectorjoin['targetFieldName'], - 'joinLayerId': locate.layerId, - 'joinLayer': lName - }]; - } - } - } - } - - // get locate by layers features - for (var lname in config.locateByLayer) { - getLocateFeature(lname); - } - $('.btn-locate-clear').click(function () { - lizMap.mainLizmap.map.clearHighlightFeatures(); - $('#locate select').val('-1'); - $('div.locate-layer span > input').val(''); - - if (lizMap.lizmapLayerFilterActive) { - lizMap.events.triggerEvent('lizmaplocatefeaturecanceled', - { 'featureType': lizMap.lizmapLayerFilterActive } - ); - } - return false; - - }); - $('#locate-close').click(function () { - $('.btn-locate-clear').click(); // deactivate locate and filter - $('#button-locate').click(); - return false; - }); - } - } - /** * PRIVATE function: createToolbar * create the tool bar (collapse switcher, etc) @@ -2708,37 +2259,6 @@ window.lizMap = function() { },'json'); } }); - - // $.post( getFeatureUrlData['url'], getFeatureUrlData['options'], function(incomingData) { - // const data = DOMPurify.sanitize(incomingData); - - // aConfig['featureCrs'] = 'EPSG:4326'; - - // if (aConfig?.['alias'] && aConfig?.['types']) { - // callFeatureDataCallBacks(poolId, data.features); - // $('body').css('cursor', 'auto'); - // } else { - // $.post(globalThis['lizUrls'].service, { - // 'SERVICE':'WFS' - // ,'VERSION':'1.0.0' - // ,'REQUEST':'DescribeFeatureType' - // ,'TYPENAME': ('typename' in aConfig) ? aConfig.typename : aName - // ,'OUTPUTFORMAT':'JSON' - // }, function(describe) { - - // aConfig['alias'] = describe.aliases; - // aConfig['types'] = describe.types; - // aConfig['columns'] = describe.columns; - - // callFeatureDataCallBacks(poolId, data.features); - - // $('body').css('cursor', 'auto'); - - // },'json'); - // } - - // },'json'); - return true; } @@ -2748,8 +2268,7 @@ window.lizMap = function() { * @param proj * @param zoomAction */ - function zoomToOlFeature( feature, proj, zoomAction ){ - zoomAction = typeof zoomAction !== 'undefined' ? zoomAction : 'zoom'; + function zoomToOlFeature( feature, proj, zoomAction = 'action' ){ var format = new OpenLayers.Format.GeoJSON({ ignoreExtraDims: true }); @@ -2758,11 +2277,11 @@ window.lizMap = function() { feat.geometry.transform( proj, lizMap.map.getProjection() ); // Zoom or center to selected feature - if( zoomAction == 'zoom' ) - map.zoomToExtent(feat.geometry.getBounds()); - if( zoomAction == 'center' ){ - var lonlat = feat.geometry.getBounds().getCenterLonLat() - map.setCenter(lonlat); + if( zoomAction == 'zoom' ){ + lizMap.mainLizmap.map.zoomToGeometryOrExtent(feat.geometry.getBounds().toArray()); + } else if( zoomAction == 'center' ){ + const lonlat = feat.geometry.getBounds().getCenterLonLat(); + lizMap.mainLizmap.map.getView().setCenter([lonlat.lon, lonlat.lat]); } } } @@ -2773,9 +2292,7 @@ window.lizMap = function() { * @param fid * @param zoomAction */ - function zoomToFeature( featureType, fid, zoomAction ){ - zoomAction = typeof zoomAction !== 'undefined' ? zoomAction : 'zoom'; - + function zoomToFeature( featureType, fid, zoomAction = 'zoom' ){ getLayerFeature(featureType, fid, function(feat) { var proj = new OpenLayers.Projection(config.layers[featureType].crs); if( config.layers[featureType].featureCrs ) @@ -2866,10 +2383,6 @@ window.lizMap = function() { if( !feat ) return false; - // Remove map popup to avoid confusion - if (lizMap.map.popups.length != 0) - lizMap.map.removePopup( lizMap.map.popups[0] ); - // Get popup content by FILTER and not with virtual click on map var filter = ''; var qgisName = aName; @@ -3828,10 +3341,6 @@ window.lizMap = function() { })); } - if ('locateByLayer' in config) { - addLocateByLayer(); - } - /** * Event when layers have been added * @event layersadded @@ -3887,12 +3396,6 @@ window.lizMap = function() { return false; }); - // Show locate by layer - if (!('locateByLayer' in config)) - $('#button-locate').parent().hide(); - else - $('#button-locate').click(); - // hide mini-dock if no tool is active if ($('#mapmenu ul li.nav-minidock.active').length == 0) { $('#mini-dock-content > .tab-pane.active').removeClass('active'); diff --git a/assets/src/legacy/switcher-layers-actions.js b/assets/src/legacy/switcher-layers-actions.js index f4dff57c10..06b96aad11 100644 --- a/assets/src/legacy/switcher-layers-actions.js +++ b/assets/src/legacy/switcher-layers-actions.js @@ -413,20 +413,6 @@ var lizLayerActionButtons = function() { if(mapProjection == 'EPSG:900913') mapProjection = 'EPSG:3857'; - /*if( !( 'bbox' in itemConfig ) || !( mapProjection in itemConfig['bbox'] ) ){ - console.log('The layer bbox information has not been found in config'); - console.log(itemConfig); - return false; - }*/ - - /*var lex = itemConfig['bbox'][mapProjection]['bbox']; - var lBounds = new OpenLayers.Bounds( - lex[0], - lex[1], - lex[2], - lex[3] - );*/ - if ( !('extent' in itemConfig) ) { console.log('The layer extent information has not been found in config'); console.log(itemConfig); @@ -440,16 +426,6 @@ var lizLayerActionButtons = function() { var lBounds = OpenLayers.Bounds.fromArray(itemConfig['extent']); lBounds = lBounds.transform(itemConfig['crs'],mapProjection); - // Reverse axis - /*if (OpenLayers.Projection.defaults[mapProjection] && - OpenLayers.Projection.defaults[mapProjection].yx) { - lBounds = new OpenLayers.Bounds( - lex[1], - lex[0], - lex[3], - lex[2] - ); - }*/ lizMap.map.zoomToExtent( lBounds ); return false; diff --git a/assets/src/modules/Lizmap.js b/assets/src/modules/Lizmap.js index 456b332ffb..6152432872 100644 --- a/assets/src/modules/Lizmap.js +++ b/assets/src/modules/Lizmap.js @@ -25,6 +25,7 @@ import Legend from './Legend.js'; import Permalink from './Permalink.js'; import Search from './Search.js'; import Tooltip from './Tooltip.js'; +import LocateByLayer from './LocateByLayer.js'; import WMSCapabilities from 'ol/format/WMSCapabilities.js'; import WFSCapabilities from 'ol-wfs-capabilities'; @@ -167,6 +168,7 @@ export default class Lizmap { this.legend = new Legend(this.state.layerTree); this.search = new Search(this.map, this.lizmap3); this.tooltip = new Tooltip(); + this.locateByLayer = new LocateByLayer(); // Removed unusable button if (!this.config['printTemplates'] || this.config.printTemplates.length == 0 ) { diff --git a/assets/src/modules/LocateByLayer.js b/assets/src/modules/LocateByLayer.js new file mode 100644 index 0000000000..45337eaee9 --- /dev/null +++ b/assets/src/modules/LocateByLayer.js @@ -0,0 +1,473 @@ +/** + * @module modules/LocateByLayer.js + * @name LocateByLayer + * @copyright 2024 3Liz + * @author BOISTEAULT Nicolas + * @license MPL-2.0 + */ + +import { mainLizmap } from '../modules/Globals.js'; +import DOMPurify from 'dompurify'; + +import GeoJSON from 'ol/format/GeoJSON.js'; + +/** + * @class + * @name LocateByLayer + */ +export default class LocateByLayer { + constructor() { + this._locateByLayerConfig = lizMap.config.locateByLayer; + const locateBtn = document.getElementById('button-locate'); + if (mainLizmap.initialConfig.locateByLayer) { + this.addLocateByLayer(); + document.querySelector('#mini-dock-content .active').classList.remove('active'); + document.querySelector('#mapmenu .locate').classList.add('active'); + document.getElementById('locate').classList.add('active'); + } else { + locateBtn?.parentNode.classList.add('hide'); + } + } + + addLocateByLayer() { + var locateByLayerList = []; + for (var lname in this._locateByLayerConfig) { + if ('order' in this._locateByLayerConfig[lname]) + locateByLayerList[this._locateByLayerConfig[lname].order] = lname; + else + locateByLayerList.push(lname); + } + var locateContent = []; + for (var l in locateByLayerList) { + var lname = locateByLayerList[l]; + var lConfig = lizMap.config.layers[lname]; + var html = '
'; + html += ''; + html += '
'; + //constructing the select + locateContent.push(html); + } + $('#locate .menu-content').html(locateContent.join('
')); + + var featureTypes = lizMap.mainLizmap.initialConfig.vectorLayerFeatureTypeList; + if (featureTypes.length == 0) { + this._locateByLayerConfig = {}; + $('#button-locate').parent().remove(); + $('#locate-menu').remove(); + } else { + for (const featureType of featureTypes) { + var typeName = featureType.Name; + var lname = lizMap.getNameByTypeName(typeName); + if (!lname) { + if (typeName in this._locateByLayerConfig) + lname = typeName + else if ((typeName in shortNameMap) && (shortNameMap[typeName] in this._locateByLayerConfig)) + lname = shortNameMap[typeName]; + else { + for (var lbl in this._locateByLayerConfig) { + if (lbl.split(' ').join('_') == typeName) { + lname = lbl; + break; + } + } + } + } + + if (!(lname in this._locateByLayerConfig)) + continue; + + var locate = this._locateByLayerConfig[lname]; + locate['crs'] = featureType.SRS; + // loadProjDefinition(locate.crs, function () { + // new OpenLayers.Projection(locate.crs); + // }); + locate['bbox'] = featureType.LatLongBoundingBox; + } + + // get joins + for (var lName in this._locateByLayerConfig) { + var locate = this._locateByLayerConfig[lName]; + if ('vectorjoins' in locate && locate['vectorjoins'].length != 0) { + var vectorjoin = locate['vectorjoins'][0]; + locate['joinFieldName'] = vectorjoin['targetFieldName']; + for (var jName in this._locateByLayerConfig) { + var jLocate = this._locateByLayerConfig[jName]; + if (jLocate.layerId == vectorjoin.joinLayerId) { + vectorjoin['joinLayer'] = jName; + locate['joinLayer'] = jName; + jLocate['joinFieldName'] = vectorjoin['joinFieldName']; + jLocate['joinLayer'] = lName; + jLocate['filterjoins'] = [{ + 'targetFieldName': vectorjoin['joinFieldName'], + 'joinFieldName': vectorjoin['targetFieldName'], + 'joinLayerId': locate.layerId, + 'joinLayer': lName + }]; + } + } + } + } + + // get locate by layers features + for (var lname in this._locateByLayerConfig) { + this.getLocateFeature(lname); + } + $('.btn-locate-clear').click(function () { + lizMap.mainLizmap.map.clearHighlightFeatures(); + $('#locate select').val('-1'); + $('div.locate-layer span > input').val(''); + + if (lizMap.lizmapLayerFilterActive) { + lizMap.events.triggerEvent('lizmaplocatefeaturecanceled', + { 'featureType': lizMap.lizmapLayerFilterActive } + ); + } + return false; + + }); + $('#locate-close').click(function () { + $('.btn-locate-clear').click(); // deactivate locate and filter + document.getElementById('button-locate')?.click(); + return false; + }); + } + } + + /** + * Get features for locate by layer tool + * @param aName + */ + getLocateFeature(aName) { + var locate = this._locateByLayerConfig[aName]; + + // get fields to retrieve + var fields = ['geometry',locate.fieldName]; + // if a filter field is defined + if ('filterFieldName' in locate) + fields.push( locate.filterFieldName ); + // check for join fields + if ( 'filterjoins' in locate ) { + var filterjoins = locate.filterjoins; + for ( var i=0, len=filterjoins.length; i { + var lConfig = lizMap.config.layers[aName]; + locate['features'] = {}; + if ( !data.features ) + data = JSON.parse(data); + var features = data.features; + // if( locate.crs != 'EPSG:4326' && features.length != 0) { + // // load projection to be sure to have the definition + // loadProjDefinition( locate.crs, function() { + // locate.crs = 'EPSG:4326'; + // }); + // } + + if ('filterFieldName' in locate) { + // create filter combobox for the layer + features.sort(function(a, b) { + var aProperty = a.properties[locate.filterFieldName]; + var bProperty = b.properties[locate.filterFieldName]; + if (isNaN(aProperty)) { + if (isNaN(bProperty)) { // a and b are strings + return aProperty.localeCompare(bProperty); + } else { // a string and b number + return 1; // a > b + } + } else { + if (isNaN(bProperty)) { // a number and b string + return -1; // a < b + } else { // a and b are numbers + return parseFloat(aProperty) - parseFloat(bProperty); + } + } + }); + var filterPlaceHolder = ''; + if ( 'filterFieldAlias' in locate && locate.filterFieldAlias!='') + filterPlaceHolder += locate.filterFieldAlias+' '; + else + filterPlaceHolder += locate.filterFieldName; + filterPlaceHolder +=' ('+ lConfig.title + ')'; + var fOptions = ''; + var fValue = '-1'; + for (var i=0, len=features.length; i'+fValue+''; + } + } + + // add filter values list + $('#locate-layer-'+layerName).parent().before('

'); + // listen to filter select changes + document.getElementById('locate-layer-'+layerName+'-'+locate.filterFieldName).addEventListener("change", () => { + var filterValue = $(this).children(':selected').val(); + this.updateLocateFeatureList( aName ); + if (filterValue == '-1') + $('#locate-layer-'+layerName+'-'+locate.filterFieldName+' ~ span > input').val(''); + $('#locate-layer-'+layerName+' ~ span > input').val(''); + $('#locate-layer-'+layerName).val('-1'); + this.zoomToLocateFeature(aName); + }); + // add combobox to the filter select + $('#locate-layer-'+layerName+'-'+locate.filterFieldName).combobox({ + position: { my : "right top", at: "right bottom" }, + "selected": function(evt, ui){ + if ( ui.item ) { + const self = this; + var uiItem = $(ui.item); + window.setTimeout(function(){ + self.value = uiItem.val(); + self.dispatchEvent(new Event('change')); + }, 1); + } + } + }); + + // add place holder to the filter combobox input + $('#locate-layer-'+layerName+'-'+locate.filterFieldName+' ~ span > input').attr('placeholder', filterPlaceHolder).val(''); + $('#locate-layer-'+layerName+'-'+locate.filterFieldName+' ~ span > input').autocomplete('close'); + } + + // create combobox for the layer + features.sort(function(a, b) { + var aProperty = a.properties[locate.fieldName]; + var bProperty = b.properties[locate.fieldName]; + if (isNaN(aProperty)) { + if (isNaN(bProperty)) { // a and b are strings + return aProperty.localeCompare(bProperty); + } else { // a string and b number + return 1; // a > b + } + } else { + if (isNaN(bProperty)) { // a number and b string + return -1; // a < b + } else { // a and b are numbers + return parseFloat(aProperty) - parseFloat(bProperty); + } + } + }); + var placeHolder = ''; + if ('filterFieldName' in locate) { + if ( 'fieldAlias' in locate && locate.fieldAlias!='' ) + placeHolder += locate.fieldAlias+' '; + else + placeHolder += locate.fieldName+' '; + placeHolder += '('+lConfig.title+')'; + } else { + placeHolder = lConfig.title; + } + var options = ''; + for (var i=0, len=features.length; i' + DOMPurify.sanitize(feat.properties[locate.fieldName]) + ''; + } + document.getElementById('locate-layer-'+layerName).innerHTML = options; + // listen to select changes + document.getElementById('locate-layer-'+layerName).addEventListener("change", (event) => { + var val = event.target.value; + if (val == '-1') { + $('#locate-layer-'+layerName+' ~ span > input').val(''); + // update to join layer + if ( 'filterjoins' in locate && locate.filterjoins.length != 0 ) { + var filterjoins = locate.filterjoins; + for (var i=0, len=filterjoins.length; i input').val(''); + } + } + } + } + $(this).blur(); + return; + }); + $('#locate-layer-'+layerName).combobox({ + "minLength": ('minLength' in locate) ? locate.minLength : 0, + "position": { my : "right top", at: "right bottom" }, + "selected": function(evt, ui){ + if ( ui.item ) { + const self = this; + var uiItem = $(ui.item); + window.setTimeout(function(){ + self.value = uiItem.val(); + self.dispatchEvent(new Event('change')); + }, 1); + } + } + }); + $('#locate-layer-'+layerName+' ~ span > input').attr('placeholder', placeHolder).val(''); + $('#locate-layer-'+layerName+' option[value=-1]').attr('label', placeHolder); + $('#locate-layer-'+layerName+' ~ span > input').autocomplete('close'); + if ( ('minLength' in locate) && locate.minLength > 0 ) + $('#locate-layer-'+layerName).parent().addClass('no-toggle'); + if(lizMap.checkMobile()){ + // autocompletion items for locatebylayer feature + $('div.locate-layer select').show(); + $('span.custom-combobox').hide(); + } + },'json'); + } + + /** + * Zoom to locate feature + * @param aName + */ + zoomToLocateFeature(aName) { + // clear highlight layer + lizMap.mainLizmap.map.clearHighlightFeatures(); + + // get locate by layer val + var locate = this._locateByLayerConfig[aName]; + var layerName = lizMap.cleanName(aName); + var val = $('#locate-layer-'+layerName).val(); + if (val == '-1') { + // Trigger event + lizMap.events.triggerEvent('lizmaplocatefeaturecanceled', {'featureType': aName }); + } else { + // zoom to val + const featGeoJSON = locate.features[val]; + if( featGeoJSON.geometry){ + const geom = (new GeoJSON()).readGeometry(featGeoJSON.geometry, { + dataProjection: 'EPSG:4326', + featureProjection: lizMap.mainLizmap.projection + }); + // Show geometry if asked + if (locate.displayGeom == 'True') { + var getFeatureUrlData = lizMap.getVectorLayerWfsUrl( aName, null, null, null ); + getFeatureUrlData['options']['PROPERTYNAME'] = ['geometry',locate.fieldName].join(','); + getFeatureUrlData['options']['FEATUREID'] = val; + // Get data + $.post( getFeatureUrlData['url'], getFeatureUrlData['options'], function(data) { + if ( !data.features ){ + data = JSON.parse(data); + } + lizMap.mainLizmap.map.setHighlightFeatures(data.features[0], "geojson"); + }).fail(function(){ + lizMap.mainLizmap.map.setHighlightFeatures(feat, "geojson"); + }); + } + // zoom to extent + lizMap.mainLizmap.map.zoomToGeometryOrExtent(geom); + } + + var fid = val.split('.')[1]; + + // Trigger event + lizMap.events.triggerEvent('lizmaplocatefeaturechanged', + { + 'featureType': aName, + 'featureId': fid + } + ); + } + } + + /** + * Get features for locate by layer tool + * @param aName + */ + updateLocateFeatureList(aName) { + var locate = this._locateByLayerConfig[aName]; + // clone features reference + var features = {}; + for ( var fid in locate.features ) { + features[fid] = locate.features[fid]; + } + // filter by filter field name + if ('filterFieldName' in locate) { + var filterValue = $('#locate-layer-' + lizMap.cleanName(aName) + '-'+locate.filterFieldName).val(); + if ( filterValue != '-1' ) { + for (var fid in features) { + var feat = features[fid]; + if (feat.properties[locate.filterFieldName] != filterValue) + delete features[fid]; + } + } else + features = {} + } + // filter by vector joins + if ( 'vectorjoins' in locate && locate.vectorjoins.length != 0 ) { + var vectorjoins = locate.vectorjoins; + for ( var i=0, len =vectorjoins.length; i< len; i++) { + var vectorjoin = vectorjoins[i]; + var jName = vectorjoin.joinLayer; + if ( jName in this._locateByLayerConfig ) { + var jLocate = this._locateByLayerConfig[jName]; + var jVal = $('#locate-layer-' + lizMap.cleanName(jName)).val(); + if ( jVal == '-1' ) continue; + var jFeat = jLocate.features[jVal]; + for (var fid in features) { + var feat = features[fid]; + if ( feat.properties[vectorjoin.targetFieldName] != jFeat.properties[vectorjoin.joinFieldName] ) + delete features[fid]; + } + } + } + } + // create the option list + const placeHolder = lizMap.config.layers[aName].title; + var options = ''; + for (var fid in features) { + var feat = features[fid]; + options += ''; + } + // add option list + $('#locate-layer-'+ lizMap.cleanName(aName)).html(options); + } +}; diff --git a/assets/src/modules/config/Options.js b/assets/src/modules/config/Options.js index cde838a777..0710016077 100644 --- a/assets/src/modules/config/Options.js +++ b/assets/src/modules/config/Options.js @@ -29,6 +29,8 @@ const optionalProperties = { 'wmsMaxHeight': {type: 'number', default: 3000}, 'wmsMaxWidth': {type: 'number', default: 3000}, 'fixed_scale_overview_map': {type: 'boolean', default: true}, + 'max_scale_points': {type: 'number', default: 5000}, + 'max_scale_lines_polygons': {type: 'number', default: 5000}, 'use_native_zoom_levels': {type: 'boolean', nullable: true, default: null}, 'hide_numeric_scale_value': {type: 'boolean', default: false}, 'hideGroupCheckbox': { type: 'boolean', default: false }, @@ -62,6 +64,8 @@ export class OptionsConfig extends BaseObjectConfig { * @param {number} [cfg.wmsMaxHeight] - the image max height for WMS GetMap request * @param {number} [cfg.wmsMaxWidth] - the image max width for WMS GetMap request * @param {boolean} [cfg.fixed_scale_overview_map] - does the Overview map have fixed scale ? + * @param {number} [cfg.max_scale_points] - maximum scale when zooming on points + * @param {boolean} [cfg.max_scale_lines_polygons] - maximum scale when zooming on lines or polygons * @param {boolean} [cfg.use_native_zoom_levels] - does the map use native zoom levels ? * @param {boolean} [cfg.hide_numeric_scale_value] - does the scale line hide numeric scale value ? * @param {boolean} [cfg.hideGroupCheckbox] - are groups checkbox hidden ? @@ -207,6 +211,22 @@ export class OptionsConfig extends BaseObjectConfig { return this._fixed_scale_overview_map; } + /** + * Maximum scale when zooming on points + * @type {boolean} + */ + get max_scale_points() { + return this._max_scale_points; + } + + /** + * Maximum scale when zooming on lines or polygons + * @type {boolean} + */ + get max_scale_lines_polygons() { + return this._max_scale_lines_polygons; + } + /** * The map uses native zoom levels * @type {boolean} diff --git a/assets/src/modules/map.js b/assets/src/modules/map.js index 707290d84d..307b24d3a7 100644 --- a/assets/src/modules/map.js +++ b/assets/src/modules/map.js @@ -1074,4 +1074,53 @@ export default class map extends olMap { removeToolLayer(layer) { this._toolsGroup.getLayers().remove(layer); } + + /** + * Zoom to given geometry or extent + * @param {geometry|extent} geometryOrExtent The geometry or extent to zoom to. CRS is 4326 by default. + * @param {object} [options] Options. + */ + zoomToGeometryOrExtent(geometryOrExtent, options) { + const geometryType = geometryOrExtent.getType?.(); + if (geometryType && (mainLizmap.initialConfig.options.max_scale_lines_polygons || mainLizmap.initialConfig.options.max_scale_lines_polygons)) { + let maxScale; + if (['Polygon', 'Linestring', 'MultiPolygon', 'MultiLinestring'].includes(geometryType)){ + maxScale = mainLizmap.initialConfig.options.max_scale_lines_polygons; + } else if (geometryType === 'Point'){ + maxScale = mainLizmap.initialConfig.options.max_scale_points; + } + const resolution = mainLizmap.utils.getResolutionFromScale( + maxScale, + mainLizmap.map.getView().getProjection().getMetersPerUnit() + ); + if (!options?.minResolution) { + if (!options) { + options = { minResolution: resolution }; + } else { + options.minResolution = resolution; + } + } + } + this.getView().fit(geometryOrExtent, options); + } + + /** + * Zoom to given feature id + * @param {string} featureTypeDotId The string as `featureType.fid` to zoom to. + * @param {object} [options] Options. + */ + zoomToFid(featureTypeDotId, options) { + const [featureType, fid] = featureTypeDotId.split('.'); + if (!featureType || !fid) { + console.log('Wrong string for featureType.fid'); + return; + } + lizMap.getLayerFeature(featureType, fid, feat => { + const olFeature = (new GeoJSON()).readFeature(feat, { + dataProjection: 'EPSG:4326', + featureProjection: mainLizmap.projection + }); + this.zoomToGeometryOrExtent(olFeature.getGeometry(), options); + }); + } }