1

I have created a Leaftlet map in an Angular project, using an extended version of tileLayer WMS class:

export class ExtendedWMS extends TileLayer.WMS {
    constructor(baseUrl: string, options: ExtendOptions, header: { header: string; value: string }[] ) {
        super (baseUrl, options, header)

        Util.setOptions(this, options);
    };
}

export interface ExtendOptions extends WMSOptions {
    myOption?: number
    myOption2?: boolean;
    myOption3?: string;
}

Then I have created a base layer:

tileLayer.ExtendedWMS(
    'https://<my-domain>/api/map/',
    {
        format: 'image/png',
        transparent: true,
        layers: 'MyLayer,MyLayer2,MyLayer3',
        myOption: 2,
        myOption2: true 
    }
)

Now I want to add some overlay layers to layers control of the map to make them selectable by the user throught related checkbox.

To obtain overlay layers from the same domain I could create new WMS layers in the following way:

tileLayer.ExtendedWMS(
    'https://<my-domain>/api/map/',
    {
        format: 'image/png',
        transparent: true,
        layers: 'MyLayer,MyLayer2,MyLayer3',
        myOption: 2,
        myOption2: true
        myOption3: 'someOption'
    }
)

just changing only a parameter ("myOption3"):

tileLayer.ExtendedWMS(
    'https://<my-domain>/api/map/',
    {
        format: 'image/png',
        transparent: true,
        layers: 'MyLayer,MyLayer2,MyLayer3',
        myOption: 2,
        myOption2: true
        myOption3: 'someOtherOption'
    }
)

Now the problem is that the map provider expect to combine different layers changing one parameter:

myOption3: 'someOption,someOtherOption'

but this is not the way layers control combine the layers.

Currently if the user selects one layer the endpoint contacted that I see on network tab of browser is the following:

service=wms&format=image%2fpng&transparent=true&layers=MyLayer%2CMyLayer2%2CMyLayer3&myOption=2&myOption2=true&myOption3=someOption

when the user selects another layer I would have the following query:

service=wms&format=image%2fpng&transparent=true&layers=MyLayer%2CMyLayer2%2CMyLayer3&myOption=2&myOption2=true&myOption3=someOption%2CsomeOtherOption

instead is mutually exclusive:

service=wms&format=image%2fpng&transparent=true&layers=MyLayer%2CMyLayer2%2CMyLayer3&myOption=2&myOption2=true&myOption3=someOtherOption

How can I get this kind of combination in that parameter and at the same time make it joinable trought the layers control by the user?

4
  • Simple solution would be to create separate layers for different combinations of options.
    – TomazicM
    Commented Feb 3 at 15:43
  • I did it, but they doesn't get merged when selected buy the user. Every layer generates new images, so every time the user selects the checkbox of a layer it overwrites the previous selection.
    – smartmouse
    Commented Feb 3 at 20:19
  • If I understand your correctly, you want to select in layers control several so called layers to be showed combined at once, but these layers are not really separate layers but combination of options for single WMS source?
    – TomazicM
    Commented Feb 4 at 16:34
  • I have added more info at the end of my question. Please check it out
    – smartmouse
    Commented Feb 5 at 9:29

2 Answers 2

1

One possible solution is to use dummy empty layer groups just for selection of properties that are to be used when requesting WMS layer.

Logic goes like this:

  • Create as many dummy empty layer groups as there are different properties you want to combine when requesting WMS layer. Assign unique custom id property to identify it when it's selected. Add all layer groups as overlays to layer control.
  • Create 'overlayadd' map event processing function. First remove current WMS layer that was previously added to the map (if it exists). Check then if combination of properties was already requested. If yes, add existing layer to the map. If not, create new WMS layer with requested properties and add it to the map.
  • Create 'overlayremove' map event processing function. First remove current WMS layer that was previously added to the map (if it exists). Check then if combination of properties that remain selected was already requested. If yes, add existing layer to the map. If not, create new WMS layer with requested properties and add it to the map.

Below is an example of how code could look like when three different layers can be selected from the same WMS source:

var map = L.map("map", {zoomControl: false}).setView([38.561303, 68.767846], 10);
var osmLayer = L.tileLayer(
  "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
  {
    maxZoom: 19,
    attribution:
      '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
  }
).addTo(map);

var layerControl = L.control.layers({'OSM': osmLayer}, null, {collapsed: false}).addTo(map);

var selectLayer = [];
var selection = 0;
var wmsLayer = [];
var layerName = ['tajikistan:EAR_Builtup', 'tajikistan:Hazard_Flood10', 'tajikistan:EAR_Road'];
var currentLayer = null;

for (var i = 0; i <= 2; i++) {
  selectLayer[i] = L.layerGroup();
  selectLayer[i].myId = 2**i;
  layerControl.addOverlay(selectLayer[i], 'Overlay ' + (i + 1));
}        

function getWmsLayer(selection) {
  if (!wmsLayer[selection]) {
    var layers = '';
    for (var i = 0; i <= 2; i++) {
      if (selection & 2**i) {
        if (layers.length > 0) layers += ',';
        layers += layerName[i];
      }
    }
    wmsLayer[selection] = L.tileLayer.wms("http://tajirisk.ait.ac.th:8080/geoserver/wms", {
      layers: layers,
      format: 'image/png',
      transparent: true
    });
  }
  return(wmsLayer[selection]);
}

map.on('overlayadd', function(evt) {
  var id = evt.layer.myId;
  selection |= id;
  if (currentLayer) map.removeLayer(currentLayer);
  currentLayer = getWmsLayer(selection);
  currentLayer.addTo(map);
});

map.on('overlayremove', function(evt) {
  var id = evt.layer.myId;
  selection &= ~id;
  
  map.removeLayer(currentLayer);
  if (selection) {
    currentLayer = getWmsLayer(selection);
    currentLayer.addTo(map);
    }
  else {
    currentLayer = null;
  }
});

Here is working JSFiddle: https://jsfiddle.net/TomazicM/ynvxa794/

2
  • Thank you for your solution. It was quite the same against to what I tried to do meanwhile but in my case using Util.setOptions(<my_base_layer>, { myOption3: myOptions.join(',') }), instead of remove and add again the map, doesn't do the trick, maybe because I can't find a way to trigger map redraw :( Anyway your solution is not perfect because I see that the map is fully removed and then added again. It could be not a nice effect for the users. Any better workaround?
    – smartmouse
    Commented Feb 5 at 21:33
  • See alternative answer.
    – TomazicM
    Commented Feb 6 at 14:11
1

Another possibility (to make transition from one selection to another more smooth) is to again use dummy empty layer groups just for selection of properties that are to be used when requesting WMS layer, but for layer display use only two layers, each on it's own map pane and then do ease in/out of layer display with pane's CSS style transition property.

Logic would then go like this:

  • Create as many dummy empty layer groups as there are different properties you want to combine when requesting WMS layer. Assign unique custom id property to identify it when it's selected. Add all layer groups as overlays to layer control.
  • Create two WMS layers, each on his own map pane, with map pane opacity set to 0.
  • Create 'overlayadd' map event processing function. There load new WMS layer with the currently selected options using setParamsmethod. Then ease out previously shown WMS by setting it's pane opacity to 0, with duration 1s and delay 1s (experiment with values) and ease in new WMS layer by setting it's pane opacity to 1, with duration 1s.
  • Create 'overlayremove' map event processing function. There do the same as in 'overlayadd' function.

Below is an example of how code could look like when three different layers can be selected from the same WMS source:

var map = L.map("map", {zoomControl: false}).setView([38.561303, 68.767846], 10);
var osmLayer = L.tileLayer(
  "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
  {
    maxZoom: 19,
    attribution:
      '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
  }
).addTo(map);

var layerControl = L.control.layers({'OSM': osmLayer}, null, {collapsed: false}).addTo(map);

var pane = [];
pane[0] = map.createPane('wms1');
pane[1] = map.createPane('wms2');
pane[0].style.opacity = 0;
pane[1].style.opacity = 0;

var selectLayer = [];
var wmsLayer = [];
var layerSel = 0;
var prevSelection = 0;
var layerName = ['tajikistan:EAR_Builtup', 'tajikistan:Hazard_Flood10', 'tajikistan:EAR_Road'];

wmsLayer[0] = L.tileLayer.wms("http://tajirisk.ait.ac.th:8080/geoserver/wms", {
  pane: pane[0],
  format: 'image/png',
  transparent: true
}).addTo(map);
wmsLayer[1] = L.tileLayer.wms("http://tajirisk.ait.ac.th:8080/geoserver/wms", {
  pane: pane[1],
  format: 'image/png',
  transparent: true
}).addTo(map);

for (var i = 0; i <= 2; i++) {
  selectLayer[i] = L.layerGroup();
  selectLayer[i].myId = 2**i;
  layerControl.addOverlay(selectLayer[i], 'Overlay ' + (i + 1));
}        

function setWmsLayer(selection) {
  var layers = '';
  for (var i = 0; i <= 2; i++) {
    if (selection & 2**i) {
      if (layers.length > 0) layers += ',';
      layers += layerName[i];
    }
  }
  layerSel = (layerSel) ? 0 : 1;
  wmsLayer[layerSel].setParams({
    layers: layers,
    format: 'image/png',
    transparent: true
  });
  pane[prevSelection].style.transition = 'opacity 1s';
  pane[prevSelection].style['transition-delay'] = '1.2s';
  pane[layerSel].style.transition = 'opacity 1s';
  pane[prevSelection].style.zIndex = 210;
  pane[layerSel].style.zIndex = 205;
  pane[prevSelection].style.opacity = 0;
  pane[layerSel].style.opacity = 1;
  prevSelection = layerSel;
}

map.on('overlayadd', function(evt) {
  var id = evt.layer.myId;
  selection |= id;
  setWmsLayer(selection);
});

map.on('overlayremove', function(evt) {
  var id = evt.layer.myId;
  selection &= ~id;        
  setWmsLayer(selection);
});

Here is working JSFiddle example: https://jsfiddle.net/TomazicM/uhforet8/

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.