Style is not done loading error: Mapbox GL JS - javascript

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.

Related

Activate mapbox draw tool only within layer boundary

I have a geojson mapbox tileset visualized on a map. I am currently able to draw shapes such as polygons,polylines and add points to the map. But I now want to limit the draw tool to only allow drawing within the boundaries of the geojson layer. How would I do this? What I currently has, lets the cursor change to a pan symbol when it's outside of the map, and to a crosshair symbol when inside the map.
But once I click the draw tool buttons, it overrides this and lets the user draw anywhere they want.
mapboxgl.accessToken = config.MAPBOX_PUBLIC;
const map = new mapboxgl.Map({
attributionControl: false,
container: "map", // container ID
style: "mapbox://styles/mapbox/outdoors-v10",
center: [-69.45, 45.07], // starting position [lng, lat]
zoom: 8, // starting zoom
});
export function makeClickableArea() {
map.on("load", () => {
map.addSource('maine', {
'type': 'geojson',
'data': {
'type': 'Feature',
'geometry': {
'type': 'Polygon',
// These coordinates outline Maine.
'coordinates': [
[
[-67.13734, 45.13745],
[-66.96466, 44.8097],
[-68.03252, 44.3252],
[-69.06, 43.98],
[-70.11617, 43.68405],
[-70.64573, 43.09008],
[-70.75102, 43.08003],
[-70.79761, 43.21973],
[-70.98176, 43.36789],
[-70.94416, 43.46633],
[-71.08482, 45.30524],
[-70.66002, 45.46022],
[-70.30495, 45.91479],
[-70.00014, 46.69317],
[-69.23708, 47.44777],
[-68.90478, 47.18479],
[-68.2343, 47.35462],
[-67.79035, 47.06624],
[-67.79141, 45.70258],
[-67.13734, 45.13745]
]
]
}
}
});
// Add a new layer to visualize the polygon.
map.addLayer({
'id': 'maine',
'type': 'fill',
'source': 'maine', // reference the data source
'layout': {},
'paint': {
'fill-color': '#0080ff', // blue color fill
'fill-opacity': 0.5
}
});
// Add a black outline around the polygon.
map.addLayer({
'id': 'outline',
'type': 'line',
'source': 'maine',
'layout': {},
'paint': {
'line-color': '#000',
'line-width': 3
}
});
});
changeMouseSymbol();
}
// add the draw tool
function changeMouseSymbol() {
const modes = MapboxDraw.modes;
var draw = new MapboxDraw({
displayControlsDefault: false,
// Select which mapbox-gl-draw control buttons to add to the map.
controls: {
point: true,
line_string: true,
polygon: true,
},});
map.addControl(draw);
map.on("mouseover", "maine", () => {
map.getCanvas().style.cursor = "crosshair";
});
map.on("mouseleave", "maine", () => {
map.getCanvas().style.cursor = "";
});
}

How to get MapLibre Layers properties?

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.

mapbox passing coordinates as variable does not work

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.

Change icon opacity when hover(symbol layer), in Mapbox GL JS

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.

Cannot display a popup on hover over a route in mapbox

I have a custom line in mapbox that displays a popup when you click it, how can I make it so it shows only when you hover it ? I changed from "click" to "mouseenter" but it did not closed the popup. What needs changing ?
Here is a jsfiddle example : https://jsfiddle.net/andrre/jb4czmew/4/
JS
mapboxgl.accessToken = 'pk.eyJ1IjoibWFya2V0aW5nYnNvIiwiYSI6ImNrYnYwZmk3YjAxZjgyem1wY2Zmc3F4Y2EifQ.gMF-eCCaAHHgWIUoRcnfkg';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-0.066985, 51.503363],
zoom: 9
});
map.on('load', function() {
map.addSource('routeThree', {
'type': 'geojson',
'data': {
'type': 'Feature',
'properties': {},
'geometry': {
'type': 'LineString',
'coordinates': [
[-0.066985, 51.503363],
[-3.550610, 40.390555 ],
[-77.140217, 38.801481],
[-118.175979, 34.008447]
]
}
}
});
map.addLayer({
'id': 'routeThree',
'type': 'line',
'source': 'routeThree',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#04A5BD',
'line-width': 8
}
});
map.on('click', 'routeThree', function(e) {
new mapboxgl.Popup()
.setLngLat(e.lngLat)
.setHTML("<h2>This is the third line that will explain something</h2>")
.addTo(map);
});
});
Your code is right, but there's a small mistake in it, you chose the same id for the Source and for the Layer... just call it differently in the map.addLayer and click event, and it works fiddle
mapboxgl.accessToken = 'pk.eyJ1IjoibWFya2V0aW5nYnNvIiwiYSI6ImNrYnYwZmk3YjAxZjgyem1wY2Zmc3F4Y2EifQ.gMF-eCCaAHHgWIUoRcnfkg';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-0.066985, 51.503363],
zoom: 9
});
map.on('load', function () {
var popup;
map.addSource('routeThree', {
'type': 'geojson',
'data': {
'type': 'Feature',
'properties': {},
'geometry': {
'type': 'LineString',
'coordinates': [
[-0.066985, 51.503363],
[-3.550610, 40.390555],
[-77.140217, 38.801481],
[-118.175979, 34.008447]
]
}
}
});
map.addLayer({
'id': 'routeThree2',
'type': 'line',
'source': 'routeThree',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#04A5BD',
'line-width': 8
}
});
map.on('mouseover', 'routeThree2', function (e) {
popup = new mapboxgl.Popup()
.setLngLat(e.lngLat)
.setHTML("<h2>This is the third line that will explain something</h2>")
.addTo(map);
});
map.on('mouseout', 'routeThree2', function (e) {
if (popup) popup.remove();
});
});

Categories