4

I'm using OpenLayers 3 and GeoServer to create a spatial web application in which I want to enable users to choose a colour ramp (using Colorbrewer) and a number of breaks for styling an attribute in their analysis layer (i.e. to style a choropleth). I have been able to get this working by dynamically generating an SLD XML string and passing it in as the value to the SLD_BODY parameter in the TileWMS source, but only with 3 breaks/classes. When I select 4 or more classes nothing is returned and I believe the issue is that the SLD becomes too long for the OpenLayers TileWMS GET request. I have read a variety of posts that explain you can do a POST request in OpenLayers 2 to get around this issue, but I haven't found anything on how to do this using OpenLayers 3. Only a response in this post hints at a solution, but it's not enough for me to work out an answer. The following is how I'm currently defining the layer and adding the SLD. Does anyone know how to get around character limit with the OpenLayers 3 GET request by using POST instead?

window.layer = new ol.layer.Tile({
  source: new ol.source.TileWMS({
   url: 'http://' + window.location.hostname + ':8080/geoserver/wms',
   params: {LAYERS: geoserverLayer, STYLES: undefined, SLD_BODY: sld, CQL_FILER: geoserverFilter, TILED: true},
   serverType: 'geoserver'
  })
});
3
  • i want to overlay my own wms url on the base map by passing wmsurl and layer name please help me out to understand that your code will help in that way i am thinking if yes please feel free to share your code i will send my code to you sir. My mail id is [email protected]
    – user28536
    Commented Jul 4, 2016 at 6:40
  • here the url is specific to the user or what does url means in source can you explain it
    – user28536
    Commented Jul 4, 2016 at 10:41
  • is that url is generic to users
    – user28536
    Commented Jul 4, 2016 at 10:41

2 Answers 2

6

I am not sure whether my answer is going to give you the final solution. But give it a try and let us know if something good comes up. So do the following:

  1. add tileLoadFunction to your source. Like so:

    window.layer = new ol.layer.Tile({
    source: new ol.source.TileWMS({
    url: 'http://' + window.location.hostname + ':8080/geoserver/wms',
    params: {LAYERS: geoserverLayer, STYLES: undefined, SLD_BODY: sld, CQL_FILER: geoserverFilter, TILED: true},
    serverType: 'geoserver',
    tileLoadFunction: function(image, src) {
       imagePostFunction(image, src);
    }
    })
    });
    
  2. create your custom imagePostFunction like so:

    function imagePostFunction(image, src) {
    var img = image.getImage();
    if (typeof window.btoa === 'function') {
     var xhr = new XMLHttpRequest();
     xhr.open('POST', src, true);
     xhr.responseType = 'arraybuffer';
     xhr.onload = function(e) {
     if (this.status === 200) {
      console.log("this.response",this.response);
      var uInt8Array = new Uint8Array(this.response);
      var i = uInt8Array.length;
      var binaryString = new Array(i);
      while (i--) {
        binaryString[i] = String.fromCharCode(uInt8Array[i]);
      }
      var data = binaryString.join('');
      var type = xhr.getResponseHeader('content-type');
      if (type.indexOf('image') === 0) {
        img.src = 'data:' + type + ';base64,' + window.btoa(data);
      }
    }
    };
    xhr.send();
    } else {
    img.src = src;
    }
    }
    

    It should force ol3 to execute a POST request but I am not sure if parameters are going to be passed to the POST body of the request and thus solve the long url problem.

Also consider the following post , might helps. In any way please let us know if it works.

UPDATE HERE FOR imagePostFunction

function imagePostFunction(image, src) {
var img = image.getImage();
if (typeof window.btoa === 'function') {
  var xhr = new XMLHttpRequest();
  console.log("src",src);
  **//GET ALL THE PARAMETERS OUT OF THE SOURCE URL**
  var dataEntries = src.split("&");
  var url;
  var params = "";
  for (var i = 0 ; i< dataEntries.length ; i++){
      console.log("dataEntries[i]",dataEntries[i]);
      if (i===0){
      url = dataEntries[i];    
      }
      else{
      params = params + "&"+dataEntries[i];
      }
  }
  console.log("params",params);
  xhr.open('POST', url, true);

  xhr.responseType = 'arraybuffer';
  xhr.onload = function(e) {
    if (this.status === 200) {
      console.log("this.response",this.response);
      var uInt8Array = new Uint8Array(this.response);
      var i = uInt8Array.length;
      var binaryString = new Array(i);
      while (i--) {
        binaryString[i] = String.fromCharCode(uInt8Array[i]);
      }
      var data = binaryString.join('');
      var type = xhr.getResponseHeader('content-type');
      if (type.indexOf('image') === 0) {
        img.src = 'data:' + type + ';base64,' + window.btoa(data);
      }
    }
  };
  //SET THE PROPER HEADERS AND FINALLY SEND THE PARAMETERS 
  xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  xhr.setRequestHeader("Content-length", params.length);
  xhr.setRequestHeader("Connection", "close");
  xhr.send(params);
} else {
  img.src = src;
}
}
3
  • Hey, thanks for the response. I really appreciate it. I'm still working on the solution based on the code you provided as I'm not seeing my layer. Admittedly I'm a bit out of my depths understanding what you're doing with the HTTP response, so I'm struggling a bit to get the code to work. When I log the src argument the request looks in order with the SLD_BODY parameter in there, so I'm guessing the issue is with handling of the response. Hopefully I'll get somewhere soon and be able to post a proper reply. Commented Jan 5, 2016 at 3:50
  • The function imagePostFunction will force ol3 to do a POST request. But it will pass the parameters within the url. So you will have a POST request but you should still have the long url problem. I ll make an update to show you a function that finally solves the problem. I have tested for a wms layer and it works fine. Check the update.
    – pavlos
    Commented Jan 5, 2016 at 13:49
  • Cheers, I finally got things working but I had to implement them a bit differently from your solution because I was running into a CORS issue. I essentially reproduced your code while moving the HTTP request to the server, but was able to simplify things a little and get the server to return the base64 string. I'll post my solution as well. Thanks a lot! Commented Jan 7, 2016 at 2:49
3

I was able to come up with a solution to the OL3 WMS POST request based on the solution offered above by @pavlos. I had to move the HTTP request to the server to get around the CORS issue I was running into. I'm using the Meteor framework so my server code is in Javascript, and I just had to add the http-extras package by aldeed so that I could specify a 'buffer' responseType, which prevents any encoding of the response. Below is my client-sided code. The key here was to keep the base url and the params separate, and not pass src present as an argument in the tileLoadFunction to the HTTP request, as src includes all the parameters in a query string and that makes the the url too long. Instead I passed the params to the HTTP request on the server, which embeds them in the request body.

var url = 'http://' + window.location.hostname + ':8080/geoserver/wms';
  window.mceLayer = new ol.layer.Tile({
    source: new ol.source.TileWMS({
      url: 'http://' + window.location.hostname + ':8080/geoserver/wms',
      params: {LAYERS: geoserverLayer, STYLES: undefined, SLD_BODY: sld, CQL_FILER: geoserverMceFilter, TILED: true},
      serverType: 'geoserver',
      tileLoadFunction: function (image, src) {
        var img = image.getImage();
        var query = src.replace(url + '?', '');
        var params = getQueryParams(query); // A separate function to decode the query string into a JSON object.
        if (typeof window.btoa === 'function') {
          Meteor.call('geoserverWms', 'POST', url, params, function(err, result) {
            if (err) {
              return console.error('err:', err);
            }
            // console.log(result);
            var type = 'image/png';
            img.src = 'data:' + type + ';base64,' + result;
          });
        } else {
          img.src = src;
        }
      }
    })
  });

In the HTTP Post request on the server, I set the content-type to 'application/x-www-form-urlencoded' and the responseType to 'buffer'. Then I made my server function return the response content as a base64 string. See below.

geoserverWms: function (method, url, params) {
    var options = {
      headers: {'Content-Type': 'application/x-www-form-urlencoded'},
      params: params,
      responseType: 'buffer'
    }
    var result = HTTP.call(method, url, options);
    console.log('Content:', result.content);
    return result.content.toString('base64');
  }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.