Forum Discussion
welcomenxj
Aug 26, 2020Copper Contributor
Azure maps: Trying to draw direct line between two point at two corners of world
Am trying to plot direct line between "green arrow location" (right top corner) to "destination popup point". But it's moving all around world as shown in picture. Any suggestion? Please find image for reference.
Other lines whose points are not on world edges are working fine.
Managed to reproduce the issue and after talking with one of our architects I was reminded that the GeoJSON specification does not allow shapes to cross the antemeridian. I'm not a fan of this, luckily there are a few ways around this:
Options 1:
-
Similar to above, but break the line into segments to minimize complexity and shift longitude values outside of the -180/180 range as needed. Then create a MultiLineString from the segments.
-
You end up with a line with coordinates outside of the standard range but should render any line back and forth over the antemeridian as needed.
Option 2:
-
Leverage a library to split the line segments on the antemeridian and create a MultiLineString.
-
This method is a lot more work and and might not be as accurate, but should also work with any line.
Here is a code sample showing all two options:
<!DOCTYPE html> <html lang="en"> <head> <title></title> <meta charset="utf-8" /> <meta http-equiv="x-ua-compatible" content="IE=Edge" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <!-- Add references to the Azure Maps Map control JavaScript and CSS files. --> <link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css" type="text/css" /> <script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.js"></script> <!-- Load turf.js a spatial math library. https://turfjs.org/ --> <script src='https://npmcdn.com/@turf/turf/turf.min.js'></script> <script type='text/javascript'> var map, datasource; var data = { "type": "Feature", "properties": {}, "geometry": { "type": "LineString", "coordinates": [ [ 144.9586, -37.848381 ], [ -118.1948, 33.767109 ] ] } }; function GetMap() { //Initialize a map instance. map = new atlas.Map('myMap', { view: 'Auto', //Add your Azure Maps subscription client ID to the map SDK. Get an Azure Maps client ID at https://azure.com/maps authOptions: { authType: 'subscriptionKey', subscriptionKey: '<Your Azure Maps Key>' } }); //Wait until the map resources are ready. map.events.add('ready', function () { //Create a data source to add your data to. datasource = new atlas.source.DataSource(); map.sources.add(datasource); //Add a layer for rendering data. map.layers.add(new atlas.layer.LineLayer(datasource)); map.setCamera({ bounds: atlas.data.BoundingBox.fromData(data) }); }); } function option1 () { //Trick map to render one of the points on an adjacent map. Doing a deep copy of the coordinates to be safe since reusing in sample. var c = data.geometry.coordinates.map(function(arr) { return arr.slice(); }); var lastCoord = c[0]; var newCoords = []; var newCoord; for(var i = 1, len = c.length; i < len; i++){ let diff = lastCoord[0] - c[i][0]; if(diff > 180){ //Line going left to right, but should go opposite direction. newCoord = [c[i][0] + 360, c[i][1]]; } else if (diff < -180) { //Line going right to left, but should go opposite direction. newCoord = [c[i][0] - 360, c[i][1]]; } else { //Leave coord as is. newCoord = c[i]; } newCoords.push([lastCoord, newCoord]); lastCoord = c[i]; } datasource.setShapes(new atlas.data.MultiLineString(newCoords)); } function option2() { //Split the line into segments and split each segment on the antimeridian, then create a MultuLineString from the segments. var c = data.geometry.coordinates; var newCoords = []; for(var i = 1, len = c.length; i < len; i++){ newCoords = newCoords.concat(splitLineSegment(c[i - 1], c[i])); } datasource.setShapes(new atlas.data.MultiLineString(newCoords)); } function splitLineSegment(fromPos, toPos) { var path; //Check to see if path should cross anti-meridian. if (Math.abs(fromPos[0] - toPos[0]) > 180 && Math.abs(fromPos[0]) > 90) { var l1, l2; var rightMostPos = toPos; var leftMostPos = fromPos; if (fromPos[0] > toPos[0]) { rightMostPos = fromPos; leftMostPos = toPos; } //Wrap the left most coordinate to a value between 180 and 540, then split on anti-meridian. var fc = turf.lineSplit( //Line to split. turf.lineString([rightMostPos, [leftMostPos[0] + 360, leftMostPos[1]]]), //Line to split with. turf.lineString([[180, 90], [180, -90]])); //Get the first line segment. var seg1 = fc.features[0].geometry.coordinates; shiftPositions(seg1[0], seg1[1]); //Its possible that there is a shared coordinate in the two line segments, do a deep copy of the second segment cooridnates to be safe. var seg2 = fc.features[1].geometry.coordinates.map(function(arr) { return arr.slice(); }); shiftPositions(seg2[0], seg2[1]); return [ //Segment 1 seg1, //Segment 2 seg2 ]; } else { //Only a single line segment. return [[fromPos, toPos]]; } } function shiftPositions(p1, p2) { //If either position has a longitude value outside of the -180 to 180 range, shift the longitude value of both positions as needed. if(p1[0] > 180 || p2[0] > 180){ p1[0] -= 360; p2[0] -= 360; } else if(p1[0] < -180 || p2[0] < -180){ p1[0] += 360; p2[0] += 360; } } </script> </head> <body onload="GetMap()"> <div id="myMap" style="position:relative;width:100%;height:600px;"></div> <input type="button" onclick="option1()" value="Option 1"/> <input type="button" onclick="option2()" value="Option 2"/> </body> </html>
-
- welcomenxjCopper Contributor
I believe, i understand the issue now but still don't have solution for it. I have added 2 images 'third quadrant coordinates' and 'fourth quadrant coordinates' for your reference. See the 'position' coords in console and how they are varying when popup point is clicked, depending on its position in map.
Why it is not returning the same co-ordinates irrespective of map position? I think answer to this will help to fix this issue- rbrundritt
Microsoft
welcomenxj Additional details and comments here: https://stackoverflow.com/questions/63596623/azure-maps-trying-to-draw-direct-line-between-two-point-at-two-corners-of-world
Does the last comment help?
- welcomenxjCopper Contributor
rbrundritt thanks for the response Ricky. With your last response, am able to understand what SHOULD be happening under the hood. But I don't understand why its not happening for me when i try to cross pacific with these line coords from north america to melbourne line co-ords [{Lng: -118.1948, Lat: 33.767109}, {Lng: 144.9586, Lat: -37.848381}]? Is there any Map configuration which i need to enable to draw direct line between these two points crossing the pacific?