Хочу поделиться опытом работы с данными JS-картографическими фреймворками, надеюсь материалы помогут сделать выбор в вопросе: какую библиотеку использовать именно в вашем проекте. Чтобы не утомлять, разобью его на несколько логических частей. Начнем с основного и исходного — кода.


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

Код 3-й версии и последующих также хорошо написан и документирован, но есть одно «но» — написан с использованием Google Closure Compiler и без использования ES6. Это весьма занятная штука, вот, например, размер самих библиотек:
Фреймворк Сборка Исходные файлы
Leaflet 1.0.3 144 кб 410 кб (90 файлов)
Openlayers 4.1.1 511 кб 3.1 мб (300 файлов)

Видно, что Сlosure Сompiler в 2+ раза эффективней сжимает исходные файлы, чем механизм Leaflet. Также предоставляет удобный механизм использования пространств имен при разработке, а не «эти ваши» import ‘../../../../src’ в ES6.

На этом все преимущества Google Closure заканчиваются (мое субъективное мнение). Дальше начинаются некоторые страдания, связаны они в основном с тем, что в сборках имена всех членов класса кроме публичных «оптимизируются» до 1-2 букв. Например, вам нужно написать потомок класса ol.source.Image под ваш источник данных, и вы можете работать только с публичными свойствами и методами. Достучаться до приватных членов класса, как и до абстрактных классов родителей, будет проблематично. Есть только один внятный вариант — делать свои сборки Openlayers (авторы OL предлагают использовать именно этот вариант), а делать плагины «рядом», не влезая в основную библиотеку, получится не очень удобно. Однако, внутри сборки Openlayers есть почти все что нужно для создания гис, даже сторонние провайдеры векторных и растровых данных (Esri например), и вероятность написания чего-либо самому или поиск плагина для большинства систем достаточно мала.

Leaflet изначально задумывался по другому принципу. В самой библиотеке есть необходимый минимум, для всего остального — плагины. Код следует этому принципу. В начале он был правда не очень документирован, комментариев не было совсем, потом появились заглавия в каждом классе, сейчас уже для всех публичных методов и свойств класса есть образцово-показательные описания и примеры использования. Код написан с использованием ES6 (пока используется только модульная система). Соответсвенно максимально удобно как создавать свои сборки Leaflet, так и писать расширения для него, авторы рекомендуют последнее.

В статье хотел показать простой пример доработки одного и второго фреймоворка на примере создания тайлового слоя с нестандартной тайловой сеткой, точка отсчета тайлов не в левом-верхнем углу, а посередине сетки (да, и такое встречается). Для Leaflet плагин выглядел примерно бы так:

export var CenterOriginTileLayer = TileLayer.extend({
  getTileUrl: function (coords) {
    // здесь идут простые математические действия по сдвигу координат тайла в центр сетки
    // и возвращание URL для данного тайла
  }
});

Но тут я внезапно понял, что для тайлового источника (ol.source.XYZ) в Openlayers есть свойство типа ol.tilegrid.TileGrid, в котором, в свою очередь, есть свойство origin — те самые координаты центра сетки. Короче говоря, в Openlayers есть все, и кодить даже не пришлось бы.

Итак, если оценивать с точки зрения кода, при прочих равных, что выбрать? Если вы понимаете, что и в Openlayers и в Leaflet (+плагины) нет готового, нужного вам функционала и придется засучив рукава писать дополнения или даже делать кастомную сборку — выбирайте Leaflet. Он проще и его легче интегрировать в ваш проект. Если функционала и там и там вам хватает, то в вопросе выбора надо руководствоваться другими условиями.

В следующей статье разберу сообщества разработчиков и расширения для данных фреймворков.
Поделиться с друзьями
-->

Комментарии (16)


  1. fiskus_boulder
    29.06.2017 11:45
    +2

    Достучаться до приватных членов класса, как и до абстрактных классов родителей, будет проблематично

    Очень странное желание. Приватные члены сделаны приватными, потому что они в будущем могут измениться, и полагаться на них нельзя.


    1. SergeyVoyteshonok
      29.06.2017 11:54
      +1

      В рамках обычных ООП-шных языков вы будете правы. Но в JS технически все члены класса публичные, и разделение их на приватные, защищенные и публичные условно. В OL у некоторых свойств ( в комментах к ним для jsdoc) стоит модификатор protected — т.е. доступ из потомков предполагается. Я не говорю здесь про доступ из других классов к приватным методам и свойствам, я говорю про доступ из потомков.


      1. fiskus_boulder
        29.06.2017 12:01

        Мне кажется, что если разработчик использует нотацию приватных членов (в Closure или в Typescript), то он воспринимает отсутсвие в js приватных свойств, как недостаток и решает его с помощью компилятора/транспайлера. Скрытие данных или реализации с помощью приватных свойств и методов даёт разработчику преимущество не думать об обратной совместимости, когда он захочет их поменять.


        Насчёт @protected не понял, там в коде есть @protected и @private у одних и тех же свойств? Я думаю, что это ошибка.


        1. SergeyVoyteshonok
          29.06.2017 12:14

          Из потомков нельзя достучаться даже до членов классов которые помечены как protected, т.е. предполагается доступ к ним.


          1. fiskus_boulder
            29.06.2017 12:23

            Я код OpenLayers не модифицировал, но с Closure Compiler/Library работал плотно, и у меня этой проблемы не было. Возможно, есть какие-то особенности, про которые я не знаю.


            1. SergeyVoyteshonok
              29.06.2017 12:30

              Вот вы-то мне и нужны ))) Может быть я что-то не понимаю, вам вопрос: могу ли я нормально наследоваться с доступом к членам protected и абстрактным классам, если код уже собран? и работаю я с ним уже после Closure Compiler.


              1. fiskus_boulder
                29.06.2017 12:37

                Я все такие телодвижения делал до сборки, так что не смогу помочь, к сожалению. И использовал Closure как программист-пользователь, т.е. не настраивал окружение.


                Если вы это будете делать средствами javascripta, то, думаю, сможете. Ведь собранный код — это javascript, и там нет @protected, @abstract, @private и т.д.


                Лично я на вашем месте попробовал бы typescript, с ним должно стать всё намного проще (например, https://stackoverflow.com/a/40732068/8159386)


      1. poxvuibr
        29.06.2017 12:17

        Во-первых, если из-за технических ограничений разработчик не может сделать настоящий приватный член, и поэтому просто помечает его, как приватный, он как раз и имеет в виду, что полагаться на него нельзя и вообще трогать его нельзя. Так что желание достучаться до приватных членов класса — всё ещё странное.


        Во-вторых в JS технически нет проблем с тем, чтобы сделать приватные члены класса. И добраться до них, в отличии от какой-нибудь джавы, будет вообще нереально.


        1. SergeyVoyteshonok
          29.06.2017 12:43

          Под словом «приватные», я больше имел ввиду не публичные.
          Простой пример по коду Leaflet.
          Есть класс Layer, у него есть свойство _map, судя по "_" оно не публичное.
          Есть класс наследник — LayerGroup, он использует свойство this._map от потомка. Т.е. по сути своей оно защищенное (protected), объявлено как приватное, но фактически оно публичное, т.к. к нему можно обратится извне.


          1. poxvuibr
            29.06.2017 13:02

            Т.е. по сути своей оно защищенное (protected)

            Так и есть.


            объявлено как приватное

            Что вы имеете в виду, когда говорите, что оно объявлено, как приватное? Что у него underscore перед именем?


            но фактически оно публичное

            Я хотел отметить, что в js нет проблем с тем, чтобы сделать его приватным фактически.


  1. prostofilya
    29.06.2017 12:34
    +2

    Только разогнался и статья закончилась, эх. Можно было и побольше, зачем делить на столь мелкие.


    1. SergeyVoyteshonok
      29.06.2017 14:14

      Согласен с вами, но последующие 2 статьи про плагины, фишки и выводы будут объемней, а моя лень затянет написание такой длинной статьи на бесконечное время. Поэтому решил подробить. Сейчас думаю: может еще рассказать как рендеры у них работают.


      1. prostofilya
        29.06.2017 16:25

        Буду ждать, интересная тема.


  1. dom1n1k
    29.06.2017 14:00
    +2

    Лично я не вижу для себя весомых преимуществ в «академичности» OL. Это совершенно абстрактное достоинство, которое на практике выливается в банальную многословность. На каждый чих у них объект, каждый объект нужно конструировать.

    Для иллюстрации приведу минимальный код, который нужен чтобы просто показать карту. Ничего с ней не делать — просто создать и показать один тайловый слой. Почувствуйте разницу, как говорится.

    Код
    // Leaflet
    var map1 = L.map('map1', {
        center: [55.8, 37.6],
        zoom: 12,
    });
    L.tileLayer('http://sometileserver.com/{z}/{x}/{y}.png', {
        attribution: '© <a href="http://sometileserver.com">sometileserver.com</a>',
    })
    .addTo(map1);
    
    // OpenLayers
    var map2 = new ol.Map({
        target: 'map2',
        layers: [
            new ol.layer.Tile({
                source: new ol.source.XYZ({
                    url: 'http://sometileserver.com/{z}/{x}/{y}.png',
                    attributions: new ol.Attribution({
                        html: '© <a href="http://sometileserver.com">sometileserver.com</a>',
                    }),
                }),
            })
        ],
        view: new ol.View({
            center: ol.proj.fromLonLat([37.6, 55.8]),
            zoom: 12,
        }),
    });
    


    1. SergeyVoyteshonok
      29.06.2017 14:19
      +1

      Ну это пока вы в «мейнстриме» держитесь, а как только шаг вправо, шаг влево, начинаются нюансы. Возьмите коническую проекцию, да разных плагинов, которые друг с другом не работают, вот тут Леафлет бибикая уезжает в кювет, но об этом в следующий раз. Так что, не все так однозначно.


      1. za90
        30.06.2017 08:58

        <сарказм>Меркаатора хватит для всего</сарказм>