---
metadata:
  - name: generator
    content: Diplodoc Platform v5.39.1
alternate:
  - https://yandex.com.tr/dev/jsapi-v2-1/doc/ru/v2-1/examples/cases/polygon_custom.md
---
> **Documentation Index:** Fetch the complete configuration index at https://yandex.com.tr/dev/jsapi-v2-1/doc/ru/llms.txt

# Нарисовать произвольную область на карте

<iframe id="LIVE-EXAMPLE" style="width:100%;height: 400px;border-radius: 8px;border: 1px solid rgba(92, 94, 102, 0.14);" frameBorder="0" src="https://yastatic.net/s3/front-maps-static/maps-front-jsapi-v2-1/examples/2/out/s3-cases/ru/polygon_custom/index.html" allow="fullscreen"></iframe>

<a target="_blank" rel="noopener noreferrer" style="display: inline-block;margin-top: 10px;cursor: pointer;border-radius: 4px;padding: 9px 12px;background: #151515;color: white;text-decoration: none;font-weight: 500" href="https://codesandbox.io/p/sandbox/ypq2lx?file=index.html">Open in CodeSandbox</a>

<div id="references">
    <a href="https://yandex.ru/dev/jsapi-v2-1/doc/ru/v2-1/ref/reference/ready">ready</a>,
    <a href="https://yandex.ru/dev/jsapi-v2-1/doc/ru/v2-1/ref/reference/Map">Map</a>,
    <a href="https://yandex.ru/dev/jsapi-v2-1/doc/ru/v2-1/ref/reference/Polygon">Polygon</a>,
    <a href="https://yandex.ru/dev/jsapi-v2-1/doc/ru/v2-1/ref/reference/map.GeoObjects">map.GeoObjects</a>
</div>
<p>
    В данном примере показано, как можно реализовать рисование произвольного контура на карте.
</p>
<p>
    Для построения геообъектов доступен <a href="https://yandex.ru/dev/jsapi-v2-1/doc/ru/v2-1/dg/concepts/geoobjects#visual_editing">визуальный редактор</a>. Однако он позволяет рисовать геообъекты по координатам клика. Если на карте нужно рисовать произвольные контуры (так, как будто вы рисуете карандашом), используйте элемент canvas.
</p>
<p>
    В примере поверх карты добавляется элемент canvas. После зажатия кнопки мыши с зажатой клавишей "alt" мы начинаем слушать событие 'mousemove'. В тот момент, когда пользователь будет рисовать на карте, в массив будут сохраняться координаты перемещений указателя мыши. Когда пользователь отпустит кнопку мыши, по сохраненным координатам АПИ построит геообъект.
</p>
<p>
    Часто на карте нужно фильтровать объекты, которые попадают в нарисованный контур. В примере  <a href="https://yandex.ru/dev/jsapi-v2-1/doc/ru/v2-1/examples/cases/placemarks_in_circle">Попадание точек в окружность</a> можно посмотреть, как это сделать.
</p>

{% list tabs %}

-   index.html

    ```html
    <!DOCTYPE html>
    <html>
        <head>
            <title>Нарисовать произвольную область на карте</title>
            <meta
                http-equiv="Content-Type"
                content="text/html; charset=utf-8"
            />
            <!--
            Укажите свой API-ключ. Тестовый ключ НЕ БУДЕТ работать на других сайтах.
            Получить ключ можно в Кабинете разработчика: https://developer.tech.yandex.ru/keys/
        -->
            
            
            
            
        </head>
        <body>
            <p>
                Чтобы включить режим рисования, нужно нажать левую кнопку мыши,
                зажав клавишу "alt".
            </p>
            <div id="map"></div>
            <canvas id="draw-canvas"></canvas>
        </body>
    </html>
    ```

-   paintOnMap.js

    ```js
    ymaps.modules.define(
        "ext.paintOnMap",
        ["meta", "util.extend", "pane.EventsPane", "Event"],
        function (provide, meta, extend, EventsPane, Event) {
            "use strict";

            // zIndex пейна событий карты по умолчанию равен 500.
            // Подробней в документации: https://tech.yandex.ru/maps/doc/jsapi/2.1/ref/reference/map.pane.Manager-docpage/
            var EVENTS_PANE_ZINDEX = 500;

            var DEFAULT_UNWANTED_BEHAVIORS = ["drag", "scrollZoom"];
            var DEFAULT_STYLE = {
                strokeColor: "#0000ff",
                strokeWidth: 1,
                strokeOpacity: 1,
            };
            var DEFAULT_TOLERANCE = 16;

            var badFinishPaintingCall = function () {
                throw new Error(
                    "(ymaps.ext.paintOnMap) некорректный вызов PaintingProcess#finishPaintingAt. Рисование уже завершено."
                );
            };

            /**
             * @interface ymaps.ext.paintOnMap.PaintingProcess
             */

            /**
             * Отключает режим рисования.
             * @function
             * @name ymaps.ext.paintOnMap.PaintingProcess#finishPaintingAt
             * @param {Number[]|ymaps.Event} [positionOrEvent] Координаты точки, в которой рисование должно закончиться.
             * Координаты задаются в пикселях относительно верхнего левого угла карты.
             * @return {Number[]} Координаты.
             */

            /**
             * Включает режим рисования.
             * @name ymaps.ext.paintOnMap
             * @param {ymaps.Map} map
             * @param {Number[]|ymaps.Event} [positionOrEvent] Координаты точки, в которой рисование должно закончиться.
             * Координаты задаются в пикселях относительно верхнего левого угла карты.
             * @param {Object} [config]
             * @param {String[]|null} [config.unwantedBehaviors] Список поведений карты, которые должны быть выключены во время
             * рисования. Перетаскивание карты и её масштабирование колесом мыши выключены по умолчанию.
             * @param {Object} [config.style] Стили такие же, как в ymaps.Polygon или ymaps.Polyline.
             * @param {String} [config.style.strokeColor='#0000ff'] Цвет линии или обводки.
             * @param {Number} [config.style.strokeWidth=1] Толщина линии или обводки.
             * @param {Number} [config.style.strokeOpacity=1] Прозрачность линии или обводки.
             * @param {Number} [config.tolerance=16] Уровень упрощения координат в пикселях.
             * @returns {ymaps.ext.paintOnMap.PaintingProcess} Процесс рисования.
             */
            function paintOnMap(map, positionOrEvent, config) {
                config = config || {};
                var style = extend(DEFAULT_STYLE, config.style || {});

                var unwantedBehaviors =
                    config.unwantedBehaviors === undefined
                        ? DEFAULT_UNWANTED_BEHAVIORS
                        : config.unwantedBehaviors;

                var pane = new EventsPane(map, {
                    css: {
                        position: "absolute",
                        width: "100%",
                        height: "100%",
                    },
                    zIndex: EVENTS_PANE_ZINDEX + 50,
                    transparent: true,
                });

                map.panes.append("ext-paint-on-map", pane);

                if (unwantedBehaviors) {
                    map.behaviors.disable(unwantedBehaviors);
                }

                // Создаём canvas-элемент.
                var canvas = document.createElement("canvas");
                var ctx2d = canvas.getContext("2d");
                var rect = map.container
                    .getParentElement()
                    .getBoundingClientRect();
                canvas.width = rect.width;
                canvas.height = rect.height;

                ctx2d.globalAlpha = style.strokeOpacity;
                ctx2d.strokeStyle = style.strokeColor;
                ctx2d.lineWidth = style.strokeWidth;

                canvas.style.width = "100%";
                canvas.style.height = "100%";

                pane.getElement().appendChild(canvas);

                var firstPosition = positionOrEvent
                    ? toPosition(positionOrEvent)
                    : null;
                var coordinates = firstPosition ? [firstPosition] : [];

                var bounds = map.getBounds();
                var latDiff = bounds[1][0] - bounds[0][0];
                var lonDiff = bounds[1][1] - bounds[0][1];

                canvas.onmousemove = function (e) {
                    coordinates.push([e.offsetX, e.offsetY]);

                    ctx2d.clearRect(0, 0, canvas.width, canvas.height);
                    ctx2d.beginPath();

                    ctx2d.moveTo(coordinates[0][0], coordinates[0][1]);
                    for (var i = 1; i < coordinates.length; i++) {
                        ctx2d.lineTo(coordinates[i][0], coordinates[i][1]);
                    }

                    ctx2d.stroke();
                }.bind(this);

                // Создаём косвенное обращение, чтобы не сдерживать сборщик мусора.
                var paintingProcess = {
                    finishPaintingAt: function (positionOrEvent) {
                        paintingProcess.finishPaintingAt =
                            badFinishPaintingCall;

                        // Получаем координаты, прежде чем удалить пейн.
                        if (positionOrEvent) {
                            coordinates.push(toPosition(positionOrEvent));
                        }

                        map.panes.remove(pane);
                        if (unwantedBehaviors) {
                            map.behaviors.enable(unwantedBehaviors);
                        }

                        var tolerance =
                            config.tolerance === undefined
                                ? DEFAULT_TOLERANCE
                                : Number(config.tolerance);
                        if (tolerance) {
                            coordinates = simplify(coordinates, tolerance);
                        }
                        // Преобразовываем координаты canvas-элемента в геодезические координаты.
                        return coordinates.map(function (x) {
                            var lon =
                                bounds[0][1] + (x[0] / canvas.width) * lonDiff;
                            var lat =
                                bounds[0][0] +
                                (1 - x[1] / canvas.height) * latDiff;

                            return meta.coordinatesOrder === "latlong"
                                ? [lat, lon]
                                : [lon, lat];
                        });
                    },
                };

                return paintingProcess;
            }

            function toPosition(positionOrEvent) {
                return positionOrEvent instanceof Event
                    ? [
                          positionOrEvent.get("offsetX"),
                          positionOrEvent.get("offsetY"),
                      ]
                    : positionOrEvent;
            }

            function simplify(coordinates, tolerance) {
                var toleranceSquared = tolerance * tolerance;
                var simplified = [coordinates[0]];

                var prev = coordinates[0];
                for (var i = 1; i < coordinates.length; i++) {
                    var curr = coordinates[i];
                    if (
                        Math.pow(prev[0] - curr[0], 2) +
                            Math.pow(prev[1] - curr[1], 2) >
                        toleranceSquared
                    ) {
                        simplified.push(curr);
                        prev = curr;
                    }
                }

                return simplified;
            }

            provide(paintOnMap);
        }
    );
    ```

-   polygon_custom.js

    ```js
    ymaps
        .ready(["ext.paintOnMap"])
        .then(function () {
            var map = new ymaps.Map("map", {
                center: [55.75, 37.62],
                zoom: 14,
                controls: [],
            });

            var paintProcess;

            // Опции многоугольника или линии.
            var styles = [
                {
                    strokeColor: "#ff00ff",
                    strokeOpacity: 0.7,
                    strokeWidth: 3,
                    fillColor: "#ff00ff",
                    fillOpacity: 0.4,
                },
                {
                    strokeColor: "#ff0000",
                    strokeOpacity: 0.6,
                    strokeWidth: 6,
                    fillColor: "#ff0000",
                    fillOpacity: 0.3,
                },
                {
                    strokeColor: "#00ff00",
                    strokeOpacity: 0.5,
                    strokeWidth: 3,
                    fillColor: "#00ff00",
                    fillOpacity: 0.2,
                },
                {
                    strokeColor: "#0000ff",
                    strokeOpacity: 0.8,
                    strokeWidth: 5,
                    fillColor: "#0000ff",
                    fillOpacity: 0.5,
                },
                {
                    strokeColor: "#000000",
                    strokeOpacity: 0.6,
                    strokeWidth: 8,
                    fillColor: "#000000",
                    fillOpacity: 0.3,
                },
            ];

            var currentIndex = 0;

            // Создадим кнопку для выбора типа рисуемого контура.
            var button = new ymaps.control.Button({
                data: { content: "Polygon / Polyline" },
                options: { maxWidth: 150 },
            });
            map.controls.add(button);

            // Подпишемся на событие нажатия кнопки мыши.
            map.events.add("mousedown", function (e) {
                // Если кнопка мыши была нажата с зажатой клавишей "alt", то начинаем рисование контура.
                if (e.get("altKey")) {
                    if (currentIndex == styles.length - 1) {
                        currentIndex = 0;
                    } else {
                        currentIndex += 1;
                    }
                    paintProcess = ymaps.ext.paintOnMap(map, e, {
                        style: styles[currentIndex],
                    });
                }
            });

            // Подпишемся на событие отпускания кнопки мыши.
            map.events.add("mouseup", function (e) {
                if (paintProcess) {
                    // Получаем координаты отрисованного контура.
                    var coordinates = paintProcess.finishPaintingAt(e);
                    paintProcess = null;
                    // В зависимости от состояния кнопки добавляем на карту многоугольник или линию с полученными координатами.
                    var geoObject = button.isSelected()
                        ? new ymaps.Polyline(
                              coordinates,
                              {},
                              styles[currentIndex]
                          )
                        : new ymaps.Polygon(
                              [coordinates],
                              {},
                              styles[currentIndex]
                          );

                    map.geoObjects.add(geoObject);
                }
            });
        })
        .catch(console.error);
    ```

{% endlist %}
