Comments 34
Недавно как раз смотрел в сторону cloudmade, но скудные маркеры меня не устроили.
Не подскажете ли API карт, где маркеры хорошо реализованны? Мне для инфографики — кружки, текстовые метки, heatmap…
Вы про гугл мапс? Так там же тоже маркеры однообразны, как, например, сделал текстовую метку вместо изображения маркера или круг со словом внутри?
Нет, не походит. Ниже написал что нашел polymaps — меня устраивает.
Я так понимаю, речь идёт о том, чтобы вместо картинки-иконки показывать свою кастомную разметку.
Ох не представляю, что может написать forgotten в обзоре OpenLayers… :) Наверное, целую многотомную книгу с повышенным содержанием матов и WTF. :)
Спасибо большое за детальную критику, это невероятно полезно для проекта!
Комментировать придётся долго… Ну что ж, приступим. :)

«Пример начинается с создания слоя с тайлами от cloudmade. Само API вроде как тоже «by cloudmade». Внимание, вопрос: а что, для родительского/дружественного проекта нельзя сделать удобный способ добавления слоя тайлов?»

Идея была в том, чтобы создать абсолютно провайдеро-независимую опенсорс-библиотеку, подчеркивая то, что она не навязывает использование сервисов конкретных компаний и не содержит провайдер-ориентированного кода (это оставим плагинам и т.д.), и использовать можно любые тайлы, поэтому пример сразу такой, чтобы человек мог за две минуты поменять на тайлы, которые ему нравятся, например, от MapQuest или MapBox.

Согласен, что нужно это на сайте подчеркнуть, чтобы было понятнее — даже багрепорт такой есть в GitHub issues. :)

«Кстати, из приведённого примера хорошо заметно неудобство смешивания объектного подхода и чайнинга — (new X()).y() не самая красивая конструкция в JS.»

Библиотека не навязывает использование чейнинга. Главное, что есть выбор — можно так, можно эдак, это как человеку удобнее. Я лично предпочитаю, например, сначала создать объект и присвоить переменной, а потом уже с отдельной строки с ней проводить операции. По этой же причине в примерах используются разные подходы — чтобы подчеркнуть наличие выбора. Хотя, наверное, это тоже нуждается в дополнительном уточнении на сайте.

«Кстати, почему маркеры добавляются через addLayer? L.Layer в Leaflet — вполне понятная отдельная сущность — слой. Почему через addLayer добавляются и другие сущности — маркеры, геометрии?»

Потому что слой в Leaflet — это всё, что можно привязать к определённому географическому положению (или положениям) на карте, будь то слой тайлов, маркер или даже попап. Не вижу смысла разделять сущности типа карты и тайлов для самой карты — пускай этой не особо существенной разницей занимаются соответствующие реализации слоёв, а карта пускай всего поменьше знает — это loose coupling.

«Во-первых, откуда ж на моей странице возьмётся папка leaflet с кодом leaflet-а? Кажется, что вот как раз получение кода библиотеки и нужно описывать в разделе «Preparing your page».»

Подразумевалось, что люди, которые этого не понимают, будут скачивать библиотеку с leaflet.cloudmade.com/download.html, а там это написано. :) Хотя вы правы, нужно чётче это прописать в примере и оставить ссылку на эту страницу.

«Далее, зачем заставлять вебмастера самого размещать код подключения css? Почему не оставить эту работу js-скрипту?»

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

«Удивляет другое — почему не хостите эту библиотеку сами и заставляете подключать с домена пользователя? Не ахти ж какая нагрузка, да и договориться с партнером каким-нибудь можно. Зато у ваших пользователей не будет проблем с обновлением версий (и с критическими багами в старых версиях, которые рано или поздно появятся) + с распространением библиотеки она очень скоро окажется у большинства пользователей в кэше.»

Тоже применяли такой подход раньше, но на практике выяснилось, что это ОЧЕНЬ плохая затея (по крайней мере URL типа "[...]/somelib-latest.js". Представьте, что у вас сайт с тысячами пользователей в день, и всё у вас прекрасно работает, ничего не трогаете, вы уезжаете в отпуск, возвращаетесь и вдруг обнаруживаете, что последние две недели нифига не работало из-за мелкой специфичной регрессии в последней версии, которая проявляется только в вашем конкретном случае и поэтому не была замечена разработчиками библиотеки (ВСЕ баги всё равно отловить невозможно, что-нибудь да вылезает время от времени). А вот будь у вас урл на конкретную версию, тогда всё бы работало, а обновляясь вручную, вы имели бы возможность всё проверить перед деплойментом на продакшн-сервер. Именно по этой причине практически ни один популярный фреймворк не представляет подобной возможности и всё по версиям тоже.

Вот сделать CDN с урлами на конкретные версии хочется, но пока не добрались до этого.

Дальше отвечу в следующем комментарии. :)
> Идея была в том, чтобы создать абсолютно провайдеро-независимую опенсорс-библиотеку, подчеркивая то, что она не навязывает использование сервисов конкретных компаний и не содержит провайдер-ориентированного кода (это оставим плагинам и т.д.), и использовать можно любые тайлы, поэтому пример сразу такой, чтобы человек мог за две минуты поменять на тайлы, которые ему нравятся, например, от MapQuest или MapBox.

Идеи-идеями, а в первую очередь нужно сделать удобно пользователю. Лучше добавить n удобных шорткатов для разных провайдеров, чем заставлять писать такие мозгоразрывающие конструкции.

> Библиотека не навязывает использование чейнинга. Главное, что есть выбор — можно так, можно эдак, это как человеку удобнее.

Мысль в том, что фабрики вместо (или вместе с) конструкторов выглядят гораздо красивее с чайнингом. Принципиально настаивать на обязательном использовании слова new несмотря на неудобства — это уже из области религии.

> Потому что слой в Leaflet — это всё, что можно привязать к определённому географическому положению (или положениям) на карте, будь то слой тайлов, маркер или даже попап.

Тогда тайловые слои надо переименовать. Но вообще идти вразрез со всеми остальными аналогичными продуктами — плохая идея. Это как на иконке «Сохранить» не рисовать дискетку.

> Во-вторых, что более серьёзная проблема, для корректной инициализации и работы карты многие элементы должны быть стилизованными, но мало того, что отслеживание onload динамически загруженного стиля нетривиальная задача…

Можно грузить css-код строкой и создавать тэг link динамически.

> Тоже применяли такой подход раньше, но на практике выяснилось, что это ОЧЕНЬ плохая затея (по крайней мере URL типа "[...]/somelib-latest.js". Представьте, что у вас сайт с тысячами пользователей в день…

Посмотрите, как решена эта проблема в Гугле.
Есть урл на последнюю _стабильную_ версию, который рекомендован всем. Для разработки есть урл на последнюю выпущенную сборку + есть урлы на каждый конкретный релиз.
Идеи-идеями, а в первую очередь нужно сделать удобно пользователю. Лучше добавить n удобных шорткатов для разных провайдеров, чем заставлять писать такие мозгоразрывающие конструкции.

По-моему конструкции элементарные, абсолютно ничего сложного в них не вижу, и разве сложно написать один раз за весь проект пару строчек? Удобные шорткаты для провайдеров я лучше вынесу в плагины. Идея, о которой я говорил, достаточно принципиальна.

Мысль в том, что фабрики вместо (или вместе с) конструкторов выглядят гораздо красивее с чайнингом. Принципиально настаивать на обязательном использовании слова new несмотря на неудобства — это уже из области религии.

То, что фабрики выглядят красивее — это тоже из области религии.

Тогда тайловые слои надо переименовать.

Зачем? Я так и не понял, чем конкретно такое обобщение осложняет пользование библиотекой.

Посмотрите, как решена эта проблема в Гугле.
Есть урл на последнюю _стабильную_ версию, который рекомендован всем. Для разработки есть урл на последнюю выпущенную сборку + есть урлы на каждый конкретный релиз.

«Стабильность» версии не гарантирует отсутствия регрессий. И если уже сталкиваться с ними, то лучше при разработке, чем вдруг внезапно обнаружить у себя на продакшне.

Мне очень не нравится, что так сделано у гугла. И делает он так совсем не для того, чтобы у всех пользователей всё было стабильно и круто. А по одной причине — для лучшего контроля над пользователями библиотеки, например, втулить насильно рекламу, если им вдруг однажды взбредёт такое в голову.
И сразу вопрос — почему некоторые ссылки серые и никуда не ведут? Документация не готова? Сломалось что-то? Roadmap? Кстати, а к какой версии эта документация — к стабильной 0.2 или dev 0.3?

Всё, что серое, не успел написать еще — наметил то, что будет в скором времени. :) Насчёт путаницы с версиями согласен, всё уточним и исправим.

Гм. Если опции карты всегда перекрывают опции слоя и у них есть дефолтное значение — зачем тогда нужны опции слоя? Непонятно…

Если жёстко не ограничить зум карты вручную, ограничения высчитываются автоматически в зависимости от того, какие слои на карте. Надо уточить там формулировку, да.

Более интересно другое — почему ILayer-ы удостоены отдельной опции-массива layers, а IHandler-ы и IControl-ы — нет. Разве так не логичнее?


Потому что layers по умолчанию пустой массив, его просто задавать сразу массивом, а в случае с контролами и хендлерами всё сложнее — есть определённый дефолтный набор для карты, который может еще и варьироваться в зависимости от браузера (напр., мобильный или десктопный). Поэтому их контролировать проще индивидуально, чем выяснять, какие должны быть в каких случаях, составлять массив в нём уже что-то добавлять/убирать.

А mouseup, contextmenu, mouseenter, mouseleave? Уж как-то совсем странно предоставлять событие mousedown и не давать слушать mouseup.

Всё есть, просто документацию доработать нужно. :)

Уберите неинициализированное состояние карты, и в этих двух событиях отпадёт нужда.

Изначально его не было, но со временем решил добавить. Бывают случаи, когда нужно инициализировать карту сначала частично (например, задать только зум и слои), а потом уже по какому-то событию доинициализировать. Чтобы убрать состояние инициализированности, нужно либо навязывать человеку инициализировать всё сразу в конструкторе и никак иначе (что неудобно), либо задавать дефолтные значения для центра и зума, что в примере выше приведёт к тому, что будет загружен сначала один набор тайлов, а потом другой. Событие load ничему не мешает, о нём в общем случае можно и не знать вообще, и использовать только в некоторых случаях, поэтому убирать нет причин.

А вот насчёт viewreset — не понял, как это отпадёт нужда? Это самый важный ивент для реализации всех слоёв карты. Он говорит слоям, когда нужно перепроецировать координаты.

Ну и многие методы просто избыточны — например, зачем нужны отдельные методы locate и locateAndSetView, если можно просто обойтись флагом setView в опциях метода locate?

Забыл удалить из документации. :)

Зачем нужны методы zoomIn/zoomOut, когда есть setZoom?

Для удобства. Чтобы не писать
map.setZoom(map.getZoom() + 1)
. Но в принципе не критично, может быть, и стоит убрать.

Вообще насчёт большого кол-ва и некоторой неконсистентности методов Map согласен, это я обязательно приведу в порядок вместе с документацией.

Невозможность перезадать иконку не радует

marker.setIcon(icon);
— опять же, пробел документации.

отсутствие нативной возможности связать с маркером какие-то данные

Идентификатор любого объекта в Leaflet можно получить так:
var id = L.Util.stamp(marker);
. А где хранить какие данные — мне кажется, это лучше оставлять на совесть разработчикам — как кому удобнее. Предлагаете сделать что-то типа jQuery
.data
?

Почему-то для других сервисов (WMS) отдельный класс для удобства заведён, а для родного — нет.

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

Также непонятно, зачем введена настройка «размер тайла».

Не все тайл-сервисы предоставляют тайлы 256х256, бывают и другие размеры, скажем, 64х64 или 1024х1024 (например kothic.org/js/).

Опция noClip с комментарием «Disabled polyline clipping.» поставила меня в тупик. Особенно отсутствием такой же опции у остальных геометрий.


Клиппинг имеет смысл только для полилайнов и полигонов, а полигон наследует опции полилайна. Или вам непонятно, что такое клиппинг?

В упор не вижу отличий от просто Circle. По названию «CircleMarker» я бы подумал, что это круг + маркер, но описание никаких наводок не даёт.

Круг фиксированного размера в пикселях — это означает, что он будет радиусом в, скажем, 10 пикселей независимо от масштаба карты. Я думал, это очевидно…

Зачем это сделано? Кому мешал радиус в сигнатуре? Или это такой способ сделать доступным изменение радиуса?

CircleMarker применяется в тех же случаях, что и обычный маркер, т.е. не для обозначения определённого географического круга, а как круглый маркер, которому в отличие от обычного можно задать любой цвет, радиус и т.д. и менять всё это динамически. Поэтому он так и называется. Радиус можно задать, еще не добавил в документацию.

Группа LayerGroup, в которую можно класть не только слои, но и маркеры/графику — это очень странно. Ссылка ILayer всё ещё никуда не ведёт. Метод clearLayers, который удаляет всех детей группы (а не очищает тайловые слои, как лично я бы подумал из названия) — тоже очень странный.


Ну это, опять же, от непонимания концепции слоёв. :) Это не странно, а удобно и даёт много разных возможностей. Зачем делать для этого разные классы, если они будут одинаковыми и делать одно и то же?

Но группа FeatureGroup, которая к LayerGroup добавляет пропагацию событий и попап — это уже прямо совсем странно. Что должно кому сказать название Feature?

Feature — это всё, что на карте имеет смысл делать интерактивным. :) Да, объяснить в документации нужно.

Самое-то интересное и скрыто за «do something». А что сделать-то можно? Группа не даёт аксессоров до дочерних объектов. Если я добавил FeatureCollection через geojson — что я сделать-то с ней могу? Или FeatureCollection нельзя добавлять, затем и накопительный addGeoJSON?


Всё можно сделать. :) А у FeatureGroup есть еще setStyle.

Недостатки функционала, положим, можно легко списать на малый размер библиотеки (25Кб — рекорд). (Только вот что тут экономить, если библиотека весит меньше, чем один стандартный тайл?)

Делать не только в весе, но и в количестве кода, на которое браузер должен потратить и процессорное время, и память. Это очень критично в мобильных браузерах.

А насчёт недостатка в функционале — в ней в общем-то не так много всего осталось реализовать. Не хочется превращать библиотеку в здоровенного неповоротливого монстра, подобного OpenLayers, в которой 95% функционала используется 5% пользователей, если не хуже.

Ну а неконсистентности и нелогичности автор, надеюсь, доработает напильником, благо версия пока всего лишь 0.3, можно себе позволить отрывать обратную совместимость.

Конечно доработаю. :) И особенно документацию, а то, как вижу, слишком мало ей уделял внимания в последнее время, нужно исправляться…

В общем спасибо большое, тонны полезных комментариев, с множеством всего согласен, буду исправлять и дорабатывать!
> Поэтому их контролировать проще индивидуально, чем выяснять, какие должны быть в каких случаях, составлять массив в нём уже что-то добавлять/убирать.

Ох я че-то в этом сомневаюсь. Попробуйте пописать код, который индивидуально (в зависимости от браузера) включает/выключает хэндлеры и контролы. Быстро поймёте, что массивом куда проще.

> Бывают случаи, когда нужно инициализировать карту сначала частично (например, задать только зум и слои), а потом уже по какому-то событию доинициализировать.

Не представляю себе таких случаев. Всегда можно дождаться готовности всех данных и потом инициализировать карту. Особенно если locate сделать статическим методом.

> Или вам непонятно, что такое клиппинг?

Мне-то понятно, да только я не очень репрезентативен.

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

Размен удобства пользователя на идеологическую чистоту, по моему скромному мнению, абсолютно не оправдан в 99% случаев.

> Круг фиксированного размера в пикселях — это означает, что он будет радиусом в, скажем, 10 пикселей независимо от масштаба карты. Я думал, это очевидно…

Вам очевидно, что Circle — круг в метрах, MarkerCircle — в пикселах? И это отличие оправдывает разные сигнатуры? И круг в пикселах логично отнаследован от круга в метрах?

> Ну это, опять же, от непонимания концепции слоёв.

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

> Feature — это всё, что на карте имеет смысл делать интерактивным.

Ну, тем самым вы закрываете себе возможность сделать слои интерактивными (ну, точнее, попадёте в неприятное с точки зрения интерфейсов положение, когда начнёте впиливать интерактивные слои). Я не рекомендовал бы так делать :)

> В общем спасибо большое, тонны полезных комментариев, с множеством всего согласен, буду исправлять и дорабатывать!

Пожалуйста.
Я пользуюсь. Очень удобно прикрутилось для моих задач (вывести в городе несколько маркеров и дать возможность пользователю добавить свой и перетащить в нужное место). Один только вопрос, если карта обновляется с openstreetmap, то как часто?
Тайлы CloudMade теоретически должны обновляться каждую неделю, но на практике бывают задержки. Но с Лифлетом хорошо то, что можно в любой момент переключиться на другого провайдера, если что. :)
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
Извините, а мне одному кажется, что это блок про пользовательские интерфейсы?
Или автор рассказывает именно про них, просто я это умудрился не понять?
UFO landed and left these words here
Эта статья написана специально по просьбе создателя Leaflet Mourner.
И, поверьте, в картографических API я кое-что понимаю.
UFO landed and left these words here
Как мне поступить, если я хочу, чтобы по клику открывался балун, а по драгу таскалась карта? Вполне обычный юзкейс


Суры и асуры! Я только что написал такое в обработке клика (this — это маркер, this._icon — его DOM-элемент):

this.dragging.enable();
this._icon.click();
this.dragging.disable();


Вот как раз ради такого юзкейса, да. Неужели так и надо?
Only those users with full accounts are able to leave comments. Log in, please.