Closed r9zzai closed 1 year ago
The request has an error 400 code because the info_format
provided by lizmap when doing GetFeatureInfo request is not available for the server . But server seems to response with a default format , and the response is handled by lizmap
For exemple request for https://www.lfu.bayern.de/gdi/wms/energieatlas/photovoltaikanlagen return an code 400 but the body contains data.
For https://geoservices.bayern.de/wms/ , it's the same , but the response is empty (even in QGIS desktop) like raw requset in broswer
Thank you for the info. For example https://geoportal.freiburg.de/wms/gdm_address/gdm_address is working (GetMap and GetFeatureInfo) even without Check Receive Images directly from that WMS-Serer in the lizmap QGIS desktop Plugin.
Can my solution be merged or is there anything against it? I mean its nearly the same if its not a gml File then return '' and if there are no children in the xml File then return '' because in both cases we try to parse the children in the xml file.
I think i got it , in your first comment the problem with the lfu.bayern
WMS is that the XML response returned has features info in the attributes of the FIELDS
tag
(see raw request )
XML response from external WMS can come in very various way, so lizmap just return the body
But IMHO, the gfiGmlToHtml
function aims to format GML response, and not XML, so your solution is too specific.
but a workaround may can be made using as js script to format the response in the popup in the browser side
lizMap.events.on({
'lizmappopupdisplayed': function(e) {
//get popupcontent
let popupContent = $('#popupcontent .lizmapPopupContent').html()
// format content
}
});
see lizmap doc
Okay, thank you for your opinion and tip. Js Script was my first approach. But here i was stuck on not recognizing that gml info_format
. So my code tried to recreate every popup and i cant recreate the buttons for example to edit a feature. Another not so nice result of the js code is that the created element in the popup table gets stuck in css format so it cant be deleted. So if i click a few times on many activated layers there are hundreds of popups stuck in the css format. But that is not a performance issue at all. It just would be cleaner to do that in PHP.
Here ist my Js script solution for external WMS layers not having GML in info_format
, maybe it does help others:
map_crs = "EPSG:3857"
wms_crs = "EPSG:31468"//transform to needed crs in wms query, most layer will work with EPSG 3857
// wms_crs = "EPSG:3857"
noresult="Kein Treffer"//condition for popups not to show if in html of getfeatureinfo
div_div1cls_arr=[]//popup features
lizMap.events.on({
uicreated: function(e) {
lizMap.map.events.register("click", lizMap.map , querywmsgetfeatureinfofun);
html=`
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/table-to-json@1.0.0/lib/jquery.tabletojson.min.js"></script>
<div id="externalwmspopupcontent"></div>
`
$("#content").append(html)
// to_observe = document.getElementById("dock")
to_observe = document.getElementById("nav-tab-switcher")
observer2.observe(to_observe, {attributes : true});
}
});
lizMap.events.on({
lizmappopupdisplayed: function(e) {
$(".lizmapPopupContent").find('h4').remove()//remove nothing is found header
}
});
querywmsgetfeatureinfofun=async function querywmsgetfeatureinfo(e){
to_observe = document.getElementById("popupcontent")
observer.observe(to_observe, {attributes : true});
//maybe observe dock and check if popupcontent and switcher is active
//then click dock-close after that click button-switcher
point=e.xy //x,y
size=lizMap.map.getSize() //w,h
extent=lizMap.map.getExtent() //left bottom right top
lyrs=lizMap.map.layers
randomstrs=[] //clear list of valid layer randomstr
for (var i=0;i<lyrs.length;i++){
randomstr=makeid(5)//randomstr for each request to hide stale css elements
randomstrs.push(randomstr)
data= await querywmslayersgetfeatureinfofun(point,size,extent,lyrs,i,randomstr)
}
$("#externalwmspopupcontent")[0].randomstrs=randomstrs
checkpopups(randomstrs) //hide stale popup features
}
//make random string
function makeid(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
//build wms query
querywmslayersgetfeatureinfofun=async function querywmslayersgetfeatureinfo(point,size,extent,lyrs,i,randomstr) {
return new Promise(function(resolve, reject) {
bsrlizmap="index.php"
bsr=window.location.href.split("?")[1] //possibility to include another lizmap project as "external" wms
dsply=lyrs[i].div.attributes.style.value
try {
if (lyrs[i].isVisible == true || dsply.includes("display: block")){
if (lyrs[i].url.includes(bsr) == true) {
urli=window.location.href.replace("view/map","lizmap/service")
urli+="&SERVICE=WMS&VERSION="
use_wms_crs=map_crs
}else if (lyrs[i].url.includes(bsrlizmap) == true) {
urli=lyrs[i].url
urli+="&SERVICE=WMS&VERSION="
use_wms_crs=map_crs
}else{
urli=lyrs[i].url
urli+="?SERVICE=WMS&VERSION="
use_wms_crs=wms_crs
}
urli+=lyrs[i].params["VERSION"]
urli+="&REQUEST=GetFeatureInfo&BBOX="
cp1 = new OpenLayers.Geometry.Point(extent["left"], extent["bottom"]);
cp2 = new OpenLayers.Geometry.Point(extent["right"], extent["top"]);
cp1.transform(map_crs, use_wms_crs);
cp2.transform(map_crs, use_wms_crs);
urli+=String(cp1.x)+","+String(cp1.y)+","+String(cp2.x)+","+String(cp2.y)
urli+="&CRS="+use_wms_crs+"&SRS="+use_wms_crs+"&LAYERS="
urli+=lyrs[i].params["LAYERS"]
urli+="&STYLES=&FORMAT=image/png&QUERY_LAYERS="
urli+=lyrs[i].params["LAYERS"]
urli+="&INFO_FORMAT=text/html&FEATURE_COUNT=10&"
urli+="I="+point["x"]+"&J="+point["y"]+"&X="+point["x"]+"&Y="+point["y"]+"&WIDTH="+size["w"]+"&HEIGHT="+size["h"]
urli=urli.replace("??","?")
// console.log("getfeatureinfourl ", urli)
$.ajax({
type: 'get',
url:urli,//+"&callback=?",
success:function(data){
// console.log("urli is",urli)
if (data.includes("lizmap")==false) {
if (data.indexOf("<iframe")>-1) {
data=new DOMParser().parseFromString(data, "text/html");
iframe= $(data).find("iframe")
urli=iframe[0].src
$.ajax({
type: 'post',
url:"../../../gis/geturlcontent.php",
data:{urli:urli},
success:function(data){
arr=parseHTMLTables(data,1)
// push arr into table
if (arr.length>0){
buildtableonpopup(lyrs[i].params["LAYERS"],arr,randomstr)
// buildtableonpopupfun(data,lyrs,i,randomstr)
}
resolve("")
}
})
}else{
arr=parseHTMLTables(data,0)
// push arr into table
if (arr.length>0){
buildtableonpopup(lyrs[i].params["LAYERS"],arr,randomstr)
// buildtableonpopupfun(data,lyrs,i,randomstr)
}
resolve("")
}
}
}
})
resolve("") //resolve nothing if there is no ajax
}
resolve("")
}catch(e){console.log("err " ,e )}
})
}
div_div1cls_arr=[]
//hide stale popup features
function checkpopups(randomstrs){
for (g in div_div1cls_arr) {
div_div1cls_arr[g].hidden=true
for ( ff in randomstrs) {
if (div_div1cls_arr[g].id.indexOf(randomstrs[ff])>-1) {
div_div1cls_arr[g].hidden=false
break
}
}
}
for (g in div_div1cls_arr) {
try{ //popup active
$(".lizmapPopupContent")[0].append(div_div1cls_arr[g])
$("#popupcontent")[0].append(div_div1cls_arr[g])
}catch(e){ }
}
}
//add active to classliste of popupcontent if it has gone and readd popup features
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
try {
let cl=mutation.target.classList
if (!(cl.contains("active"))) {
$("#nav-tab-popupcontent")[0].classList.add("active")
$(".lizmapPopupContent").find('h4').remove()
cl.add("active")
adock=$("#dock")
adock.css( "display", "block")
adock.css( "width", "auto%")
randomstrs=$("#externalwmspopupcontent")[0].randomstrs //get current identifiers of popup features
checkpopups(randomstrs) //readd popup features
observer.disconnect()
}
} catch(e) {}
});
});
//maybe observe dock and check if popupcontent and switcher is active
//then click dock-close after that click button-switcher
//happens if layer has no popup but is editable
var observer2 = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
try {
cl1=$("#nav-tab-switcher").hasClass("active")
cl2=$("#nav-tab-popupcontent").hasClass("active")
if (cl1 && cl2) {
$("nav-tab-popupcontent").removeClass("active")
cl2=$("#nav-tab-popupcontent").hasClass("active")
$("#dock-close").click()
$("#button-switcher").click()
}
} catch(e) {console.log("err ",e)}
});
});
arr=[]
//build table
function buildtableonpopup(title,arr,randomstr) {
feats=[]
//console.log("title is ", title , " arr is ", arr)
if (arr[0][0]=="0"){
narrr=[]
for (var f=1;f<arr.length;f++) {
narrr.push({'0':arr[f][0],'1':arr[f][1]})
}
arr=narrr
}
ffeat=arr[0][0]
for (f in arr) {
if (arr[f][0]==ffeat) {
feats.push(Number(f))
}
}
if (feats.length>1) {
leof=feats[1]
}else{
leof=arr.length
}
for (f in feats) {
narr=[]
for (var g=0;g<leof;g++) {
try {
if([arr[feats[f]+g][0],arr[feats[f]+g][1]].toString()!=[ "Feld", "Wert" ].toString()) {
//console.log([arr[feats[f]+g][0],arr[feats[f]+g][1]].toString!=[ "Feld", "Wert" ].toString()
narr.push([arr[feats[f]+g][0],arr[feats[f]+g][1]])
}
}catch(e) {}
}
popupbuilder(title,narr,randomstr)
}
}
function popupbuilder(title,arr,randomstr) {
div1h41cls="lizmapPopupTitle"
div_div1h41cls = document.createElement('h4');
div_div1h41cls.classList.add(div1h41cls);
div_div1h41cls.textContent=title
div1div1cls="lizmapPopupDiv"
div_div1div1cls = document.createElement('div');
div_div1div1cls.classList.add(div1div1cls);
div1divtblcls="table"// table-condensed table-striped table-bordered lizmapPopupTable"
table = document.createElement("table")
table.classList.add(div1divtblcls);
//table table-condensed table-striped table-bordered lizmapPopupTable
//table.classList.add("table-condense");
table.classList.add("table-condensed");
table.classList.add("table-striped");
table.classList.add("table-bordered");
table.classList.add("lizmapPopupTable");
//header row
header = table.createTHead()
row = header.insertRow();
cellA = row.insertCell();
cellB = row.insertCell();
cellA.outerHTML = "<th>Feld</th>";
cellB.outerHTML = "<th>Wert</th>";
tbody = table.createTBody()
for (var i = 0 ; i<arr.length;i++) {
// normal rows
row = tbody.insertRow();
cellA = row.insertCell();
cellB = row.insertCell();
cellA.outerHTML = "<th>" + arr[i][0] + "</th>";
cellB.innerHTML = arr[i][1];
}
div_div1div1cls.appendChild(table);
div1cls="lizmapPopupSingleFeature"
div_div1cls = document.createElement('div');
div_div1cls.classList.add(div1cls);
div_div1cls.id="wmsexternal"+randomstr
div_div1cls.appendChild(div_div1h41cls);
div_div1cls.appendChild(div_div1div1cls);
$(".lizmapPopupContent")[0].appendChild(div_div1cls)
$("#popupcontent")[0].appendChild(div_div1cls)
div_div1cls_arr.push(div_div1cls)
}
htmltablewtf=""
//parse tables to html tables
function parseHTMLTables(data,special) {
jobjs=[]
data=new DOMParser().parseFromString(data, "text/html");
//tbls= $("#table").tableToJSON()
tbls= $(data).find("table")
arr=[]
htmltablewtf=data
if (tbls.length>0) {
for (var i=0;i<tbls.length;i++) {
tbl=tbls[i]
tbl.id="wmstbl"+i
if ($("#" + "wmstbl"+i ).length == 1) {
// Replace
$("#" + "wmstbl"+i ).replaceWith(tbl)
}else {
// Append
$("#map-content").append(tbl)
}
if (special) {
myData = tbl.rows
my_liste = []
for (var i = 0; i < myData.length; i++) {
el = myData[i].children
my_el = []
for (var j = 0; j < el.length; j++) {
my_el.push(el[j].innerText);
}
my_liste.push(my_el)
}
jobj=[]
for (var f=1;f<my_liste.length;f++) {
jobj.push({'0':my_liste[f][0],'1':my_liste[f][1]})
}
}else{
jobj=$("#" + "wmstbl"+i).tableToJSON()
}
// console.log("obj is " , jobj)
tbl.style=[]
tbl.style["display"]="none" //needs to be displayed for tableToJSON function
jobjs.push(jobj)
arr=appendJsonTableToPopup(jobj)
}
}
return arr
}
//conditions to choose custom functions to parse different html tables in 2d array
function appendJsonTableToPopup(jobj) {
arr=[]
try {if (Object.keys(jobj[0]).length==1) { //n2 - table
arr=appendJsonTableToPopup_type_2n_1(jobj)
}}catch(e){}
try {if (Object.keys(jobj[0]).length==2) { //2n(2) - table
arr=appendJsonTableToPopup_type_2n_2(jobj)
}}catch(e){}
try {if (Object.keys(jobj[0]).length>2) { //2n - table
arr=appendJsonTableToPopup_type_2n_3(jobj)
}}catch(e){}
return arr
}
//custom functions to parse different html tables in 2d array
function appendJsonTableToPopup_type_2n_1(jobj) {
try {
arr=[]
kys=Object.keys(jobj)
for (i=0;i<kys.length;i++) {
ky=Object.keys(jobj[kys[i]])[0]
arr.push([ky, jobj[kys[i]][ky]])
}
}catch(e){
arr=[]
kys=Object.keys(jobj)
for (i=0;i<kys.length;i++) {
arr.push([kys[i], jobj[kys[i]]])
}
}
return arr
}
function appendJsonTableToPopup_type_2n_2(jobj) {
arr=[]
kys=Object.keys(jobj[0])
arr.push([kys[0],kys[1]])
for (i=0;i<jobj.length;i++) {
arr.push([jobj[i][kys[0]], jobj[i][kys[1]]])
}
return arr
}
function appendJsonTableToPopup_type_2n_3(jobj) {
arr=[]
kys=Object.keys(jobj[0])
for (i=0;i<kys.length;i++) {
arr.push([kys[i], jobj[0][kys[i]]])
}
return arr
}
Demo: http://52.59.84.1/lizmap-web-client-3.6.0/lizmap/www/index.php/view/map?repository=4&project=test2
additional tiny PHP script (requested in above Js script) for re request of WMS GetFeatureInfo having <iframe>
tag in response (e.g. https://geoservices.bayern.de/wms/v1/ogc_bauleitplan.cgi?):
<?php
$urli=$_POST["urli"];
$urli=urldecode($urli);
$urli=str_replace("xyz","?",$urli);
$ch = curl_init($urli);
curl_setopt($ch, CURLOPT_HEADER, false); // we don't want headers
curl_setopt($ch, CURLOPT_NOBODY, false); // we need body
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_TIMEOUT,10);
$response = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo $response;
?>
What is the Bug/Enhancement?
When having external WMS sources in the QGIS project. When clicking on a feature of the external WMS source then Lizmap Popup is says that there is no object on that place and there is a 400 Error in the network log.
Proposed solution: replace function gfiGmlToHtml in lizmap/modules/lizmap/lib/Request/WMSRequest.php
Further Questions: in the original mentioned function the name of the layer plus "_layer" is checked in the xml response:
how can that be achieved that the external WMS response gets modified that the response contains name of the layer plus "_layer" so that the popup is working for external WMS layers? Do I something wrong here acivating Popup for external WMS Layers?
Demo Project: http://52.59.84.1/lizmap-web-client-3.6.0/lizmap/www/index.php/view/map?repository=4&project=test
Steps to reproduce the issue
Versions
Versions :
Check Lizmap plugin
QGIS server version, only if the section above doesn't mention the QGIS Server version
No response
Operating system
Debian GNU/Linux 11 (bullseye)
Browsers
Firefox
Browsers version
110.0 (64-Bit)
Relevant log output