Lasso

Open in CodeSandbox

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
    <script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>

    <!-- To make the map appear, you must add your apikey -->
    <script src="https://js.api.mappable.world/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>

    <script
      data-plugins="transform-modules-umd"
      data-presets="typescript"
      type="text/babel"
      src="./common.ts"
    ></script>
    <script
      data-plugins="transform-modules-umd"
      data-presets="react, typescript"
      type="text/babel"
      src="../variables.ts"
    ></script>
    <script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
      import {LOCATION} from '../variables';
      import {
        InfoMessage,
        DrawButton,
        drawLineOverMap,
        transformCoordinates,
        getBoundingBox,
        POLYGON_OPTIONS,
        BOUNDING_BOX_OPTIONS,
        DRAW_BUTTON_TEXT,
        INFO_MESSAGE_TEXT
      } from './common';
      import {DomEventHandlerObject} from '@mappable-world/mappable-types';
      import {MapEvent} from '@mappable-world/mappable-types/imperative/MMapFeature/types';

      window.map = null;

      async function main() {
        // Waiting for all API elements to be loaded
        await mappable.ready;
        const {
          MMap,
          MMapDefaultSchemeLayer,
          MMapDefaultFeaturesLayer,
          MMapControls,
          MMapControl,
          MMapListener,
          MMapFeature,
          MMapMarker
        } = mappable;

        let polygonCoordinates = null;
        let boundingBoxCoordinates = null;
        let currentBoundingBox = null;
        let currentPolygon = null;
        let deleteMarker = null;

        const handleMapClick = (object: DomEventHandlerObject) => {
          if (object?.type === 'feature') {
            updateBoundingBox();
            createDeleteMarker();
          } else {
            if (currentBoundingBox) {
              map.removeChild(currentBoundingBox);
              currentBoundingBox = null;
            }
            if (deleteMarker) {
              map.removeChild(deleteMarker);
              deleteMarker = null;
            }
          }
        };

        const updateBoundingBox = () => {
          if (currentBoundingBox) {
            map.removeChild(currentBoundingBox);
          }

          currentBoundingBox = new MMapFeature({
            geometry: {
              type: 'Polygon',
              coordinates: [boundingBoxCoordinates]
            },
            style: BOUNDING_BOX_OPTIONS
          });

          map.addChild(currentBoundingBox);
        };

        const createDeleteMarker = () => {
          if (deleteMarker) {
            map.removeChild(deleteMarker);
          }

          const button = document.createElement('button');
          button.className = 'delete-button';

          // Recalculate the coordinates of the marker so that it ends up on the top right of the polygon
          deleteMarker = new MMapMarker(
            {
              coordinates: [boundingBoxCoordinates[2][0] + 0.001, boundingBoxCoordinates[2][1]],
              onClick: handleDeleteClick
            },
            button
          );

          map.addChild(deleteMarker);
        };

        const removeShapes = () => {
          if (currentBoundingBox) {
            map.removeChild(currentBoundingBox);
            currentBoundingBox = null;
          }
          if (deleteMarker) {
            map.removeChild(deleteMarker);
            deleteMarker = null;
          }
          if (currentPolygon) {
            map.removeChild(currentPolygon);
            currentPolygon = null;
          }
        };

        const handleDeleteClick = (event: MouseEvent, mapEvent: MapEvent) => {
          mapEvent.stopPropagation();
          polygonCoordinates = null;
          boundingBoxCoordinates = null;
          removeShapes();
        };

        const handleDrawClick = async () => {
          const drawButton = document.querySelector('#draw-button');
          drawButton.classList.add('active');

          const coordinates = await drawLineOverMap(map);
          const transformedCoordinates = transformCoordinates(map.bounds, coordinates);

          removeShapes();

          boundingBoxCoordinates = getBoundingBox(transformedCoordinates);
          polygonCoordinates = transformedCoordinates;

          drawButton.classList.remove('active');

          if (currentPolygon) {
            currentPolygon.update({
              coordinates: [polygonCoordinates]
            });
          } else {
            currentPolygon = new MMapFeature({
              id: 'polygon',
              geometry: {
                type: 'Polygon',
                coordinates: [polygonCoordinates]
              },
              style: POLYGON_OPTIONS
            });
          }

          map.addChild(currentPolygon);
        };

        const app = document.getElementById('app');

        // Initialize the map
        map = new MMap(app, {location: LOCATION, showScaleInCopyrights: true}, [
          new MMapDefaultSchemeLayer({}),
          new MMapDefaultFeaturesLayer({})
        ]);

        map.addChild(new MMapListener({onClick: handleMapClick}));

        map.addChild(
          new MMapControls({position: 'top left'}).addChild(
            new MMapControl({}).addChild(new InfoMessage({text: INFO_MESSAGE_TEXT}))
          )
        );

        map.addChild(
          new MMapControls({position: 'top right'}).addChild(
            new MMapControl({}).addChild(
              new DrawButton({
                text: DRAW_BUTTON_TEXT,
                onClick: handleDrawClick
              })
            )
          )
        );

        const canvas = document.createElement('canvas');
        canvas.id = 'draw-canvas';
        canvas.className = 'canvas';
        app.appendChild(canvas);
      }

      main();
    </script>

    <!-- prettier-ignore -->
    <style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; }  </style>
    <link rel="stylesheet" href="./common.css" />
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
    <script crossorigin src="https://cdn.jsdelivr.net/npm/react@17/umd/react.production.min.js"></script>
    <script crossorigin src="https://cdn.jsdelivr.net/npm/react-dom@17/umd/react-dom.production.min.js"></script>
    <script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>

    <!-- To make the map appear, you must add your apikey -->
    <script src="https://js.api.mappable.world/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>

    <script
      data-plugins="transform-modules-umd"
      data-presets="react, typescript"
      type="text/babel"
      src="./common.ts"
    ></script>
    <script
      data-plugins="transform-modules-umd"
      data-presets="react, typescript"
      type="text/babel"
      src="../variables.ts"
    ></script>
    <script data-plugins="transform-modules-umd" data-presets="react, typescript" type="text/babel">
      import {
        BOUNDING_BOX_OPTIONS,
        DRAW_BUTTON_TEXT,
        drawLineOverMap,
        getBoundingBox,
        INFO_MESSAGE_TEXT,
        POLYGON_OPTIONS,
        transformCoordinates
      } from './common';
      import {LOCATION} from '../variables';
      import {DomEventHandlerObject, LngLat} from '@mappable-world/mappable-types';

      window.map = null;

      main();
      // Importing React API for Mappable Maps
      async function main() {
        const [mappableReact] = await Promise.all([
          mappable.import('@mappable-world/mappable-reactify'),
          mappable.ready
        ]);

        const reactify = mappableReact.reactify.bindTo(React, ReactDOM);
        const {
          MMap,
          MMapDefaultSchemeLayer,
          MMapDefaultFeaturesLayer,
          MMapControls,
          MMapControl,
          MMapFeature,
          MMapListener,
          MMapMarker
        } = reactify.module(mappable);

        const {useState, useMemo} = React;

        function App() {
          const [drawMode, setDrawMode] = useState(false);
          const [polygonCoordinates, setPolygonCoordinates] = useState(null);
          const [boundingBoxCoordinates, setBoundingBoxCoordinates] = useState(null);
          const [showBoundingBox, setShowBoundingBox] = useState(false);

          const handleClick = async () => {
            setDrawMode(true);
            const coordinates = await drawLineOverMap(map);
            const transformedCoordinates = transformCoordinates(map.bounds, coordinates);
            const boxCoordinates = getBoundingBox(transformedCoordinates);

            setBoundingBoxCoordinates(boxCoordinates);
            setPolygonCoordinates(transformedCoordinates);

            setShowBoundingBox(false);
            setDrawMode(false);
          };

          const handlePolygonClick = () => {
            setShowBoundingBox(true);
          };

          const handleMapClick = (object: DomEventHandlerObject) => {
            object?.type !== 'feature' && setShowBoundingBox(false);
          };

          const handleDelete = () => {
            setPolygonCoordinates(null);
            setBoundingBoxCoordinates(null);
          };

          // Computed classes for draw button
          const drawButtonClassNames = useMemo(() => {
            const classList = ['draw-button'];
            if (drawMode) classList.push('active');
            return classList.join(' ');
          }, [drawMode]);

          // Recalculate the coordinates of the marker so that it ends up on the top right of the polygon
          const deleteButtonCoordinates: LngLat = useMemo(() => {
            return boundingBoxCoordinates && [boundingBoxCoordinates[2][0] + 0.001, boundingBoxCoordinates[2][1]];
          }, [boundingBoxCoordinates]);

          return (
            <>
              <MMap location={LOCATION} showScaleInCopyrights ref={(x) => (map = x)}>
                <MMapDefaultSchemeLayer />
                <MMapDefaultFeaturesLayer />

                {polygonCoordinates && (
                  <MMapFeature
                    geometry={{
                      type: 'Polygon',
                      coordinates: [polygonCoordinates]
                    }}
                    style={POLYGON_OPTIONS}
                    onClick={handlePolygonClick}
                  />
                )}
                {boundingBoxCoordinates && showBoundingBox && (
                  <MMapFeature
                    geometry={{
                      type: 'Polygon',
                      coordinates: [boundingBoxCoordinates]
                    }}
                    style={BOUNDING_BOX_OPTIONS}
                  />
                )}

                {boundingBoxCoordinates && showBoundingBox && (
                  <MMapMarker disableRoundCoordinates coordinates={deleteButtonCoordinates}>
                    <button onClick={handleDelete} className="delete-button"></button>
                  </MMapMarker>
                )}

                {/* Create Info Message */}
                <MMapControls position="top left">
                  <MMapControl>
                    <div className="info-window">{INFO_MESSAGE_TEXT}</div>
                  </MMapControl>
                </MMapControls>
                <MMapControls position="top right">
                  <MMapControl>
                    <button className={drawButtonClassNames} onClick={handleClick} disabled={drawMode}>
                      {DRAW_BUTTON_TEXT}
                    </button>
                  </MMapControl>
                </MMapControls>

                <MMapListener onClick={handleMapClick} />
              </MMap>
              <canvas id="draw-canvas" className="canvas" />
            </>
          );
        }

        ReactDOM.render(
          <React.StrictMode>
            <App />
          </React.StrictMode>,
          document.getElementById('app')
        );
      }
    </script>

    <!-- prettier-ignore -->
    <style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; }  </style>
    <link rel="stylesheet" href="./common.css" />
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
    <script crossorigin src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
    <script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>

    <!-- To make the map appear, you must add your apikey -->
    <script src="https://js.api.mappable.world/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>

    <script
      data-plugins="transform-modules-umd"
      data-presets="typescript"
      type="text/babel"
      src="./common.ts"
    ></script>
    <script
      data-plugins="transform-modules-umd"
      data-presets="react, typescript"
      type="text/babel"
      src="../variables.ts"
    ></script>
    <script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
      import {DomEventHandlerObject} from '@mappable-world/mappable-types';
      import {
        BOUNDING_BOX_OPTIONS,
        drawLineOverMap,
        getBoundingBox,
        POLYGON_OPTIONS,
        transformCoordinates,
        DRAW_BUTTON_TEXT,
        INFO_MESSAGE_TEXT
      } from './common';
      import {LOCATION} from '../variables';

      window.map = null;

      async function main() {
        // Importing Vue API for Mappable Maps
        const [mappableVue] = await Promise.all([mappable.import('@mappable-world/mappable-vuefy'), mappable.ready]);
        const vuefy = mappableVue.vuefy.bindTo(Vue);
        const {
          MMap,
          MMapDefaultSchemeLayer,
          MMapDefaultFeaturesLayer,
          MMapListener,
          MMapControls,
          MMapControl,
          MMapFeature,
          MMapMarker
        } = vuefy.module(mappable);

        const app = Vue.createApp({
          components: {
            MMap,
            MMapDefaultSchemeLayer,
            MMapDefaultFeaturesLayer,
            MMapListener,
            MMapControls,
            MMapControl,
            MMapFeature,
            MMapMarker
          },
          setup() {
            const refMap = (ref) => {
              window.map = ref?.entity;
            };
            const drawMode = Vue.ref(false);
            const polygonCoordinates = Vue.ref(null);
            const boundingBoxCoordinates = Vue.ref(null);
            const showBoundingBox = Vue.ref(false);

            // Recalculate the coordinates of the marker so that it ends up on the top right of the polygon
            const deleteButtonCoordinates = Vue.computed(() => {
              if (boundingBoxCoordinates.value) {
                const [lng, lat] = boundingBoxCoordinates.value[2];
                return [lng + 0.001, lat];
              }
              return null;
            });

            const handleClick = async () => {
              drawMode.value = true;
              const coordinates = await drawLineOverMap(map);
              const transformedCoordinates = transformCoordinates(map.bounds, coordinates);

              // Reset polygon and bounding box
              polygonCoordinates.value = null;
              boundingBoxCoordinates.value = null;

              boundingBoxCoordinates.value = getBoundingBox(transformedCoordinates);
              polygonCoordinates.value = transformedCoordinates;
              drawMode.value = false;
            };

            const handlePolygonClick = () => {
              showBoundingBox.value = true;
            };

            const handleMapClick = (object: DomEventHandlerObject) => {
              if (object?.type !== 'feature') {
                showBoundingBox.value = false;
              }
            };

            const handleDelete = () => {
              polygonCoordinates.value = null;
              boundingBoxCoordinates.value = null;
            };

            return {
              LOCATION,
              INFO_MESSAGE_TEXT,
              DRAW_BUTTON_TEXT,
              refMap,
              drawMode,
              polygonCoordinates,
              boundingBoxCoordinates,
              showBoundingBox,
              deleteButtonCoordinates,
              handleClick,
              handlePolygonClick,
              handleMapClick,
              handleDelete,
              POLYGON_OPTIONS,
              BOUNDING_BOX_OPTIONS
            };
          },
          template: `
      <MMap :location="LOCATION" showScaleInCopyrights :ref="refMap">
        <MMapDefaultSchemeLayer/>
        <MMapDefaultFeaturesLayer/>

        <!-- Polygon Feature -->
        <MMapFeature
          v-if="polygonCoordinates"
          :geometry="{
            type: 'Polygon',
            coordinates: [polygonCoordinates]
          }"
          :style="POLYGON_OPTIONS"
          @click="handlePolygonClick"
        />

        <!-- Bounding Box Feature -->
        <MMapFeature
          v-if="boundingBoxCoordinates && showBoundingBox"
          :geometry="{
            type: 'Polygon',
            coordinates: [boundingBoxCoordinates]
          }"
          :style="BOUNDING_BOX_OPTIONS"
        />

        <!-- Delete Marker -->
        <MMapMarker
          v-if="boundingBoxCoordinates && showBoundingBox"
          :coordinates="deleteButtonCoordinates"
        >
          <button @click="handleDelete" class="delete-button"></button>
        </MMapMarker>

        <!-- Controls -->
        <MMapControls position="top left">
          <MMapControl>
            <div class="info-window">{{INFO_MESSAGE_TEXT}}</div>
          </MMapControl>
        </MMapControls>

        <MMapControls position="top right">
          <MMapControl>
            <button
              :class="{'draw-button': true, active: drawMode}"
              :disabled="drawMode"
              @click="handleClick"
            >
              {{DRAW_BUTTON_TEXT}}
            </button>
          </MMapControl>
        </MMapControls>
        <MMapListener @click="handleMapClick" />
      </MMap>
      <canvas id="draw-canvas" class="canvas"></canvas>
    `
        });
        app.mount('#app');
      }

      main();
    </script>

    <!-- prettier-ignore -->
    <style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; }  </style>
    <link rel="stylesheet" href="./common.css" />
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
.info-window {
  padding: 8px 12px 8px 32px;
  border-radius: 8px;
  background-color: #313133;
  background-image: url('./info-icon.svg');
  background-position: 8px 50%;
  background-repeat: no-repeat;
  gap: 8px;
  color: #f2f5fa;
  font-size: 14px;
  line-height: 20px;
}

.draw-button {
  padding: 8px 12px 8px 32px;
  border-radius: 8px;
  background-color: #fff;
  background-image: url('./lasso-icon.svg');
  background-position: 8px 50%;
  background-repeat: no-repeat;
  gap: 8px;
  color: #34374a;
  border-color: unset;
  font-weight: 700;
  font-size: 14px;
  line-height: 20px;
  cursor: pointer;
  box-shadow: 0px 2px 4px 0px #5f698333;
  box-shadow: 0px 0px 2px 0px #5f698314;
  border: unset;
  transition: background-color 0.1s;
}

.draw-button.active,
.draw-button:hover {
  color: #122db2;
  background-image: url('./lasso-icon-blue.svg');
}

.delete-button {
  height: 32px;
  width: 32px;
  border-radius: 8px;
  background-color: #fff;
  background-image: url('./trash-icon.svg');
  background-position: 50% 50%;
  background-repeat: no-repeat;
  border-color: unset;
  cursor: pointer;
  box-shadow: 0px 2px 4px 0px #5f698333;
  border: unset;
}

.canvas {
  position: absolute;
  left: 0;
  top: 0;
  display: none;
}
import {LngLat, LngLatBounds, MMap} from '@mappable-world/mappable-types';

export const INFO_MESSAGE_TEXT = 'Select instrument and draw on a map';
export const DRAW_BUTTON_TEXT = 'Pencil selection';

export const POLYGON_OPTIONS = {
  cursor: 'pointer',
  stroke: [{width: 3, color: '#2E4CE599'}],
  fill: '#122DB21A'
};
export const BOUNDING_BOX_OPTIONS = {
  stroke: [{width: 1, color: '#122DB2'}],
  fillOpacity: 0
};

export const CANVAS_OPTIONS = {
  strokeStyle: '#2E4CE599',
  lineWidth: 4,
  opacity: '0.7'
};

export function drawLineOverMap(map: MMap): Promise<LngLatBounds> {
  const canvas: HTMLCanvasElement = document.querySelector('#draw-canvas');
  const ctx2d = canvas.getContext('2d');
  let drawing = false;
  let coordinates = [];

  // Set the canvas dimensions to match the map.
  const rect = map.container;
  canvas.style.width = rect.offsetWidth + 'px';
  canvas.style.height = rect.offsetHeight + 'px';
  canvas.width = rect.offsetWidth;
  canvas.height = rect.offsetHeight;

  // Apply styles.
  ctx2d.strokeStyle = CANVAS_OPTIONS.strokeStyle;
  ctx2d.lineWidth = CANVAS_OPTIONS.lineWidth;
  canvas.style.opacity = CANVAS_OPTIONS.opacity;

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

  // Show the canvas. It will be on top of the map due to position: absolute.
  canvas.style.display = 'block';

  canvas.onmousedown = function (e) {
    // When the mouse is pressed, remember that we started drawing and the coordinates.
    drawing = true;
    coordinates.push([e.offsetX, e.offsetY]);
  };

  canvas.onmousemove = function (e) {
    // When the mouse moves, remember the coordinates and draw the line.
    if (drawing) {
      var last = coordinates[coordinates.length - 1];
      ctx2d.beginPath();
      ctx2d.moveTo(last[0], last[1]);
      ctx2d.lineTo(e.offsetX, e.offsetY);
      ctx2d.stroke();

      coordinates.push([e.offsetX, e.offsetY]);
    }
  };

  return new Promise(function (resolve) {
    // When the mouse is released, remember the coordinates and hide the canvas.
    canvas.onmouseup = function (e) {
      coordinates.push([e.offsetX, e.offsetY]);
      canvas.style.display = 'none';
      drawing = false;

      coordinates = coordinates.map(function (x) {
        return [x[0] / canvas.width, x[1] / canvas.height];
      });

      resolve(coordinates as LngLatBounds);
    };
  });
}

type BoundingBox = [LngLat, LngLat, LngLat, LngLat];

export const getBoundingBox = (points: LngLatBounds): BoundingBox => {
  let minLongitude = points[0][0];
  let maxLongitude = points[0][0];
  let minLatitude = points[0][1];
  let maxLatitude = points[0][1];

  points.forEach(([longitude, latitude]) => {
    if (longitude < minLongitude) {
      minLongitude = longitude;
    }
    if (longitude > maxLongitude) {
      maxLongitude = longitude;
    }
    if (latitude < minLatitude) {
      minLatitude = latitude;
    }
    if (latitude > maxLatitude) {
      maxLatitude = latitude;
    }
  });

  // The four corners of the bounding box
  const bottomLeft: LngLat = [minLongitude, minLatitude];
  const bottomRight: LngLat = [maxLongitude, minLatitude];
  const topLeft: LngLat = [minLongitude, maxLatitude];
  const topRight: LngLat = [maxLongitude, maxLatitude];

  return [bottomLeft, bottomRight, topRight, topLeft];
};

export const transformCoordinates = (bounds: LngLatBounds, coordinates: LngLatBounds): LngLatBounds => {
  return coordinates
    .map((x) => [
      bounds[0][0] + x[0] * (bounds[1][0] - bounds[0][0]),
      bounds[0][1] + x[1] * (bounds[1][1] - bounds[0][1])
    ])
    .filter((_, index) => index % 5 === 0) as LngLatBounds; // Simplify by keeping every 5th point
};

// Create a custom information message control
export let InfoMessage = null;
export let DrawButton = null;

interface InfoMessageProps {
  text: string;
}

// Wait for the api to load to access the entity system (MMapComplexEntity)
mappable.ready.then(() => {
  class InfoMessageClass extends mappable.MMapComplexEntity<InfoMessageProps> {
    private _element!: HTMLDivElement;
    private _detachDom!: () => void;

    // Method for create a DOM control element
    _createElement(props: InfoMessageProps) {
      // Create a root element
      const infoWindow = document.createElement('div');
      infoWindow.classList.add('info-window');
      infoWindow.innerText = props.text;

      return infoWindow;
    }

    // Method for attaching the control to the map
    _onAttach() {
      this._element = this._createElement(this._props);
      this._detachDom = mappable.useDomContext(this, this._element, this._element);
    }

    // Method for detaching control from the map
    _onDetach() {
      this._detachDom();
      this._detachDom = undefined;
      this._element = undefined;
    }
  }

  class DrawButtonClass extends mappable.MMapComplexEntity<{}> {
    private _element;
    private _detachDom;

    // Method to create a DOM control element for the button
    _createElement(props) {
      const drawButton = document.createElement('button');
      drawButton.className = 'draw-button';
      drawButton.id = 'draw-button';
      drawButton.innerText = props.text;
      drawButton.onclick = props.onClick;

      return drawButton;
    }

    // Method for attaching the control to the map
    _onAttach() {
      this._element = this._createElement(this._props);
      this._detachDom = mappable.useDomContext(this, this._element, this._element);
    }

    // Method for detaching control from the map
    _onDetach() {
      this._detachDom();
      this._detachDom = undefined;
      this._element = undefined;
    }
  }

  InfoMessage = InfoMessageClass;
  DrawButton = DrawButtonClass;
});
import type {MMapLocationRequest} from '@mappable-world/mappable-types';

export const LOCATION: MMapLocationRequest = {
  center: [55.169014, 25.114102], // starting position [lng, lat]
  zoom: 13 // starting zoom
};