При разработке картографических приложений на основе Mapbox GL JS объём кода быстро увеличивается. Появляются пользовательские источники данных, слои, обработчики событий, вспомогательные функции для работы с геометрией, классы управления картой и сложные структуры конфигурации. Поддерживать такой код без качественной документации становится затруднительно.
JSDoc — это стандарт документирования JavaScript-кода при помощи специальных комментариев. Такие комментарии позволяют:
Для библиотек, работающих с геоданными и картографическими объектами, JSDoc особенно полезен, поскольку многие методы принимают сложные структуры данных.
JSDoc-комментарий начинается с последовательности /** и
завершается */.
/**
* Создает карту Mapbox.
*/
function createMap() {
}
Многострочные комментарии обычно располагаются непосредственно перед документируемым элементом.
/**
* Устанавливает начальный масштаб карты.
*/
map.setZoom(10);
На практике JSDoc чаще применяется для документирования функций, классов, методов и объектов конфигурации.
Простейший вариант включает краткое описание назначения функции.
/**
* Добавляет источник GeoJSON на карту.
*/
function addGeoJsonSource() {
}
Описание должно отвечать на вопрос: что делает функция, а не как именно реализована её логика.
Тег @param используется для описания параметров.
/**
* Центрирует карту по указанным координатам.
*
* @param {number} lng Долгота.
* @param {number} lat Широта.
*/
function flyToLocation(lng, lat) {
map.flyTo({
center: [lng, lat]
});
}
Тип указывается в фигурных скобках.
@param {string}
@param {number}
@param {boolean}
@param {Array}
@param {Object}
/**
* Изменяет параметры отображения карты.
*
* @param {number} zoom Масштаб.
* @param {number} pitch Угол наклона.
* @param {number} bearing Поворот карты.
*/
function setView(zoom, pitch, bearing) {
map.easeTo({
zoom,
pitch,
bearing
});
}
Если функция возвращает значение, используется тег
@returns.
/**
* Возвращает текущий масштаб карты.
*
* @returns {number} Текущий уровень масштабирования.
*/
function getCurrentZoom() {
return map.getZoom();
}
Допустимы также формы:
@returns {string}
@returns {Object}
@returns {Array}
/**
* Возвращает координаты центра карты.
*
* @returns {{lng:number, lat:number}}
*/
function getCenter() {
const center = map.getCenter();
return {
lng: center.lng,
lat: center.lat
};
}
Такой подход особенно полезен при работе с географическими координатами.
Mapbox GL JS активно использует конфигурационные объекты.
Например:
map.addLayer({
id: 'roads',
type: 'line',
source: 'roads'
});
Для описания подобных структур применяется несколько тегов
@property.
/**
* Параметры слоя карты.
*
* @typedef {Object} LayerOptions
* @property {string} id Идентификатор слоя.
* @property {string} type Тип слоя.
* @property {string} source Источник данных.
*/
После этого тип можно использовать повторно.
/**
* Создает слой.
*
* @param {LayerOptions} options Настройки слоя.
*/
function createLayer(options) {
}
Для работы с геоданными удобно создавать собственные типы.
/**
* Географическая точка.
*
* @typedef {Object} Coordinate
* @property {number} lng Долгота.
* @property {number} lat Широта.
*/
Использование:
/**
* Перемещает карту к координатам.
*
* @param {Coordinate} point Координаты точки.
*/
function moveTo(point) {
map.flyTo({
center: [point.lng, point.lat]
});
}
Такой подход делает код значительно понятнее.
/**
* Возвращает список идентификаторов слоев.
*
* @returns {string[]}
*/
function getLayerIds() {
}
/**
* @typedef {Object} MarkerData
* @property {number} lng
* @property {number} lat
* @property {string} title
*/
/**
* Создает набор маркеров.
*
* @param {MarkerData[]} markers
*/
function addMarkers(markers) {
}
Редактор сразу показывает структуру элементов массива.
GeoJSON является одним из основных форматов в экосистеме Mapbox.
Для него полезно создавать отдельные типы.
/**
* @typedef {Object} GeoJsonFeature
* @property {string} type
* @property {Object} geometry
* @property {Object} properties
*/
Использование:
/**
* Добавляет объект на карту.
*
* @param {GeoJsonFeature} feature Геообъект.
*/
function addFeature(feature) {
}
Необязательный параметр заключается в квадратные скобки.
/**
* Создает маркер.
*
* @param {number} lng Долгота.
* @param {number} lat Широта.
* @param {string} [color] Цвет маркера.
*/
function createMarker(lng, lat, color) {
}
/**
* Создает маркер.
*
* @param {string} [color='red'] Цвет маркера.
*/
function createMarker(color = 'red') {
}
Такой комментарий сразу показывает стандартное значение параметра.
Во многих случаях функция принимает единый объект настроек.
/**
* Добавляет источник данных.
*
* @param {Object} options Настройки источника.
* @param {string} options.id Идентификатор.
* @param {string} options.url URL данных.
* @param {boolean} options.cluster Включение кластеризации.
*/
function addSource(options) {
}
Пример вызова:
addSource({
id: 'cities',
url: '/data/cities.geojson',
cluster: true
});
JSDoc одинаково работает со стрелочными функциями.
/**
* Получает центр карты.
*
* @returns {mapboxgl.LngLat}
*/
const getCenter = () => {
return map.getCenter();
};
Mapbox GL JS активно использует события.
map.on('click', (event) => {
});
Описание обработчика:
/**
* Обрабатывает клик по карте.
*
* @param {mapboxgl.MapMouseEvent} event Событие клика.
*/
function handleMapClick(event) {
}
Подключение:
map.on('click', handleMapClick);
/**
* Класс управления пользовательскими маркерами.
*
* @class
*/
class MarkerManager {
}
На практике большинство современных инструментов способны определять классы автоматически, однако тег может использоваться для явного описания.
/**
* Управляет работой маркеров.
*/
class MarkerManager {
/**
* @param {mapboxgl.Map} map Экземпляр карты.
*/
constructor(map) {
this.map = map;
}
}
class MarkerManager {
/**
* Добавляет маркер.
*
* @param {number} lng
* @param {number} lat
*/
addMarker(lng, lat) {
}
/**
* Удаляет все маркеры.
*/
clear() {
}
}
class LayerManager {
/**
* Экземпляр карты.
*
* @type {mapboxgl.Map}
*/
map;
constructor(map) {
this.map = map;
}
}
Для сложных методов полезно добавлять примеры.
/**
* Создает слой линий.
*
* @param {string} sourceId Идентификатор источника.
*
* @example
* createLineLayer('roads');
*/
function createLineLayer(sourceId) {
}
Пример часто оказывается полезнее длинного описания.
Используется для описания возможных исключений.
/**
* Возвращает слой по идентификатору.
*
* @param {string} id Идентификатор слоя.
* @returns {Object}
* @throws {Error} Если слой не найден.
*/
function getLayer(id) {
}
Позволяет отметить устаревший API.
/**
* @deprecated Использовать createMarkerV2().
*/
function createMarker() {
}
Редакторы кода обычно подсвечивают подобные методы как устаревшие.
Используется для указания связанных сущностей.
/**
* Создает источник данных.
*
* @see createLayer
*/
function createSource() {
}
Фиксирует версию появления функциональности.
/**
* @since 2.0.0
*/
function addClusterLayer() {
}
В картографических приложениях часто используются фиксированные наборы значений.
/**
* Типы слоев.
*
* @readonly
* @enum {string}
*/
const LayerType = {
FILL: 'fill',
LINE: 'line',
SYMBOL: 'symbol',
CIRCLE: 'circle'
};
Использование:
/**
* @param {LayerType} type
*/
function createLayer(type) {
}
Mapbox-приложения часто получают данные по сети.
/**
* Загружает GeoJSON-файл.
*
* @param {string} url Адрес файла.
* @returns {Promise<Object>}
*/
async function loadGeoJson(url) {
const response = await fetch(url);
return response.json();
}
Более подробный вариант:
/**
* Получает данные объектов.
*
* @returns {Promise<GeoJsonFeature[]>}
*/
function loadFeatures() {
}
Современные редакторы понимают типы Mapbox GL JS.
/**
* @type {mapboxgl.Map}
*/
let map;
/**
* @param {mapboxgl.MapMouseEvent} event
*/
function onClick(event) {
}
/**
* @type {mapboxgl.Marker}
*/
let marker;
/**
* @type {mapboxgl.Popup}
*/
let popup;
/**
* @type {mapboxgl.LngLat}
*/
let center;
При использовании модульной архитектуры можно импортировать типы из других файлов.
/**
* @typedef {import('./types').Coordinate} Coordinate
*/
Далее тип используется как обычный пользовательский тип.
/**
* @param {Coordinate} point
*/
function flyToPoint(point) {
}
Для крупных проектов удобно документировать целые модули.
/**
* Работа со слоями карты.
*
* @module LayerManager
*/
Такой подход особенно полезен при автоматической генерации документации.
Качественные комментарии обладают несколькими характерными признаками:
@typedef;@deprecated;В крупных картографических приложениях JSDoc превращается не просто в средство документирования, а в дополнительный уровень типизации и самодокументирования архитектуры, позволяющий эффективно сопровождать кодовую базу Mapbox GL JS даже при большом количестве источников данных, слоев, обработчиков событий и пользовательских компонентов управления картой.