Drawing order of Leaflet polygon parts (ex. fill vs outlines) in relation to other layers - leaflet

We're building a new application using Leaflet as map displaying framework.
In our current application it's possible to supply the drawing order of specific 'parts' of a layer in relation to the parts of other layers.
Ex.
When the 'fill' of Layer2 is drawn over the 'fill' of Layer1, the 'outlines' of Layer1 can be drawn over the 'fill' of Layer2:
Desired
Normal
Can this be controlled in Leaflet, of do I have to add an extra layer to drawn the outlines of Layer1 over the the fill of Layer2.
EDIT
This is an example of what can be created using the demo code in the answer by 'Falke Design' - so this looks very promising:
EDIT 2
Basically the supplied answer uses two layers (not exactly what I wanted), but this code will be a good alternative.

You can also create a new layer with only borders on a new pane, that is always on top of the normal one:
// create a own pane, that is always on top of the normal drawn shapes
var bordersPane = map.createPane("borders");
bordersPane.style.zIndex = '405';
And then create, after drawing a layer, a new layer with only borders to the new pane:
// copy the latlngs of the drawn layer and create a new layer on the pane "borders" with only a storke
var layer = L.rectangle(e.layer.getLatLngs(),{pane: 'borders', fill: false, color: '#000', interactive: false}).addTo(map);
In the example I used Leaflet-Geoman for drawing:
// create a own pane, that is always on top of the normal drawn shapes
var bordersPane = map.createPane("borders");
bordersPane.style.zIndex = '405';
// add a stroke and a fill color to the drawing option
map.pm.enableDraw('Rectangle',{pathOptions: {fillOpacity: 1, stroke: true, color: '#000', fillColor: 'green'}})
// layer drawn / created event
map.on('pm:create',(e)=>{
// change the color of the drawn layer
e.layer.setStyle({fillColor: 'red'});
// copy the latlngs of the drawn layer and create a new layer on the pane "borders" with only a storke and is not editable or something (pmIgnore)
var layer = L.rectangle(e.layer.getLatLngs(),{pane: 'borders', fill: false, color: '#000', interactive: false, pmIgnore: true}).addTo(map);
// connect the two layers
layer.refLayer = e.layer;
e.layer.refLayer = layer;
// when the drawn layer is changed, change also the border layer
e.layer.on('pm:edit pm:update pm:drag pm:markerdrag',(e)=>{
e.layer.refLayer.setLatLngs(e.layer.getLatLngs())
})
})
https://jsfiddle.net/falkedesign/8owphr26/

The easiest way would be to reduce the opacity of the fill, then you see through it and see the borders:
L.rectangle(latlng,{fillOpacity: 0.5});
//or
layer.setStyle({fillOpacity: 0.5});

Related

mapbox - why do all my features disappear when I change the map style

I am using mapbox gl js to create some maps. To style the map, I use the following code;
// add a styled map to the contianer id map
const map = new mapboxgl.Map({
container: 'map', // container ID
style: 'mapbox://styles/bogdanvectuel/cjq1fxndm5qr02roa06jcfb61',
});
Above is a public map style from bogdanvectuel. I wanted to changed to my own style but when I change over to a mapbox offered style like the outdoors one, all of a sudden all my skiareas points disappear which is defined as follows;
/** Add ski areas but just the center point as source / dot **/
map.addSource('aa_winter_spoorts_points', {
type: 'vector',
// aa_winter_spoorts_points
url: 'mapbox://username.aa_winter_spoorts_points'
});
/** Add ski areas but just the center point as layer / dot **/
map.addLayer({
'id': 'aa_winter_spoorts_points',
'type': 'symbol', // background, fill, line, symbol, raster, circle, fill-extrusion, heatmap, hillshade, sky.
'source': 'aa_winter_spoorts_points',
'source-layer': 'skiareas',
'layout': {
'visibility': 'visible',
'icon-image': 'airfield-11',
'icon-allow-overlap': true
},
});
I cannot understand why that would happen. Any pointers appreciated.
When you use addSource() and addLayer(), you are manually adding the source and layer to the map's style. Changing to a different style after map load essentially starts over from scratch. Using setStyle() will clear the existing style, so anything you added after map load would need to be added again.
One approach may just be to move your addSource() and addLayer() calls to a new function like addCustomSourcesAndLayers(). You could then call that after the first map load, and again after setStyle()
You can see this in action in geojson.io. We allow you to switch to a different style and then re-draw the user-defined layers after the new style loads.

How to avoid polygon distortion when zooming?

Here is a full jsfiddle example
I use a custom series and draw a polygon:
data = [
[80.9251933067, 207.9047427038],
[52.8853803102, 337.7443022089],
[25.9926385814, 120.3586150136]
];
I use echarts.graphi.clipPointsByRect() (like in this echarts-example) to make sure, the polygon is not drawn outside of the grid.
echarts.graphic.clipPointsByRect(points, {
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height
})
Initially the polygon is drawn correctly, like this:
But when I zoom in, the polygon is distorted: e.g. you can click the zoom part buttom below the chart to zoom from 40 to 60 - in this case I'd expect to see the part of the shape (like highlighted in yellow in the image above) - but instead I see this distorted image:
Maybe this function is not meant for this use-case, or is this a bug?
Is there another function for this use-case or does anyone know a workaround?
Update
Version 4.4.x contains a new clip feature. This makes it easy to avoid the distortion:
in the render function we don't need to clip our shapes: i.e. no need to call clipPointsByRect()
instead we just activate clip on the custom series:
New series definition with clip: 'true':
series: [{
type: 'custom',
clip: 'true',
renderItem: renderItem,
data: data
}]
Here is an updated jsfiddle expample
Original Version
it seems that the function is really not working as expected - see echarts-source code comment:
export function clipPointsByRect(points, rect) {
// FIXME: this way migth be incorrect when grpahic clipped by a corner.
// and when element have border.
I've created an issue #10222 for the e-charts project
A workaround for now is to use a custom clipping function
e.g. lineclip supports the Sutherland-Hodgman algorithm for polygon clipping
Here is the updated jsfiddle-example that shows the correct result when you zoom in:

Leafletjs show a single wrapped map

I want to show a single world map and wrap it around so that each area is shown only once on the screen. Please refer the fiddle http://jsfiddle.net/8m13d6vs/
var osmUrl = 'http://{s}.tile.osm.org/{z}/{x}/{y}.png',
osm = L.tileLayer(osmUrl, {
noWrap: false,
attribution: "<a href='http://openstreetmap.org'>OpenStreetMap</a>"
});
var map = L.map('map').setView([0, 0], 1).addLayer(osm);
In the above fiddle, wrap is on, but the world map is duplicated. I want a single world map, and on left/right mouse drag, it should wrap around. It should be responsive to the area as well. Is there any way to achieve this? Hope my problem statement is understandable.
There's the Leaflet worldCopyJump option:
var map = L.map('map', {worldCopyJump: true}).setView([0, 0], 1).addLayer(osm);
It defaults to false, but setting this to true will copy the contents of the map over as the user pans beyond the map boundaries.
This sounds like a misunderstanding of the definition of "wrap":
In the context of Leaflet's Tile Layer / Grid Layer option noWrap, it says:
Whether the layer is wrapped around the antimeridian. If true, the GridLayer will only be displayed once at low zoom levels.
So it sounds like simply noWrap: true should achieve your objective.
Updated JSFiddle: http://jsfiddle.net/8m13d6vs/3/
BTW you should consider upgrading Leaflet version to 1+

Mapbox: How to continuously wrap a feature layer

Mapbox can continuously wrap tile layers such that you can scroll infinitely to the left and right over a repeating map of the world.
I have added a feature layer to a world map which displays some markers and, for some reason, this feature layer does not also wrap. The markers are only present on the original iteration of the world tiles layer.
I've tried using the noWrap: false options property that controls this characteristic on the tile layer when adding a feature layer, but it doesn't seem to have any effect.
Is there any way to do this?
Use the worldCopyJump option
example:
var map = L.mapbox.map('map', 'your.mapid', {center: [lat, long],
zoom: 4,
worldCopyJump: true
});
Not sure if I understand your question...
You can use
maxBounds: [[-90,-180],[90,180]]
for example
map = L.mapbox.map('map', '<some map id>',{minZoom: 0, maxZoom: 10, maxBounds: [[-90,-180],[90,180]]});
https://www.mapbox.com/mapbox.js/example/v1.0.0/maxbounds/

Layer order changing when turning layer on/off

I have two geoJson layers being loaded - both layers are the same data for testing purposes, but being drawn from two different json files. When I turn the layers on and off in the layer controller, the draw order of the layers change.
Any ideas why this is happening?
I have put my code into a JSFiddle: http://jsfiddle.net/lprashad/ph5y9/10/ and the JS is below:
//styling for watersheds_copy
var Orange = {
"color": "#ff7800",
"weight": 5,
"opacity": 0.65
};
var Water_Orange = L.geoJson(watersheds_copy, {
style: Orange
});
Water_Orange.addData(watersheds_copy);
//these are blue
var Water_blue = L.geoJson(watersheds, {});
Water_blue.addData(watersheds);
//This sets the inital order - last in layer list being on top. Except minimal - tile layer is always on bottom
var map = L.map('map', {
center: [41.609, -74.028],
zoom: 8,
layers: [minimal, Water_Orange, Water_blue]
});
var baseLayers = {
"Minimal": minimal,
"Night View": midnight
};
//This controls the order in the layer switcher. This does not change draw order
var overlays = {
"Water_Orange": Water_Orange,
"Water_blue": Water_blue
};
L.control.layers(baseLayers, overlays).addTo(map);
LP
While searching I happened upon this site that shows some of the Leaflet code:
http://ruby-doc.org/gems/docs/l/leaflet-js-0.7.0.3/lib/leaflet/src/control/Control_Layers_js.html
In it I found this condition for the application of autoZIndex:
if (this.options.autoZIndex && layer.setZIndex) {
this._lastZIndex++;
layer.setZIndex(this._lastZIndex);
}
TileLayer is the only layer type that has a setZIndex function, so apparently autoZIndex only works there.
I'm not sure which annoys me more. This incredible limitation or the fact that Leafet documentation doesn't point it out.
At least on 0.7.2, I had to use bringToFront in the callback of map.on('overlayadd'). autoZIndex: false did not work in my case neither. A comment on this issue may explain the reason.
It's not specific to L.GeoJson layers. As far as I can tell, it's true of all Leaflet layers with layer control. The last layer turned on is simply on top. I don't think this is a bug either. It's predictable behavior which I use and depend on when I'm designing maps with layer control...