0

There are lots of question about the topic, and a lot of answers, but none seems to work on my code, not sure if its the conversion formula or something is faulty on the overall code. The longitude value seems to work as expected, but i've failed to translate the latitude to the y value on the map. I'll add a jsfiddle as well as the code, and some of the links of the formulas i've tried. For what i've read google maps is a kind of mercator projection and it uses a dual version (ellipsoid & spherical) of the projection, and the generic formula won't be that accurate in this case, but i'm not having a discrepancy of 20km instead the points place wherever they want in the map. I'll appreciate any help with this particular case, please try to provide some code and not point me out to a link unless you test it works with what im doing, since i'll probably already tested and did't work or i'm not sure how to implement it, thanks in advance.

http://jsfiddle.net/ve94P/

Here is the code

var mapReach = {
    init: function(mapId, canvasId) {
        // Some Google map configurations here....

        this.map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);

        this.nodes = [];
        this.$mapCanvas = $('#'+ mapId);
        this.canvas = document.getElementById(canvasId);

    },

    /**
     * Gets the current boundaries of the google map view-port in latitude and longitude
     * @return {} top-rigth & bottom-left lat,lon values.
     */

    getCurrentBounds: function(){
        var lng0 = this.map.getBounds().getNorthEast().lng(); // top-right x
        var lat0 = this.map.getBounds().getNorthEast().lat(); // top-right y
        var lng1 = this.map.getBounds().getSouthWest().lng(); // bottom-left x
        var lat1 = this.map.getBounds().getSouthWest().lat(); // bottom-left y

        return {topRight: {lat: lat0, lon: lng0}, bottomLeft: {lat: lat1, lon: lng1}};
    },

    /**
     * Adds nodes to be drawn on the canvas that overlays the map
     * @param {number} lat the latitude (y) geoposition of the node
     * @param {number} lon the longitude (x) geoposition of the node
     * @return {} top-rigth & bottom-left lat,lon values.
     */

    add: function(lat, lon){ // lat y , lon x
        this.nodes.push({lat:lat, lon:lon});
    },

    canvasSetUp: function(){
        if(!this.isCanvasSetted){
            this.width = this.canvas.width =  this.$mapCanvas.width();
            this.height = this.canvas.height = this.$mapCanvas.height();
            this.isCanvasSetted = true;
                    if(LOGGING){
                console.log('width:' + this.width );
                console.log('height:' + this.height );
                    }
        }
    },

    /**
     * Draws the nodes in place
     * @return void
     */

    draw: function() {
        var ctx  = this.canvas.getContext('2d');
        var currentBounds = this.getCurrentBounds();
        var yMin = currentBounds.bottomLeft.lat;
        var yMax = currentBounds.topRight.lat;
        var tempMinY = (yMin < 0) ?  Math.abs(yMin) : -yMin;
        yMax = (yMax > 0) ?  Math.abs(yMax) : yMax;
        var yRange = yMax + tempMinY;

        var xMin = currentBounds.bottomLeft.lon;
        var xMax = currentBounds.topRight.lon;
        var tempMinX = (xMin < 0) ?  Math.abs(xMin) : xMin;
        xMax = (xMax > 0) ?  Math.abs(xMax) : xMax;
        var xRange  = xMax + tempMinX;

        var mapLatBottomDegree = yMin * Math.PI / 180;

        if(LOGGING){
            console.log(currentBounds);
            console.log('x-range:' + xRange + ', y-range:' + yRange);
        }

        ctx.clearRect(0, 0, this.width, this.height);

        for (var i in this.nodes) {
            ctx.save();

            ctx.fillStyle = '#ffff00';

            // Translates lon to x value
            var x = (this.nodes[i].lon - xMin) * (this.width / xRange);

            var latRad = this.nodes[i].lat * Math.PI / 180;
            var mercN = Math.log( Math.tan( (Math.PI/4) + (latRad / 2) ) );
            var y = (this.height/2) - ( this.width * mercN / (2*Math.PI) );

            if(LOGGING){
                //console.log('x-delta:' + xDelta );
                console.log('lat:' + this.nodes[i].lat + ', lon:' + this.nodes[i].lon);
                console.log('y:' + y + ', x:' + x);
            }

            ctx.beginPath();
            ctx.arc(x, y, 4, 0, Math.PI*2, true);
            ctx.fill();
            ctx.closePath();

            ctx.restore();

        }


    },

    placeNodes: function() {
        // Lat, Lon Values for the following cities
        this.add(5, -74.226523);          // Bogota/Colombia
        this.add(48.944151, 2.263183);    // Paris/France
        this.add(-33.907758, 18.391732);  // Cape Town/South Africa
        this.add(49.254814, -123.087512); // Vancouver/Canada
        this.add(25.792680, -80.232110);  // Miami/Florida
        this.add(35.693089, 139.704742);  // Tokyo/Japan
        this.add(30.034134, 31.218933);   // Cairo/Egypt
        this.draw();
    }

};

var LOGGING = false;

$(document).ready(function(){
    mapReach.init('map-canvas', 'activity-reach');
    google.maps.event.addListener(mapReach.map, 'bounds_changed',
            function() {
                mapReach.canvasSetUp();
                mapReach.placeNodes();
            });
});

These are some of the formulas i've look into with out success, it might be i've implemented those in the wrong way or that they in fact won't work with this.

Covert latitude/longitude point to a pixels (x,y) on mercator projection

http://alastaira.wordpress.com/2011/01/23/the-google-maps-bing-maps-spherical-mercator-projection/

Converting longitude/latitude to X/Y coordinate

Mercator longitude and latitude calculations to x and y on a cropped map (of the UK)

Thanks for taking the time to read, and helping me out.

Community
  • 1
  • 1
Lu Roman
  • 2,220
  • 3
  • 25
  • 40

2 Answers2

1

You need also the mercator projection from the top and bottom of the map and also a factor. Then you can do a linear transformation:Convert lat/lon to pixel coordinate?.

Community
  • 1
  • 1
Micromega
  • 12,486
  • 7
  • 35
  • 72
  • Thanks for pointing out that i also needed to do a mercator projection on the bounds, and getting the factor, i tried implementing part of the code on that link but it wasn't working too well, not sure if the formula was off but it didn't work as i wanted, but thanks for your help – Lu Roman Mar 11 '14 at 21:00
0

I wasn't able to find the correct formula to translate the longitude to the x position, even using the same formula for the bounds and getting the zoom factor. So i ended up using the google api methods and a custom fix to find the offset when the screen can fit more than the whole map that is forced to tile it.

I based my code on alittle code i found on this web

http://krasimirtsonev.com/blog/article/google-maps-api-v3-convert-latlng-object-to-actual-pixels-point-object

I added this method to the mapReach object:

fromLatLngToPoint: function (lat, lng) {

        var bounds,
            maxX, minX, minXDefault, maxXDefault, xOffset,
            maxY, minY, northEast, southWest, topRight, bottomLeft,
            point, scale, worldPoint;

        bounds = this.getCurrentBounds();

        minXDefault = -180; // Minumun longitude value in google maps
        maxXDefault =  180; // Maximum '                             '

        maxX = bounds.topRight.lng;
        minX = bounds.bottomLeft.lng;
        /*
        When the map is shown on a hi-res screen at zoom lvl 3 (this won't work if lvl is zoom lvl is below 3 )
        it might have to tile up on the x axis and since the google map API returns the bounds from the top-right and
        bottom-left it might return the longitude of the tiled copy and not the real starting point, so we take that
        into account by setting the minX to the minXDefault in case that is greater that the maxX value, and we need to
        the difference between our maxDefault (180) and this "minX" when this one is actually greater than the maxX.
        After finding the offset we now can change the value of minX like i said before and take that offset and add it
        to the lng of the point we are trying to get the x,y position.
        */
        xOffset = (maxX < minX) ? maxXDefault - minX : 0;
        minX = (maxX > minX) ? minX : minXDefault;

        maxY = bounds.topRight.lat;
        minY = bounds.bottomLeft.lat;

        northEast = new google.maps.LatLng(maxY, maxX);
        southWest = new google.maps.LatLng(minY, minX);

        point = new google.maps.LatLng(lat, lng + xOffset);

        topRight = this.map.getProjection().fromLatLngToPoint(northEast);
        bottomLeft = this.map.getProjection().fromLatLngToPoint(southWest);
        scale = Math.pow(2, this.map.getZoom());
        worldPoint = this.map.getProjection().fromLatLngToPoint(point);
        return new google.maps.Point((worldPoint.x - bottomLeft.x) * scale, (worldPoint.y - topRight.y) * scale);
    }

And then the draw method is like this:

draw: function () {
        var ctx  = this.canvas.getContext('2d');

        ctx.clearRect(0, 0, this.width, this.height);

        for (var i in this.nodes) {
            ctx.save();

            ctx.fillStyle = '#ffff00';

            var pos = this.fromLatLngToPoint(this.nodes[i].lat, this.nodes[i].lng);

            ctx.beginPath();
            ctx.arc(pos.x, pos.y, 4, 0, Math.PI*2, true);
            ctx.fill();
            ctx.closePath();
        }
    }

Here is the jsfiddle http://jsfiddle.net/ve94P/1/

Note: This code should be refactor to take performance into account, but the main behavior is there.

Lu Roman
  • 2,220
  • 3
  • 25
  • 40