Skip to content

Instantly share code, notes, and snippets.

@alfredotranchedone
Forked from rclark/Issues.md
Last active November 3, 2023 04:29
Show Gist options
  • Save alfredotranchedone/72326145ecff5d7d7233 to your computer and use it in GitHub Desktop.
Save alfredotranchedone/72326145ecff5d7d7233 to your computer and use it in GitHub Desktop.
L.TileLayer.BetterWMS.js extend L.TileLayer.WMS
There are a bunch of reasons why this is convoluted, mostly in building the URL to make the request:
1. You have to rely on an AJAX request, this example uses jQuery
2. To make a GetFeatureInfo request, you must provide a BBOX for a image, and the pixel coordinates for the part of the image that you want info from. A couple of squirrely lines of Leaflet code can give you that.
3. Output formats. The `info_format` parameter in the request. We don't know *a priori* which will be supported by a WMS that we might make a request to. See [Geoserver's docs](http://docs.geoserver.org/stable/en/user/services/wms/reference.html#getfeatureinfo) for what formats are available from Geoserver. That won't be the same from WMS to WMS, however.
4. WMS services return XML docs when there's a mistake in the request or in processing. This sends an HTTP 200, which jQuery doesn't think is an error.
5. added a php proxy and 2 new options (CORS workaround)
<!doctype html>
<html>
<head>
<title>WMS GetFeatureInfo</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.css" />
<!--[if lte IE 8]>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.ie.css" />
<![endif]-->
<script src="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.js"></script>
<style type="text/css">
html, body, #map {
margin: 0px;
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="L.TileLayer.BetterWMS.js"></script>
<script>
var map = L.map('map', {
center: [34,-111],
zoom: 7
});
var url = 'http://data.azgs.az.gov/arizona/wms';
L.tileLayer('http://{s}.tiles.mapbox.com/v3/rclark.map-wm3i8w1a/{z}/{x}/{y}.png').addTo(map);
L.tileLayer.betterWms(url, {
layers: 'azgs:mapunitpolys',
transparent: true,
format: 'image/png'
}).addTo(map);
</script>
</body>
</html>
L.TileLayer.BetterWMS = L.TileLayer.WMS.extend({
onAdd: function (map) {
// Triggered when the layer is added to a map.
// Register a click listener, then do all the upstream WMS things
L.TileLayer.WMS.prototype.onAdd.call(this, map);
map.on('click', this.getFeatureInfo, this);
},
onRemove: function (map) {
// Triggered when the layer is removed from a map.
// Unregister a click listener, then do all the upstream WMS things
L.TileLayer.WMS.prototype.onRemove.call(this, map);
map.off('click', this.getFeatureInfo, this);
},
getFeatureInfo: function (evt) {
// Make an AJAX request to the server and hope for the best
var url = this.getFeatureInfoUrl(evt.latlng),
showResults = L.Util.bind(this.showGetFeatureInfo, this);
$.ajax({
url: url,
success: function (data, status, xhr) {
var err = typeof data === 'string' ? null : data;
showResults(err, evt.latlng, data);
},
error: function (xhr, status, error) {
showResults(error);
}
});
},
getFeatureInfoUrl: function (latlng) {
// Construct a GetFeatureInfo request URL given a point
var point = this._map.latLngToContainerPoint(latlng, this._map.getZoom()),
size = this._map.getSize(),
params = {
request: 'GetFeatureInfo',
service: 'WMS',
srs: 'EPSG:4326',
styles: this.wmsParams.styles,
transparent: this.wmsParams.transparent,
version: this.wmsParams.version,
format: this.wmsParams.format,
bbox: this._map.getBounds().toBBoxString(),
height: size.y,
width: size.x,
layers: this.wmsParams.layers,
query_layers: this.wmsParams.layers,
info_format: 'text/html'
};
params[params.version === '1.3.0' ? 'i' : 'x'] = point.x;
params[params.version === '1.3.0' ? 'j' : 'y'] = point.y;
// return this._url + L.Util.getParamString(params, this._url, true);
var url = this._url + L.Util.getParamString(params, this._url, true);
/**
* CORS workaround (using a basic php proxy)
*
* Added 2 new options:
* - proxy
* - proxyParamName
*
*/
// check if "proxy" option is defined (PS: path and file name)
if(typeof this.wmsParams.proxy !== "undefined") {
// check if proxyParamName is defined (instead, use default value)
if(typeof this.wmsParams.proxyParamName !== "undefined")
this.wmsParams.proxyParamName = 'url';
// build proxy (es: "proxy.php?url=" )
_proxy = this.wmsParams.proxy + '?' + this.wmsParams.proxyParamName + '=';
url = _proxy + encodeURIComponent(url);
}
return url;
},
showGetFeatureInfo: function (err, latlng, content) {
if (err) { console.log(err); return; } // do nothing if there's an error
// Otherwise show the content in a popup, or something.
L.popup({ maxWidth: 800})
.setLatLng(latlng)
.setContent(content)
.openOn(this._map);
}
});
L.tileLayer.betterWms = function (url, options) {
return new L.TileLayer.BetterWMS(url, options);
};
<?php
/**
* PS: super basic template!
*
*/
// querystring parameter: same name of the WMS option
$url = $_GET['url'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,urldecode($url));
curl_setopt($ch, CURLOPT_REFERER, 'http://localhost');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
?>
@afrizalamri
Copy link

Hi, im using this code,
Screenshot (90)
but the data from the geoserver doesn't appear, what should I do?

@dazlab
Copy link

dazlab commented Sep 29, 2023

How can you style the returned table? I notice it's injecting a style tag prior to the table, which overrides any stylesheet definitions. Can styling be passed in via the params?

@ydrea
Copy link

ydrea commented Nov 2, 2023

i am trying to customize this a bit...
So far, I managed to make it filter out the rows without values.
However, I am having issues when trying to display an image, or even a link to an image!

import L from 'leaflet';
import axios from 'axios';

//
L.TileLayer.BetterWMS = L.TileLayer.WMS.extend({
  onAdd: function (map) {
    L.TileLayer.WMS.prototype.onAdd.call(this, map);
    map.on('click', this.getFeatureInfo, this);
  },

  onRemove: function (map) {
    L.TileLayer.WMS.prototype.onRemove.call(this, map);
    map.off('click', this.getFeatureInfo, this);
  },

  getFeatureInfo: function (evt) {
    var url = this.getFeatureInfoUrl(evt.latlng);
    var showResults = L.Util.bind(this.showGetFeatureInfo, this);

    axios
      .get(url)
      .then(function (response) {
        var err =
          typeof response.data === 'string' ? null : response.data;
        showResults(err, evt.latlng, response.data);
      })
      .catch(function (error) {
        showResults(error);
      });
  },

  getFeatureInfoUrl: function (latlng) {
    const point = this._map.latLngToContainerPoint(latlng);
    const size = this._map.getSize();

    const x = Math.round(point.x);
    const y = Math.round(point.y);

    const params = {
      request: 'GetFeatureInfo',
      service: 'WMS',
      crs: 'EPSG:4326',
      styles: this.wmsParams.styles,
      transparent: this.wmsParams.transparent,
      version: this.wmsParams.version,
      format: this.wmsParams.format,
      bbox: this._map.getBounds().toBBoxString(),
      height: size.y,
      width: size.x,
      layers: this.wmsParams.layers,
      query_layers: this.wmsParams.layers,
      info_format: 'text/html',
      // info_format: 'application/json',
      x: x,
      y: y,
    };

    return this._url + L.Util.getParamString(params, this._url, true);
  },
  showGetFeatureInfo: function (err, latlng, content) {
    if (err) {
      console.log(err);
      return;
    }
    var tempDiv = document.createElement('div');
    tempDiv.innerHTML = content;

    var rows = tempDiv.querySelectorAll('tr'); //

    for (var i = 0; i < rows.length; i++) {
      var row = rows[i];
      var cells = row.querySelectorAll('td');

      var isFotoUrl = cells[0].textContent.trim() === 'foto_url';

      if (isFotoUrl) {
        var imgUrl = cells[1].textContent.trim();
        imgUrl = imgUrl.replace(/["']/g, '');
        var link = document.createElement('a');
        link.href = imgUrl;
        link.target = '_blank';
        link.textContent = imgUrl;
        cells[1] = link;
      }

      var hasValue = Array.from(cells).some(function (cell) {
        return (
          cell.textContent.trim() !== '' &&
          cell.textContent.trim() !== 'NULL'
        );
      });

      if (!hasValue) {
        row.parentNode.removeChild(row);
      }
    }

    var filteredContent = tempDiv.innerHTML;

    L.popup({ maxWidth: 800 })
      .setLatLng(latlng)
      .setContent(filteredContent)
      .openOn(this._map);
  },


});

L.TileLayer.betterWms = function (url, options) {
  console.log(url, options);
  return new L.TileLayer.BetterWMS(url, options);
};

@ydrea
Copy link

ydrea commented Nov 3, 2023

did it!
had to go the hard way though...

showGetFeatureInfo: function (err, latlng, content) {
    if (err) {
      console.log(err);
      return;
    }

    var tempDiv = document.createElement('div');
    tempDiv.innerHTML = content;
    var rows = tempDiv.querySelectorAll('tr');

    for (var i = 0; i < rows.length; i++) {
      var row = rows[i];
      var cells = row.querySelectorAll('th, td');
      if (cells.length >= 2) {
        var header = cells[0].textContent.trim();
        var value = cells[1].textContent.trim();
        if (header === 'foto_url') {
          console.log('yea!');
          var imgUrl = value.replace(/["']/g, '');
          console.log(imgUrl);

          var newRow = document.createElement('tr');
          var newCell = document.createElement('td');

          var image = document.createElement('img');
          image.src = imgUrl;
          image.alt = 'Image';
          image.style.width = '177%';
          newCell.appendChild(image);

          newRow.appendChild(newCell);

          row.parentNode.replaceChild(newRow, row);
        } else {
          if (value === 'NULL' || value === '') {
            row.parentNode.removeChild(row);
          }
        }
      }
    }

    var filteredContent = tempDiv.innerHTML;
    console.log(filteredContent);
    L.popup({ maxWidth: 400 })
      .setLatLng(latlng)
      .setContent(filteredContent)
      .openOn(this._map);
  },

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment