حساب مربع المحيط مسافة معينة بعيدا عن الإحداثيات اللات / الطويلة في جافا

StackOverflow https://stackoverflow.com/questions/1689096

سؤال

بالنظر إلى الإحداثيات (LAT، Long)، أحاول حساب مربع مترابط مربعة مسافة معينة (على سبيل المثال 50 كم) بعيدا عن الإحداثيات. حتى تكون المدخلات لدي LAT، طويلة والمسافة وكإخراج وأود أن اثنين من الإحداثيات؛ واحد كونه الزاوية الجنوبية الغربية (أسفل اليسار) وكونه الزاوية الشمالية الشرقية (اليمين العلوي). لقد رأيت أكثر من إجابات هنا في محاولة لمعالجة هذا السؤال في بيثون، لكنني أبحث عن تطبيق جافا على وجه الخصوص.

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

لا يجب أن يكون دقيقا للغاية (+/- 20٪ على ما يرام) وسيتم استخدامه فقط لحساب المربعات المحيطة على مسافات صغيرة (لا يزيد عن 150 كم). لذلك أنا سعيد للتضحية ببعض الدقة لخوارزمية فعالة. أي مساعدة هي محل تقدير كبير.

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

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

المحلول

كتبت مقالة حول العثور على الإحداثيات المحيطة:

http://janmatuschek.de/latitudelongitorygounts.

تشرح المقال الصيغ ويوفر أيضا تنفيذ جافا. (يظهر أيضا سبب عدم وجود صيغة الحديد ل MIN / MAX الطول.)

نصائح أخرى

double R = 6371;  // earth radius in km

double radius = 50; // km

double x1 = lon - Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)));

double x2 = lon + Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)));

double y1 = lat + Math.toDegrees(radius/R);

double y2 = lat - Math.toDegrees(radius/R);

على الرغم من أنني سوف أوصي أيضا JTS.

import com.vividsolutions.jts.geom.Envelope;

...
Envelope env = new Envelope(centerPoint.getCoordinate());
env.expandBy(distance_in_degrees); 
...

الآن يحتوي Env على مغلفك. انها ليست في الواقع "مربع" (مهما كان ذلك يعني على سطح المجال)، ولكن يجب القيام به.

يجب أن تلاحظ أن المسافة في الدرجات ستعتمد على خط عرض نقطة المركز. في خط الاستواء، هناك درجة حرارة واحدة من خط العرض حوالي 111 كم، ولكن في نيويورك، إنها فقط حوالي 75 كم.

الشيء الرائع حقا هو أنه يمكنك إرم كل نقاطك في com.vividsolutions.jts.index.strtree.STRtree ثم استخدمه لحساب النقاط بسرعة داخل هذا المغلف.

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

خصيصا للخوارزمية من جان فيليب ماتوسشيك في http://janmatuschek.de/latitudelongituteboundingcoordinates#usindex. وشملت مستطيل كبير جدا من (-37، -90، -180، 180) مقابل كل نقطة تقريبا في أستراليا. هذا يضرب مستخدمين كبيرين في قاعدة البيانات والمسافة يجب حسابها لجميع المستخدمين في نصف البلد تقريبا.

لقد وجدت أن خوارزمية توروبال API للأرض من معهد روتشستر للتكنولوجيا يعمل بشكل أفضل حول القطب وكذلك في أي مكان آخر وهو أسهل بكثير.

https://www.rit.edu/drupal/api/drupal/sites٪21all٪21Modules٪21location٪21earth.inc/7.54.

يستخدم earth_latitude_range و earth_longitude_range من الخوارزمية أعلاه لحساب المستطيل المحيط

هنا هو التنفيذ هو جافا

    /**
 * Get bouding rectangle using Drupal Earth Algorithm
 * @see https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54
 * @param lat
 * @param lng
 * @param distance
 * @return
 */
default BoundingRectangle getBoundingRectangleDrupalEarthAlgo(double lat, double lng, int distance) {
    lng = Math.toRadians(lng);
    lat = Math.toRadians(lat);
    double radius = earth_radius(lat);
    List<Double> retLats = earth_latitude_range(lat, radius, distance);
    List<Double> retLngs = earth_longitude_range(lat, lng, radius, distance);
    return new BoundingRectangle(retLats.get(0), retLats.get(1), retLngs.get(0), retLngs.get(1));
}


/**
 * Calculate latitude range based on earths radius at a given point
 * @param latitude
 * @param longitude
 * @param distance
 * @return
 */
default List<Double> earth_latitude_range(double lat, double radius, double distance) {
      // Estimate the min and max latitudes within distance of a given location.

      double angle = distance / radius;
      double minlat = lat - angle;
      double maxlat = lat + angle;
      double rightangle = Math.PI / 2;
      // Wrapped around the south pole.
      if (minlat < -rightangle) {
        double overshoot = -minlat - rightangle;
        minlat = -rightangle + overshoot;
        if (minlat > maxlat) {
          maxlat = minlat;
        }
        minlat = -rightangle;
      }
      // Wrapped around the north pole.
      if (maxlat > rightangle) {
        double overshoot = maxlat - rightangle;
        maxlat = rightangle - overshoot;
        if (maxlat < minlat) {
          minlat = maxlat;
        }
        maxlat = rightangle;
      }
      List<Double> ret = new ArrayList<>();
      ret.add((minlat));
      ret.add((maxlat));
      return ret;
    }

/**
 * Calculate longitude range based on earths radius at a given point
 * @param lat
 * @param lng
 * @param earth_radius
 * @param distance
 * @return
 */
default List<Double> earth_longitude_range(double lat, double lng, double earth_radius, int distance) {
      // Estimate the min and max longitudes within distance of a given location.
      double radius = earth_radius * Math.cos(lat);

      double angle;
      if (radius > 0) {
        angle = Math.abs(distance / radius);
        angle = Math.min(angle, Math.PI);
      }
      else {
        angle = Math.PI;
      }
      double minlong = lng - angle;
      double maxlong = lng + angle;
      if (minlong < -Math.PI) {
        minlong = minlong + Math.PI * 2;
      }
      if (maxlong > Math.PI) {
        maxlong = maxlong - Math.PI * 2;
      }

      List<Double> ret = new ArrayList<>();
      ret.add((minlong));
      ret.add((maxlong));
      return ret;
    }

/**
 * Calculate earth radius at given latitude
 * @param latitude
 * @return
 */
default Double earth_radius(double latitude) {
      // Estimate the Earth's radius at a given latitude.
      // Default to an approximate average radius for the United States.
      double lat = Math.toRadians(latitude);

      double x = Math.cos(lat) / 6378137.0;
      double y = Math.sin(lat) / (6378137.0 * (1 - (1 / 298.257223563)));

      //Make sure earth's radius is in km , not meters
      return (1 / (Math.sqrt(x * x + y * y)))/1000;
    }

واستخدام صيغة حساب المسافة الموثقة بواسطة خرائط جوجل لحساب المسافة

https://developers.google.com/maps/solutions/store-locator/clothing-store-locator#utputting-data-as-xml-using-php.

للبحث عن طريق الكيلومترات بدلا من الأميال، استبدل 3959 مع 6371.ل (LAT، LNG) = (37، -122) وجدول علامات مع الأعمدة LAT و LNG, ، الصيغة هي:

SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;

فيما يلي حلا بسيطا اعتدت أن تولد مربع يحمل إحداثيات أستخدمه Geonames Citiejson API. للحصول على المدن الكبيرة القريبة من الإحداثيات العشرية GPS.

هذه طريقة Java من مستودع GitHub الخاص بي: fusiontablemodifyjava.

كان لدي موقع GPS عشري وأحتاج إلى العثور على أكبر مدينة / ولاية "قريبة" هذا الموقع. كنت بحاجة إلى مربع محيط دقيق نسبيا لتمرير Webservice إلى CITISEJSON GEOOMESSERVICE لإعادة أكبر مدينة في المربع المحيط. اجتاز الموقع و "دائرة نصف قطرها" أنا مهتم (على بعد كيلومتريا) ويعيد العودة إلى الشمال والجنوب والشرق.

(لقد وجدت هذه الموارد مفيدة في إجراء بحثي:

حساب المسافة، تحمل وأكثر بين نقاط خطوط الطول / خط الطول.

خط الطول - ويكيبيديا)

انها ليست دقيقة فائقة ولكن دقيقة بما فيه الكفاية لما كنت أستخدمه له:

    // Compute bounding Box coordinates for use with Geonames API.
    class BoundingBox
    {
        public double north, south, east, west;
        public BoundingBox(String location, float km)
        {
             //System.out.println(location + " : "+ km);
            String[] parts = location.replaceAll("\\s","").split(","); //remove spaces and split on ,

            double lat = Double.parseDouble(parts[0]);
            double lng = Double.parseDouble(parts[1]);

            double adjust = .008983112; // 1km in degrees at equator.
            //adjust = 0.008983152770714983; // 1km in degrees at equator.

            //System.out.println("deg: "+(1.0/40075.017)*360.0);


            north = lat + ( km * adjust);
            south = lat - ( km * adjust);

            double lngRatio = 1/Math.cos(Math.toRadians(lat)); //ratio for lng size
            //System.out.println("lngRatio: "+lngRatio);

            east = lng + (km * adjust) * lngRatio;
            west = lng - (km * adjust) * lngRatio;
        }

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