Marker Collision Management

Handle marker collisions

Vector maps provide basic support for marker collision. For example, you can modify the marker API to hide labels. Vector maps also provide support for prioritization of markers.

The following example always displays markers and hides underlying labels:

var marker = new google.maps.Marker({
  position: event.latLng,
  map: map,
  collisionBehavior:
    google.maps.CollisionBehavior.REQUIRED_AND_HIDES_OPTIONAL,
  });

The following example hides markers if they collide with a required marker and hides underlying labels:

var marker = new google.maps.Marker({
  position: event.latLng,
  map: map,
  collisionBehavior:
    google.maps.CollisionBehavior.OPTIONAL_AND_HIDES_LOWER_PRIORITY,
  });

TypeScript

let map: google.maps.Map;

// Initialize and add the map
function initMap(): void {
  let markers: google.maps.Marker[] = [];

  // @ts-ignore Beta functionality
  let collisionBehavior = google.maps.CollisionBehavior.REQUIRED;

  map = new google.maps.Map(
    document.getElementById("map") as HTMLElement,
    {
      mapId: "3a3b33f0edd6ed2a",
      center: { lat: 47.609414458375674, lng: -122.33897030353548 },
      zoom: 17,
    } as google.maps.MapOptions
  );

  const menuList = document.querySelector(".mdc-list") as HTMLUListElement;

  // Add the behaviors to the select options
  // @ts-ignore Beta functionality
  for (const [key, value] of Object.entries(google.maps.CollisionBehavior)) {
    const item = document.createElement("LI");
    item.classList.add("mdc-list-item");
    item.setAttribute("data-value", key);

    const itemText = document.createElement("SPAN") as HTMLSpanElement;
    itemText.classList.add("mdc-list-item__text");
    itemText.innerText = value as string;

    item.appendChild(itemText);
    menuList.appendChild(item);
  }

  // @ts-ignore
  const select = new mdc.select.MDCSelect(
    document.querySelector(".mdc-select") as HTMLElement
  );

  select.listen("MDCSelect:change", () => {
    collisionBehavior = select.value;
    markers.forEach((marker) => {
      marker.set("collisionBehavior", collisionBehavior);
    });
  });

  select.value = collisionBehavior;

  // Create some markers on the map
  markers = [
    [-122.3402, 47.6093],
    [-122.3402, 47.6094],
    [-122.3403, 47.6094],
    [-122.3384, 47.6098],
    [-122.3389, 47.6095],
    [-122.3396, 47.6095],
    [-122.3379, 47.6097],
    [-122.3378, 47.6097],
    [-122.3396, 47.6091],
    [-122.3383, 47.6089],
    [-122.3379, 47.6093],
    [-122.3381, 47.6095],
    [-122.3378, 47.6095],
  ].map(
    ([lng, lat]: number[]) =>
      new google.maps.Marker({
        position: new google.maps.LatLng({ lat, lng }),
        map,
        collisionBehavior: collisionBehavior,
      } as google.maps.MarkerOptions)
  );
}

JavaScript

// eslint-disable no-undef
let map;

// Initialize and add the map
function initMap() {
  let markers = [];
  let collisionBehavior = google.maps.CollisionBehavior.REQUIRED;
  map = new google.maps.Map(document.getElementById("map"), {
    mapId: "3a3b33f0edd6ed2a",
    center: { lat: 47.609414458375674, lng: -122.33897030353548 },
    zoom: 17,
  });
  const menuList = document.querySelector(".mdc-list");

  // Add the behaviors to the select options
  for (const [key, value] of Object.entries(google.maps.CollisionBehavior)) {
    const item = document.createElement("LI");
    item.classList.add("mdc-list-item");
    item.setAttribute("data-value", key);
    const itemText = document.createElement("SPAN");
    itemText.classList.add("mdc-list-item__text");
    itemText.innerText = value;
    item.appendChild(itemText);
    menuList.appendChild(item);
  }
  const select = new mdc.select.MDCSelect(
    document.querySelector(".mdc-select")
  );
  select.listen("MDCSelect:change", () => {
    collisionBehavior = select.value;
    markers.forEach((marker) => {
      marker.set("collisionBehavior", collisionBehavior);
    });
  });
  select.value = collisionBehavior;
  // Create some markers on the map
  markers = [
    [-122.3402, 47.6093],
    [-122.3402, 47.6094],
    [-122.3403, 47.6094],
    [-122.3384, 47.6098],
    [-122.3389, 47.6095],
    [-122.3396, 47.6095],
    [-122.3379, 47.6097],
    [-122.3378, 47.6097],
    [-122.3396, 47.6091],
    [-122.3383, 47.6089],
    [-122.3379, 47.6093],
    [-122.3381, 47.6095],
    [-122.3378, 47.6095],
  ].map(
    ([lng, lat]) =>
      new google.maps.Marker({
        position: new google.maps.LatLng({ lat, lng }),
        map,
        collisionBehavior: collisionBehavior,
      })
  );
}

CSS

:root {
  --mdc-theme-primary: #1a73e8;
  --mdc-theme-secondary: #rgb(225, 245, 254);
  --mdc-theme-on-primary: #fff;
  --mdc-theme-on-secondary: rgb(1, 87, 155);
}

.mdc-select--focused .mdc-select__dropdown-icon {
  background: url(data:image/svg+xml,%3Csvg%20width%3D%2210px%22%20height%3D%225px%22%20viewBox%3D%227%2010%2010%205%22%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%3E%0A%20%20%20%20%3Cpolygon%20id%3D%22Shape%22%20stroke%3D%22none%22%20fill%3D%22%23000%22%20fill-rule%3D%22evenodd%22%20opacity%3D%220.54%22%20points%3D%227%2010%2012%2015%2017%2010%22%3E%3C%2Fpolygon%3E%0A%3C%2Fsvg%3E)
    no-repeat center;
}

.mdc-select:not(.mdc-select--disabled).mdc-select--focused .mdc-floating-label {
  color: var(--mdc-theme-primary);
}

/* Optional: Makes the sample page fill the window. */
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

#container {
  height: 100%;
  display: flex;
}

#sidebar {
  flex-basis: 15rem;
  flex-grow: 1;
  padding: 1rem;
  max-width: 30rem;
  height: 100%;
  box-sizing: border-box;
  display: flex;
}

#map {
  flex-basis: 0;
  flex-grow: 4;
  height: 100%;
}

.mdc-select__anchor,
.mdc-select__menu {
  width: 100%;
}

HTML

<!DOCTYPE html>
<html>
  <head>
    <title>Marker Collision Management</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
    <link
      href="https://unpkg.com/material-components-web@6.0.0/dist/material-components-web.css"
      rel="stylesheet"
    />
    <script src="https://unpkg.com/material-components-web@6.0.0/dist/material-components-web.min.js"></script>
    <link
      rel="stylesheet"
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
    />
    <script
      src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap&libraries=&v=beta&map_ids=3a3b33f0edd6ed2a"
      defer
    ></script>
    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script src="./index.js"></script>
  </head>
  <body>
    <div id="container">
      <div id="map"></div>
      <div id="sidebar">
        <div class="mdc-select mdc-select--outlined">
          <div
            class="mdc-select__anchor"
            aria-labelledby="outlined-select-label"
          >
            <input
              type="text"
              disabled
              readonly
              id="demo-selected-text"
              class="mdc-select__selected-text"
            />
            <i class="mdc-select__dropdown-icon"></i>
            <span class="mdc-notched-outline">
              <span class="mdc-notched-outline__leading"></span>
              <span class="mdc-notched-outline__notch">
                <span
                  id="outlined-select-label"
                  class="mdc-floating-label mdc-theme--primary"
                  >Pick a Collision Behavior</span
                >
              </span>
              <span class="mdc-notched-outline__trailing"></span>
            </span>
          </div>
          <div class="mdc-select__menu mdc-menu mdc-menu-surface">
            <ul class="mdc-list"></ul>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

All

<!DOCTYPE html>
<html>
  <head>
    <title>Marker Collision Management</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
    <link
      href="https://unpkg.com/material-components-web@6.0.0/dist/material-components-web.css"
      rel="stylesheet"
    />
    <script src="https://unpkg.com/material-components-web@6.0.0/dist/material-components-web.min.js"></script>
    <link
      rel="stylesheet"
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
    />
    <script
      src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap&libraries=&v=beta&map_ids=3a3b33f0edd6ed2a"
      defer
    ></script>
    <style type="text/css">
      :root {
        --mdc-theme-primary: #1a73e8;
        --mdc-theme-secondary: #rgb(225, 245, 254);
        --mdc-theme-on-primary: #fff;
        --mdc-theme-on-secondary: rgb(1, 87, 155);
      }

      .mdc-select--focused .mdc-select__dropdown-icon {
        background: url(data:image/svg+xml,%3Csvg%20width%3D%2210px%22%20height%3D%225px%22%20viewBox%3D%227%2010%2010%205%22%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%3E%0A%20%20%20%20%3Cpolygon%20id%3D%22Shape%22%20stroke%3D%22none%22%20fill%3D%22%23000%22%20fill-rule%3D%22evenodd%22%20opacity%3D%220.54%22%20points%3D%227%2010%2012%2015%2017%2010%22%3E%3C%2Fpolygon%3E%0A%3C%2Fsvg%3E)
          no-repeat center;
      }

      .mdc-select:not(.mdc-select--disabled).mdc-select--focused
        .mdc-floating-label {
        color: var(--mdc-theme-primary);
      }

      /* Optional: Makes the sample page fill the window. */
      html,
      body {
        height: 100%;
        margin: 0;
        padding: 0;
      }

      #container {
        height: 100%;
        display: flex;
      }

      #sidebar {
        flex-basis: 15rem;
        flex-grow: 1;
        padding: 1rem;
        max-width: 30rem;
        height: 100%;
        box-sizing: border-box;
        display: flex;
      }

      #map {
        flex-basis: 0;
        flex-grow: 4;
        height: 100%;
      }

      .mdc-select__anchor,
      .mdc-select__menu {
        width: 100%;
      }
    </style>
    <script>
      // eslint-disable no-undef
      let map;

      // Initialize and add the map
      function initMap() {
        let markers = [];
        let collisionBehavior = google.maps.CollisionBehavior.REQUIRED;
        map = new google.maps.Map(document.getElementById("map"), {
          mapId: "3a3b33f0edd6ed2a",
          center: { lat: 47.609414458375674, lng: -122.33897030353548 },
          zoom: 17,
        });
        const menuList = document.querySelector(".mdc-list");

        // Add the behaviors to the select options
        for (const [key, value] of Object.entries(
          google.maps.CollisionBehavior
        )) {
          const item = document.createElement("LI");
          item.classList.add("mdc-list-item");
          item.setAttribute("data-value", key);
          const itemText = document.createElement("SPAN");
          itemText.classList.add("mdc-list-item__text");
          itemText.innerText = value;
          item.appendChild(itemText);
          menuList.appendChild(item);
        }
        const select = new mdc.select.MDCSelect(
          document.querySelector(".mdc-select")
        );
        select.listen("MDCSelect:change", () => {
          collisionBehavior = select.value;
          markers.forEach((marker) => {
            marker.set("collisionBehavior", collisionBehavior);
          });
        });
        select.value = collisionBehavior;
        // Create some markers on the map
        markers = [
          [-122.3402, 47.6093],
          [-122.3402, 47.6094],
          [-122.3403, 47.6094],
          [-122.3384, 47.6098],
          [-122.3389, 47.6095],
          [-122.3396, 47.6095],
          [-122.3379, 47.6097],
          [-122.3378, 47.6097],
          [-122.3396, 47.6091],
          [-122.3383, 47.6089],
          [-122.3379, 47.6093],
          [-122.3381, 47.6095],
          [-122.3378, 47.6095],
        ].map(
          ([lng, lat]) =>
            new google.maps.Marker({
              position: new google.maps.LatLng({ lat, lng }),
              map,
              collisionBehavior: collisionBehavior,
            })
        );
      }
    </script>
  </head>
  <body>
    <div id="container">
      <div id="map"></div>
      <div id="sidebar">
        <div class="mdc-select mdc-select--outlined">
          <div
            class="mdc-select__anchor"
            aria-labelledby="outlined-select-label"
          >
            <input
              type="text"
              disabled
              readonly
              id="demo-selected-text"
              class="mdc-select__selected-text"
            />
            <i class="mdc-select__dropdown-icon"></i>
            <span class="mdc-notched-outline">
              <span class="mdc-notched-outline__leading"></span>
              <span class="mdc-notched-outline__notch">
                <span
                  id="outlined-select-label"
                  class="mdc-floating-label mdc-theme--primary"
                  >Pick a Collision Behavior</span
                >
              </span>
              <span class="mdc-notched-outline__trailing"></span>
            </span>
          </div>
          <div class="mdc-select__menu mdc-menu mdc-menu-surface">
            <ul class="mdc-list"></ul>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>
// eslint-disable no-undef let map; // Initialize and add the map function initMap() { let markers = []; let collisionBehavior = google.maps.CollisionBehavior.REQUIRED; map = new google.maps.Map(document.getElementById("map"), { mapId: "3a3b33f0edd6ed2a", center: { lat: 47.609414458375674, lng: -122.33897030353548 }, zoom: 17, }); const menuList = document.querySelector(".mdc-list"); // Add the behaviors to the select options for (const [key, value] of Object.entries(google.maps.CollisionBehavior)) { const item = document.createElement("LI"); item.classList.add("mdc-list-item"); item.setAttribute("data-value", key); const itemText = document.createElement("SPAN"); itemText.classList.add("mdc-list-item__text"); itemText.innerText = value; item.appendChild(itemText); menuList.appendChild(item); } const select = new mdc.select.MDCSelect( document.querySelector(".mdc-select") ); select.listen("MDCSelect:change", () => { collisionBehavior = select.value; markers.forEach((marker) => { marker.set("collisionBehavior", collisionBehavior); }); }); select.value = collisionBehavior; // Create some markers on the map markers = [ [-122.3402, 47.6093], [-122.3402, 47.6094], [-122.3403, 47.6094], [-122.3384, 47.6098], [-122.3389, 47.6095], [-122.3396, 47.6095], [-122.3379, 47.6097], [-122.3378, 47.6097], [-122.3396, 47.6091], [-122.3383, 47.6089], [-122.3379, 47.6093], [-122.3381, 47.6095], [-122.3378, 47.6095], ].map( ([lng, lat]) => new google.maps.Marker({ position: new google.maps.LatLng({ lat, lng }), map, collisionBehavior: collisionBehavior, }) ); }
:root { --mdc-theme-primary: #1a73e8; --mdc-theme-secondary: #rgb(225, 245, 254); --mdc-theme-on-primary: #fff; --mdc-theme-on-secondary: rgb(1, 87, 155); } .mdc-select--focused .mdc-select__dropdown-icon { background: url(data:image/svg+xml,%3Csvg%20width%3D%2210px%22%20height%3D%225px%22%20viewBox%3D%227%2010%2010%205%22%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%3E%0A%20%20%20%20%3Cpolygon%20id%3D%22Shape%22%20stroke%3D%22none%22%20fill%3D%22%23000%22%20fill-rule%3D%22evenodd%22%20opacity%3D%220.54%22%20points%3D%227%2010%2012%2015%2017%2010%22%3E%3C%2Fpolygon%3E%0A%3C%2Fsvg%3E) no-repeat center; } .mdc-select:not(.mdc-select--disabled).mdc-select--focused .mdc-floating-label { color: var(--mdc-theme-primary); } /* Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; } #container { height: 100%; display: flex; } #sidebar { flex-basis: 15rem; flex-grow: 1; padding: 1rem; max-width: 30rem; height: 100%; box-sizing: border-box; display: flex; } #map { flex-basis: 0; flex-grow: 4; height: 100%; } .mdc-select__anchor, .mdc-select__menu { width: 100%; }
<!DOCTYPE html> <html> <head> <title>Marker Collision Management</title> <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script> <link href="https://unpkg.com/material-components-web@6.0.0/dist/material-components-web.css" rel="stylesheet" /> <script src="https://unpkg.com/material-components-web@6.0.0/dist/material-components-web.min.js"></script> <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" /> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBIwzALxUPNbatRBj3Xi1Uhp0fFzwWNBkE&callback=initMap&libraries=&v=beta&map_ids=3a3b33f0edd6ed2a" defer ></script> <!-- jsFiddle will insert css and js --> </head> <body> <div id="container"> <div id="map"></div> <div id="sidebar"> <div class="mdc-select mdc-select--outlined"> <div class="mdc-select__anchor" aria-labelledby="outlined-select-label" > <input type="text" disabled readonly id="demo-selected-text" class="mdc-select__selected-text" /> <i class="mdc-select__dropdown-icon"></i> <span class="mdc-notched-outline"> <span class="mdc-notched-outline__leading"></span> <span class="mdc-notched-outline__notch"> <span id="outlined-select-label" class="mdc-floating-label mdc-theme--primary" >Pick a Collision Behavior</span > </span> <span class="mdc-notched-outline__trailing"></span> </span> </div> <div class="mdc-select__menu mdc-menu mdc-menu-surface"> <ul class="mdc-list"></ul> </div> </div> </div> </div> </body> </html>

Create a starter application from sample

A skeleton starter application using TypeScript, Webpack, and Babel can be generated from this sample using one of the methods below.

Run Locally

Node.js is required to run this sample locally. Follow these instructions to install Node.js and NPM.

npx @googlemaps/js-samples init marker-collision-management DESTINATION_FOLDER

Run in Google Cloud Shell

Google Cloud Shell is an interactive shell environment for Google Cloud Platform that makes it easy for you to learn and experiment with GCP and manage your projects and resources from your web browser.

Run in Cloud Shell