欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Openlayers實現(xiàn)角度測量的方法

 更新時間:2024年11月07日 11:52:47   作者:Jinuss  
在Openlayers中,雖然沒有直接的角度測量API,但可以通過自定義方法實現(xiàn),首先,選取三個頂點,利用這些點的坐標(biāo)計算夾角度數(shù),接著,用SVG或canvas繪制代表角度的圓弧,并通過Overlay添加到地圖上,本文給大家介紹Openlayers實現(xiàn)角度測量的方法,感興趣的朋友一起看看吧

概述

在前面介紹了如何在 Openlayers 中進行長度和面積的測量,可以參考:《Openlayers 實現(xiàn)長度測量》,《openlayers 實現(xiàn)面積測量》。

那么如何在 Openlayers 中進行角度的測量呢?很遺憾ol/sphere模塊中沒有提供對應(yīng)角度測量的 API 或方法,但是我們可以自己實現(xiàn)。

實踐

效果展示

角度量測繪制

結(jié)果

實現(xiàn)思路

實現(xiàn)思路主要有兩點:一是計算夾角的度數(shù);二則是夾角的圓弧表示,上圖中表示角度的圓弧,可以是 0 - 180° 中的任意一個角度,且它總是包裹在夾角內(nèi),連接夾角的兩邊。

夾角的度數(shù)計算

  • 確定夾角

數(shù)學(xué)常識可知,夾角度數(shù)的區(qū)間必定是[0°,180°],而且,夾角是由三個坐標(biāo)點的位置確定大小的,如上圖中的頂點A、B、C。因此,我們可以內(nèi)定頂點 ∠ABC是我們需要測量的夾角。

  • 確定頂點坐標(biāo)位置

可以在地圖上隨機取三個點作為頂點,但是這樣并不科學(xué),這樣無法滿足繪制特定角度的需求。選點還是通過ol/interaction模塊的Draw API 進行拾取。

geom.getCoordinates()返回的坐標(biāo)個數(shù)達到四個時,就調(diào)用this.draw.finishDrawing()方法結(jié)束繪制。因為它的返回值倒數(shù)的兩個坐標(biāo)都是一樣的,因此即使我們只需要三個點的坐標(biāo),也需要等到它的返回值長度是4。

實現(xiàn)代碼如下:

this.draw.on(
  "drawstart",
  (evt: { feature: Feature<Geometry>, coordinate: Coordinate }) => {
    const { feature, coordinate } = evt;
    this.listenGeometryChange = feature.getGeometry().on("change", (evt) => {
      const geom = evt.target;
      let startPoint = geom.getFirstCoordinate();
      this.addMarker({ coordinate: startPoint, symbolId: "A", anchor: [0, 0] });
      const coordinates = geom.getCoordinates().slice(0, -1);
      if (coordinates.length > 1) {
        this.addMarker({
          coordinate: coordinates[1],
          symbolId: "B",
          anchor: [1, 1],
        });
      }
      const pointscount = geom.getCoordinates();
      if (pointscount.length >= 4) {
        this.addMarker({
          coordinate: coordinates[2],
          symbolId: "C",
          anchor: [0, 0],
        });
        this.addAngleMark({
          coordinate: coordinates[1],
          Angles: calculateAngle(coordinates),
        });
        this.draw.finishDrawing();
      }
    });
  }
);
  • 如何計算夾角

兩點的坐標(biāo)位置決定了兩點之間的距離,即當(dāng)我們知道A、BC的坐標(biāo)后,就可以知道線段ABBC的長度了,然后通過數(shù)據(jù)計算就可以知道 ∠ABC的大小了。前面提到夾角的區(qū)間范圍,因為在電腦中,夾角也可以是負(fù)值,這個取決于它對應(yīng)的方向是順時針還是逆時針方向,因此要保證夾角的范圍在區(qū)間[0°,180°]內(nèi)。

封裝的計算夾角的方法calculateAngle如下:

const calculateAngle = (points: Coordinate[]) => {
  // 提取坐標(biāo)點 A, B, C
  const [A, B, C] = points;
  // 計算向量 AB 和 BC
  const AB = { x: B[0] - A[0], y: B[1] - A[1] };
  const BC = { x: C[0] - B[0], y: C[1] - B[1] };
  // 計算點積
  const dotProduct = AB.x * BC.x + AB.y * BC.y;
  // 計算向量的模
  const magnitudeAB = Math.sqrt(AB.x ** 2 + AB.y ** 2);
  const magnitudeBC = Math.sqrt(BC.x ** 2 + BC.y ** 2);
  // 計算余弦值
  const cosTheta = dotProduct / (magnitudeAB * magnitudeBC);
  // 確保 cosTheta 在 -1 和 1 之間
  const clippedCosTheta = Math.max(-1, Math.min(1, cosTheta));
  // 計算夾角(弧度轉(zhuǎn)換為度)
  const angleInRadians = Math.acos(clippedCosTheta);
  const angleInDegrees = angleInRadians * (180 / Math.PI);
  // 計算方向(使用叉積)
  const crossProduct = AB.x * BC.y - AB.y * BC.x;
  // 如果叉積為負(fù)
  const angle = crossProduct < 0 ? angleInDegrees - 180 : 180 - angleInDegrees;
  return angle;
};
  • 夾角的圓弧

通過上面可以計算得到 ∠ABC夾角的度數(shù)了,因為是自由繪制選點,因此無法保證BA或者BC是否與水平方向平行一致。小學(xué)時代,用量角器測量時,第一步就是需要保證量角器的底邊與夾角的一邊對齊,這樣的測量結(jié)果才準(zhǔn)確。但是在 Openlayers 中,我們就是需要去實現(xiàn)這樣一個量角器,無論在地圖上選擇哪三個頂點,圓弧都能準(zhǔn)確表示角度。并且這個圓弧的長度不固定。

這個需求可以使用canvas或者svg實現(xiàn),例子中是使用svg實現(xiàn)的。

  • svg 畫一個圓弧

效果如下:

拖動滑塊,可以動態(tài)繪制任意一段圓弧,其代碼如下:

<svg width="200" height="200">
  <path id="arc" fill="none" stroke="blue" stroke-width="2" />
  <line
    id="radial1"
    x1="100"
    y1="100"
    stroke="red"
    stroke-width="2"
    stroke-dasharray="5,5"
  />
  <line
    id="radial2"
    x1="100"
    y1="100"
    stroke="red"
    stroke-width="2"
    stroke-dasharray="5,5"
  />
  <text fill="black" font-size="16" text-anchor="middle">
    <textPath href="#arc" rel="external nofollow"  startOffset="50%" side="left">
      <tspan dy="-5" id="text"></tspan>
    </textPath>
  </text>
</svg>
function drawArc(adjustedAngle) {
  const endY = centerY + radius * Math.cos((adjustedAngle * Math.PI) / 180);
  const endX = centerX - radius * Math.sin((adjustedAngle * Math.PI) / 180);
  const startY = centerX;
  const startX = centerY - radius;
  const largeArcFlag = adjustedAngle > 180 ? 0 : 1;
  const d_1 = `M ${startX} ${startY} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endX} ${endY}`; //表示非直角
  const d_2 = `M ${endX} ${endY} L ${startX} ${endY}  L${startX} ${startY}`; //表示直角
  const angle = Math.abs(adjustedAngle);
  let lastD = d_1;
  if (angle == 540) {
    lastD = d_2;
  }
  arcPath.setAttribute("d", lastD);
  radial1.setAttribute("x2", startX);
  radial1.setAttribute("y2", startY);
  radial2.setAttribute("x2", endX);
  radial2.setAttribute("y2", endY);
}
  • 圓弧貼圖

現(xiàn)在需要將圓弧以O(shè)verlay的方式添加到 ∠ABC處就完事了,結(jié)果如下:

上面的情形就很詭異,因為圓弧始終是在水平位置,而我們選點是隨意的,因此圓弧需要旋轉(zhuǎn)一定的角度,使得圓弧的兩邊與夾角的兩邊對齊。但是具體旋轉(zhuǎn)多少角度,逆時針旋轉(zhuǎn)還是順時針旋轉(zhuǎn)?這個就取決于線段BA與BC的傾斜角度,準(zhǔn)確來說是以頂點B為原點建立坐標(biāo)系,水平方向為X軸且向右為X軸正半軸,垂直方向為Y軸且向上為Y軸正半軸。那么圓弧的旋轉(zhuǎn)角度和方向就取決于BA和BC與X 軸負(fù)半軸的夾角。

和 CSS 中transform的rotate規(guī)則報紙一致,規(guī)定圓弧逆時針旋轉(zhuǎn)為負(fù),順時針旋轉(zhuǎn)為正。因此,我們可以規(guī)定如果BA(或者BC)在第三象限或者是第四象限,則它與X軸負(fù)半軸的夾角為負(fù)值;反之,如果在第一象限則為正數(shù)鈍角,第二象限則為正數(shù)銳角

計算 BA 或者 BC 與 X 軸負(fù)半軸夾角

主要還是用到數(shù)學(xué)知識,代碼如下:

function calculateAnglePoint(points) {
  const [A, B, C] = points;
  const [Ax, Ay] = A;
  const [Bx, By] = B;
  const [Cx, Cy] = C;
  // 計算向量 BA 和 BC
  const BA = { x: Ax - Bx, y: Ay - By }; // BA 向量(從 B 到 A)
  const BC = { x: Cx - Bx, y: Cy - By }; // BC 向量(從 B 到 C)
  // 計算 BA 和 BC 向量與 X 軸的夾角(單位:度)
  let angleBA = Math.atan2(BA.y, BA.x) * (180 / Math.PI); // [-180, 180] 范圍
  let angleBC = Math.atan2(BC.y, BC.x) * (180 / Math.PI); // [-180, 180] 范圍
  // 計算 BA 向量與 X 軸負(fù)半軸的夾角
  if (angleBA >= 0 && angleBA < 90) {
    // 第一象限,夾角為正鈍角
    angleBA = 180 - angleBA;
  } else if (angleBA >= 90 && angleBA <= 180) {
    // 第二象限,夾角為正銳角
    angleBA = 180 - angleBA;
  } else if (angleBA < 0 && angleBA >= -90) {
    // 第四象限,夾角為負(fù)鈍角
    angleBA = Math.abs(angleBA) - 180;
  } else {
    // 第三象限,夾角為負(fù)銳角
    angleBA = Math.abs(angleBA) - 180;
  }
  // 計算 BC 向量與 X 軸負(fù)半軸的夾角
  if (angleBC >= 0 && angleBC < 90) {
    // 第一象限,夾角為正鈍角
    angleBC = 180 - angleBC;
  } else if (angleBC >= 90 && angleBC <= 180) {
    // 第二象限,夾角為正銳角
    angleBC = 180 - angleBC;
  } else if (angleBC < 0 && angleBC >= -90) {
    // 第四象限,夾角為負(fù)鈍角
    angleBC = Math.abs(angleBC) - 180;
  } else {
    // 第三象限,夾角為負(fù)銳角
    angleBC = Math.abs(angleBC) - 180;
  }
  return {
    angleBA: angleBA, // BA 向量與 X 軸負(fù)半軸的夾角
    angleBC: angleBC, // BC 向量與 X 軸負(fù)半軸的夾角
  };
}
  • 確定旋轉(zhuǎn)角度

由上得到了angleBA和angleBC,毫無疑問,如果BA和BC在第一二象限,則圓弧需要順時針旋轉(zhuǎn),且旋轉(zhuǎn)角度為Math.min(angleBA,angleBC),反之都在第三四象限,則旋轉(zhuǎn)角度為- Math.max(Math.abs(angleBa),Math.abs(angleBC)),如果BA和BC在不同象限,那就要分情況討論了,具體判斷規(guī)則如下:

let rotate = 0;
if (angleBA < 0 && angleBC < 0) {
  rotate = -Math.max(Math.abs(angleBA), Math.abs(angleBC));
}
if (angleBA > 0 && angleBC > 0) {
  rotate = Math.min(Math.abs(angleBA), Math.abs(angleBC));
}
//第一二三象限 不同象限
if (angleBA >= 135 && angleBA <= 180 && angleBC <= -45 && angleBC >= -90) {
  rotate = angleBA;
}
if (angleBC >= 135 && angleBC <= 180 && angleBA <= -45 && angleBA >= -90) {
  rotate = angleBC;
}
if (angleBA >= 135 && angleBA <= 180 && angleBC <= 0 && angleBC >= -45) {
  rotate = angleBC;
}
if (angleBC >= 135 && angleBC <= 180 && angleBA <= 0 && angleBA >= -45) {
  rotate = angleBA;
}
if (angleBA >= 90 && angleBA <= 135 && angleBC <= 0 && angleBC >= -45) {
  rotate = angleBC;
}
if (angleBC >= 90 && angleBC <= 135 && angleBA <= 0 && angleBA >= -45) {
  rotate = angleBA;
}
if (angleBA >= 90 && angleBA <= 135 && angleBC >= -90 && angleBC <= -45) {
  rotate = angleBA;
}
if (angleBC >= 90 && angleBC <= 135 && angleBA >= -90 && angleBA <= -45) {
  rotate = angleBC;
}
if (angleBA >= 0 && angleBA <= 90 && angleBC <= 0 && angleBC >= -90) {
  rotate = angleBC;
}
if (angleBC >= 0 && angleBC <= 90 && angleBA <= 0 && angleBA >= -90) {
  rotate = angleBA;
}
//第一二四象限不同象限
if (angleBC >= -180 && angleBC <= -90 && angleBA <= 180 && angleBA >= 90) {
  rotate = angleBA;
}
if (angleBA >= -180 && angleBA <= -90 && angleBC <= 180 && angleBC >= 90) {
  rotate = angleBC;
}
if (angleBC >= -135 && angleBC <= -90 && angleBA >= 0 && angleBA <= 45) {
  rotate = angleBC;
}
if (angleBC >= -135 && angleBC <= -90 && angleBA >= 45 && angleBA <= 90) {
  rotate = angleBA;
}
if (angleBA >= -135 && angleBA <= -90 && angleBC >= 0 && angleBC <= 45) {
  rotate = angleBA;
}
if (angleBA >= -135 && angleBA <= -90 && angleBC >= 45 && angleBC <= 90) {
  rotate = angleBC;
}
if (angleBC >= -180 && angleBC <= -135 && angleBA >= 0 && angleBA <= 45) {
  rotate = angleBC;
}
if (angleBC >= -180 && angleBC <= -135 && angleBA >= 45 && angleBA <= 90) {
  rotate = angleBA;
}
if (angleBA >= -180 && angleBA <= -135 && angleBC >= 0 && angleBC <= 45) {
  rotate = angleBA;
}
if (angleBA >= -180 && angleBA <= -135 && angleBC >= 45 && angleBC <= 90) {
  rotate = angleBC;
}

總結(jié)

在 Openlayers 中測量角度主要還是要用到一些數(shù)據(jù)的基礎(chǔ)知識,重難點就是圓弧的旋轉(zhuǎn)表示,理解基本原理后發(fā)現(xiàn)也就那么一回事。

到此這篇關(guān)于Openlayers實現(xiàn)角度測量的方法的文章就介紹到這了,更多相關(guān)Openlayers角度測量內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • js獲取 gif 的幀數(shù)的代碼實例

    js獲取 gif 的幀數(shù)的代碼實例

    這篇文章主要介紹了js獲取 gif 的幀數(shù)的代碼實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-09-09
  • 理解JAVASCRIPT中hasOwnProperty()的作用

    理解JAVASCRIPT中hasOwnProperty()的作用

    JavaScript中hasOwnProperty函數(shù)方法是返回一個布爾值,指出一個對象是否具有指定名稱的屬性
    2013-06-06
  • js手機號批量滾動抽獎實現(xiàn)代碼

    js手機號批量滾動抽獎實現(xiàn)代碼

    這篇文章主要為大家詳細(xì)介紹了js手機號批量滾動抽獎實現(xiàn)代碼,s適用于年會等活動,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • layui表單驗證select下拉框?qū)崿F(xiàn)驗證的方法

    layui表單驗證select下拉框?qū)崿F(xiàn)驗證的方法

    今天小編就為大家分享一篇layui表單驗證select下拉框?qū)崿F(xiàn)驗證的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-09-09
  • Flash圖片上傳組件 swfupload使用指南

    Flash圖片上傳組件 swfupload使用指南

    這篇文章主要介紹了Flash圖片上傳組件 swfupload使用方法及示例,swfupload的使用范圍十分的廣泛,功能也很強大,今天我們就先來簡單的通過范例來學(xué)習(xí)下。
    2015-03-03
  • iframe如何動態(tài)創(chuàng)建及釋放其所占內(nèi)存

    iframe如何動態(tài)創(chuàng)建及釋放其所占內(nèi)存

    一個項目后期測試發(fā)現(xiàn)瀏覽器內(nèi)存一直居高不下,而且打開iframe頁面越多內(nèi)存占用越大,在IE系列瀏覽器中尤其明顯,下面與大家分享下iframe動態(tài)創(chuàng)建及釋放內(nèi)存
    2014-09-09
  • JS實現(xiàn)時間選擇器

    JS實現(xiàn)時間選擇器

    這篇文章主要為大家詳細(xì)介紹了JS實現(xiàn)時間選擇器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • 微信小程序?qū)崿F(xiàn)省市區(qū)三級地址選擇

    微信小程序?qū)崿F(xiàn)省市區(qū)三級地址選擇

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)省市區(qū)三級地址選擇,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • 微信小程序?qū)崿F(xiàn)跑馬燈效果

    微信小程序?qū)崿F(xiàn)跑馬燈效果

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)跑馬燈效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • js實現(xiàn)功能比較全面的全選和多選

    js實現(xiàn)功能比較全面的全選和多選

    本文主要分享了js實現(xiàn)功能比較全面的全選和多選的示例代碼,具有很好的參考價值。下面跟著小編一起來看下吧
    2017-03-03

最新評論