OpenStreetMap es una alternativa más que viable para incrustar mapas en los sitios web. No se requiere dejar una tarjeta de crédito ni nada más que poner una atribución (que viene ya incluída en el mapa al incrustarlo).

Incrustar un mapa es súper simple y se puede hacer desde el sitio usando el botón de compartir.


Ver mapa más grande

Para agregar varios marcadores y tener un api súper completa hay que usar OpenLayers que es una biblioteca con todas las herramientas n~~~~ecesarias y muchísimas cosas más.

OpenLayers

Incrustar un mapa con OpenLayers

El ejemplo más sencillo para esto va así:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.3.1/css/ol.css" type="text/css">
    <style>
      .map {
        height: 400px;
      }
    </style>
    <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.3.1/build/ol.js"></script>
    <title>Ejemplo</title>
  </head>
  <body>
    <div id="map" class="map"></div>

    <script type="text/javascript">
      var map = new ol.Map({
        target: 'map',
        layers: [
          new ol.layer.Tile({
            source: new ol.source.OSM()
          })
        ],
        view: new ol.View({
          center: ol.proj.fromLonLat([-102.788, 23.504]),
          zoom: 5
        })
      });

    </script>
  </body>
</html>

Que es lo mismo que encuentras en su Quick Start. Vamos a desglosar esto súper rápido:

  1. El mapa necesita un destino (target= 'map') que es el id de la div.

  2. Los mapas y los marcadores van en capas. Pedimos una capa de tiles que vengan de OSM (OpenStreetMaps).

  3. Un mapa necesita un centro (nay que notar que las coordenadas están en longitud y latitud y no al revés como es la costumbre).

  4. Además del centro, necesita un nivel de zoom (5). Puedes probar con números más grandes para acercar más el mapa a las coordenadas iniciales.

Agregar multiples marcadores al mapa

El concepto de los marcadores es un Feature; los Features tienen una geometría y esa geometría tiene un juego de coordenadas. Esto será útil cuando queramos obtener esas coordenadas para mover el centro del mapa.

Finalmente creamos una nueva capa que tenga como fuente la lista de features que acabamos de crear.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.3.1/css/ol.css" type="text/css">
    <style>
      .map {
        height: 400px;
      }
    </style>
    <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.3.1/build/ol.js"></script>
    <title>Ejemplo</title>
  </head>
  <body>
    <div id="map" class="map"></div>

    <script type="text/javascript">
      var map = new ol.Map({
        target: 'map',
        layers: [
          new ol.layer.Tile({
            source: new ol.source.OSM()
          })
        ],
        view: new ol.View({
          center: ol.proj.fromLonLat([-101.5, 19.5]),
          zoom: 9
        })
      });


      var feats = [

        new ol.Feature({
          geometry: new ol.geom.Point(ol.proj.fromLonLat([-101.16706400, 19.69828030])),
        }),

        new ol.Feature({
          geometry: new ol.geom.Point(ol.proj.fromLonLat([-101.61405750, 19.51183710])),
        })

      ];
      var layer = new ol.layer.Vector({
        source: new ol.source.Vector({
          features: feats
        })
      });
      map.addLayer(layer);


    </script>
  </body>
</html>

Agrega los features necesarios para que quede a tus necesidades.

Eventos en el mapa

Queremos agregar una capa sobrepuesta en el mapa para que la dar click sobre algún punto marcado, muestre un texto.

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.3.1/css/ol.css" type="text/css">
  <style>
    .map {
      height: 400px;
    }
    .ol-popup {
      background: rgba(255, 255, 255, 0.7);
      border: solid 1px #999;
      border-radius: 10px;
      color: #000;
      font-family: sans-serif;
      font-size: 0.7em;
      max-width: 150px;
      padding: 3px;
      text-align: center;
    }
  </style>
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.3.1/build/ol.js"></script>
  <title>Ejemplo</title>
</head>

<body>

  <div id="map" class="map"></div>

  <div id="popup" class="ol-popup"></div>

  <script type="text/javascript">
    var map = new ol.Map({
      target: 'map',
      layers: [
        new ol.layer.Tile({
          source: new ol.source.OSM()
        })
      ],
      view: new ol.View({
        center: ol.proj.fromLonLat([-101.5, 19.5]),
        zoom: 9
      })
    });


    var feats = [

      new ol.Feature({
        geometry: new ol.geom.Point(ol.proj.fromLonLat([-101.16706400, 19.69828030])),
        texto: '<strong>Instituto de la Mujer Moreliana -H. Ayuntamiento de Morelia</strong><br>Michoacán',
      }),

      new ol.Feature({
        geometry: new ol.geom.Point(ol.proj.fromLonLat([-101.61405750, 19.51183710])),
        texto: '<strong>Coordinación Nacional de Mujeres y Organizaciones Civiles por un Milenio Feminista</strong><br>Michoacán',
      })

    ];
    var layer = new ol.layer.Vector({
      source: new ol.source.Vector({
        features: feats
      })
    });
    map.addLayer(layer);


    var overlay = new ol.Overlay({
      element: document.getElementById('popup')
    });
    map.addOverlay(overlay);

    map.on('singleclick', function(evt) {
      if (map.hasFeatureAtPixel(evt.pixel) === true) {
        feats_in_pixel = map.getFeaturesAtPixel(evt.pixel);

        var feat = feats_in_pixel[0];
        var overlay_element = overlay.getElement();

        var texto = feat.get('texto');
        overlay_element.innerHTML = texto;

        overlay.setPosition(feat.getGeometry().getCoordinates());
      } else {
        // Oculta el overlay
        overlay.setPosition(undefined);
      }
    });
  </script>
</body>

</html>

Por favor nota que en el mismo objeto que creamos los puntos pusimos el contenido que irá en el overlay.

Además de eso, lo que hicimos fue:

  • Agregamos el tag del overlay
  <div id="popup" class="ol-popup"></div>
  • Agregamos el CSS
    .ol-popup {
        background: rgba(255, 255, 255, 0.7);
        border: solid 1px #999;
        border-radius: 10px;
        color: #000;
        font-family: sans-serif;
        font-size: 0.7em;
        max-width: 150px;
        padding: 3px;
        text-align: center;
      }
  • Agregamos el overlay
    var overlay = new ol.Overlay({
      element: document.getElementById('popup')
    });
    map.addOverlay(overlay);

Hasta aquí no hay nada fuera de lo común ni demasiado complicado.

  • Agregamos el código del evento
    map.on('singleclick', function(evt) {
      if (map.hasFeatureAtPixel(evt.pixel) === true) {
        feats_in_pixel = map.getFeaturesAtPixel(evt.pixel);

        var feat = feats_in_pixel[0];
        var overlay_element = overlay.getElement();

        var texto = feat.get('texto');
        overlay_element.innerHTML = texto;

        overlay.setPosition(feat.getGeometry().getCoordinates());
      } else {
        // Oculta el overlay
        overlay.setPosition(undefined);
      }
    });

Aquí es donde suceden la mayor parte del código. El mapa tiene el evento 'singleclick' (aquí puedes ver una lista de los eventos y puede detectar si hay algún feature donde se hizo click.

feats_in_pixel obtiene una lista de los features que hay en tal o cuál pixel (porque dependiendo del zoom se pueden encimar) así que tomamos el 0, que es el primero de esa lista.

De ese feature, obtenemos el texto y la coordenada.

Del overlay obtenemos el elemento (del DOM) para cambiarle el innetHTML a lo que pusimos a la hora de registrar los puntos del mapa. También posicionamos el overlay en la coordenada del punto encontrado.

Y finalmente con el overlay.setPosition(undefined); escondemos el overlay, en caso de que se haya dado un click en el mapa pero no sobre un feature.

¿Complicado?