Search code examples
javascriptmapsgoogle-maps-markersopenlayersopenlayers-7

Issue placing a marker in the correct spot on an OpenLayers 7 map


I am creating a webapp that allows user to create mulitple maps. On each map the user has the ability to add/remove a marker. They are then able to drag it into the appropriate location and/or change the size of the marker. For the most part the functionality is working but I am having a small issues.

When the user switches back and fourth between maps the app should reference the lat/lon of the marker (held in a variable) and places it back on the map. However when I switch to a previous map with a marker the marker location is slightly off. I have a feeling this has something to do with the anchor values of the marker but can't quite figure out how to solve the problem. Any help is greatly appreciated.

To replicate this issue I take the following steps:

  1. Click on the first map text box, type/select an address to populate the map.
  2. Click the 'Include a marker on this map' checkbox to add a marker.
  3. Drag the marker to a new location on the map.
  4. Click on the second map tab (map should be hidden as no address is selected for this map)
  5. Click back to the first map tab.
  6. Marker is no longer in the correct place. It appears slightly above where it should on the map.

Here is a link to a JSFiddle with my current code.

/*jshint multistr: true */

/*these variables will come from previous user elections */
var numMaps = 2;
var selectedMapStyle = "OSMHumanitarian" //this will be selected on previous step
/* end user variables */

var currentMap;
var mapDetails = [];
var layoutDetails = [];
var tmpMapData = [];

// set some variables
var currentLonLat = [-63.5859487, 44.648618]; //Halifax Lon/Lat
//var currentLonLat = [-63.61089784934739,44.67050745]; //Leeds St. Lon/Lat
var defaultZoom = 12;
var defaultRotation = 0;

mapCenter = (ol.proj.fromLonLat(currentLonLat)); //Converts Lon/Lat to center

//array with available markers
var availableMarkers = [
  ['https://testing.52weekdog.com/images/markers/marker_marker01_black.svg', 0.06, [0.5, 1], 'standard'],
  ['https://testing.52weekdog.com/images/markers/marker_marker02_black.svg', 1.1, [0.5, 1], 'minimal'],
  ['https://testing.52weekdog.com/images/markers/marker_house01_black.svg', 0.8, [0.5, 1], 'house'],
  ['https://testing.52weekdog.com/images/markers/marker_pin01_black.svg', 1, [0.5, 1], 'pin']
];





//setup the map
const marker = new ol.Feature({
  geometry: new ol.geom.Point(ol.proj.fromLonLat([-63.5859487, 44.648618])),
});
const markerStyle = new ol.style.Style({
  image: new ol.style.Icon({
    anchor: availableMarkers[0][2],
        anchorYUnits: 'pixels',
    scale: availableMarkers[0][1],
    src: availableMarkers[0][0]
  })
});
const vectorSource = new ol.layer.Vector({
  source: new ol.source.Vector({
    features: [marker]
  }),
  style: markerStyle
});
marker.setStyle([markerStyle]);


const map = new ol.Map({
  view: new ol.View({
    center: mapCenter,
    zoom: defaultZoom,
    rotation: defaultRotation,
  }),
  target: 'map',
  controls: []
});
var view = map.getView();

var translate = new ol.interaction.Translate({
  features: new ol.Collection([marker])
});

map.addInteraction(translate);

translate.on('translating', function(evt) {
  var lonlat = ol.proj.transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326');
  tmpMapData[currentMap][5] = lonlat;
  document.getElementById("markerDetails" + currentMap + "_lonlat").innerHTML = "[" + lonlat[0].toFixed(5) + ", " + lonlat[1].toFixed(5) + "]";
  checkMarkerLocation();
});

//setup the map controls
const rotationbutton = new ol.control.Rotate({
  target: 'rotateButton',
  autoHide: false
});

const zoombuttons = new ol.control.Zoom({
  target: 'zoom'
})
const zoomslider = new ol.control.ZoomSlider();
zoomslider.setTarget('zoom');
const attribution = new ol.control.Attribution({
  className: 'ol-attribution customAttribution',
  collapsed: false,
  collapsible: false,
  target: document.getElementById('attribution')
});

map.addControl(zoomslider);
map.addControl(zoombuttons);
map.addControl(rotationbutton);
map.addControl(attribution);

// when rotating using slider
rotationSlider.oninput = function() {
  map.getView().setRotation(degreesToRads(this.value - 180));
}

// update the rotation
map.getView().on("change:rotation", function(event) {
  deg = radsToDegrees(event.target.getRotation());
  if (deg < -180) {
    deg = deg + 360
  } else if (deg > 180) {
    deg = deg - 360;
  }
  document.getElementById('rotationSlider').value = deg + 180;

  updateMapDetails();


});
// END ROTATION //

map.getView().on(['change:center', 'change:resolution', 'change:rotation'], function() {
  updateMapDetails();
});


/* Base Map Layer */
const openStreetMapStandard = new ol.layer.Tile({
  source: new ol.source.OSM(),
  visible: false,
  title: 'OSMStandard'
})
const openStreetMapHumanitarian = new ol.layer.Tile({
  source: new ol.source.OSM({
    url: 'https://{a-c}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png'
  }),
  visible: false,
  title: 'OSMHumanitarian'
})
const stamenToner = new ol.layer.Tile({
  source: new ol.source.XYZ({
    url: 'https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png',
    attributions: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, \
                        under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. \
                        Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.'
  }),
  visible: false,
  title: 'StamenToner'
})
const stamenTerrain = new ol.layer.Tile({
  source: new ol.source.XYZ({
    url: 'https://stamen-tiles.a.ssl.fastly.net/terrain/{z}/{x}/{y}.jpg',
    attributions: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, \
                        under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. \
                        Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.'
  }),
  visible: false,
  title: 'StamenTerrain'
})
const stamenWaterColor = new ol.layer.Tile({
  source: new ol.source.XYZ({
    url: 'https://stamen-tiles.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg',
    attributions: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, \
                        under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. \
                        Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.'
  }),
  visible: false,
  title: 'StamenWatercolor'
})

/* End Base Map Layer */

//Layer Group
const baseLayerGroup = new ol.layer.Group({
  layers: [
    openStreetMapStandard, openStreetMapHumanitarian, stamenToner, stamenTerrain, stamenWaterColor, OpenStreetMap_Mapnik
  ]
})

map.addLayer(baseLayerGroup);


//populate based on number of maps
addressesContainer = document.getElementById('addressPickerContainer').getElementsByClassName('addressPickerTabColumn')[0];
for (i = 1; i <= numMaps; i++) {

  html = "<div id='MapTab" + i + "' class='addressPickerTab' onclick='clickTab(this)''>";
  html += "<div>";
  html += "<label>Map " + i + "</label><span>Incomplete</span>";
  html += "<div class='autocomplete-container' id='autocomplete-container" + i + "'></div>";
  html += "</div>";
  html += "</div>";
  html += "<div class='addressPickerDataBlock'>";
  html += "<div class='mapDetails'>";
  html += "<h4>Details for Map " + i + "</h4>";
  html += "Address: <span class='mapInfo' id='mapDetails" + i + "_address1'></span><br>";
  html += "Lon:  <span class='mapInfo' id='mapDetails" + i + "_lon'> </span> Lat: <span class='mapInfo' id='mapDetails" + i + "_lat'></span><br>";
  html += "Zoom: <span class='mapInfo' id='mapDetails" + i + "_zoom'></span> Map Rotation: <span class='mapInfo' id='mapDetails" + i + "_rotation'></span><br>";
  html += "</div>";
  html += "<div>";
  html += "<label class='includeMarkerChk'>";
  html += "<input type='checkbox' class='includeMarkerChk' id='includeMarkerChk" + i + "' onclick='toggleMarker(this)' /> Include a marker on this map.</label>";

  html += "<div class='chkBoxArea'>";
  html += "<button type='button' id='resetMarker" + i + "_btn' class='resetMarkerBtn' onclick='resetMarker()'>reset marker</button>";

  html += "<select name='markerSelect' onchange='changeMarker(this)' id='markerSelect" + i + "'>";
  for (m = 0; m < availableMarkers.length; m++) {
    html += "<option value='" + m + "'>" + availableMarkers[m][3] + "</option>";
  }
  html += "</select><br>";
  html += "Marker Size: <input type='range' oninput='resizeMarker(this)' id='markerSize" + i + "' min='1' max='100' value='10' class='markerSlider' onchange='resizeMarker(this)'>";
  html += "<span class='mapInfo' id='markerDetails" + i + "_size'>10</span><br>";
  html += "Current Marker Location: <span class='mapInfo' id='markerDetails" + i + "_lonlat'></span><br>";
  html += "<span class='mapInfo markerVisible' id='markerDetails" + i + "_visible'>The location of the marker is not currently on the map.</span><br>";
  html += "</div>";
  html += "</div>";
  html += "<div style='margin-top: -10px'>";
 
  html += "<button type='button' id='saveMap" + i + "_btn' class='btn btn-primary btn-sm saveMapBtn'>Save Details</button>";
  html += "</div>";
  html += "</div>";
  addressesContainer.innerHTML += html;
}

tmpMapData.push(["formatted", "lat", "lon", "zoom", "rotation", "markerLocation", "markerStyle", "markerSize", "mapStyle"]);

for (i = 1; i <= numMaps; i++) {
  const j = i;
  addressAutocomplete(document.getElementById("autocomplete-container" + j), (data) => {
    if (data) {
      //THIS IS WHAT HAPPENS WHEN AN ADDRESS IS SELECTED
      tmp = [data.properties.lon, data.properties.lat];
      tmpArray = [data.properties.formatted, data.properties.lat, data.properties.lon, defaultZoom, defaultRotation,
        tmp, availableMarkers[0][0], "10", selectedMapStyle
      ];
      tmpMapData[j] = tmpArray;

      //updateMap();
      document.getElementById("MapTab" + currentMap).nextElementSibling.classList.add("active");

      document.getElementById('MapTab' + currentMap).children[0].children[1].innerHTML = "In Progress";
      document.getElementById('MapTab' + currentMap).children[0].children[1].style.color = "orange";
    } else {
            // when delete X is pressed on autocomplete
    }

  }, {
    placeholder: "Enter an address for map " + j
  });

}



function updateMapDetails() {
  var center = ol.proj.transform(view.getCenter(), 'EPSG:3857', 'EPSG:4326');
  var zoom = view.getZoom();
  var rotation = view.getRotation();

  document.getElementById('mapDetails' + currentMap + '_address1').innerHTML = tmpMapData[currentMap][0];

  document.getElementById('mapDetails' + currentMap + '_lat').innerHTML = center[1];
  document.getElementById('mapDetails' + currentMap + '_lon').innerHTML = center[0];
  document.getElementById('mapDetails' + currentMap + '_zoom').innerHTML = zoom;
  document.getElementById('mapDetails' + currentMap + '_rotation').innerHTML = radsToDegrees(rotation) + "\u00B0";
  document.getElementById('markerDetails' + currentMap + '_lonlat').innerHTML = "[" + tmpMapData[currentMap][5][0].toFixed(5) + ", " + tmpMapData[currentMap][5][1].toFixed(5) + "]";
  document.getElementById('markerSize' + currentMap).value = tmpMapData[currentMap][7];

  tmpMapData[currentMap][1] = center[1];
  tmpMapData[currentMap][2] = center[0];
  tmpMapData[currentMap][3] = zoom;
  tmpMapData[currentMap][4] = radsToDegrees(rotation);

}

function clickTab(tab) {
  clickedTab = document.getElementById(tab.id);
  var tabNum = clickedTab.id.substring(clickedTab.id.indexOf("MapTab") + 6);
  currentMap = tabNum;
            
  //if map details already exist
  if (tmpMapData[tabNum]) {

    //hide the image
    document.getElementById("layoutImage").style.display = "none";
    //load the mapData
    tmp_lat = tmpMapData[tabNum][1];
    tmp_lon = tmpMapData[tabNum][2];
    tmp_center = ol.proj.fromLonLat([tmp_lon, tmp_lat]);
    map.removeLayer(vectorSource);
    
        changeMarker(document.getElementById('markerSelect' + tabNum));
    resizeMarker(document.getElementById('markerSize' + tabNum));
    toggleMarker(document.getElementById('includeMarkerChk' + tabNum));


    setMap(tmp_center, tmpMapData[tabNum][3], tmpMapData[tabNum][4]);
    updateMapDetails();     

  } else {

    //hide the map and show the layout
    document.getElementById("layoutImage").style.display = "flex";

  }

  //switch the mask svg path

  if (clickedTab.classList.contains('active')) { //if the clicked tab is already active
    if (document.activeElement.classList[0] != "addressAutocompleteInput") { //if the active element isn't the autocomplete dropdown then minimize
      clickedTab.classList.remove("active");
      clickedTab.nextElementSibling.classList.remove("active");
      document.getElementById("layoutImage").style.display = "flex";
      document.getElementById("layoutImage").focus();
    }
  } else { //if the clicked tab is not currently active

    for (i = 0; i < clickedTab.parentElement.children.length; i++) { //close any tabs that are 'active'
      if (clickedTab.parentElement.children[i].classList.contains('active')) {
        // minimize the tab and hide the details
        clickedTab.parentElement.children[i].classList.remove("active");
        clickedTab.parentElement.children[i].nextElementSibling.classList.remove("active");
      }
    }
    //set the clicked tab to active
    clickedTab.classList.add("active");
    if (tmpMapData[currentMap]) {
      clickedTab.nextElementSibling.classList.add("active");
    }
    document.getElementById("autocomplete-container" + currentMap + "-input").focus();

  }

}

function setMap(center, zoom, rotation) {
  map.getView().setCenter(center);
  map.getView().setZoom(zoom);
  map.getView().setRotation(degreesToRads(rotation));
  //should add marker to this?
}

function toggleMarker(input) {

  if (input.checked == true) {
    input.parentElement.nextElementSibling.style.display = "block";

    //if there is no marker location set then set it to the center of the map
    marker.getGeometry().setCoordinates((ol.proj.fromLonLat(tmpMapData[currentMap][5])));
    /* reset marker to center if the marker is not visible on map when markerchk is checked
    if (checkMarkerLocation() == true){
          marker.getGeometry().setCoordinates((ol.proj.fromLonLat([tmpMapData[currentMap][2], tmpMapData[currentMap][1]])));
    }
    */

    map.addLayer(vectorSource);
    checkMarkerLocation();

  } else {
    input.parentElement.nextElementSibling.style.display = "none";
    map.removeLayer(vectorSource);
  }


}

function changeMarker(input) {
  markerStyle.setImage(new ol.style.Icon({
    anchor: availableMarkers[input.value][2],
    scale: availableMarkers[input.value][1],
    src: availableMarkers[input.value][0]
  }));
  marker.changed();
  resizeMarker(document.getElementById('markerSize' + currentMap));
  tmpMapData[currentMap][6] = availableMarkers[input.value][0];

  //tmpMapData[currentMap][6] = availableMarkers[input.value][2];
  //tmpMapData[currentMap][7] = availableMarkers[input.value][1];
}

function resizeMarker(input) {
  newScale = input.value / 10 * availableMarkers[document.getElementById('markerSelect' + currentMap).value][1];
  markerStyle.getImage().setScale([parseFloat(newScale), parseFloat(newScale)]);
  document.getElementById('markerDetails' + currentMap + '_size').innerHTML = input.value;
    tmpMapData[currentMap][7] = input.value;
  marker.changed();
}

function resetMarker() {
  marker.getGeometry().setCoordinates((ol.proj.fromLonLat(ol.proj.transform(view.getCenter(), 'EPSG:3857', 'EPSG:4326'))));
  tmpMapData[currentMap][5] = ol.proj.transform(view.getCenter(), 'EPSG:3857', 'EPSG:4326');
  updateMapDetails();
  resizeMarker(document.getElementById('markerSize' + currentMap));
}

function degreesToRads(deg) {
  return (deg) * (Math.PI / 180);
}

function radsToDegrees(rad) {
  return (rad) / (Math.PI / 180);
}

function checkMarkerLocation() {
  const size = map.getSize();
  const tl = map.getCoordinateFromPixel([0, 0]);
  const tr = map.getCoordinateFromPixel([size[0], 0]);
  const bl = map.getCoordinateFromPixel([0, size[1]]);
  const br = map.getCoordinateFromPixel(size);
  const polygon = new ol.geom.Polygon([
    [tl, tr, br, bl, tl]
  ]);
  if (polygon.intersectsCoordinate(marker.getGeometry().getCoordinates())) {
    document.getElementById('markerDetails' + currentMap + '_visible').style.visibility = 'hidden';
    return false;
  } else {
    document.getElementById('markerDetails' + currentMap + '_visible').style.visibility = 'visible';
    return true;
  }
}





function addressAutocomplete(containerElement, callback, options) {
  // create input element
  var inputElement = document.createElement("input");
  inputElement.setAttribute("type", "text");
  inputElement.setAttribute("class", "addressAutocompleteInput");
  inputElement.setAttribute("id", containerElement.id + "-input");
  inputElement.setAttribute("placeholder", options.placeholder);

  containerElement.appendChild(inputElement);

  // add input field clear button
  var clearButton = document.createElement("div");
  clearButton.classList.add("clear-button");
  addIcon(clearButton);

  //when a clear button is clicked
  clearButton.addEventListener("click", (e) => {
    //clear the currently selected address
    e.stopPropagation();
    inputElement.value = '';
    callback(null);
    clearButton.classList.remove("visible");
    document.getElementById("autocomplete-container" + currentMap + "-input").focus();
    document.getElementById('MapTab' + currentMap).children[0].children[1].style.color = "red";
    closeDropDownList();
  });
  containerElement.appendChild(clearButton);

  /* Current autocomplete items data (GeoJSON.Feature) */
  var currentItems;

  /* Active request promise reject function. To be able to cancel the promise when a new request comes */
  var currentPromiseReject;

  /* Focused item in the autocomplete list. This variable is used to navigate with buttons */
  var focusedItemIndex;

  /* Execute a function when someone writes in the text field: */
  inputElement.addEventListener("input", function(e) {
    var currentValue = this.value;
    var currentInput = this.id;


    /* Close any already open dropdown list */
    closeDropDownList();

    // Cancel previous request promise
    if (currentPromiseReject) {
      currentPromiseReject({
        canceled: true
      });
    }

    if (!currentValue) {
      clearButton.classList.remove("visible");
      return false;
    }

    // Show clearButton when there is a text
    clearButton.classList.add("visible");

    /* Create a new promise and send geocoding request */
    var promise = new Promise((resolve, reject) => {
      currentPromiseReject = reject;

      var apiKey = "47f523a46b944b47862e39509a7833a9";
      var url = `https://api.geoapify.com/v1/geocode/autocomplete?text=${encodeURIComponent(currentValue)}&limit=5&apiKey=${apiKey}`;

      if (options.type) {
        url += `&type=${options.type}`;
      }

      fetch(url)
        .then(response => {
          // check if the call was successful
          if (response.ok) {
            response.json().then(data => resolve(data));
          } else {
            response.json().then(data => reject(data));
          }
        });
    });

    promise.then((data) => {
      currentItems = data.features;

      /*create a DIV element that will contain the items (values):*/
      var autocompleteItemsElement = document.createElement("div");
      autocompleteItemsElement.setAttribute("class", "autocomplete-items");
      containerElement.appendChild(autocompleteItemsElement);

      /* For each item in the results */
      data.features.forEach((feature, index) => {
        /* Create a DIV element for each element: */
        var itemElement = document.createElement("DIV");
        /* Set formatted address as item value */
        itemElement.innerHTML = feature.properties.formatted;

        /* Set the value for the autocomplete text field and notify: */
        itemElement.addEventListener("click", function(e) {
          inputElement.value = currentItems[index].properties.formatted;

          //selecting address by clicking          
          callback(currentItems[index]);
          /* Close the list of autocompleted values: */
          closeDropDownList();
          /* give the focus back to the input field */
          document.getElementById(inputElement.id).focus();

          /* get the number of the input being seelected */
          /*
                    var inputNum = inputElement.id.substring(
            inputElement.id.indexOf("autocomplete-container") + 22,
            inputElement.id.lastIndexOf("-input")
          );
                    */
          //alert(currentMap + ' - ' + currentItems[index]);
          /* end */

        });

        autocompleteItemsElement.appendChild(itemElement);
      });
    }, (err) => {
      if (!err.canceled) {
        console.log(err);
      }
    });
  });

  /* Add support for keyboard navigation */
  inputElement.addEventListener("keydown", function(e) {
    var autocompleteItemsElement = containerElement.querySelector(".autocomplete-items");
    if (autocompleteItemsElement) {
      var itemElements = autocompleteItemsElement.getElementsByTagName("div");
      if (e.keyCode == 40) { //downarrow
        e.preventDefault();
        /*If the arrow DOWN key is pressed, increase the focusedItemIndex variable:*/
        focusedItemIndex = focusedItemIndex !== itemElements.length - 1 ? focusedItemIndex + 1 : 0;
        /*and and make the current item more visible:*/
        setActive(itemElements, focusedItemIndex);
        /* get the number of the input being seelected */
        /*
                var mySubString = inputElement.id.substring(
          inputElement.id.indexOf("autocomplete-container") + 22,
          inputElement.id.lastIndexOf("-input")
        );
                */
        //inputNum = mySubString.charCodeAt(0) - 64;
        //alert('input ' + inputNum + ' selected.');
        /* end */
      } else if (e.keyCode == 38) { //uparrow
        e.preventDefault();

        /*If the arrow UP key is pressed, decrease the focusedItemIndex variable:*/
        focusedItemIndex = focusedItemIndex !== 0 ? focusedItemIndex - 1 : focusedItemIndex = (itemElements.length - 1);
        /*and and make the current item more visible:*/
        setActive(itemElements, focusedItemIndex);
        /* get the number of the input being seelected */
        /*
             var mySubString = inputElement.id.substring(
          inputElement.id.indexOf("autocomplete-container") + 22,
          inputElement.id.lastIndexOf("-input")
        );
        inputNum = mySubString.charCodeAt(0) - 64;
                */
        //alert('input ' + inputNum + ' selected.');
        /* end */
      } else if (e.keyCode == 13) { //enter

        /* If the ENTER key is pressed and value as selected, close the list*/
        e.preventDefault();
        const itmNum = focusedItemIndex;

        if (focusedItemIndex > -1) {
          closeDropDownList();
        }

        document.getElementById("layoutImage").style.display = "none";

        setActive(itemElements, itmNum);
        tmp_center = ol.proj.fromLonLat([currentItems[itmNum].properties.lon, currentItems[itmNum].properties.lat]);
        setMap(tmp_center, defaultZoom, defaultRotation);

        /* get the number of the input being seelected */
        /*
                var mySubString = inputElement.id.substring(
          inputElement.id.indexOf("autocomplete-container") + 22,
          inputElement.id.lastIndexOf("-input")
        );
        inputNum = mySubString.charCodeAt(0) - 64;
                */
        //alert('input ' + inputNum + ' selected.');
        /* end */
      }
    } else {
      if (e.keyCode == 40) { //downarrow
        /* Open dropdown list again */
        var event = document.createEvent('Event');
        event.initEvent('input', true, true);
        inputElement.dispatchEvent(event);
      }
    }


  });

  // Used when scrolling autocomplete dropdown with keyboard
  function setActive(items, index) {
    if (!items || !items.length) return false;

    for (var i = 0; i < items.length; i++) {
      items[i].classList.remove("autocomplete-active");
    }
    /* Add class "autocomplete-active" to the active element*/
    items[index].classList.add("autocomplete-active");


    // Change input value and notify
    inputElement.value = currentItems[index].properties.formatted;

    callback(currentItems[index]);

  }

  function closeDropDownList() {
    var autocompleteItemsElement = containerElement.querySelector(".autocomplete-items");
    if (autocompleteItemsElement) {
      containerElement.removeChild(autocompleteItemsElement);
    }

    focusedItemIndex = -1;
  }

  function addIcon(buttonElement) {
    var svgElement = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
    svgElement.setAttribute('viewBox', "0 0 24 24");
    svgElement.setAttribute('height', "24");

    var iconElement = document.createElementNS("http://www.w3.org/2000/svg", 'path');
    iconElement.setAttribute("d", "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z");
    iconElement.setAttribute('fill', 'currentColor');
    svgElement.appendChild(iconElement);
    buttonElement.appendChild(svgElement);
  }

  /* Close the autocomplete dropdown when the document is clicked. 
    Skip, when a user clicks on the input field */
  document.addEventListener("click", function(e) {
    if (e.target !== inputElement) {
      closeDropDownList();
    } else if (!containerElement.querySelector(".autocomplete-items")) {
      // open dropdown list again
      var event = document.createEvent('Event');
      event.initEvent('input', true, true);
      inputElement.dispatchEvent(event);
    }
  });

}

Solution

  • translate.on('translating', function(evt) {
      var lonlat = ol.proj.transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326');
    

    is remembering the pointer position used to drag the marker, not the point at which the marker is anchored - as long as you are always using Point geometry you can use the geometry coordinates. It might also be missing the final move, so listening for translateend as well as translating may also help:

    translate.on(['translating', 'translateend'], function(evt) {
      var lonlat = ol.proj.transform(evt.features.item(0).getGeometry().getCoordinates(), 'EPSG:3857', 'EPSG:4326');