Làm cách nào tôi có thể sử dụng ArcGIS 10.1 để tìm điểm tương đương trắc địa được xác định bởi ba điểm?


12

Ví dụ: tôi có tọa độ cho ba điểm cơ bản trên một đường bờ biển và tôi cần tìm tọa độ của điểm ngoài bờ biển tương đương với cả ba điểm. Đây là một bài tập đơn giản trong hình học, nhưng tất cả các phép đo phải tính đến trắc địa.

Nếu tôi đang tiếp cận điều này theo cách Euclidian, tôi có thể đo các đường trắc địa nối các điểm cơ sở, tìm trung điểm của các cạnh của tam giác thu được và tạo ra các đường thẳng vuông góc với mỗi đường đó. Ba loxodromes có lẽ sẽ hội tụ tại điểm tương đương. Nếu đây là phương pháp chính xác, thì phải có một cách dễ dàng hơn để thực hiện nó trong Arc.

Tôi cần tìm O


Có những hạn chế về vị trí tương đối của 3 điểm không? Hình ảnh bờ đông, điểm giữa là xa nhất về phía đông. Giải pháp của bạn sẽ không hoạt động vì các ống vuông góc sẽ không hội tụ ngoài khơi. Tôi chắc rằng chúng ta có thể đưa ra những trường hợp xấu khác!
mkennedy

Tôi tự hỏi nếu bạn có thể sử dụng một phép chiếu bảo toàn khoảng cách và chạy tính toán từ đó? progonos.com/furuti/MapProj/Normal/CartProp/DistPres/... Không chắc của thuật toán để làm điều đó, phải có một ... có lẽ đó là trung tâm khối lượng: en.wikipedia.org/wiki/Barycentric_coordinate_system
Alex Leith

Đối với các giải pháp cho một vấn đề liên quan chặt chẽ, tìm kiếm trang web của chúng tôi cho "trilateration" . Ngoài ra, gis.stackexchange.com/questions/10332/ Quảng cáo là một bản sao nhưng không có câu trả lời đầy đủ (rất có thể vì câu hỏi đã được hỏi một cách khó hiểu).
whuber

@mkennedy Về nguyên tắc, không có trường hợp xấu nào, chỉ có những trường hợp không ổn định về số lượng. Những điều này xảy ra khi ba điểm cơ sở là cộng tuyến; hai giải pháp (trên một mô hình hình cầu) xảy ra ở hai cực của trắc địa chung; trong một mô hình ellipsoidal chúng xảy ra gần nơi mà các cực đó sẽ được mong đợi.
whuber

Việc sử dụng loxodromes ở đây sẽ không chính xác: chúng không phải là bộ chia vuông góc. Trên mặt cầu, các đường này sẽ là một phần của các vòng tròn lớn (trắc địa), nhưng trên ellipsoid, chúng sẽ hơi rời khỏi trạng thái trắc địa.
whuber

Câu trả lời:


10

Câu trả lời này được chia thành nhiều phần:

  • Phân tích và giảm thiểu vấn đề , chỉ ra cách tìm điểm mong muốn với thói quen "đóng hộp".

  • Minh họa: một Nguyên mẫu làm việc , đưa ra mã làm việc.

  • Ví dụ , hiển thị ví dụ về các giải pháp.

  • Cạm bẫy , thảo luận về các vấn đề tiềm năng và làm thế nào để đối phó với chúng.

  • Triển khai ArcGIS , nhận xét về việc tạo một công cụ ArcGIS tùy chỉnh và nơi để có được các thói quen cần thiết.


Phân tích và giảm thiểu vấn đề

Hãy bắt đầu bằng cách quan sát rằng trong mô hình hình cầu (hoàn hảo tròn) sẽ luôn có một giải pháp - thực tế, chính xác là hai giải pháp. Cho các điểm cơ sở A, B và C, mỗi cặp xác định "bisector vuông góc" của nó, là tập hợp các điểm tương đương từ hai điểm đã cho. Bisector này là một trắc địa (vòng tròn lớn). Hình học hình cầu là hình elip : bất kỳ hai trắc địa giao nhau (trong hai điểm duy nhất). Do đó, các điểm giao nhau giữa bisector của AB và bisector của BC là - theo định nghĩa - tương đương từ A, B và C, từ đó giải quyết vấn đề. (Xem hình đầu tiên bên dưới.)

Mọi thứ trông phức tạp hơn trên một ellipsoid, nhưng vì nó là một nhiễu loạn nhỏ của hình cầu, chúng ta có thể mong đợi hành vi tương tự. (Việc phân tích điều này sẽ đưa chúng ta đi quá xa.) Các công thức phức tạp được sử dụng (bên trong một hệ thống GIS) để tính khoảng cách chính xác trên ellipsoid không phải là một biến chứng về mặt khái niệm: mặc dù vấn đề về cơ bản là giống nhau. Để xem vấn đề thực sự đơn giản như thế nào, hãy nêu nó một cách trừu tượng. Trong tuyên bố này, "d (U, V)" đề cập đến khoảng cách chính xác, hoàn toàn chính xác giữa các điểm U và V.

Cho ba điểm A, B, C (dưới dạng cặp lat-lon) trên một ellipsoid, tìm một điểm X mà (1) d (X, A) = d (X, B) = d (X, C) và ( 2) khoảng cách chung này càng nhỏ càng tốt.

Ba khoảng cách này đều phụ thuộc vào X chưa biết . Do đó, sự khác biệt về khoảng cách u (X) = d (X, A) - d (X, B) và v (X) = d (X, B) - d (X, C) là các hàm có giá trị thực của X. Một lần nữa, hơi trừu tượng, chúng ta có thể tập hợp những khác biệt này thành một cặp theo thứ tự. Chúng tôi cũng sẽ sử dụng (lat, lon) làm tọa độ cho X, cho phép chúng tôi coi đó là một cặp theo thứ tự, giả sử X = (phi, lambda). Trong thiết lập này, chức năng

F (phi, lambda) = (u (X), v (X))

là một hàm từ một phần của không gian hai chiều lấy các giá trị trong không gian hai chiều và vấn đề của chúng tôi giảm xuống còn

Tìm tất cả có thể (phi, lambda) mà F (phi, lambda) = (0,0).

Đây là nơi mà sự trừu tượng được đền đáp: có rất nhiều phần mềm tuyệt vời để giải quyết vấn đề này (hoàn toàn là tìm kiếm gốc đa chiều số). Cách thức hoạt động là bạn viết một thói quen để tính F , sau đó bạn chuyển nó cùng với phần mềm cùng với bất kỳ thông tin nào về các hạn chế đối với đầu vào của nó ( phi phải nằm trong khoảng -90 đến 90 độ và lambda phải nằm trong khoảng -180 đến 180 độ). Nó biến mất trong một phần của giây và trả về (thường) chỉ một giá trị của ( phi , lambda ), nếu nó có thể tìm thấy một giá trị.

Có nhiều chi tiết để xử lý, bởi vì có một nghệ thuật cho vấn đề này: có nhiều phương pháp giải pháp khác nhau để lựa chọn, tùy thuộc vào cách F "cư xử"; nó giúp "điều khiển" phần mềm bằng cách cho nó một điểm khởi đầu hợp lý cho tìm kiếm của nó (đây là một cách chúng ta có thể có được giải pháp gần nhất , thay vì bất kỳ giải pháp nào khác); và bạn thường cần xác định mức độ chính xác mà bạn muốn giải pháp đạt được (để biết khi nào nên dừng tìm kiếm). (Để biết thêm về những gì các nhà phân tích của GIS cần biết về các chi tiết như vậy, điều này xuất hiện rất nhiều trong các vấn đề về GIS, vui lòng truy cập các chủ đề được đề xuất trong khóa học về Khoa học máy tính cho Công nghệ không gian địa lý và xem phần "Miscellany" gần cuối. )


Minh họa: một nguyên mẫu làm việc

Phân tích cho thấy chúng ta cần lập trình hai điều: ước tính ban đầu về giải pháp và tính toán F chính

Ước tính ban đầu có thể được thực hiện bằng "trung bình hình cầu" của ba điểm cơ bản. Điều này có được bằng cách biểu diễn chúng theo tọa độ Cartesian (x, y, z), lấy trung bình các tọa độ đó và chiếu trung bình đó trở lại hình cầu và biểu thị lại theo vĩ độ và kinh độ. Kích thước của hình cầu là phi vật chất và do đó các tính toán được thực hiện đơn giản: bởi vì đây chỉ là điểm khởi đầu, chúng tôi không cần tính toán ellipsoidal.

Đối với nguyên mẫu làm việc này, tôi đã sử dụng Mathicala 8.

sphericalMean[points_] := Module[{sToC, cToS, cMean},
  sToC[{f_, l_}] := {Cos[f] Cos[l], Cos[f] Sin[l], Sin[f]};
  cToS[{x_, y_, z_}] := {ArcTan[x, y], ArcTan[Norm[{x, y}], z]};
  cMean = Mean[sToC /@ (points Degree)];
  If[Norm[Most@cMean] < 10^(-8), Mean[points], cToS[cMean]] / Degree
  ]

(Điều Ifkiện cuối cùng kiểm tra xem trung bình có thể thất bại rõ ràng khi chỉ ra kinh độ hay không; nếu vậy, nó sẽ trở về trung bình số học thẳng của vĩ độ và kinh độ của đầu vào - có thể không phải là một lựa chọn tuyệt vời, nhưng ít nhất là một giá trị hợp lệ. Đối với những người sử dụng mã này để hướng dẫn triển khai, lưu ý rằng các đối số của Mathicala ArcTan bị đảo ngược so với hầu hết các triển khai khác: đối số đầu tiên của nó là tọa độ x, tọa độ thứ hai là tọa độ y và nó trả về góc do vectơ tạo ra ( x, y).)

Theo như phần thứ hai đi, vì Mathematica --like ArcGIS và gần như tất cả GISes khác - có chứa mã để tính toán khoảng cách chính xác trên ellipsoid, có gần như không có gì để viết. Chúng tôi chỉ gọi thói quen tìm kiếm gốc:

tri[a_, b_, c_] := Block[{d = sphericalMean[{a, b, c}], sol, f, q},
   sol = FindRoot[{GeoDistance[{Mod[f, 180, -90], Mod[q, 360, -180]}, a] == 
                   GeoDistance[{Mod[f, 180, -90], Mod[q, 360, -180]}, b] ==
                   GeoDistance[{Mod[f, 180, -90], Mod[q, 360, -180]}, c]}, 
           {{f, d[[1]]}, {q, d[[2]]}}, 
           MaxIterations -> 1000, AccuracyGoal -> Infinity, PrecisionGoal -> 8];
   {Mod[f, 180, -90], Mod[q, 360, -180]} /. sol
   ];

Khía cạnh đáng chú ý nhất của việc triển khai này là cách nó tránh được sự cần thiết phải hạn chế vĩ độ ( f) và kinh độ ( q) bằng cách luôn tính toán chúng theo modulo 180 và 360 độ, tương ứng. Điều này tránh phải hạn chế vấn đề (thường tạo ra các biến chứng). Các thông số điều khiểnMaxIterations vv được điều chỉnh để làm cho mã này cung cấp độ chính xác cao nhất có thể.

Để thấy nó hoạt động, hãy áp dụng nó cho ba điểm cơ bản được đưa ra trong một câu hỏi liên quan :

sol = tri @@ (bases = {{-6.28530175, 106.9004975375}, {-6.28955287, 106.89573839}, {-6.28388865789474, 106.908087643421}})

{-6,29692, 106,87}

Khoảng cách tính toán giữa giải pháp này và ba điểm là

{1450.23206979, 1450.23206979, 1450.23206978}

(đây là những mét). Họ đồng ý thông qua chữ số có nghĩa thứ mười một (thực tế là quá chính xác, vì khoảng cách hiếm khi chính xác đến tốt hơn một milimet hoặc hơn). Dưới đây là hình ảnh của ba điểm này (màu đen), ba điểm chia cắt lẫn nhau và giải pháp (màu đỏ):

Hình 1


Thí dụ

Để kiểm tra việc thực hiện này và hiểu rõ hơn về cách xử lý vấn đề, đây là một biểu đồ đường viền của sai lệch bình phương trung bình gốc trong khoảng cách cho ba điểm cơ bản cách nhau rộng rãi. (Sự khác biệt RMS có được bằng cách tính cả ba khác biệt d (X, A) -d (X, B), d (X, B) -d (X, C) và d (X, C) -d (X , A), tính trung bình các hình vuông của chúng và lấy căn bậc hai. Nó bằng 0 khi X giải quyết vấn đề và nếu không thì X di chuyển ra khỏi một giải pháp, và do đó đo lường mức độ "đóng" của chúng ta là một giải pháp tại bất kỳ vị trí nào. )

Hình 2

Các điểm cơ bản (60, -120), (10, -40) và (45,10) được hiển thị bằng màu đỏ trong phép chiếu Tấm Carree này; dung dịch (49,2644488, -49,9052992) - cần 0,03 giây để tính toán - có màu vàng. Sự khác biệt RMS của nó là ít hơn ba nanomet , mặc dù tất cả các khoảng cách có liên quan là hàng ngàn km. Vùng tối hiển thị giá trị nhỏ của RMS và vùng sáng hiển thị giá trị cao.

Bản đồ này cho thấy rõ một giải pháp khác nằm gần (-49.2018206, 130.0297177) (được tính với RMS gồm hai nanomet bằng cách đặt giá trị tìm kiếm ban đầu đối diện với giải pháp đầu tiên.)


Cạm bẫy

Bất ổn định số

Khi các điểm cơ sở gần như thẳng hàng và gần nhau, tất cả các giải pháp sẽ cách nhau gần nửa thế giới và cực kỳ khó xác định chính xác. Lý do là những thay đổi nhỏ ở một vị trí trên toàn cầu - di chuyển nó tới hoặc ra khỏi các điểm cơ bản - sẽ chỉ tạo ra những thay đổi cực kỳ nhỏ trong sự khác biệt của khoảng cách. Không có đủ độ chính xác và độ chính xác được xây dựng trong tính toán thông thường của khoảng cách trắc địa để xác định kết quả.

Ví dụ: bắt đầu với các điểm cơ bản tại (45.001, 0), (45, 0) và (44.999,0), được phân tách dọc theo Kinh tuyến gốc chỉ cách nhau 111 mét giữa mỗi cặp, tri thu được giải pháp (11.8213, 77.745 ). Khoảng cách từ nó đến các điểm cơ sở là 8.127.964,998 77; 8.127.964,998 41; và 8.127.964,998 65 mét, tương ứng. Họ đồng ý đến milimet gần nhất! Tôi không chắc kết quả này có thể chính xác đến mức nào, nhưng sẽ không ngạc nhiên nhất nếu các triển khai khác trả lại các địa điểm cách xa địa điểm này, cho thấy sự bình đẳng tốt của ba khoảng cách.

Thời gian tính toán

Các tính toán này, vì chúng liên quan đến tìm kiếm đáng kể bằng cách sử dụng các tính toán khoảng cách phức tạp, không nhanh, thường đòi hỏi một phần đáng chú ý của một giây. Các ứng dụng thời gian thực cần phải nhận thức được điều này.


Triển khai ArcGIS

Python là môi trường kịch bản ưa thích cho ArcGIS (bắt đầu với phiên bản 9). Các gói scipy.optimize có rootfinder đa biến rootmà nên làm những gì FindRootthực hiện trong Mathematica mã. Tất nhiên ArcGIS tự cung cấp các tính toán khoảng cách ellipsoidal chính xác. Phần còn lại là tất cả các chi tiết triển khai: quyết định cách lấy tọa độ điểm cơ sở (từ một lớp được người dùng nhập vào từ tệp văn bản? Từ chuột?) Và cách trình bày đầu ra (dưới dạng tọa độ hiển thị trên màn hình? như là một điểm đồ họa? như một đối tượng điểm mới trong một lớp?), viết rằng giao diện, cảng Mathematica mã hiển thị ở đây (đơn giản), và bạn sẽ có tất cả các thiết lập.


3
+1 Rất kỹ lưỡng. Tôi nghĩ rằng bạn có thể phải bắt đầu tính phí cho việc này, @whuber.
Radar

2
@Radar Cảm ơn. Tôi hy vọng mọi người sẽ mua sách của tôi khi (bao giờ) cuối cùng nó xuất hiện :-).
whuber

1
Sẽ làm Bill ... Gửi một bản nháp !!!

Thông minh! Tuy nhiên, có vẻ như một giải pháp phân tích sẽ có thể. Bằng cách đặt lại vấn đề vào không gian cartesian 3d với 3 điểm A, B, C và E trong đó E là tâm của trái đất. Tiếp theo tìm hai mặt phẳng mặt phẳng1 và mặt phẳng2. Mặt phẳng1 sẽ là mặt phẳng bình thường với mặt phẳngABE và đi qua E, trung điểm (A, B). Tương tự như vậy, mặt phẳng2 sẽ là mặt phẳng bình thường đối với mặt phẳng và đi qua E, trung điểm (C, E). DòngO được hình thành bởi giao điểm của mặt phẳng1 và mặt phẳng 2 biểu thị các điểm tương đương với 3 điểm. Điểm gần hơn của hai điểm với A (hoặc B hoặc C) trong đó lineO cắt nhau hình cầu là pointO.
Kirk Kuykendall

Giải pháp phân tích đó, @Kirk, chỉ áp dụng cho hình cầu. (Giao điểm của các mặt phẳng với ellipsoid không bao giờ là đường phân giác vuông góc trong số liệu của ellipsoid ngoại trừ một vài trường hợp ngoại lệ rõ ràng: khi chúng là kinh tuyến hoặc Xích đạo.)
whuber

3

Như bạn lưu ý, vấn đề này phát sinh trong việc xác định ranh giới hàng hải; nó thường được gọi là vấn đề "ba điểm" và bạn có thể Google vấn đề này và tìm một số bài viết giải quyết nó. Một trong những giấy tờ này là của tôi (!) Và tôi đưa ra một giải pháp hội tụ chính xác và nhanh chóng. Xem Phần 14 của http://arxiv.org/abs/1102.1215

Phương pháp này bao gồm các bước sau:

  1. đoán một điểm ba
  2. sử dụng O làm tâm của phép chiếu đẳng thức phương vị
  3. dự án A, B, C, cho phép chiếu này
  4. tìm điểm ba trong phép chiếu này, O '
  5. sử dụng O 'làm trung tâm chiếu mới
  6. lặp lại cho đến khi O 'và O trùng nhau

Công thức cần thiết cho giải pháp ba điểm trong phép chiếu được đưa ra trong bài báo. Miễn là bạn đang sử dụng phép chiếu tương đương phương vị chính xác, câu trả lời sẽ chính xác. Hội tụ là ý nghĩa bậc hai mà chỉ cần một vài lần lặp. Điều này gần như chắc chắn sẽ tốt hơn các phương pháp tìm kiếm gốc chung được đề xuất bởi @whuber.

Tôi không thể trực tiếp giúp bạn với ArcGIS. Bạn có thể lấy gói python của tôi để thực hiện các phép tính trắc địa từ https://pypi.python.org/pypi/geographiclib và mã hóa phép chiếu dựa trên điều này rất đơn giản.


Biên tập

Vấn đề tìm kiếm điểm ba trong trường hợp suy biến @ whuber (45 + eps, 0) (45,0) (45-eps, 0) đã được Cayley xem xét trong Trên các đường trắc địa trên một hình cầu bắt buộc , Phil. Mag. (1870), http://books.google.com.vn/books?id=4XGIOoCMYYAC&pg=PA15

Trong trường hợp này, điểm ba được lấy bằng cách theo trắc địa từ (45,0) với góc phương vị 90 và tìm điểm tại đó thang đo trắc địa biến mất. Đối với ellipsoid WGS84, điểm này là (-0.10690908732248, 89,89291072793167). Khoảng cách từ điểm này đến từng điểm (45.001,0), (45,0), (44.999) là 10010287.665788943 m (trong phạm vi nanomet hoặc hơn). Con số này cao hơn khoảng 1882 km so với ước tính của người đánh bóng (điều này chỉ cho thấy trường hợp này không ổn định như thế nào). Đối với một trái đất hình cầu, ba điểm sẽ là (0,90) hoặc (0, -90), tất nhiên.

ĐỊA CHỈ: Đây là một triển khai của phương pháp đẳng thức phương vị bằng Matlab

function [lat, lon] = tripoint(lat1, lon1, lat2, lon2, lat3, lon3)
% Compute point equidistant from arguments
% Requires:
%   http://www.mathworks.com/matlabcentral/fileexchange/39108
%   http://www.mathworks.com/matlabcentral/fileexchange/39366
  lats = [lat1, lat2, lat3];
  lons = [lon1, lon2, lon3];
  lat0 = lat1;  lon0 = lon1; % feeble guess for tri point
  for i = 1:6
    [x, y] = eqdazim_fwd(lat0, lon0, lats, lons);
    a = [x(1), y(1), 0];
    b = [x(2), y(2), 0];
    c = [x(3), y(3), 0];
    z = [0, 0, 1];
    % Eq. (97) of http://arxiv.org/abs/1102.1215
    o = cross((a*a') * (b - c) + (b*b') * (c - a) + (c*c') * (a - b), z) ...
        / (2 * dot(cross(a - b, b - c), z));
    [lat0, lon0] = eqdazim_inv(lat0, lon0, o(1), o(2))
  end
  % optional check
  s12 = geoddistance(lat0, lon0, lats, lons); ds12 = max(s12) - min(s12)
  lat = lat0; lon = lon0;
end

Thử nghiệm điều này bằng Octave tôi nhận được

quãng tám: 1> định dạng dài
quãng tám: 2> [lat0, lon0] = tripoint (41, -74,36,140, ​​-41,175)
lat0 = 15.4151378380375
lon0 = -162.479314381144
lat0 = 15,9969703299812
lon0 = -147.046790722192
lat0 = 16.2232960167545
lon0 = -147.157646039471
lat0 = 16,2233394851560
lon0 = -147.157748279290
lat0 = 16,2233394851809
lon0 = -147.157748279312
lat0 = 16,2233394851809
lon0 = -147.157748279312
DS12 = 3.72529029846191e-09
lat0 = 16,2233394851809
lon0 = -147.157748279312

là điểm tri âm cho New York, Tokyo và Wellington.

Phương pháp này không chính xác cho các điểm colinear lân cận, ví dụ: [45.001,0], [45,0], [44.999,0]. Trong trường hợp đó, bạn nên giải quyết M 12 = 0 trên một trắc địa phát ra từ [45,0] ở góc phương vị 90. Hàm sau đây thực hiện thủ thuật (sử dụng phương pháp của Newton):

function [lat2,lon2] = semiconj(lat1, lon1, azi1)
% Find the point where neighboring parallel geodesics emanating from
% close to [lat1, lon1] with azimuth azi1 intersect.

  % First guess is 90 deg on aux sphere
  [lat2, lon2, ~, ~, m12, M12, M21, s12] = ...
      geodreckon(lat1, lon1, 90, azi1, defaultellipsoid(), true);
  M12
  % dM12/ds2 = - (1 - M12*M21/m12)
  for i = 1:3
    s12 = s12 - M12 / ( -(1 - M12*M21)/m12 ); % Newton
    [lat2, lon2, ~, ~, m12, M12, M21] = geodreckon(lat1, lon1, s12, azi1);
    M12
  end
end

Ví dụ, điều này mang lại:

[lat2, lon2] = semiconj (45, 0, 90)
M12 = 0,00262997817649321
M12 = -6,08402492665097e-09
M12 = 4.38017677684144e-17
M12 = 4.38017677684144e-17
lat2 = -0.106909087322479
lon2 = 89,8929107279317

+1. Tuy nhiên, không rõ là một công cụ tìm gốc chung sẽ hoạt động kém hơn: chức năng được xử lý độc đáo gần phương pháp tối ưu của nó và ví dụ, phương pháp của Newton cũng sẽ hội tụ theo phương trình bậc hai. ( Mathicala thường thực hiện khoảng bốn bước để hội tụ; mỗi bước cần bốn đánh giá để ước tính Jacobian.) Ưu điểm thực sự tôi thấy đối với phương pháp của bạn là nó có thể dễ dàng được viết mã trong một hệ thống GIS mà không cần sử dụng công cụ tìm gốc.
whuber

Tôi đồng ý. Phương pháp của tôi tương đương với Newton và do đó, trái ngược với phương pháp tìm gốc của Mathicala, không cần ước tính độ dốc bằng cách lấy sự khác biệt.
cffk

Đúng - nhưng bạn phải thực hiện việc từ chối mỗi lần, có vẻ như đó là về cùng một lượng công việc. Tôi đánh giá cao sự đơn giản và thanh lịch trong cách tiếp cận của bạn, mặc dù: rõ ràng là nó sẽ hoạt động và sẽ hội tụ nhanh chóng.
whuber

Tôi đã đăng kết quả cho cùng một điểm kiểm tra trong câu trả lời của tôi.
Kirk Kuykendall

3

Tôi tò mò muốn xem cách tiếp cận của @ cffk hội tụ nhanh chóng như thế nào về một giải pháp, vì vậy tôi đã viết một bài kiểm tra bằng cách sử dụng arcobjects, tạo ra kết quả này. Khoảng cách được tính bằng mét:

0 longitude: 0 latitude: 90
    Distances: 3134.05443974188 2844.67237777542 3234.33025754997
    Diffs: 289.382061966458 -389.657879774548 -100.27581780809
1 longitude: 106.906152157596 latitude: -6.31307123035178
    Distances: 1450.23208989615 1450.23208089398 1450.23209429293
    Diffs: 9.00216559784894E-06 -1.33989510686661E-05 -4.39678547081712E-06
2 longitude: 106.906583669013 latitude: -6.29691590176649
    Distances: 1450.23206976414 1450.23206976408 1450.23206976433
    Diffs: 6.18456397205591E-11 -2.47382558882236E-10 -1.85536919161677E-10
3 longitude: 106.906583669041 latitude: -6.29691590154641
    Distances: 1450.23206976438 1450.23206976423 1450.23206976459
    Diffs: 1.47565515362658E-10 -3.61751517630182E-10 -2.14186002267525E-10
4 longitude: 106.906583669041 latitude: -6.29691590154641
    Distances: 1450.23206976438 1450.23206976423 1450.23206976459
    Diffs: 1.47565515362658E-10 -3.61751517630182E-10 -2.14186002267525E-10

Đây là mã nguồn. (Chỉnh sửa) Đã thay đổi FindCircleCenter để xử lý các giao điểm (điểm trung tâm) rơi ra khỏi cạnh của phép chiếu phương vị:

public static void Test()
{
    var t = Type.GetTypeFromProgID("esriGeometry.SpatialReferenceEnvironment");
    var srf = Activator.CreateInstance(t) as ISpatialReferenceFactory2;
    var pcs = srf.CreateProjectedCoordinateSystem((int)esriSRProjCSType.esriSRProjCS_WGS1984N_PoleAziEqui)
        as IProjectedCoordinateSystem2;

    var pntA = MakePoint(106.9004975375, -6.28530175, pcs.GeographicCoordinateSystem);
    var pntB = MakePoint(106.89573839, -6.28955287, pcs.GeographicCoordinateSystem);
    var pntC = MakePoint(106.908087643421, -6.28388865789474, pcs.GeographicCoordinateSystem);

    int maxIter = 5;
    for (int i = 0; i < maxIter; i++)
    {
        var msg = string.Format("{0} longitude: {1} latitude: {2}", i, pcs.get_CentralMeridian(true), pcs.LatitudeOfOrigin);
        Debug.Print(msg);
        var newCenter = FindCircleCenter(ProjectClone(pntA, pcs), ProjectClone(pntB, pcs), ProjectClone(pntC, pcs));
        newCenter.Project(pcs.GeographicCoordinateSystem); // unproject
        var distA = GetGeodesicDistance(newCenter, pntA);
        var distB = GetGeodesicDistance(newCenter, pntB);
        var distC = GetGeodesicDistance(newCenter, pntC);
        Debug.Print("\tDistances: {0} {1} {2}", distA, distB, distC);
        var diffAB = distA - distB;
        var diffBC = distB - distC;
        var diffAC = distA - distC;
        Debug.Print("\tDiffs: {0} {1} {2}", diffAB, diffBC, diffAC);

        pcs.set_CentralMeridian(true, newCenter.X);
        pcs.LatitudeOfOrigin = newCenter.Y;
    }
}
public static IPoint FindCircleCenter(IPoint a, IPoint b, IPoint c)
{
    // from http://blog.csharphelper.com/2011/11/08/draw-a-circle-through-three-points-in-c.aspx
    // Get the perpendicular bisector of (x1, y1) and (x2, y2).
    var x1 = (b.X + a.X) / 2;
    var y1 = (b.Y + a.Y) / 2;
    var dy1 = b.X - a.X;
    var dx1 = -(b.Y - a.Y);

    // Get the perpendicular bisector of (x2, y2) and (x3, y3).
    var x2 = (c.X + b.X) / 2;
    var y2 = (c.Y + b.Y) / 2;
    var dy2 = c.X - b.X;
    var dx2 = -(c.Y - b.Y);

    // See where the lines intersect.
    var cx = (y1 * dx1 * dx2 + x2 * dx1 * dy2 - x1 * dy1 * dx2 - y2 * dx1 * dx2)
        / (dx1 * dy2 - dy1 * dx2);
    var cy = (cx - x1) * dy1 / dx1 + y1;

    // make sure the intersection point falls
    // within the projection.
    var earthRadius = ((IProjectedCoordinateSystem)a.SpatialReference).GeographicCoordinateSystem.Datum.Spheroid.SemiMinorAxis;

    // distance is from center of projection
    var dist = Math.Sqrt((cx * cx) + (cy * cy));
    double factor = 1.0;
    if (dist > earthRadius * Math.PI)
    {
        // apply a factor so we don't fall off the edge
        // of the projection
        factor = earthRadius / dist;
    }
    var outPoint = new PointClass() as IPoint;
    outPoint.PutCoords(cx * factor, cy* factor);
    outPoint.SpatialReference = a.SpatialReference;
    return outPoint;
}

public static double GetGeodesicDistance(IPoint pnt1, IPoint pnt2)
{
    var pc = new PolylineClass() as IPointCollection;
    var gcs = pnt1.SpatialReference as IGeographicCoordinateSystem;
    if (gcs == null)
        throw new Exception("point does not have a gcs");
    ((IGeometry)pc).SpatialReference = gcs;
    pc.AddPoint(pnt1);
    pc.AddPoint(pnt2);
    var t = Type.GetTypeFromProgID("esriGeometry.SpatialReferenceEnvironment");
    var srf = Activator.CreateInstance(t) as ISpatialReferenceFactory2;
    var unit = srf.CreateUnit((int)esriSRUnitType.esriSRUnit_Meter) as ILinearUnit;
    var pcGeodetic = pc as IPolycurveGeodetic;
    return pcGeodetic.get_LengthGeodetic(esriGeodeticType.esriGeodeticTypeGeodesic, unit);
}

public static IPoint ProjectClone(IPoint pnt, ISpatialReference sr)
{
    var clone = ((IClone)pnt).Clone() as IPoint;
    clone.Project(sr);
    return clone;
}

public static IPoint MakePoint(double longitude, double latitude, ISpatialReference sr)
{
    var pnt = new PointClass() as IPoint;
    pnt.PutCoords(longitude, latitude);
    pnt.SpatialReference = sr;
    return pnt;
}

Ngoài ra còn có một cách tiếp cận khác trong số tháng 6 năm 2013 của Tạp chí MSDN, Tối ưu hóa phương pháp Amoeba bằng C # .


Biên tập

Mã được đăng trước đó đã hội tụ đến antipode trong một số trường hợp. Tôi đã thay đổi mã để nó tạo đầu ra này cho các điểm kiểm tra của @ cffk.

Đây là đầu ra mà nó tạo ra:

0 0
0 longitude: 0 latitude: 0
    MaxDiff: 1859074.90170379 Distances: 13541157.6493561 11682082.7476523 11863320.2116807
1 longitude: 43.5318402621384 latitude: -17.1167429904981
    MaxDiff: 21796.9793742411 Distances: 12584188.7592282 12588146.4851222 12566349.505748
2 longitude: 32.8331167578493 latitude: -16.2707976739314
    MaxDiff: 6.05585224926472 Distances: 12577536.3369782 12577541.3560203 12577542.3928305
3 longitude: 32.8623898057665 latitude: -16.1374156408507
    MaxDiff: 5.58793544769287E-07 Distances: 12577539.6118671 12577539.6118666 12577539.6118669
4 longitude: -147.137582018133 latitude: 16.1374288796667
    MaxDiff: 1.12284109462053 Distances: 7441375.08265703 7441376.12671342 7441376.20549812
5 longitude: -147.157742373074 latitude: 16.2233413614432
    MaxDiff: 7.45058059692383E-09 Distances: 7441375.70752843 7441375.70752842 7441375.70752842
5 longitude: -147.157742373074 latitude: 16.2233413614432 Distance 7441375.70752843
iterations: 5

Đây là mã sửa đổi:

class Program
{
    private static LicenseInitializer m_AOLicenseInitializer = new tripoint.LicenseInitializer();

    [STAThread()]
    static void Main(string[] args)
    {
        //ESRI License Initializer generated code.
        m_AOLicenseInitializer.InitializeApplication(new esriLicenseProductCode[] { esriLicenseProductCode.esriLicenseProductCodeStandard },
        new esriLicenseExtensionCode[] { });
        try
        {
            var t = Type.GetTypeFromProgID("esriGeometry.SpatialReferenceEnvironment");
            var srf = Activator.CreateInstance(t) as ISpatialReferenceFactory2;
            var pcs = srf.CreateProjectedCoordinateSystem((int)esriSRProjCSType.esriSRProjCS_World_AzimuthalEquidistant)
                as IProjectedCoordinateSystem2;
            Debug.Print("{0} {1}", pcs.get_CentralMeridian(true), pcs.LatitudeOfOrigin);
            int max = int.MinValue;
            for (int i = 0; i < 1; i++)
            {
                var iterations = Test(pcs);
                max = Math.Max(max, iterations);
                Debug.Print("iterations: {0}", iterations);
            }
            Debug.Print("max number of iterations: {0}", max);
        }
        catch (Exception ex)
        {
            Debug.Print(ex.Message);
            Debug.Print(ex.StackTrace);
        }
        //ESRI License Initializer generated code.
        //Do not make any call to ArcObjects after ShutDownApplication()
        m_AOLicenseInitializer.ShutdownApplication();
    }
    public static int Test(IProjectedCoordinateSystem2 pcs)
    {
        var pntA = MakePoint(-74.0, 41.0, pcs.GeographicCoordinateSystem);
        var pntB = MakePoint(140.0, 36.0, pcs.GeographicCoordinateSystem);
        var pntC = MakePoint(175.0, -41.0, pcs.GeographicCoordinateSystem);


        //var r = new Random();
        //var pntA = MakeRandomPoint(r, pcs.GeographicCoordinateSystem);
        //var pntB = MakeRandomPoint(r, pcs.GeographicCoordinateSystem);
        //var pntC = MakeRandomPoint(r, pcs.GeographicCoordinateSystem);

        int maxIterations = 100;
        for (int i = 0; i < maxIterations; i++)
        {
            var msg = string.Format("{0} longitude: {1} latitude: {2}", i, pcs.get_CentralMeridian(true), pcs.LatitudeOfOrigin);
            Debug.Print(msg);
            var newCenter = FindCircleCenter(ProjectClone(pntA, pcs), ProjectClone(pntB, pcs), ProjectClone(pntC, pcs));
            var c = ((IClone)newCenter).Clone() as IPoint;
            newCenter.Project(pcs.GeographicCoordinateSystem); // unproject
            //newCenter = MakePoint(-147.1577482, 16.2233394, pcs.GeographicCoordinateSystem);
            var distA = GetGeodesicDistance(newCenter, pntA);
            var distB = GetGeodesicDistance(newCenter, pntB);
            var distC = GetGeodesicDistance(newCenter, pntC);
            var diffAB = Math.Abs(distA - distB);
            var diffBC = Math.Abs(distB - distC);
            var diffAC = Math.Abs(distA - distC);
            var maxDiff = GetMax(new double[] {diffAB,diffAC,diffBC});
            Debug.Print("\tMaxDiff: {0} Distances: {1} {2} {3}",maxDiff, distA, distB, distC);
            if (maxDiff < 0.000001)
            {
                var earthRadius = pcs.GeographicCoordinateSystem.Datum.Spheroid.SemiMinorAxis;
                if (distA > earthRadius * Math.PI / 2.0)
                {
                    newCenter = AntiPode(newCenter);
                }
                else
                {
                    Debug.Print("{0} longitude: {1} latitude: {2} Distance {3}", i, pcs.get_CentralMeridian(true), pcs.LatitudeOfOrigin, distA);
                    return i;
                }
            }
            //Debug.Print("\tDiffs: {0} {1} {2}", diffAB, diffBC, diffAC);

            pcs.set_CentralMeridian(true, newCenter.X);
            pcs.LatitudeOfOrigin = newCenter.Y;
        }
        return maxIterations;
    }

    public static IPoint FindCircleCenter(IPoint a, IPoint b, IPoint c)
    {
        // from http://blog.csharphelper.com/2011/11/08/draw-a-circle-through-three-points-in-c.aspx
        // Get the perpendicular bisector of (x1, y1) and (x2, y2).
        var x1 = (b.X + a.X) / 2;
        var y1 = (b.Y + a.Y) / 2;
        var dy1 = b.X - a.X;
        var dx1 = -(b.Y - a.Y);

        // Get the perpendicular bisector of (x2, y2) and (x3, y3).
        var x2 = (c.X + b.X) / 2;
        var y2 = (c.Y + b.Y) / 2;
        var dy2 = c.X - b.X;
        var dx2 = -(c.Y - b.Y);

        // See where the lines intersect.
        var cx = (y1 * dx1 * dx2 + x2 * dx1 * dy2 - x1 * dy1 * dx2 - y2 * dx1 * dx2)
            / (dx1 * dy2 - dy1 * dx2);
        var cy = (cx - x1) * dy1 / dx1 + y1;

        // make sure the intersection point falls
        // within the projection.
        var earthRadius = ((IProjectedCoordinateSystem)a.SpatialReference).GeographicCoordinateSystem.Datum.Spheroid.SemiMinorAxis;

        // distance is from center of projection
        var dist = Math.Sqrt((cx * cx) + (cy * cy));
        double factor = 1.0;
        if (dist > earthRadius * Math.PI)
        {
            // apply a factor so we don't fall off the edge
            // of the projection
            factor = earthRadius / dist;
        }
        var outPoint = new PointClass() as IPoint;
        outPoint.PutCoords(cx * factor, cy* factor);
        outPoint.SpatialReference = a.SpatialReference;
        return outPoint;
    }

    public static IPoint AntiPode(IPoint pnt)
    {
        if (!(pnt.SpatialReference is IGeographicCoordinateSystem))
            throw new Exception("antipode of non-gcs projection not supported");
        var outPnt = new PointClass() as IPoint;
        outPnt.SpatialReference = pnt.SpatialReference;
        if (pnt.X < 0.0)
            outPnt.X = 180.0 + pnt.X;
        else
            outPnt.X = pnt.X - 180.0;
        outPnt.Y = -pnt.Y;
        return outPnt;
    }

    public static IPoint MakeRandomPoint(Random r, IGeographicCoordinateSystem gcs)
    {
        var latitude = (r.NextDouble() - 0.5) * 180.0;
        var longitude = (r.NextDouble() - 0.5) * 360.0;
        //Debug.Print("{0} {1}", latitude, longitude);
        return MakePoint(longitude, latitude, gcs);
    }
    public static double GetMax(double[] dbls)
    {
        var max = double.MinValue;
        foreach (var d in dbls)
        {
            if (d > max)
                max = d;
        }
        return max;
    }
    public static IPoint MakePoint(IPoint[] pnts)
    {
        double sumx = 0.0;
        double sumy = 0.0;
        foreach (var pnt in pnts)
        {
            sumx += pnt.X;
            sumy += pnt.Y;
        }
        return MakePoint(sumx / pnts.Length, sumy / pnts.Length, pnts[0].SpatialReference);
    }
    public static double GetGeodesicDistance(IPoint pnt1, IPoint pnt2)
    {
        var pc = new PolylineClass() as IPointCollection;
        var gcs = pnt1.SpatialReference as IGeographicCoordinateSystem;
        if (gcs == null)
            throw new Exception("point does not have a gcs");
        ((IGeometry)pc).SpatialReference = gcs;
        pc.AddPoint(pnt1);
        pc.AddPoint(pnt2);
        var t = Type.GetTypeFromProgID("esriGeometry.SpatialReferenceEnvironment");
        var srf = Activator.CreateInstance(t) as ISpatialReferenceFactory2;
        var unit = srf.CreateUnit((int)esriSRUnitType.esriSRUnit_Meter) as ILinearUnit;
        var pcGeodetic = pc as IPolycurveGeodetic;
        return pcGeodetic.get_LengthGeodetic(esriGeodeticType.esriGeodeticTypeGeodesic, unit);
    }

    public static IPoint ProjectClone(IPoint pnt, ISpatialReference sr)
    {
        var clone = ((IClone)pnt).Clone() as IPoint;
        clone.Project(sr);
        return clone;
    }

    public static IPoint MakePoint(double longitude, double latitude, ISpatialReference sr)
    {
        var pnt = new PointClass() as IPoint;
        pnt.PutCoords(longitude, latitude);
        pnt.SpatialReference = sr;
        return pnt;
    }
}

Biên tập

Đây là kết quả tôi nhận được với esriSRProjCS_WGS1984N_PoleAziEqui

0 90
0 longitude: 0 latitude: 90
    MaxDiff: 1275775.91880553 Distances: 8003451.67666723 7797996.2370572 6727675.7578617
1 longitude: -148.003774863594 latitude: 9.20238223616225
    MaxDiff: 14487.6784785809 Distances: 7439006.46128994 7432752.45732905 7447240.13580763
2 longitude: -147.197808459106 latitude: 16.3073233548167
    MaxDiff: 2.32572609744966 Distances: 7441374.94409209 7441377.26981819 7441375.90768183
3 longitude: -147.157734641831 latitude: 16.2233338760411
    MaxDiff: 7.72997736930847E-08 Distances: 7441375.70752842 7441375.70752848 7441375.7075284
3 longitude: -147.157734641831 latitude: 16.2233338760411 Distance 7441375.70752842

Đó là sự hội tụ nhanh chóng ấn tượng! (+1)
whuber

Bạn nên sử dụng phép chiếu tương đương phương vị trung thực đến tốt đẹp tập trung vào newCenter. Thay vào đó, bạn đang sử dụng phép chiếu tập trung vào cực N và chuyển nguồn gốc sang newCenter. Do đó, có thể vô tình bạn có được một giải pháp hợp lý trong trường hợp này (có lẽ vì các điểm gần nhau?). Sẽ rất tốt nếu thử nó với 3 điểm cách nhau hàng ngàn km. Việc triển khai phép chiếu
đẳng thức

@cffk Cách duy nhất khác mà tôi thấy là tạo ra một phép chiếu tương đương phương vị trung tâm tập trung vào một điểm cụ thể tập trung vào một điểm cụ thể là sử dụng cùng một phương pháp nhưng sử dụng cùng một phương pháp nhưng sử dụng cùng một phương pháp nhưng sử dụng cùng một phương pháp nhưng với esriSRProjCS_World_AzimuthalEquidistant thay vì esriSRProjCS_WGS1984N_PoleAzi Tuy nhiên, sự khác biệt duy nhất là nó tập trung vào 0,0 thay vì 0,90 (hoặc 0, -90). Bạn có thể hướng dẫn tôi thực hiện một bài kiểm tra với mathworks để xem liệu điều này có tạo ra kết quả khác nhau từ một phép chiếu "trung thực đến tốt lành" không?
Kirk Kuykendall

@KirkKuykendall: xem phần phụ lục cho câu trả lời đầu tiên của tôi.
cffk

1
@KirkKuykendall Vì vậy, có lẽ ESRI đã thực hiện phép chiếu "trung thực với lòng tốt"? Thuộc tính quan trọng cần thiết để thuật toán này hoạt động là khoảng cách đo từ "điểm trung tâm" là đúng. Và tài sản này là đủ dễ dàng để kiểm tra. (Thuộc tính phương vị so với điểm trung tâm là thứ yếu và chỉ có thể ảnh hưởng đến tốc độ hội tụ.)
cffk
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.