1

I have a fully working map with a few base layers and some buttons, each of which loads and displays different resources. One loads some historical map overlays and uses the L.Control.Opacity plugin https://github.com/dayjournal/Leaflet.Control.Opacity to individually adjust visibility. In the stripped down tester code below I show a single baseLayer and an example overlay OS_1956 in historyMaps, which I hope is sufficient to demonstrate the outline of my scheme.

I'm seeking a generic method to add one or more geoJSON layers into the existing scheme.

One example is to show Roman roads in Britain. The data is available at https://dh.gu.se/dare/ and, as shown, can be very easily added into my map, with opacity control, using the supplied link

roman: new L.tileLayer("https://dh.gu.se/tiles/imperium/{z}/{x}/{y}.png")

However there are two issues. Firstly it covers the entire Roman empire and has far more detail than I want, but more seriously that detail comes married to an underlying map layer which I really don't want.

The project data is available at https://github.com/klokantech/roman-empire. All I want is (some of) the road data at https://github.com/klokantech/roman-empire/blob/master/data/roads_high.geojson. Clicking on that link displays the roads overlaid on a (different) base layer, while the 'download' button produces a geoJson FeatureCollection which has no reference to that baselayer.
So while it is apparent that L.Control.Opacity can adjust a layer ('roman') made by marrying a geoJSON resource to a tileLayer, I'm afraid the page source code is beyond my grasp and I can't figure out how it is achieved. Yet klokantech (who know a lot more about maps than me) have somehow done it in their git code such that clicking on a link brings up a working map with overlay but downloading the apparent contents makes no mention of that map. I'm a bit stumped. I want to reproduce what they've done, using as a base either a tileLayer of my choosing or an entirely transparent tileLayer.

As an alternative, I have turned a sample from the JSON FeatureCollection into a small js object called rroads, which I can easily add to the map on top of my chosen base layer. var myLayer = L.geoJSON(rroads).addTo(map); However that neither fits my existing scheme nor has opacity control.

I can achieve opacity control using the technique shown at https://stackoverflow.com/questions/47270647/change-opacity-using-leaflet-without-plugin That adds a geoJSON layer called geojson and works, though not as smoothly as I would like, but the control is outside the map area, and doesn't properly integrate with my existing scheme.

I have attempted to add the same data to historyMaps as histjson: new L.geoJson(rroads, {opacity: 0.01 }). That integrates a new L.Control.Opacity slider into my scheme but it doesn't do anything (possibly because L.Control.Opacity relies on .setOpacity which is not available to L.geoJson which for some reason only has .setStyle?)

So I'm stuck. All of my attempts have foundered for different reasons and I don't know what to do next. Can anyone advise on the best way to use L.Control.Opacity with geoJSON data please.

<!DOCTYPE html>
<html>
<head>
 
 <title>Roman</title>
 <meta charset="utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/>
 <script src="https://unpkg.com/[email protected]/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
 <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/L.Control.Opacity.min.js"></script> 
 <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/L.Control.Opacity.css" rel="stylesheet" > 
 <span id="image-opacity">geojson</span>
 <input type="range" id="sldOpacity" min="0" max="1" step="0.1" value="0.01" />
</head>
<body>
<div id="map" style="width: 600px; height: 400px;"></div>
<script>
// from https://github.com/klokantech/roman-empire/blob/master/data/roads_high.geojson edited from 850 features to 2
var rroads = {
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "Name": "Margary 1c", "descriptio": null }, "geometry": { "type": "LineString", "coordinates": [ [ 0.502122913133777, 51.391440023017289, 0.0 ], [ 0.495790717490887, 51.395644730481983, 0.0 ], [ 0.467996181852347, 51.398570131125844, 0.0 ], [ 0.416152252131068, 51.400357741123933, 0.0 ], [ 0.369580604776158, 51.414122946206994, 0.0 ], [ 0.326046318291103, 51.428705828643693, 0.0 ], [ 0.294349044754911, 51.430916683549633, 0.0 ], [ 0.265543899221985, 51.436409391955181, 0.0 ], [ 0.161395541110144, 51.453685025740953, 0.0 ], [ 0.123662308338893, 51.459592810897803, 0.0 ], [ 0.046184044141974, 51.472842635574253, 0.0 ], [ 0.019196574232533, 51.47698452023878, 0.0 ], [ -0.026549796331548, 51.481223032795768, 0.0 ], [ -0.034092702533585, 51.484955510323758, 0.0 ], [ -0.084812782781212, 51.493732620765158, 0.0 ], [ -0.090320915703155, 51.498060695182005, 0.0 ], [ -0.092991357371235, 51.501225076998104, 0.0 ], [ -0.091134672201963, 51.503997443720763, 0.0 ], [ -0.087451419447726, 51.506807540455391, 0.0 ], [ -0.086412464168425, 51.509372384094299, 0.0 ] ] } },
{ "type": "Feature", "properties": { "Name": "Margary 2d", "descriptio": null }, "geometry": { "type": "LineString", "coordinates": [ [ -0.538186341951123, 53.237073005890736, 0.0 ], [ -0.537926882789907, 53.245346023934971, 0.0 ], [ -0.545858081796996, 53.366221001670091, 0.0 ], [ -0.559928349082197, 53.594268830796381, 0.0 ], [ -0.563521370860075, 53.609530235359955, 0.0 ], [ -0.58216819366816, 53.69286831070356, 0.0 ] ] } },
]
};
 var map = L.map('map').setView([51.505, -0.09], 6);
 const baseMaps = L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
 maxZoom: 18,
 attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, ' +
 'Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
 id: 'mapbox/streets-v11',
 tileSize: 512,
 zoomOffset: -1
 }).addTo(map);
 var geojson = L.geoJson(rroads, {
 style: {
 "opacity": 0.01
 },
 }).addTo(map);
 var historyMaps = {
 OS_1956: new L.tileLayer('https://mapseries-tilesets.s3.amazonaws.com/ten_mile/general/{z}/{x}/{y}.png', { opacity: 0.01, maxNativeZoom: 12}),
 Roman: new L.tileLayer("https://dh.gu.se/tiles/imperium/{z}/{x}/{y}.png", { opacity: 0.01, maxNativeZoom: 11 }),
 histjson: new L.geoJson(rroads, {opacity: 0.01 }),
 };
 hlay = L.control.opacity(historyMaps, {collapsed: false, label: "History" } );
 function history_ctl() {
 if (hctl !== 'show') {
 show_history() 
 map.on('baselayerchange', function(e) { ; // say('baselayerchange to ' + e.name);
 baseMaps[e.name].bringToBack()
 });
 } 
 else 
 lose_history()
 }
 function show_history() {
 hctl = 'show' ; // say('show_history: hctl ', hctl)
 historyMaps.OS_1956.addTo(map);
 historyMaps.Roman.addTo(map);
 historyMaps.histjson.addTo(map);
 hlay.addTo(map); ; // say('map: ', map)
 }
 function lose_history() {
 hctl = 'hide' ; // say('lose_history: hctl ', hctl)
 historyMaps.OS_1956.removeFrom(map);
 historyMaps.Roman.removeFrom(map);
 historyMaps.histjson.removeFrom(map);
 hlay.remove();
 }
 hctl = 'hide'
 history_ctl();
 L.DomEvent.on(L.DomUtil.get('sldOpacity'), 'change', function () {
 L.DomUtil.get('image-opacity').textContent = this.value;
 geojson.setStyle({
 opacity: this.value
 });
 });
</script>
</body>
</html>
TomazicM
27.3k25 gold badges33 silver badges43 bronze badges
asked Jan 20, 2022 at 12:56
2
  • If I understand your question correctly, you want to change opacity just for GeoJSON layer? Commented Jan 20, 2022 at 17:44
  • I want to change the opacity of the GeoJSON layer individually, but within the same History control as the OS_1956 layer. Commented Jan 20, 2022 at 18:04

1 Answer 1

1

As you correctly figured it out, vector layers in Leaflet do not have opacity option to set layer opacity, since opacity is regulated through layer style option. That's the reason why Leaflet.Control.Opacity plugin does not work on these layers.

One possible solution for this is to slightly modify Leaflet.Control.Opacity plugin so that for vector layers function is called which can change vector layer opacity through it's style.

There are only two lines to be added to the plugin internal _addItem method. Below is relevant part of the code where lines have to be inserted:

input.addEventListener('input', (event) => {
 const rgValue = event.target.value;
 const layer = this._getLayer(input.layerId).layer;
 if (this.options.vectorLayerOpacity) { // added line
 this.options.vectorLayerOpacity(layer, rgValue / 100); // added line
 } else if (typeof layer._url === 'undefined') {
 } else {
 layer.setOpacity(Number(rgValue / 100));
 }
});

As a consequence function specified in newly introduced vectorLayerOpacity option will be called at each slider change, with two parameters: layer and opacity.

Below is an example of modified plugin used (only relevant part of code included):

var initLayerOpacity = 0.4;
var initLineOpacity = 1;
var initFillOpacity = 0.7;
var lineOpacity = initLineOpacity * initLayerOpacity;
var fillOpacity = initFillOpacity * initLayerOpacity;
function style(feature) {
 return {
 weight: 2,
 opacity: lineOpacity,
 color: 'white',
 dashArray: '3',
 fillOpacity: fillOpacity,
 fillColor: getColor(feature.properties.density)
 };
}
var geojson = L.geoJson(statesData, {
 style: style,
 opacity: initLayerOpacity
}).addTo(map);
 var layers = {
 'OSM': osmLayer,
 'GeoJSON': geojson
 }
 
function vectorLayerOpacity(layer, opacity) {
 lineOpacity = opacity * initLineOpacity;
 fillOpacity = opacity * initFillOpacity;
 geojson.setStyle(style);
}
L.control.opacity(layers, {
 label: 'Layers Opacity',
 vectorLayerOpacity: vectorLayerOpacity
}).addTo(map); 
answered Jan 20, 2022 at 20:49
1
  • TomazicM, that is fantastic, thankyou so much, it works perfectly with minimal fiddling. Commented Jan 21, 2022 at 7:55

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.