Please help, I have been suffering since 5 days.
How can I get all layer Idsof a MapLibre map ?
function addLayer(map, options, layer) {
let currentLayer = edges_data_api.find(
element => element.edge_id === layer.feature.properties.edge_id)
map.setPaintProperty('lines', 'fill-color', ['interpolate', ['linear'],
['get', currentLayer.lanes], 0, 'rgb(255, 255, 255)', 5, 'rgb(255, 0, 0)'])
}
Here is how I defined the map with MapLibre
map.on('load', function() {
map.addSource('lines', {
type: 'geojson',
data: data
});
map.addLayer({
'id': 'lines',
'type': 'fill',
'source': 'lines',
'layout': {},
'paint': {
'fill-color': '#4682B4',
'fill-opacity': 0.8,
}
});
map.setPaintProperty('lines', 'fill-color', ['get', 'color'])
})
As i know, there are no api for getAllLayers(). So u should keep list of layers' ids by you own.
//global variable
const layerIds = new Set()
map.on('load', function() {
layerIds.add('lines') //keep list of ids without duplicates
map.addSource('lines', {
type: 'geojson',
data: data
});
map.addLayer({
'id': 'lines',
'type': 'fill',
'source': 'lines',
'layout': {},
'paint': {
'fill-color': '#4682B4',
'fill-opacity': 0.8,
}
});
map.setPaintProperty('lines', 'fill-color', ['get', 'color'])
})
//You can iterate your list ids where it is needed
layerIds.forEach(value => {//action here})
From Style Specification
A style's layers property lists all the layers available in that style.
So you can get map's layers from the style object using Map.getStyle()
map.getStyle().layers
This returns an array containing the ids of all layers:
Object.keys( map.style._layers )
But beware: the code accesses private fields both of Map and Style, therefore it may, however unlikely, no longer work for new versions of MapLibre.
Related
It tried to implement the solution to draw a polygon from external data as shown in https://jsfiddle.net/zxaktouy/1/ but I get the error:
Input data given to 'frag15' is not a valid GeoJSON object.
My JS-method:
drawFragment : function(pFRAGMENT) {
const wPolygon = pFRAGMENT.coordinates;
console.log("drawFragment: Coords="+wPolygon);
wSourceId = "frag"+pFRAGMENT.id;
wFillId = "fragfill"+pFRAGMENT.id;
wOutlineId = "fragoutline"+pFRAGMENT.id;
map.addSource(wSourceId,{
'type': 'geojson',
'data': {
'type': 'Feature',
'geometry': {
'type': 'Polygon',
'coordinates': [
wPolygon
]
}
}
});
// Add a new layer to visualize the polygon.
map.addLayer({
'id': wFillId,
'type': 'fill',
'source': wSourceId, // reference the data source
'layout': {},
'paint': {
'fill-color': '#00ff80', // green color fill
'fill-opacity': 0.5
}
});
// Add a black outline around the polygon.
map.addLayer({
'id': wOutlineId,
'type': 'line',
'source': wSourceId,
'layout': {},
'paint': {
'line-color': '#0d0',
'line-width': 2
}
});
},
And the data passed (perfectly shown via console.log) look like this:
[[8.543590974130666,47.377830192117756],
[8.543641551219707,47.37784384335191],
[8.543634914341965,47.37789513281288],
[8.543582309906242,47.37791046432616],
[8.543590974130666,47.377830192117756]]
when I replace the "wPolygon" just by copy-pasting the data into the code everything works fine.
After replacing:
const wPolygon = pFRAGMENT.coordinates;
with
var wPolygon = JSON.parse(pFRAGMENT.coordinates);
it works.
I see in the documentation that icon-opacity supports feature-state,
https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#paint-symbol-icon-opacity
Therefore I use this code for making a change of opacity when user hovers over a icon:
map.addLayer({
type: 'symbol',
layout: {
'icon-image': 'point',
...
},
paint: {
'icon-opacity':
['case', ['boolean', ['feature-state','hover'], false], 1, 0.3]
}
});
The problem is that it takes the original opacity right(0.3) but does not change on hover.
Any idea?
Thanks.
The code you´re posting is right, but it's only half of the solution.
You already implemented the definition of the layer paint behavior for any feature (by default 0.3 as you say), the second part is to change the feature-state hover to true on mouseover.
The state of the features on 'hover' in your symbol layer don´t change automatically, you need to change it to true in a map.on('mousemove', 'youLayerId'... method, and again to false in a map.on('mouseout', 'youLayerId'... .
Check out this fiddle I have created for you on how to change opacity of an icon.
The relevant code is below
mapboxgl.accessToken = 'PUT HERE YOUR TOKEN';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11'
});
let fHover = null;
map.on('load', function() {
map.loadImage(
'https://upload.wikimedia.org/wikipedia/commons/7/7c/201408_cat.png',
function(error, image) {
if (error) throw error;
map.addImage('cat', image);
map.addSource('point', {
'type': 'geojson',
'generateId': true,
'data': {
'type': 'FeatureCollection',
'features': [{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [0, 0]
}
}]
}
});
map.addLayer({
'id': 'points',
'type': 'symbol',
'source': 'point',
'layout': {
'icon-image': 'cat',
'icon-size': 0.25,
},
'paint': {
'icon-opacity': [
'case',
['boolean', ['feature-state', 'hover'], false],
1,
0.3
]
}
});
}
);
map.on('mousemove', 'points', function(e) {
if (e.features[0]) {
mouseover(e.features[0]);
} else {
mouseout();
}
});
map.on('mouseout', 'points', function(e) {
mouseout();
});
function mouseover(feature) {
fHover = feature;
map.getCanvasContainer().style.cursor = 'pointer';
map.setFeatureState({
source: 'point',
id: fHover.id
}, {
hover: true
});
}
function mouseout() {
if (!fHover) return;
map.getCanvasContainer().style.cursor = 'default';
map.setFeatureState({
source: 'point',
id: fHover.id
}, {
hover: false
});
fHover = null;
}
});
Important, to change a feature state every feature must have an id in the source, so I strongly recommend to set always 'generateId': true in the addSource method.
PS.- If this answer solves your question, please mark it as answer accepted, in that way it will also help other users to know it was the right solution.
I'm making a choropleth map with Mapbox that changes according to a user-defined day. The color is based on the prob for a given day. For now, I'm passing prob1 ... prob10 as properties in a geojson, but I would prefer to have a single prob property that contains an object that I can subset.
//initialize map on day 1
var day = 1;
//prepare color scale
function makeColorScale(d) {
return ([
'interpolate', ['linear'],
['get', 'prob' + d], // how to get object property here?
0, '#440154',
0.04, '#3B528B',
0.08, '#21908C',
0.25, '#5DC863',
0.4, '#FDE725',
1, '#696969'
])
};
map.addSource('county-data', {
type: 'geojson',
data: data
});
map.addLayer({
'id': 'counties-join',
'type': 'fill',
'source': 'county-data',
'paint': {
'fill-opacity': 0.3,
'fill-color': makeColorScale(day)
}
}, 'waterway-label' // ensures polygons are rendered above waterway-labels
);
I've tried e.g. modifying to ['get', 'prob.1'] while adding the object prob: {'1': 0.05, '2':0.01}; to my geojson, but that doesn't work.
I have not tried this, but I believe you can access attributes of an object property by using this form of the ['get'] operator:
["get", string, object]: value
So if your features had a prob property that looked like {'1': 0.05, '2': 0.01} then you could write your makeColorScale() function like this:
function makeColorScale(d) {
return ([
'interpolate', ['linear'],
['get', String(d), ['get', 'prob']],
0, '#440154',
0.04, '#3B528B',
0.08, '#21908C',
0.25, '#5DC863',
0.4, '#FDE725',
1, '#696969'
])
};
I think. The ['get', 'prob'] fetches the object, then the ['get', String(d)] looks up the property you want within that object.
I have built a website using Mapbox GL JS to display a number of layered routes and points on a map to track different teams progress on a route. However when testing on a large number of page reloads the tracks on the map along with a number of other page elements sometimes don't load and I get a Style is not done loading error.
Code Extract:
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/outdoors-v11',
center: [15, 50],
zoom: 4 // zoom range is 0 (the whole world) to 22 (street level)
});
var distanceContainer = document.getElementById('distance');
// GeoJSON object to hold our measurement features
var geojson = {
'type': 'FeatureCollection',
'features': []
};
var finishPoint = {
'type': 'FeatureCollection',
'features': []
};
var progressLine = {
'type': 'FeatureCollection',
'features': []
};
map.on('load', function () {
var routeLineString = getRoute();
var line = routeLineString;
var options = {units: 'kilometers'};
var endPoint = turf.along(line, turf.length(line), options);
map.addSource('route-source', {
'type': 'geojson',
'data': line
});
// Add styles to the map. Style layer: A style layer ties together the source and image and specifies how they are displayed on the map.
// 'line-color': '#0C7CBB', // dark cyan
// NHS blue: 005EB8
map.addLayer({
'id': 'route',
'type': 'line',
'source': 'route-source',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#000000',
'line-width': 3
}
},"team1RouteProgress"); // placement of this line below the progress line layer
// destination marker
map.loadImage("https://i.imgur.com/MK4NUzI.png", function (error, image) {
if (error) throw error;
map.addImage("finish-flag", image);
map.addSource('finish-source', {
'type': 'geojson',
'data': finishPoint
});
map.addLayer({
id: 'finish',
type: 'symbol',
source: 'finish-source',
layout: {
"icon-image": "finish-flag",
'icon-anchor': "bottom"
}
});
});
finishPoint.features.push(endPoint);
//map.getSource('geojson').setData(geojson);
});
$(document).ready(function() {
// Clear the Distance container to populate it with a new value
distanceContainer.innerHTML = '';
var line = getRoute();
var options = {units: 'kilometers'};
$.ajax({ //create an ajax request to getProgress.php
type: "GET",
url: "./php/getTeamProgress.php",
dataType: "json", //expect json to be returned
data: {
//access_key: access_key,
},
success: function (response) {
var Teams_arr = [];
var dist_arr = [];
var colour_arr = [];
response.data.forEach(function (dat) {
Teams_arr.push(dat.team);
dist_arr.push(dat.distance);
//Colour Setting
if(dat.team === "Typhoon Squadron"){
colour_arr.push("purple");
}else if (dat.team === "Gloucester Penguins; for Ben"){
colour_arr.push("red");
}else if (dat.team === "Community"){
colour_arr.push("yellow");
}else if(dat.team === "HMS Grimsby"){
colour_arr.push("navy");
}else if(dat.team === "Thunderer Squadron"){
colour_arr.push("blue");
}else{
colour_arr.push("grey");
}
});
//alert(response.totaldist);
var distStart = 0;
//Team 1
var team1DistAlongRoute = dist_arr[0]
var team1Along = turf.along(line, team1DistAlongRoute, options);
if (team1DistAlongRoute<0.1) {
team1DistAlongRoute = 0.1; // prevent error in lineSliceAlong if dist = 0
}
var team1SliceLine = turf.lineSliceAlong(line, distStart, team1DistAlongRoute, {units: 'kilometers'});
map.addSource('team1progress-source', {
'type': 'geojson',
'data': team1SliceLine
});
map.addLayer({
'id': 'team1RouteProgress',
'type': 'line',
'source': 'team1progress-source',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': colour_arr[0],
'line-width': 5
}
});
// Progress marker- Purple
// Image: An image is loaded and added to the map.
map.loadImage("./assets/black.png", function (error, image) {
if (error) throw error;
map.addImage("team1custom-marker", image);
map.addSource('geojson', {
'type': 'geojson',
'data': geojson
});
map.addLayer({
id: 'team1Progress',
type: 'symbol',
source: 'geojson',
layout: {
"icon-image": "team1custom-marker",
'icon-anchor': "bottom"
}
});
});
geojson.features.push(team1Along);
progressLine.features.push(team1SliceLine);
//Team 2
var team2DistAlongRoute = dist_arr[1]
var team2Along = turf.along(line, team2DistAlongRoute, options);
if (team2DistAlongRoute<0.1) {
team2DistAlongRoute = 0.1; // prevent error in lineSliceAlong if dist = 0
}
var team2SliceLine = turf.lineSliceAlong(line, distStart, team2DistAlongRoute, {units: 'kilometers'});
map.addSource('team2progress-source', {
'type': 'geojson',
'data': team2SliceLine
});
map.addLayer({
'id': 'team2RouteProgress',
'type': 'line',
'source': 'team2progress-source',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': colour_arr[1],
'line-width': 5
}
});
geojson.features.push(team2Along);
progressLine.features.push(team2SliceLine);
etc etc
The quick fix:
map = new Mapboxgl.map({ ... })
map.on('load', () => {
...add all your `addSource()`, `addLayer()` etc here.
});
The 'Style is not done loading' error is thrown by the _checkLoaded method in Mapbox GL JS's style.js. This method is called each time a modification is made to the style -- for example when the Map#addSource and Map#addLayer methods are called. In addition to making use of the map#on('load', function() {}) listener to ensure that all necessary map resources are loaded before attempting to make a modification to the map's style, you could also take a look at some of these examples from our documentation, which demonstrate strategies for updating map sources and layers dynamically:
Update a feature in realtime.
Add live realtime data.
Using the GeoJSONSource#setData method to update an existing source's data and re-render the map.
Change a layer's color with buttons.
I am using a mapbox example in order to create multiple polygons on a map, and I have pop-up event for each. My problem is that I need to set each polygon's fill color differently based on it's geojson properties.
This is my example.
I am using the following javascript code:
mapboxgl.accessToken = 'pk.eyJ1IjoibWFoYW5tZWhydmFyeiIsImEiOiJ6SDdSWldRIn0.8zUNm01094D1aoSeHpWYqA';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [51.40545845031738,
35.75069181054449],
zoom: 10
});
map.on('load', function (e) {
// Add a layer showing the state polygons.
map.addLayer({
'id': 'states-layer',
'type': 'fill',
'source': {
'type': 'geojson',
'data': 'geojson.js'
},
'paint': {
'fill-color': 'rgba(200, 100, 240, 0.4)',
'fill-outline-color': 'rgba(200, 100, 240, 1)'
}
});
// When a click event occurs on a feature in the states layer, open a popup at the
// location of the click, with description HTML from its properties.
map.on('click', 'states-layer', function (e) {
new mapboxgl.Popup()
.setLngLat(e.lngLat)
//.setHTML(e.features[0].properties.name)
.setHTML("<h1>"+e.features[0].properties.userone+"</h1>"+e.features[0].properties.name)
.addTo(map);
});
// Change the cursor to a pointer when the mouse is over the states layer.
map.on('mouseenter', 'states-layer', function () {
map.getCanvas().style.cursor = 'pointer';
});
// Change it back to a pointer when it leaves.
map.on('mouseleave', 'states-layer', function () {
map.getCanvas().style.cursor = '';
});
});
Here it loads the colors all the same
'paint': {
'fill-color': 'rgba(200, 100, 240, 0.4)',
'fill-outline-color': 'rgba(200, 100, 240, 1)'
}
On my geojson file I have a key for color:
"type": "Feature",
"properties": {
"userone":"پیروزی",
"name":"North Dafkota",
"featureclass":"Admin-1 scale rank",
"color":"red"
}
I want to use it to define the polygons fill color.
If you just want to use a color that you define in geojson feature properties. Then you could use the layers identity property like this:
map.addLayer({
'id': 'states-layer',
'type': 'fill',
'source': {
'type': 'geojson',
'data': 'geojson.js'
},
'paint': {
'fill-color': {
type: 'identity',
property: 'color',
},
'fill-outline-color': 'rgba(200, 100, 240, 1)'
}
});
Also see: https://www.mapbox.com/mapbox-gl-js/style-spec/#function-type
And: https://www.mapbox.com/mapbox-gl-js/style-spec/#types-color