Index: GMap.NET.Core/GMap.NET.Internals/Cache.cs
===================================================================
--- GMap.NET.Core/GMap.NET.Internals/Cache.cs	(revision 34795)
+++ GMap.NET.Core/GMap.NET.Internals/Cache.cs	(working copy)
@@ -405,6 +405,49 @@
          return ret;
       }
 
+      ///////////////////////
+      public void CacheTileOffset(string urlEnd, string content)
+      {
+          try
+          {
+              // precrete dir
+              if (!Directory.Exists(geoCache))
+              {
+                  Directory.CreateDirectory(geoCache);
+              }
+
+              StringBuilder file = new StringBuilder(geoCache);
+              file.AppendFormat("{0}.tileoffset", urlEnd);
+
+              File.WriteAllText(file.ToString(), content, Encoding.UTF8);
+          }
+          catch
+          {
+          }
+      }
+
+      public string GetTileOffsetFromCache(string urlEnd)
+      {
+          string ret = null;
+
+          try
+          {
+              StringBuilder file = new StringBuilder(geoCache);
+              file.AppendFormat("{0}.tileoffset", urlEnd);
+
+              if (File.Exists(file.ToString()))
+              {
+                  ret = File.ReadAllText(file.ToString(), Encoding.UTF8);
+              }
+          }
+          catch
+          {
+              ret = null;
+          }
+
+          return ret;
+      }
+
       #region PureImageCache Members
 
       bool PureImageCache.PutImageToCache(MemoryStream tile, MapType type, Point pos, int zoom)
Index: GMap.NET.Core/GMap.NET.Internals/Core.cs
===================================================================
--- GMap.NET.Core/GMap.NET.Internals/Core.cs	(revision 34795)
+++ GMap.NET.Core/GMap.NET.Internals/Core.cs	(working copy)
@@ -594,36 +594,37 @@
                {
                   PureImage img = GMaps.Instance.GetImageFrom(MapType.GoogleSatellite, task.Pos, task.Zoom);
                   PureImage img2 = GMaps.Instance.GetImageFrom(MapType.GoogleLabels, task.Pos, task.Zoom);
+
                   lock(t.Overlays)
                   {
-                     t.Overlays.Add(img);
-                     t.Overlays.Add(img2);
+                     t.Overlays.Add(new PureOverlay(img, GMaps.Instance.GetTileOffsetForChinaMap(MapType.GoogleSatellite, task.Pos, task.Zoom)));
+                     t.Overlays.Add(new PureOverlay(img2, GMaps.Instance.GetTileOffsetForChinaMap(MapType.GoogleLabels, task.Pos, task.Zoom)));
                   }
                }
                else if(MapType == MapType.YahooHybrid)
                {
                   PureImage img = GMaps.Instance.GetImageFrom(MapType.YahooSatellite, task.Pos, task.Zoom);
                   PureImage img2 = GMaps.Instance.GetImageFrom(MapType.YahooLabels, task.Pos, task.Zoom);
-                  lock(t.Overlays)
+                  lock (t.Overlays)
                   {
-                     t.Overlays.Add(img);
-                     t.Overlays.Add(img2);
+                      t.Overlays.Add(new PureOverlay(img));
+                      t.Overlays.Add(new PureOverlay(img2));
                   }
                }
                else if(MapType == MapType.GoogleHybridChina)
                {
                   PureImage img = GMaps.Instance.GetImageFrom(MapType.GoogleSatelliteChina, task.Pos, task.Zoom);
                   PureImage img2 = GMaps.Instance.GetImageFrom(MapType.GoogleLabelsChina, task.Pos, task.Zoom);
-                  lock(t.Overlays)
+                  lock (t.Overlays)
                   {
-                     t.Overlays.Add(img);
-                     t.Overlays.Add(img2);
+                      t.Overlays.Add(new PureOverlay(img, GMaps.Instance.GetTileOffsetForChinaMap(MapType.GoogleSatelliteChina, task.Pos, task.Zoom)));
+                      t.Overlays.Add(new PureOverlay(img2, GMaps.Instance.GetTileOffsetForChinaMap(MapType.GoogleLabelsChina, task.Pos, task.Zoom)));
                   }
                }
                else // single layer
                {
                   PureImage img = GMaps.Instance.GetImageFrom(MapType, task.Pos, task.Zoom);
-                  t.Overlays.Add(img);
+                  t.Overlays.Add(new PureOverlay(img, GMaps.Instance.GetTileOffsetForChinaMap(MapType, task.Pos, task.Zoom)));
                }
                Matrix[task.Pos] = t;
             }
Index: GMap.NET.Core/GMap.NET.Internals/Tile.cs
===================================================================
--- GMap.NET.Core/GMap.NET.Internals/Tile.cs	(revision 34795)
+++ GMap.NET.Core/GMap.NET.Internals/Tile.cs	(working copy)
@@ -3,6 +3,31 @@
 {
    using System.Collections.Generic;
 
+    internal class PureOverlay
+    {
+        public PureOverlay(PureImage image)
+        {
+            this.Image = image;
+            this.Offset = Point.Empty;
+        }
+        public PureOverlay(PureImage image, Point offset)
+        {
+            this.Image = image;
+            this.Offset = offset;
+        }
+        public PureImage Image
+        {
+            get;
+            set;
+        }
+
+        public Point Offset
+        {
+            get;
+            set;
+        }
+    }
+
    /// <summary>
    /// represent tile
    /// </summary>
@@ -11,7 +36,7 @@
       RenderMode mode;
       Point pos;
       int zoom;
-      public readonly List<PureImage> Overlays = new List<PureImage>(1);
+      public readonly List<PureOverlay> Overlays = new List<PureOverlay>(1);
 
       public Tile(int zoom, Point pos, RenderMode mode)
       {
@@ -24,11 +49,11 @@
       {
          lock(Overlays)
          {
-            foreach(PureImage img in Overlays)
+             foreach (PureOverlay img in Overlays)
             {
-               if(img != null)
+               if(img != null && img.Image != null)
                {
-                  img.Dispose();
+                  img.Image.Dispose();
                }
             }
             Overlays.Clear();
Index: GMap.NET.Core/GMap.NET/GMaps.cs
===================================================================
--- GMap.NET.Core/GMap.NET/GMaps.cs	(revision 34796)
+++ GMap.NET.Core/GMap.NET/GMaps.cs	(working copy)
@@ -586,7 +586,6 @@
 
                return string.Format("http://{0}{1}.google.com/{2}/v={3}&hl={4}&x={5}{6}&y={7}&z={8}&s={9}", server, GetServerNum(pos), request, VersionGoogleMap, language, pos.X, sec1, pos.Y, zoom, sec2);
             }
-            break;
 
             case MapType.GoogleSatellite:
             {
@@ -598,7 +597,6 @@
 
                return string.Format("http://{0}{1}.google.com/{2}/v={3}&hl={4}&x={5}{6}&y={7}&z={8}&s={9}", server, GetServerNum(pos), request, VersionGoogleSatellite, language, pos.X, sec1, pos.Y, zoom, sec2);
             }
-            break;
 
             case MapType.GoogleLabels:
             {
@@ -610,7 +608,6 @@
 
                return string.Format("http://{0}{1}.google.com/{2}/v={3}&hl={4}&x={5}{6}&y={7}&z={8}&s={9}", server, GetServerNum(pos), request, VersionGoogleLabels, language, pos.X, sec1, pos.Y, zoom, sec2);
             }
-            break;
 
             case MapType.GoogleTerrain:
             {
@@ -622,7 +619,6 @@
 
                return string.Format("http://{0}{1}.google.com/{2}/v={3}&hl={4}&x={5}{6}&y={7}&z={8}&s={9}", server, GetServerNum(pos), request, VersionGoogleTerrain, language, pos.X, sec1, pos.Y, zoom, sec2);
             }
-            break; 
             #endregion
 
             #region -- Google (China) version --
@@ -638,7 +634,6 @@
 
                return string.Format("http://{0}{1}.google.cn/{2}/v={3}&hl={4}&gl=cn&x={5}{6}&y={7}&z={8}&s={9}", server, GetServerNum(pos), request, VersionGoogleMapChina, "zh-CN", pos.X, sec1, pos.Y, zoom, sec2);
             }
-            break;
 
             case MapType.GoogleSatelliteChina:
             {
@@ -652,7 +647,6 @@
 
                return string.Format("http://{0}{1}.google.cn/{2}/v={3}&x={4}{5}&y={6}&z={7}&s={8}", server, GetServerNum(pos), request, VersionGoogleSatelliteChina, pos.X, sec1, pos.Y, zoom, sec2);
             }
-            break;
 
             case MapType.GoogleLabelsChina:
             {
@@ -666,7 +660,6 @@
 
                return string.Format("http://{0}{1}.google.cn/{2}/v={3}&hl={4}&gl=cn&x={5}{6}&y={7}&z={8}&s={9}", server, GetServerNum(pos), request, VersionGoogleLabelsChina, "zh-CN", pos.X, sec1, pos.Y, zoom, sec2);
             }
-            break;
 
             case MapType.GoogleTerrainChina:
             {
@@ -680,7 +673,6 @@
 
                return string.Format("http://{0}{1}.google.cn/{2}/v={3}&hl={4}&x={5}{6}&y={7}&z={8}&s={9}", server, GetServerNum(pos), request, VersionGoogleTerrainChina, "zh-CN", pos.X, sec1, pos.Y, zoom, sec2);
             }
-            break; 
             #endregion
 
             #region -- Yahoo --
@@ -756,6 +748,7 @@
          }
       }
 
+
       /// <summary>
       /// gets server num based on position
       /// </summary>
@@ -1454,5 +1447,100 @@
          return ret;
       }
       #endregion
+
+      /// <summary>
+      /// gets lat, lng from geocoder keys
+      /// </summary>
+      /// <param name="keywords"></param>
+      /// <param name="status"></param>
+      /// <returns></returns>
+      public Point GetTileOffsetForChinaMap(MapType type, Point pos, int zoom)
+      {
+          if (type != MapType.GoogleMapChina && type != MapType.GoogleLabelsChina)
+              return Point.Empty;
+          return GetTileOffsetForChinaMap(MakeTileOffsetForChinaMapUrl(type, pos, zoom), zoom, UseGeocoderCache);
+      }
+
+      public string MakeTileOffsetForChinaMapUrl(MapType type, Point pos, int zoom)
+      {
+          Point p5 = GMaps.Instance.FromTileXYToPixel(pos);
+          PointLatLng p6 = GMaps.Instance.FromPixelToLatLng(p5, zoom);
+          string url = string.Format("http://ditu.google.cn/maps/vp?spn=0.002382,0.002747&z={0}&mapclient=jsapi&vp={1},{2}", zoom, p6.Lat.ToString("N6"), p6.Lng.ToString("N6"));
+          return url;
+      }
+      public Point GetTileOffsetForChinaMap(string url, int zoom, bool useCache)
+      {
+          // http://ditu.google.cn/maps/vp?spn=0.002382,0.002747&z=19&key=ABQIAAAAzr2EBOXUKnm_jVnk0OJI7xSosDVG8KKPE1-m51RBrvYughuyMxQ-i1QfUnH94QxWIa6N4U6MouMmBA&mapclient=jsapi&vp=29.869192,121.55724
+          // window.GTileShiftUpdateOffset && window.GTileShiftUpdateOffset(39.926588, 118.389160, 14, [9, -2, 18, -5, 37, -10, 74, -21, 148, -43, 296, -87, 592, -175, 1185, -350]);
+
+          string urlEnd = url.Substring(url.IndexOf("vp?spn="));
+
+          char[] ilg = Path.GetInvalidFileNameChars();
+          foreach (char c in ilg)
+          {
+              urlEnd = urlEnd.Replace(c, '_');
+          }
+
+          string strOffset = useCache ? Cache.Instance.GetTileOffsetFromCache(urlEnd) : string.Empty;
+
+          if (string.IsNullOrEmpty(strOffset))
+          {
+              try
+              {
+                  HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
+                  request.ServicePoint.ConnectionLimit = 50;
+                  request.Proxy = Proxy != null ? Proxy : WebRequest.DefaultWebProxy;
+                  request.AutomaticDecompression = DecompressionMethods.GZip;
+                  request.UserAgent = UserAgent;
+                  request.Timeout = Timeout;
+                  request.ReadWriteTimeout = Timeout * 6;
+                  request.Referer = "http://ditu.google.cn/";
+                  request.KeepAlive = false;
+
+                  string strRes;
+                  using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
+                  {
+                      using (Stream responseStream = response.GetResponseStream())
+                      {
+                          using (StreamReader read = new StreamReader(responseStream))
+                          {
+                              strRes = read.ReadToEnd();
+                          }
+                      }
+                  }
+
+                  int idx = strRes.IndexOf("window.GTileShiftUpdateOffset && window.GTileShiftUpdateOffset");
+                  if (idx != -1)
+                  {
+                      strRes = strRes.Substring(idx);
+                      idx = strRes.IndexOf('[');
+                      int idx2 = strRes.IndexOf(']');
+                      strOffset = strRes.Substring(idx + 1, idx2 - idx - 1);
+
+                      // cache geocoding
+                      if (useCache && !string.IsNullOrEmpty(strOffset))
+                      {
+                          Cache.Instance.CacheTileOffset(urlEnd, strOffset);
+                      }
+                  }
+              }
+              catch (Exception ex)
+              {
+                  Debug.WriteLine("ModifiPosForChinaMap: " + ex.ToString());
+              }
+          }
+
+          if (!string.IsNullOrEmpty(strOffset))
+          {
+              string[] ss = strOffset.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+              int zoomOffset = zoom - 11;
+              if (zoomOffset >= 0 && zoomOffset + 1 < ss.Length)
+              {
+                  return new Point(Convert.ToInt32(ss[2 * zoomOffset]), Convert.ToInt32(ss[2 * zoomOffset + 1]));
+              }
+          }
+
+          return Point.Empty;
+      }
    }
 }
Index: GMap.NET.WindowsForms/System.Windows.Forms/GMapControl.cs
===================================================================
--- GMap.NET.WindowsForms/System.Windows.Forms/GMapControl.cs	(revision 34795)
+++ GMap.NET.WindowsForms/System.Windows.Forms/GMapControl.cs	(working copy)
@@ -196,21 +196,45 @@
                   Core.tileRect.Y = Core.tilePoint.Y*Core.tileRect.Height;
                   Core.tileRect.Offset(Core.renderOffset);
 
-                  if(Core.CurrentRegion.IntersectsWith(Core.tileRect))
+                  //if(Core.CurrentRegion.IntersectsWith(Core.tileRect))
+                  //{
+                  //   bool found = false;
+
+                  //   // render tile
+                  //   lock(t.Overlays)
+                  //   {
+                  //      foreach (PureOverlay img in t.Overlays)
+                  //      {
+                  //         if(img != null && img.Image != null 
+                  //             && img.Image is WindowsFormsImage && (img.Image as WindowsFormsImage).Img != null)
+                  //         {
+                  //            if(!found)
+                  //               found = true;
+
+                  //            g.DrawImage((img.Image as WindowsFormsImage).Img, Core.tileRect.X - img.Offset.X, Core.tileRect.Y - img.Offset.Y, Core.tileRect.Width, Core.tileRect.Height);
+                  //         }
+                  //      }
+                  //   }
+
+                      
                   {
                      bool found = false;
 
                      // render tile
                      lock(t.Overlays)
                      {
-                        foreach(WindowsFormsImage img in t.Overlays)
+                        foreach (PureOverlay img in t.Overlays)
                         {
-                           if(img != null && img.Img != null)
+                           if(img != null && img.Image != null 
+                               && img.Image is WindowsFormsImage && (img.Image as WindowsFormsImage).Img != null)
                            {
-                              if(!found)
-                                 found = true;
-
-                              g.DrawImage(img.Img, Core.tileRect.X, Core.tileRect.Y, Core.tileRect.Width, Core.tileRect.Height);
+                              GMap.NET.Rectangle realRect = new GMap.NET.Rectangle(Core.tileRect.X - img.Offset.X, Core.tileRect.Y - img.Offset.Y, Core.tileRect.Width, Core.tileRect.Height);
+                              if (Core.CurrentRegion.IntersectsWith(realRect))
+                              {
+                                  if (!found)
+                                      found = true;
+                                  g.DrawImage((img.Image as WindowsFormsImage).Img, Core.tileRect.X - img.Offset.X, Core.tileRect.Y - img.Offset.Y, Core.tileRect.Width, Core.tileRect.Height);
+                              }
                            }
                         }
                      }
@@ -671,8 +695,8 @@
       {
          base.OnSizeChanged(e);
 
-         Core.sizeOfMapArea.Width = 1 + (Width/GMaps.Instance.TileSize.Width)/2;
-         Core.sizeOfMapArea.Height = 1 + (Height/GMaps.Instance.TileSize.Height)/2;
+         Core.sizeOfMapArea.Width = 1 + (Width / GMaps.Instance.TileSize.Width) / 2;
+         Core.sizeOfMapArea.Height = 1 + (Height / GMaps.Instance.TileSize.Height) / 2;
 
          // 50px outside control
          Core.CurrentRegion = new GMap.NET.Rectangle(-50, -50, Size.Width+100, Size.Height+100);
Index: GMap.NET.WindowsPresentation/System.Windows.Controls/GMapControl.cs
===================================================================
--- GMap.NET.WindowsPresentation/System.Windows.Controls/GMapControl.cs	(revision 34795)
+++ GMap.NET.WindowsPresentation/System.Windows.Controls/GMapControl.cs	(working copy)
@@ -182,8 +182,8 @@
       {
          System.Windows.Size constraint = e.NewSize;
 
-         Core.sizeOfMapArea.Width = 1 + ((int) constraint.Width/GMaps.Instance.TileSize.Width)/2;
-         Core.sizeOfMapArea.Height = 1 + ((int) constraint.Height/GMaps.Instance.TileSize.Height)/2;
+         Core.sizeOfMapArea.Width = 1 + ((int)constraint.Width / GMaps.Instance.TileSize.Width) / 2;
+         Core.sizeOfMapArea.Height = 1 + ((int)constraint.Height / GMaps.Instance.TileSize.Height) / 2;
 
          // 50px outside control
          region = new GMap.NET.Rectangle(-50, -50, (int) constraint.Width+100, (int) constraint.Height+100);
@@ -286,17 +286,29 @@
                   {
                      bool found = false;
 
-                     foreach(WindowsPresentationImage img in t.Overlays)
+                     foreach (PureOverlay img in t.Overlays)
                      {
-                        if(img != null && img.Img != null)
-                        {
-                           if(!found)
-                              found = true;
+                         if (img != null && img.Image != null
+                             && img.Image is WindowsPresentationImage && (img.Image as WindowsPresentationImage).Img != null)
+                         {
+                             if (!found)
+                                 found = true;
 
-                           g.DrawImage(img.Img, new Rect(Core.tileRect.X*scaleFactor, Core.tileRect.Y*scaleFactor, Core.tileRect.Width*scaleFactor, Core.tileRect.Height*scaleFactor));
-                        }
+                             g.DrawImage((img.Image as WindowsPresentationImage).Img, new Rect((Core.tileRect.X + img.Offset.X) * scaleFactor, (Core.tileRect.Y + img.Offset.Y) * scaleFactor, Core.tileRect.Width * scaleFactor, Core.tileRect.Height * scaleFactor));
+                         }
                      }
 
+                     //foreach(WindowsPresentationImage img in t.Overlays)
+                     //{
+                     //   if(img != null && img.Img != null)
+                     //   {
+                     //      if(!found)
+                     //         found = true;
+
+                     //      g.DrawImage(img.Img, new Rect((Core.tileRect.X + img.Offset.X)*scaleFactor, (Core.tileRect.Y + img.Offset.Y)*scaleFactor, Core.tileRect.Width*scaleFactor, Core.tileRect.Height*scaleFactor));
+                     //   }
+                     //}
+
                      // add text if tile is missing
                      if(!found)
                      {
