почему рисование круга в северном полушарии приводит к овалу?

#javascript #openlayers

#javascript #openlayers

Вопрос:

Я знаю, что, вероятно, есть какая-то географическая / математическая / проекционная причина, но почему мой круг приводит к овальному многоугольнику? я пытаюсь нарисовать круг из точки.

код

https://jsfiddle.net/mdecker84/1embLyhj/9/

Когда моя точка имеет координаты: [0, 0] или в южном полушарии, я получаю идеальный круг. Я не знаю, что происходит.

введите описание изображения здесь

 var map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  view: new ol.View({
    center: ol.proj.fromLonLat([-71.076056, 42.388399], 'EPSG:3857'),
    zoom: 9
  })
});
var layer = new ol.layer.Vector({
  source: new ol.source.Vector({
    projection: 'EPSG:4326',
    features: []
  }),
});
map.addLayer(layer);
var vectorSource = layer.getSource();

function createCircle(circleCenterX, circleCenterY, circleRadius, pointsToEnd) {
  let angleToAdd = 360 / pointsToEnd;
  let coords = [];
  let angle = 0;
  for (let i = 0; i < pointsToEnd; i  ) {
    angle  = angleToAdd;
    let coordX = circleCenterX   circleRadius * Math.cos(angle * Math.PI / 180);
    let coordY = circleCenterY   circleRadius * Math.sin(angle * Math.PI / 180);
    coords.push([coordX, coordY]);
  }
  return coords;
}

function addMarker(coordinates) {
  console.log(coordinates);
  var marker = new ol.Feature(new ol.geom.Point(coordinates));
  marker.setStyle(new ol.style.Style({
    image: new ol.style.Circle({
      radius: 5,
      fill: new ol.style.Fill({
        color: 'red'
      })
    })
  }));
  vectorSource.addFeature(marker);
}

function addCircle(coords) {
  // var lonlat1 = ol.proj.transform([0, 0], 'EPSG:4326','EPSG:3857');
  // console.log('var lonlat1',lonlat1)
  var circleCoords = createCircle(coords[0], coords[1], 0.2, 180);
  console.log(circleCoords);
  var polygon = new ol.geom.Polygon([circleCoords]);
  polygon.transform('EPSG:4326', 'EPSG:3857');
  polygon = new ol.Feature(polygon);
  vectorSource.addFeature(polygon);
}

addCircle(ol.proj.toLonLat([-7912507.055205271, 5196764.057392394]));
addMarker([-7912507.055205271, 5196764.057392394]);  
 html,
body {
  height: 100%;
  width: 100%;
  padding: 0px;
  margin: 0px;
}

.map {
  height: 100%;
  width: 100%;
}  
 <html>

<head>
  <link href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css" rel="stylesheet" />
  <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
</head>

<body>
  <div id="map" class="map"></div>
</body>
</html>  

Комментарии:

1. У него должна быть та же проблема в южном полушарии.

Ответ №1:

Ваш код показывает в представлении EPSG: 3857, каким будет круг на экране в проекции EPSG: 4326, но на глобусе долготы приближаются по мере продвижения к полюсам. Для круга на экране используйте ol.geom.Circle . Но ни один из них не будет кругом на реальном земном шаре, для этого вам нужно ol.geom.Polygon.circular

Ответ №2:

Это связано с тем, что земля является шаром. Это действительно так. Это все еще круг, но изогнутый по оси z

Комментарии:

1. Так что нет никакого способа обойти это? На полюсах становится хуже @s.khetcho

2. поскольку вы используете EPSG 3857, это ожидаемое поведение, потому что вы центрируете на экваторе. Вот почему вы получаете круг на [0,0] и ухудшается при движении с севера на юг. используйте epsg.io это помогает визуализировать решение

Ответ №3:

Если вам нужно что-то, что выглядит круглым, вы можете использовать drawCircle функцию, которая учитывает кривизну земли (однако на самом деле это не будет круг на земле).

 
function drawCircle(point, radius, dir) {
  var d2r = Math.PI / 180; // degrees to radians 
  var r2d = 180 / Math.PI; // radians to degrees 
  var earthsradius = 6378137.0; // 6378137.0 is the radius of the earth in meters

  var points = 32;

  // find the raidus in lat/lon 
  var rlat = (radius / earthsradius) * r2d;
  var rlng = rlat / Math.cos(point[1] * d2r);


  var extp = new Array();
  if (dir == 1) {
    var start = 0;
    var end = points   1
  } // one extra here makes sure we connect the
  else {
    var start = points   1;
    var end = 0
  }
  for (var i = start;
    (dir == 1 ? i < end : i > end); i = i   dir) {
    var theta = Math.PI * (i / (points / 2));
    ey = point[0]   (rlng * Math.cos(theta)); // center a   radius x * cos(theta) 
    ex = point[1]   (rlat * Math.sin(theta)); // center b   radius y * sin(theta) 
    extp.push([ey, ex]);
  }
  return extp;
}

  

живой пример

скриншот результирующей карты

 var map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  view: new ol.View({
    center: ol.proj.fromLonLat([-71.076056, 42.388399], 'EPSG:3857'),
    zoom: 9
  })
});
var layer = new ol.layer.Vector({
  source: new ol.source.Vector({
    projection: 'EPSG:4326',
    features: []
  }),
});
map.addLayer(layer);
var vectorSource = layer.getSource();

function drawCircle(point, radius, dir) {
  var d2r = Math.PI / 180; // degrees to radians 
  var r2d = 180 / Math.PI; // radians to degrees 
  var earthsradius = 6378137.0; // 6378137.0 is the radius of the earth in meters

  var points = 32;

  // find the raidus in lat/lon 
  var rlat = (radius / earthsradius) * r2d;
  var rlng = rlat / Math.cos(point[1] * d2r);


  var extp = new Array();
  if (dir == 1) {
    var start = 0;
    var end = points   1
  } // one extra here makes sure we connect the
  else {
    var start = points   1;
    var end = 0
  }
  for (var i = start;
    (dir == 1 ? i < end : i > end); i = i   dir) {
    var theta = Math.PI * (i / (points / 2));
    ey = point[0]   (rlng * Math.cos(theta)); // center a   radius x * cos(theta) 
    ex = point[1]   (rlat * Math.sin(theta)); // center b   radius y * sin(theta) 
    extp.push([ey, ex]);
  }
  return extp;
}

function addMarker(coordinates) {
  console.log(coordinates);
  var marker = new ol.Feature(new ol.geom.Point(coordinates));
  marker.setStyle(new ol.style.Style({
    image: new ol.style.Circle({
      radius: 5,
      fill: new ol.style.Fill({
        color: 'red'
      })
    })
  }));
  vectorSource.addFeature(marker);
}

function addCircle(coords) {
  // var lonlat1 = ol.proj.transform([0, 0], 'EPSG:4326','EPSG:3857');
  // console.log('var lonlat1',lonlat1)
  var circleCoords = drawCircle(coords, 20000, 1);
  console.log(circleCoords);
  var polygon = new ol.geom.Polygon([circleCoords]);
  polygon.transform('EPSG:4326', 'EPSG:3857');
  polygon = new ol.Feature(polygon);
  vectorSource.addFeature(polygon);
}

addCircle(ol.proj.toLonLat([-7912507.055205271, 5196764.057392394]), 20000, 1);
addMarker([-7912507.055205271, 5196764.057392394]);  
 html,
body {
  height: 100%;
  width: 100%;
  padding: 0px;
  margin: 0px;
}

.map {
  height: 100%;
  width: 100%;
}  
 <html>

<head>
  <link href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/css/ol.css" rel="stylesheet" />
  <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.4.3/build/ol.js"></script>
</head>

<body>
  <div id="map" class="map"></div>
</body>

</html>