1

I'm trying to create a circle with a specific on-the-ground radius in OpenLayers v5. But I do not seem to understand how to create it with a specific unit of measure such as meters, miles, etc. The radius argument in the constructor appears to be some non-specified map unit, as noted in this issue, which is concluded with using the getMetersPerUnit method from the projection. However, everytime this method is called, it always seems to return 1. I've also looked at doing something with the METERS_PER_UNIT export in the projection library, but again, the value for meters is 1.

My drawing tries to give a good starting point that's based on the current view's extent. The previous work used 1/8th the lateral distance of the current view.

function getRoundedEigthedDistanceBetweenPoints(coordinateA, coordinateB) {
  /**
   * Distance between bottom right and left corners in meters
   * We want 1/8 of the distance, rounded to nearest 10?
   * Why? /shrug ‍♂️
   */
  return (getDistanceBetweenPoints(coordinateA, coordinateB) / 8 / 10) * 10;
}

/**
 * @link https://stackoverflow.com/a/28454386/168005
 * @param coordA
 * @param coordB
 */
function getDistanceBetweenPoints(coordA, coordB) {
  return (new LineString([coordA, coordB]).getLength() * 100) / 100;
}

const [minX, _, maxX, __] = map.getView().calculateExtent();

// Since all I want is a straight line, I just give it the minX and maxX values, and zero out the "Y"s. 
const startingDistance = getRoundedEigthedDistanceBetweenPoints([minX, 0], [maxX, 0]);

Given the above, I was presuming the result of this startingDistance was in meters. But if I compare my circles's values to a normal LineString length, the numbers are off quite a bit.

The problem gets worse when I try to change the radius later from user input, because I was thinking I was working in meters (clearly that's not true), so I convert the values provided down to meters and set it directly as the radius.

enum Units {
  Meters = "m",
  Miles = "mi",
  Kilometers = "km",
  Feet = "ft",
}

const METERS_PER_FEET = 0.3048;
const FEET_PER_MILE = 5280;
const METERS_PER_KILO = 1000;

function convertFeetToMeters(feet) {
  return feet * METERS_PER_FEET;
}

function convertMilesToMeters(miles) {
  return miles * METERS_PER_MILE;
}

function convertMetersToKilometers(meters) {
  return meters / METERS_PER_KILO;
}

function convertUnitToMeters(unit: Units, value: number) {
  switch (unit) {
    case Units.Feet:
      return convertFeetToMeters(value);
    case Units.Miles:
      return convertMilesToMeters(value);
    case Units.Kilometers:
      return convertKilometersToMeters(value);
    default:
      return value;
  }
}

function updateFeature({ type, feature, unit, value }) {
  /**
   * Mutably make the change
   */
  if (type === "radius" || type === "unit") {
    // Take in the units listed in the UI and convert the value, respectively.
    const computedRadius = convertUnitToMeters(unit.value, parseFloat(value));
    feature.getGeometry().setRadius(computedRadius);
  }
}

I know there's this whole Web Mercator distortion business, and using something like getLength from the ol/sphere module will convert the radius out of an already created circle, but what about setting the radius of a circle? Is there a tool available for that?

donjuedo
  • 2,475
  • 18
  • 28
jktravis
  • 1,427
  • 4
  • 20
  • 36
  • 1
    Web merator projection units are meters but the scale is true only at the equator. Elsewhere you need to adjust using ol/proj.getPointResolution – Mike Sep 23 '19 at 18:05
  • @Mike. Thanks. But, what's the relationship to the result from this function? Do I multiply, divide it by my value? Vice/versa? And, do I get the resolution from the map's View? – jktravis Sep 23 '19 at 18:35
  • 1
    Resolution doesn't matter if you only need the ratio for example `ol.proj.getPointResolution('EPSG:3857', 1, ol.proj.fromLonLat([0, 0])));` returns 1 at the equator and `ol.proj.getPointResolution('EPSG:3857', 1, ol.proj.fromLonLat([0, 60])));` return 0.5 at 60 north. Multiply to convert map units to real distance on the ground, divide to convert distance on the ground to the size needed on a map. – Mike Sep 23 '19 at 18:54
  • @Mike. Thank you very much. This was very helpful! – jktravis Sep 23 '19 at 20:13

1 Answers1

0

If then radius is small like 100km -1000km then there is may be projection issue. I solved it by dividing radius with

ol.proj.getPointResolution('EPSG:3857', 1, centerLongitudeLatitude)

where centerLongitudeLatitude is center of circle. Here iscode for displaying circle on map

geometry:new ol.geom.Circle(centerLongitudeLatitude,200*1000/ol.proj.getPointResolution('EPSG:3857', 1, centerLongitudeLatitude)),
Primit
  • 825
  • 7
  • 13