Dont afraid maps. Google maps v3, v2, ovi, yandex maps. Maps is your friend. Try them, love them. I`ll show how to cook them

Tuesday, March 23, 2010

Back to basics - lets start the map

Mission:Start map

Goal:Reduce fails

Step1. Test yourself

In the beggining - test your own map application.

Do this in two steps:

Impliment some king of "loading guard", it will fires if map wont start in 40 seconds



 setTimeout(function(){

    somehow test what map is ready and workable.

    for example - map will fires event "weAreDone".

    catch it!

 },40000);

 /* i thing 40sec is alot of time*/


and then define global error catcher



function global_error_catcher(msg, url, num, more){

    if(msg && msg.name){

        var _event=msg;

        msg=(_event.name+"] "+e.fileName+":"+e.lineNumber+"\n"+e+"\n"+e.stack);

    }

    if(typeof(msg)=='object'){

     var evt=msg;

     msg='';

     for(i in evt)msg+=i+":"+evt[i]+"\n";

    }

    var textout=("global error:\n"+msg+",\n"+url+":"+num+"\n:"+more);

    new Ajax.Request("loader/report_site_error.php",{parameters:{'message':textout},async:true});

    return true;

 }

 window.onerror=global_error_catcher;


Then put this application online and see logs.

So i say "if map working for you - it can not work for others"

And this is true.



some time ago i got bug report "hey men, there is no map on your site".

I test it - map exists, i test my site on other PC.. map exists..

I try to ask friends to test site... and.. MAP EXISTS!!!

but next day i`v got same bug report from other user..

So i impliment listed debug scripts and then i see..
2000 users per day see my site with out map.
2000 users of 50k - 4%

So how to fix it

Step2. Start map

First include google loader


 

function add_script(src){

    var fileref=document.createElement("script");

    fileref.type="text/javascript";

      fileref.src=src;

      document.getElementsByTagName("head")[0].appendChild(fileref);

}



function _do_requestLoader(domain){

    add_script("http://www.google."+domain+"/jsapi?hl=ru&key="+GM_APIKEY);

}



var requestLoader_try=0;

function requestLoader(){

    if(requestLoader_try==0)_do_requestLoader("your domain code(.us,.ru,not just .com)");

    else                    _do_requestLoader("com");

    requestLoader_try++;

    

    map.__init_loadingCycle(0);

}


Next try to call it and include google maps



GoogleMap.prototype.__init_loadingCycle = function (tick){

    clearTimeout(this.__loadingTm); 

    if(tick++>200){

        requestLoader();

        return;

    }

    var thisworker=this;

    //we have google loaded. sometimes( apple\safari google loads in 2-5 tick! )

    if(window.google && typeof(window.google)!=undefined && typeof(window.google.load)=='function'){

        this.console("google start at "+tick+" tick");

        thisworker._enterState('GMaps.load',function(){

        google.load("maps", "3", {other_params:"sensor=true",

                    "callback" : function(){

                        if(thisworker.isClosed){

                            thisworker.console("google post-activaion");

                            return;

                        }

                        thisworker._enterState("google start",function(){

                        thisworker.start();

                        },0);

                        },                

                    "sensor":"true",

                    //and some others params like

                    "language" : "ru",

                    "hl":"ru",

                    "base_domain":"maps.google.ru"

                    });

        },0);

    }

    else{

        //start timer

        this.__loadingTm=setTimeout(function(){thisworker.__init_loadingCycle(tick)},10);

    }

}


Next wait until maps starts, and request maps projection



//lest define projection helper

function GoogleV3ProjectionHelperOverlay(map)

{

    this.setMap(map);

}



GoogleMap.prototype.start = function ()

{

    var _this=this;

    //leave loading state

    this._leaveState(this.loadingState);

    var myOptions = {

      zoom: this.driver.getZoom(),

      center: this.fromLatLng(this.driver.getPosition()),

      mapTypeId: this.decodeMapType(this.driver.getMapType()),

      disableDefaultUI:true,

      scrollwheel:true,

      keyboardShortcuts:false      

    }

    var thisworker=this;    

    this.api = new google.maps.Map(this.divId,myOptions);        

    this.helperOverlay=0;

    this.startIniter=0;

    

    //then define help projector

    GoogleV3ProjectionHelperOverlay.prototype = new google.maps.OverlayView();

    GoogleV3ProjectionHelperOverlay.prototype.draw = function () {

        if (!this.ready) {

            google.maps.event.trigger(this, 'ready');            

            this.ready = true;

            

            //if this overlay ready - start maps

            this.tearMapOn(0);

        }

    };

    

    GoogleV3ProjectionHelperOverlay.prototype.tearMapOn = function(tick){

        //if it have panes - start map

        if(this.getPanes()){

                 _this._enterState("google-doStartInit - "+tick,function(){                 

                 _this.doStartInit();

            });

        }

        else{

            var __this=this;

            setTimeout(function(){__this.tearMapOn(tick++)},1);

        }

    }

    

    //sometimes it good to know how much time took loading of all tiles of map

    this.idleTimer=google.maps.event.addListener(this.api,'idle',function(){

     _this.weAreDone();

     google.maps.event.removeListener(_this.idleTimer);   

    });

    

    this.api.setCenter(myOptions.center);

    this.api.setZoom(myOptions.zoom);

    this.api.setMapTypeId(myOptions.mapTypeId);

    

    //call some callback on API init

    this.onReadyAPI('google','init',this);

    

    //init helperOverlay

    this.helperOverlay=new GoogleV3ProjectionHelperOverlay(this.api);

    this.helperOverlay.setMap(this.api);

    this.helperOverlay.draw();

}



//this function will be called this helperOverlay starts

GoogleMap.prototype.doStartInit = function ()

{

   if( this.startIniter )

     google.maps.event.removeListener(this.startIniter);   

   this.onMapReady();

}




Map started!

and after this step we can convert latlng to pixels as in v2.
Many users want fromLatLngToContainerPixel - here it is!



GoogleMap.prototype.modPointByOffset=function(point,fac){

     var bounds=this.api.getBounds();//this.getBounds();

     var swPoint = this.fromLatLngToDivPixel(bounds.getSouthWest());

     var nePoint = this.fromLatLngToDivPixel(bounds.getNorthEast());

     return {x:point.x-swPoint.x*fac,y:point.y-nePoint.y*fac};

}

     

GoogleMap.prototype.fromLatLngToContainerPixel=function(a){

    var point=this.fromLatLngToDivPixel(a);

    return this.modPointByOffset(point,1);

}

GoogleMap.prototype.fromContainerPixelToLatLng=function(a){

    var point=this.modPointByOffset(a,-1);

    return this.fromDivPixelToLatLng(point);

}

GoogleMap.prototype.fromLatLngToDivPixel      =function(a){

    if(!this.helperOverlay) return {x:0,y:0};

    return this.helperOverlay.getProjection().fromLatLngToDivPixel(a);



}

GoogleMap.prototype.fromDivPixelToLatLng      =function(a){

    return this.toLatLng(this.helperOverlay.getProjection().fromDivPixelToLatLng(a));

}


Step3. Test it again

So we use enter and leavestate functions. The log working flow. If some event "enters", but not "leaves" - we will log.



GoogleMap.prototype._leaveState = function(_event){

    var _this=this;

    _event.leave=_this.microtime();

    _this.console("["+_event.name+"] took " +(_event.leave-_event.enter));

    return _event;

}



GoogleMap.prototype.__callState = function (_event){

    try

    {

            _event.func();

            this._leaveState(_event) ;

    }catch(e){

        _event.error=e;

        this.console("state-error ["+_event.name+"] "+e.fileName+":"+e.lineNumber+"\n"+e+"\n"+e.stack);

        this._leaveState(_event) ;

        this.__stateException(_event,e);

        ;

    }

}



GoogleMap.prototype._enterState = function(name,func,sync,catcheble){

    var _event={'name':name,'func':func,'enter':this.microtime(),'leave':0,'catcheble':catcheble};

    var _this=this;

    this.console('entering ['+name+']'+this._stages.length);

    this._stages.push(_event);

    if(func){

        clearTimeout(this._stageTimeout);

        if(sync){ _this.__callState(_event);

        }

        else{

            this._stageTimeout=setTimeout(

                function(){

                    _this.__callState(_event);

                },1);

        }

    }

    return _event;

}






See error logs again.

W\O this patches 1-5% of request fails

with this patches still some request fails, but just some of this.



so test yourself and see. I test this on 4 sites with 150k unique visitors per day. And i think it is true that map exists not for all of then then you use "standart" way

What about you?




All of sections, i list here, is allready done by me. I need just to wrap code into article, and, yap, english article :)

If you want to know more - ask

How to start Google V3

No comments:

Post a Comment