title: Using Yandex map tiles in Google Maps v3
body: <p>I have Yandex map tiles (Russian) working in Google Maps API v2 but something's not working in Google Maps API v3, see the following jsfiddle: <a href="http://jsfiddle.net/VkGjq/1/" rel="nofollow">http://jsfiddle.net/VkGjq/1/</a></p>
  
  <p>Note that when switching between the Google road map and the Yandex tiles they don't line up &amp; the marker is in the wrong position.</p>
  
  <p>For Maps v2, I made an equivalent jsfiddle but it's broken since it needs an API key: <a href="http://jsfiddle.net/ggrgQ/1/" rel="nofollow">http://jsfiddle.net/ggrgQ/1/</a></p>
  
  <p>You can see similar code here, but you will have to navigate to Moscow manually as Yandex doesn't have any decent data outside of Russia: <a href="http://gpsloglabs.com/share/2367c16f3a0e75b05ac8a5529afba225dd929518/" rel="nofollow">http://gpsloglabs.com/share/2367c16f3a0e75b05ac8a5529afba225dd929518/</a></p>
  
  <p>I don't recall where I got the v3 code, but the constants appear to roughly correspond to the v2 version. Other than that, I don't understand what the projection is doing so I'm stuck.</p>
  
  <p>Any ideas?</p>
  
  <p>The code from the jsfiddle is as follows:</p>
  
  <pre><code>var center = new google.maps.LatLng(55.75, 37.62);
  var mapOptions = {
      zoom: 10,
      center: center,
      mapTypeId: google.maps.MapTypeId.ROADMAP
  };
  var map = new google.maps.Map(document.getElementById('map'), mapOptions);
  
  new google.maps.Marker({map: map, position: center});
  
  map.mapTypes.set("Yandex",
                   new google.maps.ImageMapType(
                       {getTileUrl: function(coord, zoom) {
                           return "http://vec0"+((coord.x+coord.y)%5)+".maps.yandex.net/tiles?l=map&amp;v=2.16.0&amp;x=" + 
                               coord.x + "&amp;y=" + coord.y + "&amp;z=" + zoom + "";
                       },
                        tileSize: new google.maps.Size(256, 256),
                        isPng: true,
                        alt: "Yandex",
                        name: "Yandex",
                        projection: new YandexProjection(),
                        maxZoom: 17}));
  
  map.setOptions({mapTypeControlOptions: {mapTypeIds: [google.maps.MapTypeId.ROADMAP, "Yandex"]} });
  
  function YandexProjection() {
      this.pixelOrigin_ = new google.maps.Point(128,128);
      var MERCATOR_RANGE = 256;
      this.pixelsPerLonDegree_ = MERCATOR_RANGE / 360;
      this.pixelsPerLonRadian_ = MERCATOR_RANGE / (2 * Math.PI);
  
      this.fromLatLngToPoint = function(latLng) {
          function atanh(x) {
              return 0.5*Math.log((1+x)/(1-x));
          }
          function degreesToRadians(deg) {
              return deg * (Math.PI / 180);
          }
          function bound(value, opt_min, opt_max) {
              if (opt_min != null) value = Math.max(value, opt_min);
              if (opt_max != null) value = Math.min(value, opt_max);
              return value;
          }
  
          var origin = this.pixelOrigin_;
          var exct = 0.0818197;
          var z = Math.sin(latLng.lat()/180*Math.PI);
          return new google.maps.Point(origin.x + latLng.lng() *this.pixelsPerLonDegree_,
                                       Math.abs(origin.y - this.pixelsPerLonRadian_*(atanh(z)-exct*atanh(exct*z))));
      };
  
      this.fromPointToLatLng = function(point) {
          var origin = this.pixelOrigin_;
          var lng = (point.x - origin.x) / this.pixelsPerLonDegree_;
          var latRadians = (point.y - origin.y) / -this.pixelsPerLonRadian_;
          var lat = Math.abs((2*Math.atan(Math.exp(latRadians))-Math.PI/2)*180/Math.PI);
          var Zu = lat/(180/Math.PI);
          var Zum1 = Zu+1;
          var exct = 0.0818197;
          var yy = -Math.abs(((point.y)-128));
          while (Math.abs(Zum1-Zu)&gt;0.0000001){
          Zum1 = Zu;
          Zu = Math.asin(1-((1+Math.sin(Zum1))*Math.pow(1-exct*Math.sin(Zum1),exct))
                         / (Math.exp((2*yy)/-(256/(2*Math.PI)))*Math.pow(1+exct*Math.sin(Zum1),exct)));
          }
          if (point.y&gt;256/2) {
              lat=-Zu*180/Math.PI;
          } else {
              lat=Zu*180/Math.PI;
          }
          return new google.maps.LatLng(lat, lng);
      };
  
      return this;
  }
  </code></pre>