Search code examples
rr-leaflet

Change addMouseCoordinates to display decimal minutes seconds instead of decimal degrees?


I need to be able to display a leaflet map in a shiny, where the user an see the position of the mouse on the map in degrees minutes seconds.

addMouseCoordinates adds the coordinates of the mouse, but the format is in decimal degrees. Is there a way to change it to display in degrees minutes seconds?

library(leaflet)
library(leafem)

leaflet() %>%
  addProviderTiles("OpenStreetMap") %>%
  addMouseCoordinates()

Solution

  • I've edited the function from leafem (addMouseCoordinates) to convert the lat and lon to degrees minutes seconds. I've also changed the labelling in the box.

    Now the mouse position displays as "Longitude: dd°mm ss.sss E | Latitude: dd°mm ss.sss S" when ctrl + hover only the lat and lon in decimal degrees are shown.

    Note: my area to display is South-west Australia. So I manually added the "S" and "E" but if you wanted to display multiple hemispheres the code would need to be adjusted.

    clipboardDependency = function() {
      list(
        htmltools::htmlDependency(
          name = "clipboard",
          version = "0.0.1",
          src = system.file("htmlwidgets/lib/clipboard", package = "leafem"),
          script = "setClipboardText.js"
        )
      )
    }
    
    addMouseCoordinates <- function(map,
                                    epsg = NULL,
                                    proj4string = NULL,
                                    native.crs = FALSE) {
      
      if (inherits(map, "mapview")) map <- mapview2leaflet(map)
      stopifnot(inherits(map, c("leaflet", "leaflet_proxy", "mapdeck")))
      
      if (native.crs) { 
        txt_detailed <- paste0("
                               ' x: ' + (e.latlng.lng).toFixed(5) +
                               ' | y: ' + (e.latlng.lat).toFixed(5) +
                               ' | epsg: ", epsg, " ' +
                               ' | proj4: ", proj4string, " ' +
                               ' | zoom: ' + map.getZoom() + ' '")
      } else {
        txt_detailed <- paste0("
                                'Longitude: ' + (e.latlng.lng).toFixed(5) +
                                ' | Latitude: ' + ((e.latlng.lat).toFixed(5)) + 
                               ' (Decimal Degrees)'")
      }
      
    
      txt_basic <- paste0("
                          'Longitude: ' + [0|(e.latlng.lng)] + 
                          '° ' + 
                          [0|((e.latlng.lng)<0?(e.latlng.lng)=-(e.latlng.lng):(e.latlng.lng))%1*60] +
                          ' ' + 
                          ((e.latlng.lng)*60%1*60).toFixed(3) +
                          ' E' +
                          
                          ' | Latitude: ' + [0|(e.latlng.lat)] + 
                          '° ' + 
                          [0|((e.latlng.lat)<0?(e.latlng.lat)=-(e.latlng.lat):(e.latlng.lat))%1*60] +
                          ' ' + 
                          ((e.latlng.lat)*60%1*60).toFixed(3) +
                          ' S (Degrees Minutes Seconds) '")
      
      map$dependencies = c(
        map$dependencies,
        clipboardDependency()
      )
      
      map <- htmlwidgets::onRender(
        map,
        paste0(
          "
          function(el, x, data) {
          // get the leaflet map
          var map = this; //HTMLWidgets.find('#' + el.id);
          // we need a new div element because we have to handle
          // the mouseover output separately
          // debugger;
          function addElement () {
          // generate new div Element
          var newDiv = $(document.createElement('div'));
          // append at end of leaflet htmlwidget container
          $(el).append(newDiv);
          //provide ID and style
          newDiv.addClass('lnlt');
          newDiv.css({
          'position': 'relative',
          'bottomleft':  '0px',
          'background-color': 'rgba(255, 255, 255, 0.7)',
          'box-shadow': '0 0 2px #bbb',
          'background-clip': 'padding-box',
          'margin': '0',
          'padding-left': '5px',
          'color': '#333',
          'font': '9px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif',
          'z-index': '700',
          });
          return newDiv;
          }
          // check for already existing lnlt class to not duplicate
          var lnlt = $(el).find('.lnlt');
          if(!lnlt.length) {
          lnlt = addElement();
          // grab the special div we generated in the beginning
          // and put the mousmove output there
          map.on('mousemove', function (e) {
          if (e.originalEvent.ctrlKey) {
          if (document.querySelector('.lnlt') === null) lnlt = addElement();
          lnlt.text(", txt_detailed, ");
          } else {
          if (document.querySelector('.lnlt') === null) lnlt = addElement();
          lnlt.text(", txt_basic, ");
          }
          });
          // remove the lnlt div when mouse leaves map
          map.on('mouseout', function (e) {
          var strip = document.querySelector('.lnlt');
          if( strip !==null) strip.remove();
          });
          };
          //$(el).keypress(67, function(e) {
          map.on('preclick', function(e) {
          if (e.originalEvent.ctrlKey) {
          if (document.querySelector('.lnlt') === null) lnlt = addElement();
          lnlt.text(", txt_basic, ");
          var txt = document.querySelector('.lnlt').textContent;
          console.log(txt);
          //txt.innerText.focus();
          //txt.select();
          setClipboardText('\"' + txt + '\"');
          }
          });
          }
          "
        )
      )
      map
    }