Convert from latitude, longitude to x, y

后端 未结 5 1793
夕颜
夕颜 2020-11-30 05:10

I want to convert GPS location (latitude, longitude) into x,y coordinates. I found many links about this topic and applied it, but it doesn\'t give me the correct answer!

相关标签:
5条回答
  • 2020-11-30 05:15

    Since this page shows up on top of google while i searched for this same problem, I would like to provide a more practical answers. The answer by MVG is correct but rather theoratical.

    I have made a track plotting app for the fitbit ionic in javascript. The code below is how I tackled the problem.

    //LOCATION PROVIDER
    index.js
    var gpsFix = false;
    var circumferenceAtLat = 0;
    function locationSuccess(pos){
      if(!gpsFix){
        gpsFix = true;
        circumferenceAtLat = Math.cos(pos.coords.latitude*0.01745329251)*111305;
      }
      pos.x:Math.round(pos.coords.longitude*circumferenceAtLat),
      pos.y:Math.round(pos.coords.latitude*110919), 
      plotTrack(pos);
    }
    

    plotting.js

    plotTrack(position){
    
    let x = Math.round((this.segments[i].start.x - this.bounds.minX)*this.scale);
    let y = Math.round(this.bounds.maxY - this.segments[i].start.y)*this.scale; //heights needs to be inverted
    
    //redraw?
    let redraw = false;
    
    //x or y bounds?
     if(position.x>this.bounds.maxX){
       this.bounds.maxX = (position.x-this.bounds.minX)*1.1+this.bounds.minX; //increase by 10%
       redraw = true;
     }
     if(position.x<this.bounds.minX){
       this.bounds.minX = this.bounds.maxX-(this.bounds.maxX-position.x)*1.1;
        redraw = true;
     };
     if(position.y>this.bounds.maxY){
       this.bounds.maxY = (position.y-this.bounds.minY)*1.1+this.bounds.minY; //increase by 10%
        redraw = true;
     }
     if(position.y<this.bounds.minY){
       this.bounds.minY = this.bounds.maxY-(this.bounds.maxY-position.y)*1.1;
        redraw = true;
     }
     if(redraw){
       reDraw();
     }
    }
    
    
    function reDraw(){
    
    let xScale = device.screen.width / (this.bounds.maxX-this.bounds.minX);
    let yScale = device.screen.height / (this.bounds.maxY-this.bounds.minY); 
    if(xScale<yScale) this.scale = xScale; 
    else this.scale = yScale;
    
    //Loop trough your object to redraw all of them
    }
    
    0 讨论(0)
  • 2020-11-30 05:16

    No exact solution exists

    There is no isometric map from the sphere to the plane. When you convert lat/lon coordinates from the sphere to x/y coordinates in the plane, you cannot hope that all lengths will be preserved by this operation. You have to accept some kind of deformation. Many different map projections do exist, which can achieve different compromises between preservations of lengths, angles and areas. For smallish parts of earth's surface, transverse Mercator is quite common. You might have heard about UTM. But there are many more.

    The formulas you quote compute x/y/z, i.e. a point in 3D space. But even there you'd not get correct distances automatically. The shortest distance between two points on the surface of the sphere would go through that sphere, whereas distances on the earth are mostly geodesic lengths following the surface. So they will be longer.

    Approximation for small areas

    If the part of the surface of the earth which you want to draw is relatively small, then you can use a very simple approximation. You can simply use the horizontal axis x to denote longitude λ, the vertical axis y to denote latitude φ. The ratio between these should not be 1:1, though. Instead you should use cos(φ0) as the aspect ratio, where φ0 denotes a latitude close to the center of your map. Furthermore, to convert from angles (measured in radians) to lengths, you multiply by the radius of the earth (which in this model is assumed to be a sphere).

    • x = r λ cos(φ0)
    • y = r φ

    This is simple equirectangular projection. In most cases, you'll be able to compute cos(φ0) only once, which makes subsequent computations of large numbers of points really cheap.

    0 讨论(0)
  • 2020-11-30 05:16

    For completeness I like to add my python adaption of @allexrm code which worked really well. Thanks again!

    radius = 6371    #Earth Radius in KM
    
    class referencePoint:
        def __init__(self, scrX, scrY, lat, lng):
            self.scrX = scrX
            self.scrY = scrY
            self.lat = lat
            self.lng = lng
    
    
    # Calculate global X and Y for top-left reference point        
    p0 = referencePoint(0, 0, 52.526470, 13.403215)
    # Calculate global X and Y for bottom-right reference point
    p1 = referencePoint(2244, 2060, 52.525035, 13.405809) 
    
    
    # This function converts lat and lng coordinates to GLOBAL X and Y positions
    def latlngToGlobalXY(lat, lng):
        # Calculates x based on cos of average of the latitudes
        x = radius*lng*math.cos((p0.lat + p1.lat)/2)
        # Calculates y based on latitude
        y = radius*lat
        return {'x': x, 'y': y}
    
    
    # This function converts lat and lng coordinates to SCREEN X and Y positions
    def latlngToScreenXY(lat, lng):
        # Calculate global X and Y for projection point
        pos = latlngToGlobalXY(lat, lng)
        # Calculate the percentage of Global X position in relation to total global width
        perX = ((pos['x']-p0.pos['x'])/(p1.pos['x'] - p0.pos['x']))
        # Calculate the percentage of Global Y position in relation to total global height
        perY = ((pos['y']-p0.pos['y'])/(p1.pos['y'] - p0.pos['y']))
    
        # Returns the screen position based on reference points
        return {
            'x': p0.scrX + (p1.scrX - p0.scrX)*perX,
            'y': p0.scrY + (p1.scrY - p0.scrY)*perY
        }
    
    
    pos = latlngToScreenXY(52.525607, 13.404572);
    
    

    pos['x] and pos['y] contain the translated x & y coordinates of the lat & lng (52.525607, 13.404572)

    I hope this is helpful for anyone looking like me for the proper solution to the problem of translating lat lng into a local reference coordinate system.

    Best

    0 讨论(0)
  • 2020-11-30 05:24

    Its better to convert to utm coordinates, and treat that as x and y.

    import utm
    u = utm.from_latlon(12.917091, 77.573586)
    

    The result will be (779260.623156606, 1429369.8665238516, 43, 'P') The first two can be treated as x,y coordinates, the 43P is the UTM Zone, which can be ignored for small areas (width upto 668 km).

    0 讨论(0)
  • 2020-11-30 05:27

    I want to share with you how I managed the problem. I've used the equirectangular projection just like @MvG said, but this method gives you X and Y positions related to the globe (or the entire map), this means that you get global positions. In my case, I wanted to convert coordinates in a small area (about 500m square), so I related the projection point to another 2 points, getting the global positions and relating to local (on screen) positions, just like this:

    First, I choose 2 points (top-left and bottom-right) around the area where I want to project, just like this picture:

    Once I have the global reference area in lat and lng, I do the same for screen positions. The objects containing this data are shown below.

    //top-left reference point
    var p0 = {
        scrX: 23.69,        // Minimum X position on screen
        scrY: -0.5,         // Minimum Y position on screen
        lat: -22.814895,    // Latitude
        lng: -47.072892     // Longitude
    }
    //bottom-right reference point
    var p1 = {
        scrX: 276,          // Maximum X position on screen
        scrY: 178.9,        // Maximum Y position on screen
        lat: -22.816419,    // Latitude
        lng: -47.070563     // Longitude
    }
    var radius = 6.371;     //Earth Radius in Km
    
    //## Now I can calculate the global X and Y for each reference point ##\\
    
    // This function converts lat and lng coordinates to GLOBAL X and Y positions
    function latlngToGlobalXY(lat, lng){
        //Calculates x based on cos of average of the latitudes
        let x = radius*lng*Math.cos((p0.lat + p1.lat)/2);
        //Calculates y based on latitude
        let y = radius*lat;
        return {x: x, y: y}
    }
    // Calculate global X and Y for top-left reference point
    p0.pos = latlngToGlobalXY(p0.lat, p0.lng);
    // Calculate global X and Y for bottom-right reference point
    p1.pos = latlngToGlobalXY(p1.lat, p1.lng);
    
    /*
    * This gives me the X and Y in relation to map for the 2 reference points.
    * Now we have the global AND screen areas and then we can relate both for the projection point.
    */
    
    // This function converts lat and lng coordinates to SCREEN X and Y positions
    function latlngToScreenXY(lat, lng){
        //Calculate global X and Y for projection point
        let pos = latlngToGlobalXY(lat, lng);
        //Calculate the percentage of Global X position in relation to total global width
        pos.perX = ((pos.x-p0.pos.x)/(p1.pos.x - p0.pos.x));
        //Calculate the percentage of Global Y position in relation to total global height
        pos.perY = ((pos.y-p0.pos.y)/(p1.pos.y - p0.pos.y));
    
        //Returns the screen position based on reference points
        return {
            x: p0.scrX + (p1.scrX - p0.scrX)*pos.perX,
            y: p0.scrY + (p1.scrY - p0.scrY)*pos.perY
        }
    }
    
    //# The usage is like this #\\
    
    var pos = latlngToScreenXY(-22.815319, -47.071718);
    $point = $("#point-to-project");
    $point.css("left", pos.x+"em");
    $point.css("top", pos.y+"em");
    

    As you can see, I made this in javascript, but the calculations can be translated to any language.

    PS: I'm applying the converted positions to an html element whose id is "point-to-project". In order to use this piece of code on your project you shall create this element (styled as position absolute) or change the "usage" block.

    0 讨论(0)
提交回复
热议问题