Change 'prefer canvas' setting per leaflet map - leaflet

I'm using Leaflet (v 0.7.7). It expects setting L_PREFER_CANVAS as a script tag, which is global. I wish to create 2 maps on same page, one with L_PREFER_CANVAS flag ON and once with OFF. How can I do that ?
1) I've tried setting window.L_PREFER_CANVAS before the map creation.
2) I've tried creating my layers with extended classes like this
var MyCircle = L.Circle.extend({
statics: {
CANVAS: true,
SVG: false
}
});
then using 'new MyCircle' instead of 'L.circle'.
Neither of the two methods have the desired effect, even though the map is rendered successfully
I'm looking into leaflet code but i'm not very comfortable with its inner workings yet, due to lack of js sorcery know-how i believe
Edit: A thing that partly works is cloning the entire leaflet source under a new object (M.* instead of L.), and keep my desired flag enabled for it. But its clumsy and breaks with plugins which add their functionality to L. classes. Thereby requiring more duplication to fix, which i'm trying to avoid

Would recommend you look into migrating onto Leaflet 1.0, where preferCanvas is now a traditional option inside the map constructor...Among many other significant improvements.
http://leafletjs.com/reference-1.0.0.html#map-prefercanvas

Related

Markers in the same position don't work properly with Vue3 + Leaflet Markercluster

The problem is that the leaflet map inside the vue3 App loads perfectly and looks great. Also, when you click on a location with two icons in the same position, they open perfectly, but when you click on the same place again, the icons disappear and the "spider" remains visible (see picture).
spider remains
The methods in the Vue3 App are:
methods:{
setupMarkers(){
this.markers.clearLayers();
this.cursesData.forEach(cursa =>this.ficaMarkers(cursa));
this.map.addLayer(this.markers);
},
setupLeafletMap(){
this.map=L.map("mapContainer").setView(this.center,6);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",{
attribution:'OpenStreetMap',
}).addTo(this.map);
this.markers= L.markerClusterGroup({
//spiderfyOnMaxZoom: true,
});
},
ficaMarkers(cursa){
this.markers.addLayer(L.marker([cursa.coordenades[0],cursa.coordenades[1]],{title:cursa.nom})
.bindPopup(cursa.distancies)
)
},
},
If someone could help me, I would be very grateful.
Thanks.
It is a similar issue as in Uncaught TypeError: this._map is null (Vue.js 3, Leaflet) :
What seems to be the culprit is the proxying of this.map by Vue, which seems to interfere with Leaflet events (un)binding. It looks like Vue 3 now automatically performs deep proxying, whereas Vue 2 was shallow.
In your case, the same thing happens for this.markers (which is the Leaflet MarkerClusterGroup).
A solution consists in "unwrapping" / un-proxying the map and the mcg whenever you use them, e.g. with Vue3's toRaw:
toRaw(this.map).addLayer(toRaw(this.markers));
Then we retrieve the normal MCG behaviour, i.e. if you click on the cluster while it is already spiderfied, nothing happens (whereas initially the Markers were collapsing, but the spider legs were remaining indefinitely)
Fixed CodeSandbox: https://codesandbox.io/s/markers-hide-spiders-stay-forked-l2ruqh?file=/src/App.vue

Leaflet labels when upgrading to 1.0.3

I try to use leaflet labels and on several pages it is successful. I wanted to use the same approach to display labels on another page and it responded with an obscure technical message which I did not understand:
Uncaught TypeError: Cannot read property 'call' of undefined
at e.whenReady (leaflet.js:6)
at e.addLayer (leaflet.js:6)
at e.showLabel (leaflet.label-src.js:538)
at e.showLabel (leaflet.label-src.js:287)
at e._onMarkerAdd (leaflet.label-src.js:383)
at e.fire (leaflet.js:5)
at e._layerAdd (leaflet.js:6)
at e.whenReady (leaflet.js:6)
at e.addLayer (leaflet.js:6)
at e.onAdd (leaflet.js:7)
After a lot of researching I found out that leaflet.label is deprecated for Leaflet 1.0.3, which makes sense, since we are using older leaflet version in other pages where the labels appear correctly, but on the newer leaflet versions it crashes the map. The docs say that I should use bindTooltip instead of bindLabel, but I do not know how. I have tried to call it with some content on a marker but it did not appear on the map and I did not find it in the generated structure.
So, my question is as follows: How can I use labels with Leaflet 1.0.3, which will be bound to a marker and will adjust in case of translation/zoom?
The Tooltip is indeed now directly part of Leaflet main library.
Note that it appears only on mouseover by default, but you can use the permanent option to have it remain always visible.
marker.bindTooltip("Some tooltip content", {
permanent: true
});
Example: http://playground-leaflet.rhcloud.com/tiqo/1/edit?html,output

Leaflet: How to specifiy order of overlays in the layers control?

Is it possible to order overlays within the layers control?
Here's my issue. When I do this with my overlays:
var overlays = {
"Apples": apples,
"Bananas": bananas,
"Peaches": peaches
};
L.control.layers(baseLayers, overlays).addTo(map);
Leaflet adds the overlays to the layer control on the map in a random fashion (due to iteration):
- Bananas
- Apples
- Peaches
Is it possible to specificy the order I want to show the overlays? *(In my case, I'd like to have it alphabetical. Also, I am using the Mapbox API)
Thank you.
I am not sure if Mapbox API has changed the default Layers Control code / behaviour, but the latter does not provide a 100% reliable way of displaying overlays in a specific order. See that post for details.
The explanation is that the Layers Control iterates overlays by their "stamp" (a unique identifying integer that is created using L.stamp(myLayer)). Depending on your type of layer, this stamp is assigned automatically but at different moments.
You can force its creation right after having instantiated your layer. For example in your case:
var apples = L.layerGroup();
L.stamp(apples);
var bananas = L.layerGroup();
L.stamp(bananas);
var peaches = L.layerGroup();
L.stamp(peaches);
Unfortunately the order of iteration is not guaranteed as per JS spec, hence the impossibility to have a 100% reliability in this behaviour.
That being said, in all browsers I have tested, the order is in accordance with the natural number order, so as of today, this trick works.
If you want more confidence, you should look for Control plugins that explicitly provide you with the functionality to specify manually the order of listing.

How to combine js lines into one?

Hi I have several js/mootools code in a coffee file that I want to combine together as in
$$("#head").setStyle('border-right-color','#e64626');
$$("#head").setStyle('background','#e64626');
$$("#console").setStyle('border-right-color','#e64626');
$$("#console").setStyle('background','#e64626');
Is it possible to combine this into one line?
Although what #sergio gave you is the correct answer and what you wanted, it's probably the wrong thing to do (though his answer is not at fault, its the question that is off).
Setting element inline styles in JavaScript for effects is just not a very good practice. It has its uses in animation/fx but this does not seem to be the case. The reason is that it breaks cascading due to the high specificity and makes it much more difficult to revert, control or change afterwards. It also makes it harder to skin and restyle your app as there's a clear lack of separation of concerns, style in code is rigid.
You need to solve this via CSS
scenario 1: static, non mutable - not very good due to use of ID rather than classes but:
#head, #console {
border-right-color: #e6426;
background-color: #e6426;
}
scenario 2: dynamic, need to differentiate elements on some event such as click, focus, mouseover - though you can solve most of these with pseudos like :focus or :hover, then abstract this into a class:
css:
.highlight-red {
border-right-color: #e6426;
background-color: #e6426;
}
javascript:
var els = $$('#head,#console');
// on whatever event...
els.addClass('highlight-red');
// ... later
els.removeClass('highlight-red');
you will thank yourself later or your successor will or your client, whatever. this has the added benefit that next time you need to change styling or add more rules to differentiate, you have one place to go to. you can even add transitions / animation effects for evergreen browsers.
You can use setStyles() and pass it a object. You can also use , in the string you pass to $$ to select multiple elements. So it would result into:
$$("#head, #console").setStyles({
'border-right-color': '#e64626',
'background': '#e64626'
});
jsFiddle: http://jsfiddle.net/sevvtsx0/

Use Template on Invisible Table for inserting into existing bound sap.m.Table

Excuse the confusing language but hopefully this makes sense: (see code for more clear explanation)
I have a requirement to display a list of "spare parts" in an sap.m.Table but there is the ability if one of these "spare parts" has a related "spare part" (e.g. A heavy duty version, a light version, etc) , that you can click a button on the row and display these related "spare parts" by inserting them immediately below the "spare part" in question.
While I can get the sap.m.Table doing what I want to do, I would like to take advantage of templates and binding to create a temporary sap.m.Table; bind it to the relationship that returns these alternate spare parts; and reuse the template for a row to give me an array of ColumnListItems which I can insert into the Table at the right place.
Unfortunately, doing this, a sap.m.Table has a feature that if it is not displayed, it doesn't actually make the Odata call and leverage the template function.
To explain possibly much clearer, refer to this jsbin:
http://jsbin.com/sihofu/4/edit?html,js,output
Any better ideas on how to generate template output for a binding without using a sap.m.Table; or alternative, getting the sap.m.Table to make the call without placing it on the screen visible (temporarily)?
The specific code to look at is as follows:
var oTable2 = new sap.m.Table();
oTable2.attachUpdateFinished(function() {
console.log("But this one doesn't");
// What I'm trying to do here is insert these entries below Key 1
});
oTable2.bindAggregation("items", {
path: "/ExampleSecondaryValues",
template: oTemplate,
});
Thanks,
Matt
Back from Holidays now and solved this problem with a bit of brute force by simply enhancing/extending the sap.m.table control slightly.
The problem was if the control was invisible, nothing was rendered, and some optimisation within UI5 core means that in the case nothing is rendered, the AfterRender event is not called on the control and this event is what fires the UpdateFinished event.
I won't debate whether that optimisation is appropriate or not, but to fix this I simply extended the table control with a new control which looks like as follows:
sap.m.Table.extend("my.InvisibleTable", {
renderer: function(oRm, oControl) {
oRm.write("<span");
oRm.writeControlData(oControl);
oRm.write("></span>");
}
});
e.g. Simply always rendering something in the render function, causes the AfterRender event to be called; which in turns allows the sap.m.Table to fire the UpdateFinished event which allows me to then safely get the rendered template items to insert in my visible table.
Would love to know a much better way of doing this (possibly using the template control or similar), but this works okay to solve the problem.
Cheers,
Matt