Mapbox GL JS: стиль не загружен

У меня есть карта, на которой мы можем классически переключаться с одного стиля на другой, например, с улиц на спутники.

Я хочу получить информацию о том, что стиль загружен, чтобы затем добавить слой.

Согласно doc, я пытался дождаться этого загружаемый стиль для добавления слоя на основе GEOJson набора данных.

Это отлично работает, когда загружается страница, которая запускается map.on('load'), но я получаю сообщение об ошибке, когда просто меняю стиль, поэтому при добавлении слоя из map.on('styledataloading') у меня даже возникают проблемы с памятью в Firefox.

Мой код:

mapboxgl.accessToken = 'pk.token';
var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/streets-v10',
    center: [5,45.5],
    zoom: 7
});

map.on('load', function () {

    loadRegionMask();
});

map.on('styledataloading', function (styledata) {

    if (map.isStyleLoaded()) {
        loadRegionMask();
    }
});

$('#typeMap').on('click', function switchLayer(layer) {
    var layerId = layer.target.control.id;

    switch (layerId) {
        case 'streets':
            map.setStyle('mapbox://styles/mapbox/' + layerId + '-v10');
        break;

        case 'satellite':
            map.setStyle('mapbox://styles/mapbox/satellite-streets-v9');
        break;
    }
});

function loadJSON(callback) {   

  var xobj = new XMLHttpRequest();
      xobj.overrideMimeType("application/json");

  xobj.open('GET', 'regions.json', true);

  xobj.onreadystatechange = function () {
        if (xobj.readyState == 4 && xobj.status == "200") {
          callback(xobj.responseText);
        }
  };
  xobj.send(null);  
}

function loadRegionMask() {

  loadJSON(function(response) {

    var geoPoints_JSON = JSON.parse(response);

    map.addSource("region-boundaries", {
      'type': 'geojson',
      'data': geoPoints_JSON,
    });

    map.addLayer({
        'id': 'region-fill',
        'type': 'fill',
        'source': "region-boundaries",
        'layout': {},
        'paint': {
            'fill-color': '#C4633F',
            'fill-opacity': 0.5
        },
        "filter": ["==", "$type", "Polygon"]
    });
  });
}

И ошибка:

Uncaught Error: Style is not done loading
    at t._checkLoaded (mapbox-gl.js:308)
    at t.addSource (mapbox-gl.js:308)
    at e.addSource (mapbox-gl.js:390)
    at map.js:92 (map.addSource("region-boundaries",...)
    at XMLHttpRequest.xobj.onreadystatechange (map.js:63)

Почему я получаю эту ошибку, когда после проверки загрузки стиля вызываю loadRegionMask()?


person fralbo    schedule 06.06.2017    source источник
comment
Возможный дубликат Стиль не загружен: Mapbox GL JS   -  person Eldan Goldenberg    schedule 07.10.2017


Ответы (9)


1. Прослушайте styledata событие, чтобы решить вашу проблему.

Возможно, вам потребуется прослушать событие styledata в вашем проекте, поскольку это единственное стандартное событие, упомянутое в документах mapbox-gl-js, см. https://docs.mapbox.com/mapbox-gl-js/api/#map.event:styledata.

Вы можете использовать это так:

map.on('styledata', function() {
    addLayer();
});

2. Причины, по которым вам не следует использовать другие методы, упомянутые выше.

  1. setTimeout может работать, но это не рекомендуемый способ решения проблемы, и вы получите неожиданный результат, если ваша работа по рендерингу будет тяжелой;
  2. style.load - это частное событие в mapbox, как описано в проблеме https://github.com/mapbox/mapbox-gl-js/issues/7579, поэтому нам явно не следует его слушать;
  3. .isStyleLoaded() работает, но не может вызываться все время, пока стиль не будет полностью загружен, вам нужен слушатель, а не метод оценки;
person hijiangtao    schedule 26.01.2019
comment
С помощью mapbox-gl 1.1.1 я обнаружил, что это не работает без изменений: событие styledata запускается несколько раз, поэтому я получаю ошибки при попытке повторно добавить уже существующий слой. Мне также нужно было создать логический флаг, который отслеживает, были ли уже добавлены слои: он устанавливается на false при изменении стиля и на true при вызове addLayer(). Это очень хакерски, но кажется необходимым. - person thund; 26.07.2019
comment
@thund верен, это событие запускается несколько раз. Более того, он никогда не запускается после загрузки стиля. См. мой ответ для лучшего решения. - person Yarin; 04.09.2019

Хорошо, эта проблема с mapbox - отстой, но у меня есть решение

myMap.on('styledata', () => {
  const waiting = () => {
    if (!myMap.isStyleLoaded()) {
      setTimeout(waiting, 200);
    } else {
      loadMyLayers();
    }
  };
  waiting();
});

Смешиваю оба раствора.

person Nathan Redblur    schedule 03.07.2018
comment
Обратите внимание, что style.load, по-видимому, не является частью общедоступного API, и, похоже, он не запускается, когда стиль определяется в строке, а не из URL-адреса: github.com/mapbox/mapbox-gl-js/issues/7579 - person fooquency; 13.11.2018
comment
Это хорошо работает при использовании styledata вместо style.load. Спасибо! - person tempranova; 01.03.2019

Я столкнулся с аналогичной проблемой и пришел к следующему решению:

Я создал небольшую функцию, которая проверяет, загрузился ли стиль:

// Check if the Mapbox-GL style is loaded.
function checkIfMapboxStyleIsLoaded() {
  if (map.isStyleLoaded()) {
    return true; // When it is safe to manipulate layers
  } else {
    return false; // When it is not safe to manipulate layers
  }
}

Затем всякий раз, когда я меняю местами или иным образом изменяю слои в приложении, я использую такую ​​функцию:

function swapLayer() {
  var check = checkIfMapboxStyleIsLoaded();
  if (!check) {
    // It's not safe to manipulate layers yet, so wait 200ms and then check again
    setTimeout(function() {
      swapLayer();
    }, 200);
    return;
  }

  // Whew, now it's safe to manipulate layers!
  the rest of the swapLayer logic goes here...

}
person Bwyss    schedule 15.11.2017
comment
да, я сделал то же самое, ожидая, что команда Mapbox исправит эту ошибку. - person fralbo; 18.11.2017
comment
Я тоже этим хаком пользуюсь, но начиная с MapBox v0.42.1 проверять isStyleLoaded недостаточно. Я сейчас делаю typeof map.getSource('foo') === undefined. (FWIW, я думаю, что другой ответ правильный, лучше сделать так, чтобы ваш код вообще не запускался до тех пор, пока карта и стиль не загрузятся.) - person Nelson; 21.11.2017
comment
Этот первый блок кода совершенно не нужен. Если вы когда-нибудь увидите return true, за которым следует return false, просто верните условие. Тогда остается только возврат вызова mapbox isStyleLoaded (). У вас должно быть просто var = map.isStyleLoaded () - person four43; 23.06.2021

Используйте событие style.load. Он срабатывает один раз при каждой загрузке нового стиля.

map.on('style.load', function() {
    addLayer();
});
person Luis Estevez    schedule 18.11.2017
comment
Обратите внимание, что style.load, по-видимому, не является частью общедоступного API, и, похоже, он не запускается, когда стиль определяется в строке, а не из URL-адреса: github.com/mapbox/mapbox-gl-js/issues/7579 - person fooquency; 13.11.2018

Мой рабочий пример:

когда я меняю стиль map.setStyle()

Я получаю ошибку Uncaught Error: Style is not done loading

Это решило мою проблему

Не используйте map.on("load", loadTiles);

вместо этого используйте

map.on('styledata', function() { addLayer(); });

когда вы меняете стиль, map.setStyle(), вы должны дождаться завершения setStyle (), а затем добавить другие слои. пока map.setStyle('xxx', callback) Не разрешено. Чтобы дождаться обратного вызова, используйте map.on("styledata" map.on("load". Не работает, если вы измените map.setStyle(). вы получите ошибку: Uncaught Error: Style is not done loading

person hoogw    schedule 09.08.2019

Текущая структура событий стиля нарушена (по крайней мере, в Mapbox GL v1.3.0). Если вы отметите map.isStyleLoaded() в обработчике событий styledata , он всегда принимает значение false:

map.on('styledata', function (e) {
  if (map.isStyleLoaded()){
    // This never happens...
  }
}

Мое решение - создать новое событие с именем "style_finally_loaded", которое запускается только один раз и только тогда, когда стиль действительно загружен:

var checking_style_status = false;
map.on('styledata', function (e) {
  if (checking_style_status){
    // If already checking style status, bail out
    // (important because styledata event may fire multiple times)
    return;
  } else {
    checking_style_status = true;
    check_style_status();
  }
});
function check_style_status() {
  if (map.isStyleLoaded()) {
    checking_style_status = false;
    map._container.trigger('map_style_finally_loaded');
  } else {
    // If not yet loaded, repeat check after delay:
    setTimeout(function() {check_style_status();}, 200);
    return;
  }
}
person Yarin    schedule 04.09.2019
comment
Для некоторых стилей это работает, isStyleLoaded возвращает истину при финальном styledata событии, для некоторых стилей - нет (1.12.0). - person kolen; 20.01.2021
comment
Соответствующая проблема: github.com/mapbox/mapbox-gl-js/issues/8691 - person kolen; 20.01.2021

Я создал простое решение. Дайте 1 секунду mapbox для загрузки стиля после того, как вы установите стиль, и вы можете нарисовать слой

map.setStyle(styleUrl);     
setTimeout(function(){
   reDrawMapSourceAndLayer(); /// your function layer
}, 1000);

когда вы используете map.on ('styledataloading'), он запускает пару раз, когда вы меняете стиль

map.on('styledataloading', () => {
  const waiting = () => {
    if (!myMap.isStyleLoaded()) {
      setTimeout(waiting, 200);
    } else {
      loadMyLayers();
    }
  };
  waiting();
});
person rully martanto    schedule 27.01.2021

В итоге я получил: map.once("idle", ()=>{ ... some function here}); Если у вас есть куча вещей, которые вы хотите сделать, я бы сделал что-то вроде этого = ›добавьте их в массив, который выглядит как [{func: function, param: params}], тогда у вас есть другая функция, которая делает это:

executeActions(actions) {
  actions.forEach((action) => {
  action.func(action.params);
});

И в конце у вас есть

this.map.once("idle", () => {
    this.executeActions(actionsArray);
 });
person vvn050    schedule 30.01.2021

У меня была такая же проблема при добавлении маркеров недвижимости на карту. Впервые добавляя маркеры, жду пока карта простаивает. После того, как он был добавлен, я сохраняю его в realEstateWasInitialLoaded и просто добавляю его потом, не дожидаясь. Но не забудьте сбросить realEstateWasInitialLoaded на false при изменении базовой карты или чего-то подобного.

checkIfRealEstateLayerCanBeAddedAndAdd() {
      /* The map must exist and real estates must be ready */
      if (this.map && this.realEstates) {
        this.map.once('idle', () => {
          if (!this.realEstateWasInitialLoaded) {
            this.addRealEstatesLayer();
            this.realEstateWasInitialLoaded = true
          }
        })
        if(this.realEstateWasInitialLoaded) {
          this.addRealEstatesLayer();
        }

      }
    },

person Beeblebrox    schedule 25.06.2021