google-mapsgoogle-maps-api-3google-maps-advanced-marker-element

Issue: Google Maps AdvancedMarkerView from Array of Images


i'm trying to generate Google Maps Markers from image atributes from an array.

with the former version of the maps Api it worked fine. i now tried to make it work with the new version (AdvancedMarkerView)

console says Uncaught ReferenceError: AdvancedMarkerView is not defined / Uncaught (in promise) TypeError: AdvancedMarkerView is not a constructor

any help is greatly appreciated

"use strict";

var active_img = 0;
var stage_img;
var img_lat;
var img_lng;
var parsedLat;
var parsedLng;
var autoplayInterval;
var isautoplay = true;
var firstautoplay = 0;
var autoplaylock = 0;
var mouse_onplay = false;
var map_active = false;
var main_visible = false;
var main_v_lock = false;
var myLatLng = {
  lat: 0,
  lng: 0
};

const images = [{
    path: 'img/img_3.webp',
    img_lat: '41,47873',
    img_lng: '11,99995',
  },
  {
    path: 'img/img_2.webp',
    img_lat: '49,47873',
    img_lng: '10,99995',
  },
];


let map;

async function initMap() {
  var position = myLatLng;
  const {
    Map
  } = await google.maps.importLibrary("maps");
  const {
    AdvancedMarkerView
  } = await google.maps.importLibrary("marker");


  map = new Map(document.getElementById("map"), {
    mapId: "559497c6e95d32fc",
    zoom: 18,
    center: myLatLng,
    fullscreenControl: false,
    zoomControl: true,
    streetViewControl: false
  });

  createMarkers(images, map, AdvancedMarkerView);
}

function createMarkers(images, map, AdvancedMarkerView) {
  const markers = [];

  images.forEach((image, index) => {
    const {
      img_lat,
      img_lng,
      path
    } = image;
    if (img_lat !== '0' && img_lng !== '0') {
      const latLng = {
        lat: parseFloat(img_lat.replace(',', '.')),
        lng: parseFloat(img_lng.replace(',', '.'))
      };

      const marker = new AdvancedMarkerView({
        position: latLng,
        map: map,
        title: path
      });

      marker.addListener('click', () => {
        handleMarkerClick(index);
      });

      markers.push(marker);
    }
  });

  return markers;
}

initMap();


function ifLost() {
  if (img_lat === '0' && img_lng === '0') {

    img_lng = 'data lost';
    img_lat = '';
  }
}


function img_update() {
  document.getElementById('stage_img').src = stage_img;
  ifLost();
  document.getElementById('lat').innerHTML = `<div>${img_lat}</div>`;
  document.getElementById('lng').innerHTML = `<div>${img_lng}</div>`;
}

$(document).ready(function() {

  stage_img = images[0].path;
  img_lat = images[0].img_lat;
  img_lng = images[0].img_lng;

  parsedLat = parseFloat(img_lat.replace(',', '.'));
  parsedLng = parseFloat(img_lng.replace(',', '.'));

  myLatLng = {
    lat: parsedLat,
    lng: parsedLng
  };

  if (map) {
    map.setCenter(new google.maps.LatLng(parsedLat, parsedLng));
  }

  function coordinates_update_move() {
    img_lat = images[active_img].img_lat;
    img_lng = images[active_img].img_lng;

    parsedLat = parseFloat(img_lat.replace(',', '.'));
    parsedLng = parseFloat(img_lng.replace(',', '.'));

    if (map) {

      if (img_lat == '0') {} else {
        map.panTo(new google.maps.LatLng(parsedLat, parsedLng));
      }

    }
    ifLost();
  }


  function handleMarkerClick(index) {

    active_img = index;

    stage_img = images[active_img].path;
    img_lat = images[active_img].img_lat;
    img_lng = images[active_img].img_lng;

    img_update();
    coordinates_update_move();

    setTimeout(close_btn2, 440);

  }
});
body {
  position: relative;
  width: 100vw;
  height: 100vh;
}

#map_stage {
  width: 100%;
  height: 100%;
}

#map {
  height: 50%;
  width: 90%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>drau3en</title>

  <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>

  <script>
    (g => {
      var h, a, k, p = "The Google Maps JavaScript API",
        c = "google",
        l = "importLibrary",
        q = "__ib__",
        m = document,
        b = window;
      b = b[c] || (b[c] = {});
      var d = b.maps || (b.maps = {}),
        r = new Set,
        e = new URLSearchParams,
        u = () => h || (h = new Promise(async(f, n) => {
          await (a = m.createElement("script"));
          e.set("libraries", [...r] + "");
          for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]);
          e.set("callback", c + ".maps." + q);
          a.src = `https://maps.${c}apis.com/maps/api/js?` + e;
          d[q] = f;
          a.onerror = () => h = n(Error(p + " could not load."));
          a.nonce = m.querySelector("script[nonce]") ? .nonce || "";
          m.head.append(a)
        }));
      d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n))
    })({
      key: "AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk",
      v: "weekly",
    });
  </script>

  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
  <link rel="stylesheet" type="text/css" href="main.css">

  <script src="jquery-3.1.0.min.js" type="text/javascript"></script>

  <script src="main.js" type="text/javascript"></script>
</head>

<body>

  <div id="map_stage">
    <div id="map"></div>
    <p id="lat"></p>
    <p id="lng"></p>
  </div>

  <footer>
  </footer>

  <script>
  </script>

</body>

</html>


Solution

  • As has been noted, you need to use AdvancedMarkerElement rather than AdvancedMarkerView. With that changed you should find it fairly straightforward to add your own custom designed markers to the map at whatever locations you wish using HTML for the marker content. Perhaps the following, modified code, might help.

    The gmp-click event which is used below is currently available within the beta channel but this does help simplify accessing content within the AdvancedMarkerElement - using the older click event is still possible but the clickhandler code would need to be modified to accommodate the change.

    <!DOCTYPE html>
    <html lang='en'>
        <head>
            <meta charset='utf-8' />
            <title></title>
            <style>
                :root{--accent:blue}
                body { width: 100vw; height: 100vh; overflow:hidden; padding:0; margin:0; display:flex; flex-direction:column;  }
                body, body *{ box-sizing:border-box; font-family:monospace }
                *:focus { outline: none; }
                
                .advmarker{ background:white; padding:0.5rem; border-radius:1rem; border:1px solid var(--accent); display:flex; justify-content:center; align-items:center; flex-direction:column }
                .map-icon{ width:50px; height:50px; border-radius:50%; border:2px solid black; }
                .advmarker h3{color:var(--accent)}
                
                #map_stage{ width:100%; height:100%; margin:0 auto; display:flex; flex-direction:column; justify-content:flex-start; align-items:center; }
                #map_stage p:before,
                .advmarker span:before{ content:attr( data-id )':'; color:var(--accent) }
                
                #map { height:65%;width:100%; }
            </style>
        </head>
        <body>
        
            <div id='map_stage'>
              <div id='map'></div>
              <p data-id='lat' id='lat'></p>
              <p data-id='lng' id='lng'></p>
              <img id='stage_img' />
            </div>
            <footer></footer>
            
            
            <script>
            
                const APIKEY='AIza.....';
            
                (g => {
                  var h, a, k, p = "The Google Maps JavaScript API",
                    c = "google",
                    l = "importLibrary",
                    q = "__ib__",
                    m = document,
                    b = window;
                  b = b[c] || (b[c] = {});
                  var d = b.maps || (b.maps = {}),
                    r = new Set,
                    e = new URLSearchParams,
                    u = () => h || (h = new Promise(async(f, n) => {
                      await (a = m.createElement("script"));
                      e.set("libraries", [...r] + "");
                      for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]);
                      e.set("callback", c + ".maps." + q);
                      a.src = `https://maps.${c}apis.com/maps/api/js?` + e;
                      d[q] = f;
                      m.head.append(a)
                    }));
                    
                  d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n))
                })({
                  key:APIKEY,
                  v:'beta'
                });
                
                // above is slightly modified
                
    
                const myLatLng = {// central europe - ish
                  'lat':46.553469,
                  'lng':11.761824
                };
                const images = [
                    {
                        path: '//placekitten.com/200/200?image=1',
                        img_lat: 41.47873,
                        img_lng: 11.99995
                    },
                    {
                        path: '//placekitten.com/200/200?image=2',
                        img_lat: 49.47873,
                        img_lng: 10.99995
                    },
                    {
                        path: '//placekitten.com/200/200?image=3',
                        img_lat: 47.013545,
                        img_lng: 2.373301
                    },
                    {
                        path: '//placekitten.com/200/200?image=4',
                        img_lat: 47.504609, 
                        img_lng: 19.050323
                    }
                ];
    
    
    
    
    
    
    
    
    
    
                async function initMap() {
                  // store references to each marker here.
                  let markers={};
                
                  const { Map } = await google.maps.importLibrary("maps");
                  const { AdvancedMarkerElement } = await google.maps.importLibrary("marker");
                  
                  
                  
                  /* 
                      Conventional style callback functions 
                      allow access to the actual AdvancedMarkerElement 
                      via `this`
                      
                      markers are stored within `markers` object
                      so you can iterate through that Object to
                      perform various operations upon each marker
                      if required.
                  */
                  function clickhandler(e){
                    document.querySelector('p#lat').textContent=Number(this.position.lat.toFixed(6));
                    document.querySelector('p#lng').textContent=Number(this.position.lng.toFixed(6));
                    document.querySelector('img#stage_img').src=this.content.querySelector('img.map-icon').src;
                  }
    
                  function draghandler(e){
                    console.log( this, e.latLng )
                    this.content.querySelector('[data-id="lat"]').textContent=Number(e.latLng.lat().toFixed(6));
                    this.content.querySelector('[data-id="lng"]').textContent=Number(e.latLng.lng().toFixed(6));
                  }
                  
                  
                  
                  
                  
                  // utility to generate suitable HTML content used within the AdvancedMarkerElement
                  const createicon=( obj )=>{
                    let div=document.createElement('div');
                        div.className='advmarker';
                        div.innerHTML=`
                            <h3>${obj.path.split('?').pop()}</h3>
                            <img class='map-icon' src='${obj.path}' />
                            <span data-id='lat'>${obj.img_lat}</span>
                            <span data-id='lng'>${obj.img_lng}</span>`;
                    return div;
                  };
                  
                  
                  
                  /* 
                    utility to add a reasonable good id to each marker
                    - allows easy access to each marker within global markers object
                    - 
                  */
                  const createid=()=>window.URL.createObjectURL( new Blob( [] ) ).split('/').pop();
                  
                  
                  const addmarker=( obj, callbacks={} )=>{
                    let mkr=new AdvancedMarkerElement({
                        map:map,
                        gmpDraggable:obj.draggable || true,
                        gmpClickable:obj.clickable || true,
                        position:{ 
                            'lat':obj.img_lat,
                            'lng':obj.img_lng 
                        },
                        content:createicon( obj )
                    });
                    
                    /*
                        add data and custom propertiesy to the marker which 
                        allows access to them data within click handler.
                    */
                    mkr.data=obj;
                    mkr.id=obj.id;
                    
                    
                    if( callbacks && typeof( callbacks )=='object' ) {
                        Object.keys( callbacks ).forEach( type=>{
                            if( !~type.indexOf('gmp') )mkr.addListener( type, callbacks[ type ] );
                            else mkr.addEventListener( type, callbacks[ type ] );
    
                        });
                    }
                    
                    return mkr;
                  };
                  
                  
                  
                  const createMarkers=()=>{
                    const callbacks={
                        'gmp-click':clickhandler,
                        'dragend':draghandler
                    };
                    
                    images.forEach( obj => {
                        let id=createid();
                        let args=Object.assign( obj, {'id':id } );
                        let marker=addmarker( args, callbacks );
                        
                        markers[ id ]=marker;
                    });
                    return markers;
                  };
    
    
    
    
                  const map = new Map(document.getElementById("map"), {
                    mapId: "559497c6e95d32fc",
                    zoom: 6,
                    center: myLatLng,
                    fullscreenControl: false,
                    streetViewControl: false,
                    zoomControl: true
                  });
                  
                  return createMarkers();
                }
    
    
                initMap()
                    .then(console.log)
                    .catch(console.warn)
    
            </script>
        </body>
    </html>