حسابات خط الطول وخطوط الطول إلى x و y على خريطة اقتصاص (في المملكة المتحدة)

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

سؤال

لدي هذه الصورة: http://imgur.com/99tsz.png. خريطة المملكة المتحدة (لا تشمل أيرلندا الجنوبية).

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

هذا هو الكود (للاستخدام في المعالجة. Js ولكن يمكن استخدامه كـ JS أو أي شيء):

// Size of the map
int width = 538;
int height = 811;
// X and Y boundaries
float westLong = -8.166667;
float eastLong = 1.762833;
float northLat = 58.666667;
float southLat = 49.95;

void drawPoint(float latitude, float longitude){

 fill(#000000);

 x = width * ((westLong-longitude)/(westLong-eastLong));
 y = (height * ((northLat-latitude)/(northLat-southLat)));

 console.log(x + ", " + y);
 ellipseMode(RADIUS);
 ellipse(x, y, 2, 2);    

}

ومع ذلك ، لم أتمكن من تنفيذ إسقاط Mercator على هذه القيم. المؤامرات دقيقة بشكل معقول لكنها ليست جيدة بما فيه الكفاية وهذا الإسقاط سيحلها.

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

مورد آخر هو النقاط المتطرفة في المملكة المتحدة حيث حصلت على قيم خطوط الطول والعرض في المربع المحيط حول المملكة المتحدة. هم أيضا هنا:

northLat = 58.666667; 
northLong = -3.366667; 
eastLat = 52.481167; 
eastLong = 1.762833; 
southLat = 49.95;
southLong = -5.2; 
westLat = 54.45;
westLong = -8.166667;

إذا كان بإمكان أي شخص مساعدتي في هذا ، فسأقدر ذلك كثيرًا!

شكرًا

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

المحلول

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

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

نصائح أخرى

كتبت وظيفة تفعل بالضبط ما كنت تبحث عنه. أعلم أن الوقت متأخر بعض الشيء ، لكن ربما هناك بعض الأشخاص الآخرين المهتمين.

تحتاج إلى خريطة هي إسقاط Mercator وتحتاج إلى معرفة مواقع LAT / LON لخريطتك. يمكنك الحصول على خرائط Mercator رائعة مع مواقف Lat / Lon مطابقة مثالية Tilemill وهو برنامج مجاني من MAPBOX!

أنا أستخدم هذا البرنامج النصي واختبرته مع بعض مواقع Google Earth. عملت مثالية على مستوى بكسل. في الواقع لم أختبر هذا على خرائط مختلفة أو أكبر. أتمنى ان تساعدك!

رافائيل ؛)

<?php

$mapWidth = 1500;
$mapHeight = 1577;

$mapLonLeft = 9.8;
$mapLonRight = 10.2;
$mapLonDelta = $mapLonRight - $mapLonLeft;

$mapLatBottom = 53.45;
$mapLatBottomDegree = $mapLatBottom * M_PI / 180;

function convertGeoToPixel($lat, $lon)
{
    global $mapWidth, $mapHeight, $mapLonLeft, $mapLonDelta, $mapLatBottom, $mapLatBottomDegree;

    $x = ($lon - $mapLonLeft) * ($mapWidth / $mapLonDelta);

    $lat = $lat * M_PI / 180;
    $worldMapWidth = (($mapWidth / $mapLonDelta) * 360) / (2 * M_PI);
    $mapOffsetY = ($worldMapWidth / 2 * log((1 + sin($mapLatBottomDegree)) / (1 - sin($mapLatBottomDegree))));
    $y = $mapHeight - (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);

    return array($x, $y);
}

$position = convertGeoToPixel(53.7, 9.95);
echo "x: ".$position[0]." / ".$position[1];

?>

إليكم الصورة التي قمت بإنشائها باستخدام Tilemill والتي استخدمتها في هذا المثال: map image

بالإضافة إلى ما نشره Raphael Wichmann (شكرًا ، بالمناسبة!) ، إليك الوظيفة العكسية ، في ActionScript:

function convertPixelToGeo(tx:Number, ty:Number):Point
{   
    /* called worldMapWidth in Raphael's Code, but I think that's the radius since it's the map width or circumference divided by 2*PI  */   
    var worldMapRadius:Number = mapWidth / mapLonDelta * 360/(2 * Math.PI);     
    var mapOffsetY:Number = ( worldMapRadius / 2 * Math.log( (1 + Math.sin(mapLatBottomRadian) ) / (1 - Math.sin(mapLatBottomRadian))  ));
    var equatorY:Number = mapHeight + mapOffsetY;   
    var a:Number = (equatorY-ty)/worldMapRadius;

    var lat:Number = 180/Math.PI * (2 * Math.atan(Math.exp(a)) - Math.PI/2);
    var long:Number = mapLonLeft+tx/mapWidth*mapLonDelta;
    return new Point(lat,long);
}

لقد قمت بتحويل رمز PHP الذي قدمه Raphael إلى JavaScript ويمكنك تأكيده على عمله ويعمل هذا الرمز بنفسي. كل الفضل في رافائيل.

/*
var mapWidth = 1500;
var mapHeight = 1577;

var mapLonLeft = 9.8;
var mapLonRight = 10.2;
var mapLonDelta = mapLonRight - mapLonLeft;

var mapLatBottom = 53.45;
var mapLatBottomDegree = mapLatBottom * Math.PI / 180;
*/

function convertGeoToPixel(latitude, longitude ,
                           mapWidth , // in pixels
                           mapHeight , // in pixels
                           mapLonLeft , // in degrees
                           mapLonDelta , // in degrees (mapLonRight - mapLonLeft);
                           mapLatBottom , // in degrees
                           mapLatBottomDegree) // in Radians
{
    var x = (longitude - mapLonLeft) * (mapWidth / mapLonDelta);

    latitude = latitude * Math.PI / 180;
    var worldMapWidth = ((mapWidth / mapLonDelta) * 360) / (2 * Math.PI);
    var mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomDegree)) / (1 - Math.sin(mapLatBottomDegree))));
    var y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(latitude)) / (1 - Math.sin(latitude)))) - mapOffsetY);

    return { "x": x , "y": y};
}

إليك تطبيق JavaScript آخر. هذا هو تبسيط حل Rob Willet أعلاه. بدلاً من طلب القيم المحسوبة كمعلمات للوظيفة ، فإنه يتطلب فقط قيمًا أساسية ويحسب كل شيء منها:

function convertGeoToPixel(latitude, longitude,
                  mapWidth, // in pixels
                  mapHeight, // in pixels
                  mapLngLeft, // in degrees. the longitude of the left side of the map (i.e. the longitude of whatever is depicted on the left-most part of the map image)
                  mapLngRight, // in degrees. the longitude of the right side of the map
                  mapLatBottom) // in degrees.  the latitude of the bottom of the map
{
    const mapLatBottomRad = mapLatBottom * Math.PI / 180
    const latitudeRad = latitude * Math.PI / 180
    const mapLngDelta = (mapLngRight - mapLngLeft)

    const worldMapWidth = ((mapWidth / mapLngDelta) * 360) / (2 * Math.PI)
    const mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomRad)) / (1 - Math.sin(mapLatBottomRad))))

    const x = (longitude - mapLngLeft) * (mapWidth / mapLngDelta)
    const y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(latitudeRad)) / (1 - Math.sin(latitudeRad)))) - mapOffsetY)

    return {x, y} // the pixel x,y value of this point on the map image
}

أعلم أن السؤال تم طرحه منذ فترة ، لكن مكتبة Proj4JS مثالية للتحول بين توقعات الخريطة المختلفة في JavaScript.

تميل خرائط المملكة المتحدة إلى استخدام الشبكة الوطنية لـ OSGB والتي تستند إلى إسقاط مركبة عرضية. بمعنى آخر. مثل Mercator التقليدية ولكن تحولت 90 درجة ، بحيث يصبح "خط الاستواء" خطوطا.

مقتطف Xarinko Actionscript في JavaScript (مع بعض قيم الاختبار)

var mapWidth = 1500;
var mapHeight = 1577;

var mapLonLeft = 9.8;
var mapLonRight = 10.2;
var mapLonDelta = mapLonRight - mapLonLeft;

var mapLatBottom = 53.45;
var mapLatBottomRadian = mapLatBottom * Math.PI / 180;



function convertPixelToGeo(tx, ty)
{   
    /* called worldMapWidth in Raphael's Code, but I think that's the radius since it's the map width or circumference divided by 2*PI  */   
    var worldMapRadius = mapWidth / mapLonDelta * 360/(2 * Math.PI);     
    var mapOffsetY = ( worldMapRadius / 2 * Math.log( (1 + Math.sin(mapLatBottomRadian) ) / (1 - Math.sin(mapLatBottomRadian))  ));
    var equatorY = mapHeight + mapOffsetY;   
    var a = (equatorY-ty)/worldMapRadius;

    var lat = 180/Math.PI * (2 * Math.atan(Math.exp(a)) - Math.PI/2);
    var long = mapLonLeft+tx/mapWidth*mapLonDelta;
    return [lat,long];
}

convertPixelToGeo(241,444)

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

إذا لم يكن D3 خيارًا للمستخدم النهائي-على سبيل المثال ، تحتاج إلى دعم IE 7/8-يمكنك تقديم D3 ثم تعطل الإحداثيات XY من ملف SVG الناتج الذي ينشئه D3. يمكنك بعد ذلك تقديم تلك الإحداثيات XY في رافائيل.

تعمل هذه الوظيفة بشكل رائع بالنسبة لي لأنني أريد تحديد mapheight استنادًا إلى الخريطة التي أريد رسمها. أقوم بإنشاء خرائط PDF. كل ما أحتاج إلى القيام به هو المرور في MAX LAT للخريطة ، Min LON ويعيد حجم البكسل للخريطة كـ [الارتفاع ، العرض].

Convergeotopixel (MaxLatitude ، MaxLongitude)

ملاحظة واحدة في الخطوة الأخيرة التي يتم فيها تعيين $ y ، لا تقم بطرح الحساب من mapheight إذا بدأ نظام الإحداثيات الخاص بك 'xy' في الأسفل/اليسار ، كما هو الحال مع pdfs ، وهذا سوف يقلل من الخريطة.

$y =  (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);

C# التنفيذ:

private Point ConvertGeoToPixel(
    double latitude, double longitude, // The coordinate to translate
    int imageWidth, int imageHeight, // The dimensions of the target space (in pixels)
    double mapLonLeft, double mapLonRight, double mapLatBottom // The bounds of the target space (in geo coordinates)
) {
    double mapLatBottomRad = mapLatBottom * Math.PI / 180;
    double latitudeRad = latitude * Math.PI / 180;

    double mapLonDelta = mapLonRight - mapLonLeft;
    double worldMapWidth = (imageWidth / mapLonDelta * 360) / (2 * Math.PI);
    double mapOffsetY = worldMapWidth / 2 * Math.Log((1 + Math.Sin(mapLatBottomRad)) / (1 - Math.Sin(mapLatBottomRad)));

    double x = (longitude - mapLonLeft) * (imageWidth / mapLonDelta);
    double y = imageHeight - ((worldMapWidth / 2 * Math.Log((1 + Math.Sin(latitudeRad)) / (1 - Math.Sin(latitudeRad)))) - mapOffsetY);

    return new Point()
    {
        X = Convert.ToInt32(x),
        Y = Convert.ToInt32(y)
    };
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top