Eric G
Eric G

Reputation: 742

How can I have OpenLayers draw the shortest line between two coordinates

I am specifying the coordinates of

[-122, 47],   (in USA)
[147, -32]  (in Australia)

The image it produces is:

result image

I was under the false impression that OpenLayers would draw the shortest line between two coordinates.

What would I need to change so the line drawn is the shortest?

(In this case, the line would cross over the Pacific instead of Atlantic Ocean)

let coordinates = [
  [-122, 47],
  [147, -32]
];

const myFeature = new ol.Feature({
  geometry: new ol.geom.LineString(coordinates),
  name: "Line"
});

myFeature.setStyle(
  new ol.style.Style({
    stroke: new ol.style.Stroke({ color: "green", width: 8 })
  })
);

const vectorSource = new ol.source.Vector({
  features: [myFeature]
});

const vectorLayer = new ol.layer.Vector({
  source: vectorSource
});

const map = new ol.Map({
  target: "map",
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    }),
    vectorLayer
  ],
  view: new ol.View({
    projection: "EPSG:4326",
    center: [0, 0],
    zoom: 1
  })
});
#map {
  height: 450px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.1.0/ol.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.1.0/dist/ol.min.js"></script>

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

Upvotes: 0

Views: 451

Answers (1)

Mike
Mike

Reputation: 17962

Split the line at the antimeridian as in https://github.com/openlayers/openlayers/issues/11681#issuecomment-715583978

function splitAtDateLine(coords) {
  const lineStrings = [];
  let lastX = Infinity
  let lineString;
  for (let i = 0, ii = coords.length; i < ii; ++i) {
    const coord = coords[i];
    const x = coord[0];
    if (Math.abs(lastX - x) > 180) { // Crossing date line will be shorter
      if (lineString) {
        const prevCoord = coords[i - 1];
        const w1 = 180 - Math.abs(lastX);
        const w2 = 180 - Math.abs(x);
        const y = (w1 / (w1 + w2)) * (coord[1] - prevCoord[1]) + prevCoord[1];
        if (Math.abs(lastX) !== 180) {
          lineString.push([lastX > 0 ? 180 : -180, y]);
        }
        lineStrings.push(lineString = []);
        if (Math.abs(x) !== 180) {
          lineString.push([x > 0 ? 180 : -180, y]);
        }
      } else {
        lineStrings.push(lineString = []);
      }
    }
    lastX = x;
    lineString.push(coord);
  }
  return lineStrings;
}


let coordinates = [
  [-122, 47],
  [147, -32]
];

const myFeature = new ol.Feature({
  geometry: new ol.geom.MultiLineString(splitAtDateLine(coordinates)),
  name: "Line"
});

myFeature.setStyle(
  new ol.style.Style({
    stroke: new ol.style.Stroke({ color: "green", width: 8 })
  })
);

const vectorSource = new ol.source.Vector({
  features: [myFeature]
});

const vectorLayer = new ol.layer.Vector({
  source: vectorSource
});

const map = new ol.Map({
  target: "map",
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    }),
    vectorLayer
  ],
  view: new ol.View({
    projection: "EPSG:4326",
    center: [0, 0],
    zoom: 1
  })
});
#map {
  height: 450px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.1.0/ol.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.1.0/dist/ol.min.js"></script>

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

This will produce what looks like the shortest route on a flat map. In reality on a round planet the shortest route is a great circle route as in https://openlayers.org/en/latest/examples/flight-animation.html

Upvotes: 1

Related Questions