Getting resolution (for setting min/max resolution) in OpenLayers from QGIS scale

openlayersqgisscale

I have designed a map in QGIS that I'm putting into OpenLayers. In QGIS they use scale, where it's just a ratio of real world units to map units. In OpenLayers, however, they use resolution.

I have read this post on GIS SE, but I'm not that advanced, and I couldn't really follow along as I'm not sure what DPI, or resolution for that matter, is. The accepted answer said that resolution is meters per pixel, but what is a pixel? Is it a CSS pixel or screen resolution? If it was based on screen resolution, wouldn't min/max resolution be dependent on user screen resolution? Either way, how do I take a specific map scale and find the corresponding OpenLayers resolution?

Best Answer

I do not propose this as a solution to the problem, just practical solution I cooked up a few years ago. Solution is some kind of a hack based on displayed ol.control.ScaleLine control. So the solution to work, it needs active OL map with scale line control displayed. Don't ask me about theory behind it, I forgot where I picked all the pieces to put it together.

This is the JS code:

var scaleLineControl = new ol.control.ScaleLine({
  dpi: 110
});

function screenDPI() {
  for (var dpi = 56; dpi < 2600; dpi++) {
    if (matchMedia('(max-resolution: ' + dpi + 'dpi)').matches) {
      return(dpi);
    }
  }
  return(dpi);
}

function getMapScale() {
  var scaleElement, scaleWidth, scaleContent, scaleValue, dpi, refDPI, refPixSize, pixSize, scaleSize, scale, roundFactor;
  
  scaleElement = Util.getElementByClass('ol-scale-line-inner');
  scaleWidth = scaleElement.clientWidth;
  scaleContent = Util.getElementHTML(scaleElement);
  scaleValue = parseInt(scaleContent);
  if (scaleContent.indexOf('km') >= 0) {
    scaleValue *= 1000;
  }
  dpi = screenDPI();
  refDPI = 110;
  refPixSize = 0.2072;
  pixSize = (dpi / refDPI) * refPixSize;
  scaleSize = pixSize * scaleWidth;
  scale = (scaleValue / scaleSize) * 1000;
  
  roundFactor = Math.pow(10, (Math.floor(Math.log10(scale)) - 2));
  scale = Math.round(scale / roundFactor) * roundFactor;
  
  return(scale);
}