Highmaps의 세계지도에서 두 마커 사이에 작은 대칭 호선 경로를 그립니다.

금연 건강 증진 협회

저는 Highmaps를 사용하여 데모 단순 비행 경로 ( jsfiddle )를 시작점으로 사용하여 비행 경로 차트를 만들고 있습니다 . 세계지도를 사용하도록 코드를 업데이트하면 위치 / 마커 사이의 선 경로가 과장된 곡선으로 왜곡됩니다.

데모에서 다음으로 만 수정 한 jsfiddle을 참조하십시오 .

HTML

<!-- line 5 -->
<script src="https://code.highcharts.com/mapdata/custom/world.js"></script>

자바 스크립트

// line 39
mapData: Highcharts.maps['custom/world'],
// line 47
data: Highcharts.geojson(Highcharts.maps['custom/world'], 'mapline'),

어디에 둘 사이의 차이를보기 전에 Highcharts 데모를하고 내 몇 가지 변화입니다 위에서 언급 한 바와 같이를 :

지도선 경로 비교

The paths are calculated through function pointsToPath which uses quadratic Bézier curve Q in SVG to curve the line drawn between markers.

// Function to return an SVG path between two points, with an arc
function pointsToPath(from, to, invertArc) {
    var arcPointX = (from.x + to.x) / (invertArc ? 2.4 : 1.6),
        arcPointY = (from.y + to.y) / (invertArc ? 2.4 : 1.6);
    return 'M' + from.x + ',' + from.y + 'Q' + arcPointX + ' ' + arcPointY +
            ',' + to.x + ' ' + to.y;
}

If I modify the function to always divide by 2 for arc points x and y then I get a straight line between markers:

var arcPointX = (from.x + to.x) / 2,
    arcPointY = (from.y + to.y) / 2;

I'm not sure of the math to get a smaller, less exaggerated curve.

Ideally I would like for the line to be symmetrical as per an example from MDN - Paths:

그리드가있는 2 차 베 지어

<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M 10 80 Q 95 10 180 80" stroke="black" fill="transparent"/>
</svg>

Using world map data, how do I calculate line paths between markers to appear with a smaller or symmetrical curve?

Alex L

You just need to get your denominator closer to 2.0 as when it is 2.0 is a perfectly straight line: https://jsfiddle.net/my7bx50p/1/

So I chose 2.03 and 1.97 and that gives you much "softer" curves. Hope that helps.

function pointsToPath(from, to, invertArc) {
    var arcPointX = (from.x + to.x) / (invertArc ? 2.03 : 1.97),
        arcPointY = (from.y + to.y) / (invertArc ? 2.03 : 1.97);
    return 'M' + from.x + ' ' + from.y + 'Q' + arcPointX + ' ' + arcPointY + ' ' + to.x + ' ' + to.y;
}

여기에 이미지 설명 입력

UPDATE:

I tried to focus in only on the math: https://jsfiddle.net/9gkvhfuL/1/

I think the math is now correct:

여기에 이미지 설명 입력

Which back to the real example: https://jsfiddle.net/my7bx50p/6/

Gives, I believe, the desired outcome :):

여기에 이미지 설명 입력

From the code (https://jsfiddle.net/my7bx50p/6/):

  function pointsToPath(from, to, invertArc) {
var centerPoint = [ (from.x + to.x) / 2, (from.y + to.y) / 2];
var slope = (to.x - from.x) / (to.y - from.y);
var invSlope = -1 / slope;
var distance = Math.sqrt( Math.pow((to.x - from.x), 2) + Math.pow((to.y - from.y), 2) );

if (Math.abs(slope) > Math.abs(invSlope) ){
  //then we should offset in the y direction
  var offset = (invertArc ? -1 : 1) * 2 * Math.sqrt(distance);
  var min_slope = Math.min( Math.abs(slope), Math.abs(invSlope) );
  var final_slope = Math.max(min_slope, 1);
  var offsetCenter = [centerPoint[0] + (offset * (1/slope)), centerPoint[1] + offset];
  //console.log(centerPoint, slope, invSlope, distance);
  var arcPointX = offsetCenter[0], //(from.x + to.x) / (invertArc ? 2.03 : 1.97),
      arcPointY = offsetCenter[1] //(from.y + to.y) / (invertArc ? 2.03 : 1.97);
} else{ //invSlope <= slope
  //then we should offset in the x direction
  var offset = (invertArc ? -1 : 1) * 2 * Math.sqrt(distance);
  var min_slope = Math.min( Math.abs(slope), Math.abs(invSlope) );
  var final_slope = Math.max(min_slope, 1);
  var offsetCenter = [centerPoint[0] + offset, centerPoint[1] + (offset * (1/invSlope))];
  //console.log(centerPoint, slope, invSlope, distance);
  var arcPointX = offsetCenter[0], //(from.x + to.x) / (invertArc ? 2.03 : 1.97),
      arcPointY = offsetCenter[1] //(from.y + to.y) / (invertArc ? 2.03 : 1.97);
}   
return 'M' + from.x + ' ' + from.y + 'Q' + arcPointX + ' ' + arcPointY +
        ' ' + to.x + ' ' + to.y;
}

UPDATE 2: (to try to explain the math and clean up the code)

Check out the Math Fiddle: https://jsfiddle.net/alexander_L/dcormfxy/53/

여기에 이미지 설명 입력

The solid black line between the two points is the straight line between them and it also has the corresponding slope (used in the code later). I also drew a centre point on each line. Then I drew the inverse slope as a dotted line (also used in the code) The inverse slope is by definition perpendicular to the slope and is related by invSlope = -1/slope. From this, we are now set up to find the perpendicular points to the left or right of the centre point, that will become the centre of our symmetrical arcs. We do this by first figuring out if the slope is greater than the inverse slope or if the inverse slope is greater than the slope (absolute values). This is only necessary because when we have a perfectly horizontal or perfectly vertical line then the slope is zero and undefined respectively and then our math doesn't work. (remember slope = (y2 - y1)/(x2 - x1) so when the line is vertical y-changes but x-doesn't so x2 = x1 and then the denominator is zero and gives us undefined slope)

Let's think about line C from : {x: 40, y: 40}, to : {x: 220, y: 40}

slope = (y2 - y1)/(x2 - x1)

slope = (40 - 40)/(220 - 40)

slope = 0 / 180

slope = 0

invSlope = -1/slope

invSlope = undefined

This is why we need to have the two cases (the if else) in the code as whenever we get slope or invSlope as undefined the math is not going to work. So now, although slope is zero, it is greater than invSlope (undefined). (note SVGs are upside down compared to normal charts and how we think about them so it requires your brain to keep that in mind, otherwise, it's easy to get lost)

So now we can offset the centre point in the y-direction and then figure out how much we have to offset in the x-direction. If you had a line with slope 1, then you would offset the same in the x and the y-directions because the slope of the line is 1 (line makes a 45-degree angle with the x-axis) and so moving perpendicularly away from that line is achieved just by moving for example 5 in the x-direction and -5 in the y-direction.

Luckily, with this edge case (slope = 0), then we just move in the y-direction and the x-direction offset = 0. Look at line C in the math example and you can see what I mean, to move perpendicularly, we just move either positive or negative y-direction from the centre point. From the code:

offsetCenter = [centerPoint[0] + (offset * (1/slope)), centerPoint[1] + offset];

So as I said, we offset in the y-direction from the centerPoint and the term + (offset * (1/slope)) will be zero here because 1/slope is undefined. We can chose to offset "left" or "right" by the function's argument invertArc which is used in this line: var offset = (invertArc ? -1 : 1) * 2 * Math.sqrt(distance); which basically means move postive or negative direction away from the centerPoint in a magnitude equal to two times the square root of the distance between the points. I settled on two times the square root of the distance between the points because this gives us an offsetCenter of our arc that gives similarly soft curves for all lines both short and long.

Now, let's think about line A from : {x: 40, y: 40}, to : {x: 320, y: 360}

slope = (y2 - y1)/(x2 - x1)

slope = (360 - 40)/(320 - 40)

slope = 320 / 280

slope = 1.143

invSlope = -1/slope

invSlope = -0.875

Final cleaned up code and real example here https://jsfiddle.net/alexander_L/o43ka9u5/4/:

  function pointsToPath(from, to, invertArc) {
  var centerPoint = [ (from.x + to.x) / 2, (from.y + to.y) / 2];
  var slope = (to.x - from.x) / (to.y - from.y);
  var invSlope = -1 / slope;
  var distance = Math.sqrt( Math.pow((to.x - from.x), 2) + Math.pow((to.y - from.y), 2) );
  var arcPointX = 0;
  var arcPointY = 0;
  var offset = 0;
  var offsetCenter = 0;

  if (Math.abs(slope) > Math.abs(invSlope) ){
    //then we should offset in the y direction (then calc. x-offset)
    offset = (invertArc ? -1 : 1) * 2 * Math.sqrt(distance);

    offsetCenter = [centerPoint[0] + (offset * (1/slope)), centerPoint[1] + offset];

    arcPointX = offsetCenter[0] 
    arcPointY = offsetCenter[1]
  } else{ //invSlope >= slope
    //then we should offset in the x direction (then calc. y-offset)
    offset = (invertArc ? -1 : 1) * 2 * Math.sqrt(distance);
    offsetCenter = [centerPoint[0] + offset, centerPoint[1] + (offset * (1/invSlope))];

    arcPointX = offsetCenter[0] 
    arcPointY = offsetCenter[1] 
  }   
  return 'M' + from.x + ' ' + from.y + 'Q' + arcPointX + ' ' + arcPointY +
          ' ' + to.x + ' ' + to.y;
  }

UPDATE 3:

I figured out how to remove the need for the if else control flow / switch statement by using trigonometry instead. I hope my sketch helps explain the logic, you might also want to read some stuff (https://study.com/academy/lesson/sohcahtoa-definition-example-problems-quiz.html) etc. as I would struggle to explain briefly here (and I am already writing an essay here :) so will not explain SOH CAH TOA etc.)

여기에 이미지 설명 입력

This makes the core function code like this (Math only - https://jsfiddle.net/alexander_L/dcormfxy/107/) (full example - https://jsfiddle.net/alexander_L/o43ka9u5/6/):

function pointsToPath(from, to, invertArc) {
  const centerPoint = [ (from.x + to.x) / 2, (from.y + to.y) / 2];
  const slope = (to.y - from.y) / (to.x - from.x);
  const invSlope = -1 / slope;
  const distance = Math.sqrt( Math.pow((to.x - from.x), 2) + Math.pow((to.y - from.y), 2) );
  const offset = (invertArc ? -1 : 1) * 2 * Math.sqrt(distance);

  const angle = Math.atan(slope);
  //Math.cos(angle) = offsetY/offset;
  //Math.sin(angle) = offsetX/offset;
  const offsetY = Math.cos(angle)*offset;
  const offsetX = Math.sin(angle)*offset;
  //if slope = 0 then effectively only offset y-direction
  const offsetCenter = [centerPoint[0] - offsetX, centerPoint[1] + offsetY];
  const arcPointX = offsetCenter[0]
  const arcPointY = offsetCenter[1]   
  return 'M' + from.x + ' ' + from.y + 'Q' + arcPointX + ' ' + arcPointY +
          ' ' + to.x + ' ' + to.y;
 }

이 코드가 더 우아하고 깨끗하며 더 강력하고 수학적으로 유효하다고 생각합니다. :) Const & Let use와 var에 대한 팁에 대해 Ash도 감사드립니다.

이것은 또한 최종 결과를 제공합니다.

여기에 이미지 설명 입력

여기에 이미지 설명 입력

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

분류에서Dev

Google지도에서 두 마커 사이의 경로를 표시하는 방법

분류에서Dev

Highcharts : 같은 지점에 대한 두 개의 마커 기호

분류에서Dev

Google Maps API에서 두 마커 사이에 경로를 그리는 방법은 무엇입니까?

분류에서Dev

Powershell을 사용하여 애플리케이션 풀에 대한 프로세서 선호도 마스크를 계산하는 방법은 무엇입니까?

분류에서Dev

지도에서 두 마커 사이를 확대하는 방법

분류에서Dev

두 개의 선이 대칭 경우 계산

분류에서Dev

videojs에서 두 마커 사이에 선을 그리는 방법

분류에서Dev

Google Maps API를 사용하여 테이블에서 주소를 가져오고지도에 마커를 표시하지만 마커의 호버 제목 또는 infoWindow (onclick)가 작동하지 않습니다.

분류에서Dev

Android Google지도는 두 지점 사이에 경로를 그립니다.

분류에서Dev

파일에서 두 마커 사이의 텍스트를 다른 파일의 텍스트 섹션으로 바꾸는 방법은 무엇입니까?

분류에서Dev

파일에서 두 마커 사이의 텍스트를 다른 파일의 텍스트 섹션으로 바꾸는 방법은 무엇입니까?

분류에서Dev

탭한 상태와 선택하지 않은 상태에 대한 Google지도의 다른 마커 이미지

분류에서Dev

한 플롯이 선형이고 다른 플롯이 로그인 경우 틱 간격은 이중 Y 축 플롯의 두 y 축에 대한 로그입니다.

분류에서Dev

Java 8에서 두 스트림 간의 대칭 차이를 얻는 방법은 무엇입니까?

분류에서Dev

(파이썬에서) 두 문자열 사이의 대칭 차이를 얻는 가장 효과적인 방법은 무엇입니까?

분류에서Dev

마커에 대한 경계 맞춤이 다중 마커에 대한 다음 클릭에 대해 제대로 작동하지 않음

분류에서Dev

자바 스크립트에서 동적으로 높이 속성을 변경하는 것은 두 번의 클릭 후에 작동하지 않습니다. 클릭 할 때마다 계속 토글하고 싶습니다.

분류에서Dev

Three.js에서 두 점 사이에 타원 호를 그립니다.

분류에서Dev

pathlib를 사용하여 Python에서 두 개의 절대 경로 사이의 상대 경로를 얻는 방법은 무엇입니까?

분류에서Dev

두 데이터 레이어의 지오 마커를 선으로 연결

분류에서Dev

선택한 두 IP에서만 http 액세스를 허용하고 다른 로그인 시도에 대한 경고를받습니다.

분류에서Dev

지도에서 다른 마커로부터의 거리를 계산 한 다음 적어도 하나를 선택하는 방법

분류에서Dev

두 지점 사이의 각 교차점에 마커를 놓고 좌표를 얻습니다.

분류에서Dev

laravel5.2를 사용하여 Google지도의 마커 사이에 선을 그리는 방법

분류에서Dev

각 비동기 호출 및 약속에 대한 특정 작업은 모두를 기다립니다.

분류에서Dev

마커 목록의 다른 마커에 특정 마커 표시를 설정하는 방법은 Angular Google지도를 사용합니다.

분류에서Dev

GMSMapView를 사용하여 두 위치 사이의 최단 경로를 찾고 iOS에서 지그재그 경로를 그리는 방법은 무엇입니까?

분류에서Dev

두 마커 사이의 KML 파일에서 미리 정의 된 경로에 폴리 라인을 그리시겠습니까?

분류에서Dev

시계의 자세와 네게 지 모두에서 신호를 설정하는 방법은 무엇입니까?

Related 관련 기사

  1. 1

    Google지도에서 두 마커 사이의 경로를 표시하는 방법

  2. 2

    Highcharts : 같은 지점에 대한 두 개의 마커 기호

  3. 3

    Google Maps API에서 두 마커 사이에 경로를 그리는 방법은 무엇입니까?

  4. 4

    Powershell을 사용하여 애플리케이션 풀에 대한 프로세서 선호도 마스크를 계산하는 방법은 무엇입니까?

  5. 5

    지도에서 두 마커 사이를 확대하는 방법

  6. 6

    두 개의 선이 대칭 경우 계산

  7. 7

    videojs에서 두 마커 사이에 선을 그리는 방법

  8. 8

    Google Maps API를 사용하여 테이블에서 주소를 가져오고지도에 마커를 표시하지만 마커의 호버 제목 또는 infoWindow (onclick)가 작동하지 않습니다.

  9. 9

    Android Google지도는 두 지점 사이에 경로를 그립니다.

  10. 10

    파일에서 두 마커 사이의 텍스트를 다른 파일의 텍스트 섹션으로 바꾸는 방법은 무엇입니까?

  11. 11

    파일에서 두 마커 사이의 텍스트를 다른 파일의 텍스트 섹션으로 바꾸는 방법은 무엇입니까?

  12. 12

    탭한 상태와 선택하지 않은 상태에 대한 Google지도의 다른 마커 이미지

  13. 13

    한 플롯이 선형이고 다른 플롯이 로그인 경우 틱 간격은 이중 Y 축 플롯의 두 y 축에 대한 로그입니다.

  14. 14

    Java 8에서 두 스트림 간의 대칭 차이를 얻는 방법은 무엇입니까?

  15. 15

    (파이썬에서) 두 문자열 사이의 대칭 차이를 얻는 가장 효과적인 방법은 무엇입니까?

  16. 16

    마커에 대한 경계 맞춤이 다중 마커에 대한 다음 클릭에 대해 제대로 작동하지 않음

  17. 17

    자바 스크립트에서 동적으로 높이 속성을 변경하는 것은 두 번의 클릭 후에 작동하지 않습니다. 클릭 할 때마다 계속 토글하고 싶습니다.

  18. 18

    Three.js에서 두 점 사이에 타원 호를 그립니다.

  19. 19

    pathlib를 사용하여 Python에서 두 개의 절대 경로 사이의 상대 경로를 얻는 방법은 무엇입니까?

  20. 20

    두 데이터 레이어의 지오 마커를 선으로 연결

  21. 21

    선택한 두 IP에서만 http 액세스를 허용하고 다른 로그인 시도에 대한 경고를받습니다.

  22. 22

    지도에서 다른 마커로부터의 거리를 계산 한 다음 적어도 하나를 선택하는 방법

  23. 23

    두 지점 사이의 각 교차점에 마커를 놓고 좌표를 얻습니다.

  24. 24

    laravel5.2를 사용하여 Google지도의 마커 사이에 선을 그리는 방법

  25. 25

    각 비동기 호출 및 약속에 대한 특정 작업은 모두를 기다립니다.

  26. 26

    마커 목록의 다른 마커에 특정 마커 표시를 설정하는 방법은 Angular Google지도를 사용합니다.

  27. 27

    GMSMapView를 사용하여 두 위치 사이의 최단 경로를 찾고 iOS에서 지그재그 경로를 그리는 방법은 무엇입니까?

  28. 28

    두 마커 사이의 KML 파일에서 미리 정의 된 경로에 폴리 라인을 그리시겠습니까?

  29. 29

    시계의 자세와 네게 지 모두에서 신호를 설정하는 방법은 무엇입니까?

뜨겁다태그

보관