سؤال

لدي شريحة خط (جزء دائرة كبيرة) على الأرض. يتم تعريف شريحة الخط من قبل إحداثيات نهايته. من الواضح أن نقطتين تحدد قطاعات خطين، لذا افترض أنني مهتم بأقصر واحدة.

أنا أعطيت نقطة ثالثة، وأنا أبحث عن المسافة (أقصر) بين الخط والنقطة.

يتم تقديم جميع الإحداثيات في خط الطول Latitude (WGS 84).

كيف يمكنني حساب المسافة؟

سيفعل الحل في أي لغة برمجة معقولة.

هل كانت مفيدة؟

المحلول

إليك الحل الخاص بي، بناء على الفكرة في اسأل الدكتور الرياضيات. وبعد سأكون سعيدا لرؤية ملاحظاتك.

إخلاء المسئولية أولا. هذا الحل صحيح بالنسبة للركاب. الأرض ليست كرة، ونظام الإحداثيات (WGS 84) لا يفترض أنه كرة. لذلك هذا مجرد تقريب، ولا يمكنني تقديره حقا هو الخطأ. أيضا، بالنسبة لمسافات صغيرة جدا، من الممكن أيضا الحصول على تقريب جيد من خلال افتراض أن كل شيء مجرد coplanar. مرة أخرى لا أعرف كيف يجب أن تكون المسافات "صغيرة".

الآن للعمل. سأتصل بنهاى الخطوط A، B والجزء الثالث C. أساسا، الخوارزمية هي:

  1. تحويل الإحداثيات أولا إلى إحداثيات الديكارتية (مع الأصل في وسط الأرض) - على سبيل المثال هنا.
  2. احسب ر، النقطة الموجودة على السطر AB الأقرب إلى C، باستخدام منتجات Vector التالية التالية:

    ز = x ب

    f = c x g

    T = G X F

  3. تطبيع ر وضرب دائرة نصف قطر الأرض.

  4. تحويل ر العودة إلى خط الطول خط العرض.
  5. احسب المسافة بين T و C - على سبيل المثال هنا.

هذه الخطوات كافية إذا كنت تبحث عن المسافة بين C والدائرة العظيمة التي تحددها A و B. إذا كنت مهتما بك في المسافة بين C وشركة الخطوط الأقصر، فأنت بحاجة إلى اتخاذ خطوة إضافية للتحقق من ذلك ر هو في الواقع في هذا القطاع. إذا لم يكن الأمر كذلك، فإن أقرب نقطة هي واحدة من النهايات A أو B - أسهل طريقة للتحقق من أي منها.

بعبارات عامة، فإن الفكرة وراء منتجات ناقلات الثلاثة هي ما يلي. أول واحد (ز) يعطينا طائرة الدائرة الكبرى من A و B (حتى الطائرة التي تحتوي على أ، ب والأصل). والثاني (F) يعطينا الدائرة العظيمة التي يمر بها من خلال C ويعتاد على G. ثم تي تقاطع الدوائر العظيمة التي يحددها F و G، إلى الطول الصحيح عن طريق التطبيع والضرب بواسطة R.

إليك بعض كود Java الجزئي للقيام بذلك.

العثور على أقرب نقطة في الدائرة الكبرى. المدخلات والإخراج هي صفائف طول 2. المصفوفات المتوسطة طولها 3.

double[] nearestPointGreatCircle(double[] a, double[] b, double c[])
{
    double[] a_ = toCartsian(a);
    double[] b_ = toCartsian(b);
    double[] c_ = toCartsian(c);

    double[] G = vectorProduct(a_, b_);
    double[] F = vectorProduct(c_, G);
    double[] t = vectorProduct(G, F);
    normalize(t);
    multiplyByScalar(t, R_EARTH);
    return fromCartsian(t);
}

العثور على أقرب نقطة في القطاع:

double[] nearestPointSegment (double[] a, double[] b, double[] c)
{
   double[] t= nearestPointGreatCircle(a,b,c);
   if (onSegment(a,b,t))
     return t;
   return (distance(a,c) < distance(b,c)) ? a : c;
} 

هذه طريقة بسيطة لاختبار إذا كانت النقطة ر، التي نعرفها هي نفس الدائرة الكبيرة مثل A و B، على الجزء الأقصر من هذه الدائرة الكبرى. ومع ذلك، هناك طرق أكثر كفاءة للقيام بذلك:

   boolean onSegment (double[] a, double[] b, double[] t)
   {
     // should be   return distance(a,t)+distance(b,t)==distance(a,b), 
     // but due to rounding errors, we use: 
     return Math.abs(distance(a,b)-distance(a,t)-distance(b,t)) < PRECISION;
   }    

نصائح أخرى

يحاول المسافة من نقطة إلى دائرة كبيرة, من اسأل الدكتور الرياضيات. لا تزال بحاجة إلى تحويل خط الطول / خط الطول للإحداثيات الكروية وحجم دائرة نصف قطرها الأرض، ولكن هذا يبدو وكأنه اتجاه جيد.

هذا هو رمز كامل للإجابة المقبولة كهندسون (تم العثور عليه هنا):

import java.util.*;
import java.lang.*;
import java.io.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{



    private static final double _eQuatorialEarthRadius = 6378.1370D;
    private static final double _d2r = (Math.PI / 180D);
    private static double PRECISION = 0.1;





    // Haversine Algorithm
    // source: http://stackoverflow.com/questions/365826/calculate-distance-between-2-gps-coordinates

    private static double HaversineInM(double lat1, double long1, double lat2, double long2) {
        return  (1000D * HaversineInKM(lat1, long1, lat2, long2));
    }

    private static double HaversineInKM(double lat1, double long1, double lat2, double long2) {
        double dlong = (long2 - long1) * _d2r;
        double dlat = (lat2 - lat1) * _d2r;
        double a = Math.pow(Math.sin(dlat / 2D), 2D) + Math.cos(lat1 * _d2r) * Math.cos(lat2 * _d2r)
                * Math.pow(Math.sin(dlong / 2D), 2D);
        double c = 2D * Math.atan2(Math.sqrt(a), Math.sqrt(1D - a));
        double d = _eQuatorialEarthRadius * c;
        return d;
    }

    // Distance between a point and a line

    public static void pointLineDistanceTest() {

        //line
        //double [] a = {50.174315,19.054743};
        //double [] b = {50.176019,19.065042};
        double [] a = {52.00118, 17.53933};
        double [] b = {52.00278, 17.54008};

        //point
        //double [] c = {50.184373,19.054657};
        double [] c = {52.008308, 17.542927};
        double[] nearestNode = nearestPointGreatCircle(a, b, c);
        System.out.println("nearest node: " + Double.toString(nearestNode[0]) + "," + Double.toString(nearestNode[1]));
        double result =  HaversineInM(c[0], c[1], nearestNode[0], nearestNode[1]);
        System.out.println("result: " + Double.toString(result));
    }

    // source: http://stackoverflow.com/questions/1299567/how-to-calculate-distance-from-a-point-to-a-line-segment-on-a-sphere
    private static double[] nearestPointGreatCircle(double[] a, double[] b, double c[])
    {
        double[] a_ = toCartsian(a);
        double[] b_ = toCartsian(b);
        double[] c_ = toCartsian(c);

        double[] G = vectorProduct(a_, b_);
        double[] F = vectorProduct(c_, G);
        double[] t = vectorProduct(G, F);

        return fromCartsian(multiplyByScalar(normalize(t), _eQuatorialEarthRadius));
    }

    @SuppressWarnings("unused")
    private static double[] nearestPointSegment (double[] a, double[] b, double[] c)
    {
       double[] t= nearestPointGreatCircle(a,b,c);
       if (onSegment(a,b,t))
         return t;
       return (HaversineInKM(a[0], a[1], c[0], c[1]) < HaversineInKM(b[0], b[1], c[0], c[1])) ? a : b;
    }

     private static boolean onSegment (double[] a, double[] b, double[] t)
       {
         // should be   return distance(a,t)+distance(b,t)==distance(a,b), 
         // but due to rounding errors, we use: 
         return Math.abs(HaversineInKM(a[0], a[1], b[0], b[1])-HaversineInKM(a[0], a[1], t[0], t[1])-HaversineInKM(b[0], b[1], t[0], t[1])) < PRECISION;
       }


    // source: http://stackoverflow.com/questions/1185408/converting-from-longitude-latitude-to-cartesian-coordinates
    private static double[] toCartsian(double[] coord) {
        double[] result = new double[3];
        result[0] = _eQuatorialEarthRadius * Math.cos(Math.toRadians(coord[0])) * Math.cos(Math.toRadians(coord[1]));
        result[1] = _eQuatorialEarthRadius * Math.cos(Math.toRadians(coord[0])) * Math.sin(Math.toRadians(coord[1]));
        result[2] = _eQuatorialEarthRadius * Math.sin(Math.toRadians(coord[0]));
        return result;
    }

    private static double[] fromCartsian(double[] coord){
        double[] result = new double[2];
        result[0] = Math.toDegrees(Math.asin(coord[2] / _eQuatorialEarthRadius));
        result[1] = Math.toDegrees(Math.atan2(coord[1], coord[0]));

        return result;
    }


    // Basic functions
    private static double[] vectorProduct (double[] a, double[] b){
        double[] result = new double[3];
        result[0] = a[1] * b[2] - a[2] * b[1];
        result[1] = a[2] * b[0] - a[0] * b[2];
        result[2] = a[0] * b[1] - a[1] * b[0];

        return result;
    }

    private static double[] normalize(double[] t) {
        double length = Math.sqrt((t[0] * t[0]) + (t[1] * t[1]) + (t[2] * t[2]));
        double[] result = new double[3];
        result[0] = t[0]/length;
        result[1] = t[1]/length;
        result[2] = t[2]/length;
        return result;
    }

    private static double[] multiplyByScalar(double[] normalize, double k) {
        double[] result = new double[3];
        result[0] = normalize[0]*k;
        result[1] = normalize[1]*k;
        result[2] = normalize[2]*k;
        return result;
    }

     public static void main(String []args){
        System.out.println("Hello World");
        Ideone.pointLineDistanceTest();

     }



}

إنه يعمل بشكل جيد للبيانات المعرفة:

//line
double [] a = {50.174315,19.054743};
double [] b = {50.176019,19.065042};
//point
double [] c = {50.184373,19.054657};

أقرب عقدة هي: 50.17493121381319،19.0584668493702

ولكن لدي مشكلة في هذه البيانات:

double [] a = {52.00118, 17.53933};
double [] b = {52.00278, 17.54008};
//point
double [] c = {52.008308, 17.542927};

أقرب عقدة هي: 52.00834987257176،17.542691313436357 وهي خاطئة.

أعتقد أن هذا الخط المحدد من نقطتين ليس شريحة مغلقة.

إذا احتاج شخص ما، فهذه هي الإجابة Loleksy على C #

        private static double _eQuatorialEarthRadius = 6378.1370D;
        private static double _d2r = (Math.PI / 180D);
        private static double PRECISION = 0.1;

        // Haversine Algorithm
        // source: http://stackoverflow.com/questions/365826/calculate-distance-between-2-gps-coordinates

        private static double HaversineInM(double lat1, double long1, double lat2, double long2) {
            return  (1000D * HaversineInKM(lat1, long1, lat2, long2));
        }

        private static double HaversineInKM(double lat1, double long1, double lat2, double long2) {
            double dlong = (long2 - long1) * _d2r;
            double dlat = (lat2 - lat1) * _d2r;
            double a = Math.Pow(Math.Sin(dlat / 2D), 2D) + Math.Cos(lat1 * _d2r) * Math.Cos(lat2 * _d2r)
                    * Math.Pow(Math.Sin(dlong / 2D), 2D);
            double c = 2D * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1D - a));
            double d = _eQuatorialEarthRadius * c;
            return d;
        }

        // Distance between a point and a line
        static double pointLineDistanceGEO(double[] a, double[] b, double[] c)
        {

            double[] nearestNode = nearestPointGreatCircle(a, b, c);
            double result = HaversineInKM(c[0], c[1], nearestNode[0], nearestNode[1]);

            return result;
        }

        // source: http://stackoverflow.com/questions/1299567/how-to-calculate-distance-from-a-point-to-a-line-segment-on-a-sphere
        private static double[] nearestPointGreatCircle(double[] a, double[] b, double [] c)
        {
            double[] a_ = toCartsian(a);
            double[] b_ = toCartsian(b);
            double[] c_ = toCartsian(c);

            double[] G = vectorProduct(a_, b_);
            double[] F = vectorProduct(c_, G);
            double[] t = vectorProduct(G, F);

            return fromCartsian(multiplyByScalar(normalize(t), _eQuatorialEarthRadius));
        }

        private static double[] nearestPointSegment (double[] a, double[] b, double[] c)
        {
           double[] t= nearestPointGreatCircle(a,b,c);
           if (onSegment(a,b,t))
             return t;
           return (HaversineInKM(a[0], a[1], c[0], c[1]) < HaversineInKM(b[0], b[1], c[0], c[1])) ? a : b;
        }

         private static bool onSegment (double[] a, double[] b, double[] t)
           {
             // should be   return distance(a,t)+distance(b,t)==distance(a,b), 
             // but due to rounding errors, we use: 
             return Math.Abs(HaversineInKM(a[0], a[1], b[0], b[1])-HaversineInKM(a[0], a[1], t[0], t[1])-HaversineInKM(b[0], b[1], t[0], t[1])) < PRECISION;
           }


        // source: http://stackoverflow.com/questions/1185408/converting-from-longitude-latitude-to-cartesian-coordinates
        private static double[] toCartsian(double[] coord) {
            double[] result = new double[3];
            result[0] = _eQuatorialEarthRadius * Math.Cos(deg2rad(coord[0])) * Math.Cos(deg2rad(coord[1]));
            result[1] = _eQuatorialEarthRadius * Math.Cos(deg2rad(coord[0])) * Math.Sin(deg2rad(coord[1]));
            result[2] = _eQuatorialEarthRadius * Math.Sin(deg2rad(coord[0]));
            return result;
        }

        private static double[] fromCartsian(double[] coord){
            double[] result = new double[2];
            result[0] = rad2deg(Math.Asin(coord[2] / _eQuatorialEarthRadius));
            result[1] = rad2deg(Math.Atan2(coord[1], coord[0]));

            return result;
        }


        // Basic functions
        private static double[] vectorProduct (double[] a, double[] b){
            double[] result = new double[3];
            result[0] = a[1] * b[2] - a[2] * b[1];
            result[1] = a[2] * b[0] - a[0] * b[2];
            result[2] = a[0] * b[1] - a[1] * b[0];

            return result;
        }

        private static double[] normalize(double[] t) {
            double length = Math.Sqrt((t[0] * t[0]) + (t[1] * t[1]) + (t[2] * t[2]));
            double[] result = new double[3];
            result[0] = t[0]/length;
            result[1] = t[1]/length;
            result[2] = t[2]/length;
            return result;
        }

        private static double[] multiplyByScalar(double[] normalize, double k) {
            double[] result = new double[3];
            result[0] = normalize[0]*k;
            result[1] = normalize[1]*k;
            result[2] = normalize[2]*k;
            return result;
        }

للمسافة حتى بضعة آلاف متر وأود أن تبسيط المشكلة من المجال إلى الطائرة. ثم، فإن القضية جميلة ببساطة كحساسبة مثل مثلث سهل الاستخدام:

لدينا نقاط A و B وابحث عن المسافة X إلى LINE AB. ثم:

Location a;
Location b;
Location x;

double ax = a.distanceTo(x);
double alfa = (Math.abs(a.bearingTo(b) - a.bearingTo(x))) / 180
            * Math.PI;
double distance = Math.sin(alfa) * ax;

أقصر مسافة بين نقطتين على الكرة هو الجانب الأصغر من الدائرة العظيمة التي تمر عبر النقطتين. أنا متأكد من أنك تعرف هذا بالفعل. هناك سؤال مماثل هنا http://www.physicsforums.com/archive/index.php/t-178252.html. هذا قد يساعدك على طرازه رياضيا.

لست متأكدا من مدى احتمال الحصول على مثال مشفر لهذا، ليكون صادقا.

أبحث في الأساس عن نفس الشيء الآن، إلا أنني أتحدث بدقة لا يهتم بوجود شريحة من دائرة رائعة، بل فقط أريد المسافة إلى أي نقطة على الدائرة الكاملة.

اثنين من الروابط التي أقوم بها حاليا:

هذه الصفحة يذكر "المسافة عبر المسافة"، والتي يبدو أنها أساسا ما تبحث عنه.

أيضا، في مؤشر الترابط التالي الموجود في القائمة البريدية التالية، يبدو أن هذه المحاولة (1) تحديد أقرب نقطة في الدائرة الكبرى مع نفس الصيغة المستخدمة للمسافة المسافة على مستوى 2D (مع line_location_locate_point)، ثم (2) حساب المسافة بين ذلك والشدة الثالثة على كروي. ليس لدي أي فكرة عما إذا كانت الخطوة الرياضية (1) صحيحة، لكنني سأفاجأت.

http://postgis.refractions.net/pipermail/postgis-users/2009-july/023903.html

أخيرا، رأيت فقط أن ما يلي مرتبط تحت "ذات الصلة":

المسافة من نقطة إلى خط وظيفة دائرة كبيرة لا تعمل بشكل صحيح.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top