Support new MapType - YandexMap

Topics: Feature Requests
Dec 9, 2009 at 4:58 AM
I added a new type of maps in the project - YandexMap (http://maps.yandex.ru)
Make the description and the formation of URL
Tiles have a standard size 256x256

Everything works, maps are displayed.
There is only one problem, not suitable projection MercatorProjection, on a large scale there are differences in latitude.

YandexMap using Mercator projection, but with some changes.
Maybe someone knows what the problem?

Coordinate System - http://api.yandex.ru/maps/jsapi/doc/dg/concepts/coordinates_systems.xml

 // In MapType.cs
   YandexMap=1000
//-----------------------------------------------------

// In GMaps.cs
internal string MakeImageUrl(MapType type, Point pos, int zoom, string language)
case MapType.YandexMap:  
   {    
     string server = "vec";
     string VersionYandexMap = "2.10.2";
   //http://vec01.maps.yandex.ru/tiles?l=map&v=2.10.2&x=1494&y=650&z=11
 
 return string.Format("http://{0}0{1}.maps.yandex.ru/tiles?l=map&v={2}&x={3}&y={4}&z={5}", server, GetServerNum(pos, 4)+1, VersionYandexMap, pos.X, pos.Y, zoom);  
}
.....
public PureImage GetImageFrom(
....
case
MapType.YandexMap:
{
    request.Referer = "http://maps.yandex.ru/";
}
break;

Dec 9, 2009 at 5:14 AM

yandex.maps uses Mercator projection with Earth represented as ellipsoid (google maps represented as SPHEROID)

 

Dec 10, 2009 at 8:29 PM
Edited Dec 11, 2009 at 7:52 PM

Hi. Do you found any solution for this problem? Is there code sample for Mercator projection for yandex maps?

Apr 7, 2010 at 12:36 PM

In addition sevacom message

// In Core.cs property MapType set...

 case MapType.YandexMap:
                  {
                      if (false == (Projection is MercatorProjectionYandex))
                      {
                          Projection = new MercatorProjectionYandex();
                      }
                   }
                  break;
Apr 7, 2010 at 12:53 PM

Hi, for yandex maps i write some additional code for coodinate convertion. Whithout it GMap cannot correctly work with yandex.

If you intersted for my correction code for yandex maps, i can post it.

Apr 7, 2010 at 1:02 PM

Yes i create new Projection for Yandex maps, i hope it included in the next versions of GMap:

 

 

using System;
using System.Collections.Generic;
using System.Text;

namespace GMap.NET.Projections
{
    public class YandexProjection : PureProjection
    {
        //const double MinLatitude = -85.05112878;
        //const double MaxLatitude = 85.05112878;
        //const double MinLongitude = -177;
        //const double MaxLongitude = 177;

        const double MinLatitude = -80.0;
        const double MaxLatitude = 84.0;
        const double MinLongitude = -180.0;
        const double MaxLongitude = 180.0;
        
        
        // http://vec.maps.yandex.net/tiles?l=map&v=2.8.1&x=%d&y=%d&z=%d — векторная карта (режим "Схема" на maps.yandex.ru),
        // http://sat.maps.yandex.net/tiles?l=sat&v=1.11.0&x=%d&y=%d&z=%d — спутниковые снимки.

        // function _geoToMercator(n)
        //{
        // var l = n.getLng() * Math.PI / 180,
        // u = O(n.getLat(), -90, 90) * Math.PI / 180,
        // t = 6378137,
        // s = 0.0818191908426, m = s * Math.sin(u);
        // var o = Math.tan(Math.PI / 4 + u / 2) || 1e-13, r = Math.pow(Math.tan(Math.PI / 4 + Math.asin(m) / 2), s),
        // q = o / r;
        // return new YMaps.Point(Math.round(t * l), Math.round(t * Math.log(q)));
        //}
        
        // function _mercatorToTiles(e)
        // {
        //   return new YMaps.Point(Math.round((20037508.342789 + e.X) * 53.5865938),
        //      Math.round((20037508.342789 - e.y) * 53.5865938));
        // }

//        Смещение вызвано тем, что у гугла используется сфера, а у яндекса- эллипсоид.
//Поэтому расчеты немного посложнее:

//  Dim rLon  As Double, rLat As Double, a As Double, k As Double, z As Double 
//      rLon = GeoCrd.Lon * pii / 180 
//      rLat = GeoCrd.Lat * pii / 180 
//      a = 6378137 
//      k = 0.0818191908426 
//      z = Tan(pii / 4 + rLat / 2)/(Tan(pii / 4 + ArcSin(k * Sin(rLat)) / 2)) ^ k 
//      GetPix.X = CLng((20037508.342789 +a * rLon) * 53.5865938 / 2 ^ (23 - Mstb)) 
//      GetPix.Y = CLng((20037508.342789 -a * Log(z)) * 53.5865938 / 2 ^ (23 - Mstb)) 

//Mstb-это зум на гугле.

//Обратный пересчет:


//  Dim a As Double, c1 As Double, c2 As Double, c3 As Double, c4 As Double, g As Double, z As Double 
//  Dim mercX As Double, mercY As Double 
//      a= 6378137 
//      c1 = 0.00335655146887969: c2 = 0.00000657187271079536: c3 = 0.00000001764564338702: c4=0.00000000005328478445 
//      mercX = (AbsPxl.X * 2 ^ (23 - Mstb)) / 53.5865938 - 20037508.342789 
//      mercY = 20037508.342789 - (AbsPxl.Y * 2 ^ (23 - Mstb)) / 53.5865938 

//      g = pii/2 - 2 * Atn(1 / Exp(mercY /a)) 
//      z = g + c1 * Sin(2 * g) + c2 * Sin(4 * g) + c3 * Sin(6 * g) + c4 * Sin(8 * g) 

//      GetLatLng.Lat = z * 180 / pii 
//      GetLatLng.Lon = mercX / a * 180 / pii 


        readonly Size _tileSize = new Size(256, 256);
        public override Size TileSize
        {
            get
            {
                return _tileSize;
            }
        }

        public override double Axis
        {
            get
            {
                return 6378137;
            }
        }

        public override double Flattening
        {
            get
            {
                return (1.0 / 298.257223563);
                // return 0.0818191908426;
            }
        }

        public override Point FromLatLngToPixel(double lat, double lng, int zoom)
        {
            Point ret = Point.Empty;

            //lat = Clip(lat, MinLatitude, MaxLatitude);
            //lng = Clip(lng, MinLongitude, MaxLongitude);

            //double x = (lng + 180) / 360;
            //double sinLatitude = Math.Sin(lat * Math.PI / 180);
            //double y = 0.5 - Math.Log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI);

            //Size s = GetTileMatrixSizePixel(zoom);
            //int mapSizeX = s.Width;
            //int mapSizeY = s.Height;

            //ret.X = (int)Clip(x * mapSizeX + 0.5, 0, mapSizeX - 1);
            //ret.Y = (int)Clip(y * mapSizeY + 0.5, 0, mapSizeY - 1);

            var merkator = YandexUtils.GeoToMercator(new DoublePoint(lng, lat));
            var tile = YandexUtils.MercatorToTiles(merkator);
            var pix = YandexUtils.TileCoordinatesToPixels(tile, zoom);

            ret.X = (int)pix.X;
            ret.Y = (int)pix.Y;

            return ret;

            //Point ret = Point.Empty;

            //double rLon, rLat, a, k, z;

            //rLon = lng * Math.PI / 180;
            //rLat = lat * Math.PI / 180;
            //a = 6378137;
            //k = 0.0818191908426;
            //z = Math.Tan(Math.PI / 4 + rLat / 2) / (Math.Tan(Math.PI / 4 + Math.Asin(k * Math.Sin(rLat)) / 2));
            //z = Math.Pow(z, k);


            //ret.X = (int)((20037508.342789 + a * rLon) * 53.5865938 / Math.Pow(2, (23 - zoom)));
            //ret.Y = (int)((20037508.342789 - a * Math.Log(z)) * 53.5865938 / Math.Pow(2, (23 - zoom)));

            //return ret;
        }

        public override PointLatLng FromPixelToLatLng(int x, int y, int zoom)
        {
            PointLatLng ret = PointLatLng.Empty;

            //Size s = GetTileMatrixSizePixel(zoom);
            //double mapSizeX = s.Width;
            //double mapSizeY = s.Height;

            //double xx = (Clip(x, 0, mapSizeX - 1) / mapSizeX) - 0.5;
            //double yy = 0.5 - (Clip(y, 0, mapSizeY - 1) / mapSizeY);

            //ret.Lat = 90 - 360 * Math.Atan(Math.Exp(-yy * 2 * Math.PI)) / Math.PI;
            //ret.Lng = 360 * xx;

            // return ret;

            var tile = YandexUtils.GetTileCoordFromPixCoord(new IntPoint(x, y), zoom);
            var merkartor = YandexUtils.TileToMercator(tile);
            var geo = YandexUtils.MercatorToGeo(merkartor);

            ret.Lat = geo.Y;
            ret.Lng = geo.X;

            return ret;

            //PointLatLng ret = PointLatLng.Empty;

            //double a, c1, c2, c3, c4, g, z;
            //double mercX, mercY;

            //a = 6378137;
            //c1 = 0.00335655146887969;
            //c2 = 0.00000657187271079536;
            //c3 = 0.00000001764564338702;
            //c4 = 0.00000000005328478445;
            //mercX = (Math.Abs(x) * Math.Pow(2, (23 - zoom))) / 53.5865938 - 20037508.342789;
            //mercY = 20037508.342789 - (Math.Abs(y) * Math.Pow(2, (23 - zoom))) / 53.5865938;

            //g = Math.PI / 2 - 2 * Math.Atan(1 / Math.Exp(mercY / a));
            //z = g + c1 * Math.Sin(2 * g) + c2 * Math.Sin(4 * g) + c3 * Math.Sin(6 * g) + c4 * Math.Sin(8 * g);

            //ret.Lat = z * 180 / Math.PI;
            //ret.Lng = mercX / a * 180 / Math.PI;

            //return ret;
        }

        public override Size GetTileMatrixMinXY(int zoom)
        {
            return new Size(0, 0);
        }

        public override Size GetTileMatrixMaxXY(int zoom)
        {
            int xy = (1 << zoom);
            return new Size(xy - 1, xy - 1);
        }

        /// <summary>
        /// Clips a number to the specified minimum and maximum values.
        /// </summary>
        /// <param name="n">The number to clip.</param>
        /// <param name="minValue">Minimum allowable value.</param>
        /// <param name="maxValue">Maximum allowable value.</param>
        /// <returns>The clipped value.</returns>
        double Clip(double n, double minValue, double maxValue)
        {
            return Math.Min(Math.Max(n, minValue), maxValue);
        }
    }

    public class YandexUtils
    {
        public static DoublePoint GeoToMercator(DoublePoint g)
        {
            double d = g.X * Math.PI / 180, m = g.Y * Math.PI / 180,
                l = 6378137,
                k = 0.0818191908426,
                f = k * Math.Sin(m);
            double h = Math.Tan(Math.PI / 4 + m / 2),
                j = Math.Pow(Math.Tan(Math.PI / 4 + Math.Asin(f) / 2), k), i = h / j;
            // return new DoublePoint(Math.round(l * d), Math.round(l * Math.log(i)));
            return new DoublePoint(l * d, l * Math.Log(i));
        }


        public static DoublePoint MercatorToGeo(DoublePoint e)
        {
            double j = Math.PI, f = j / 2, i = 6378137, n = 0.003356551468879694, k = 0.00000657187271079536,
                h = 1.764564338702e-8, m = 5.328478445e-11;
            double g = f - 2 * Math.Atan(1 / Math.Exp(e.Y / i));
            double l = g + n * Math.Sin(2 * g) + k * Math.Sin(4 * g) + h * Math.Sin(6 * g) + m * Math.Sin(8 * g);
            double d = e.X / i;
            return new DoublePoint(d * 180 / Math.PI, l * 180 / Math.PI);
        }

        public static DoublePoint MercatorToTiles(DoublePoint e)
        {
            double d = Math.Round((20037508.342789 + e.X) * 53.5865938);
            double f = Math.Round((20037508.342789 - e.Y) * 53.5865938);
            d = BoundaryRestrict(d, 0, 2147483647);
            f = BoundaryRestrict(f, 0, 2147483647);
            return new DoublePoint(d, f);
        }

        public static DoublePoint TileToMercator(IntPoint d)
        {
            return new DoublePoint(Math.Round(d.X / 53.5865938 - 20037508.342789),
                Math.Round(20037508.342789 - d.Y / 53.5865938));
        }

        public static DoublePoint TileCoordinatesToPixels(DoublePoint i, int h)
        {
            double g = Math.Pow(2, ToScale(h));
            return new DoublePoint((int)i.X / g, (int)i.Y / g);
        }

        public static double BoundaryRestrict(double f, double e, double d)
        {
            return Math.Max(Math.Min(f, d), e);
        }

        public static int ToScale(int i)
        {
            return 23 - i;
        }

        public static IntPoint GetTile(DoublePoint h, int i)
        {
            var e = 8;
            var j = ToScale(i);
            var g = (int) h.X >> j;
            var f = (int)h.Y >> j;
            return new IntPoint(g >> e, f >> e);
        }

        public static IntPoint GetPxCoordFromTileCoord(DoublePoint h, int i)
        {
            var j = ToScale(i);
            var g = (int)h.X >> j;
            var f = (int)h.Y >> j;
            return new IntPoint(g, f);
        }

        public static IntPoint GetTileCoordFromPixCoord(IntPoint h, int i)
        {
            var j = ToScale(i);
            var g = h.X << j;
            var f = h.Y << j;
            return new IntPoint(g, f);
        }
    }

    public struct DoublePoint
    {
        public double X;
        public double Y;

        public DoublePoint(double x, double y)
        {
            X = x;
            Y = y;
        }
    }

    public struct IntPoint
    {
        public int X;
        public int Y;

        public IntPoint(int x, int y)
        {
            X = x;
            Y = y;
        }
    }
}

 

 

Apr 7, 2010 at 1:14 PM
//in MercatorProjection.cs  


public class MercatorProjectionYandex : PureProjection
   {
       const double MinLatitude = -85.05112878;
       const double MaxLatitude = 85.05112878;
       const double MinLongitude = -177;
       const double MaxLongitude = 177;
       const double  RAD_DEG = 180 / Math.PI;
       const double  DEG_RAD = Math.PI / 180;
       const double MathPiDiv4 = Math.PI / 4;
       

       Size tileSize = new Size(256, 256);
       public override Size TileSize
       {
           get
           {
               return tileSize;
           }
       }

       public override double Axis
       {
           get
           {
               return 6356752.3142;
           }
       }

       public override double Flattening
       {
           get
           {
               return (1.0 / 298.257223563);
           }
       }

       public override Point FromLatLngToPixel(double lat, double lng, int zoom)
       {
           Point ret = Point.Empty;

           lat = Clip(lat, MinLatitude, MaxLatitude);
           lng = Clip(lng, MinLongitude, MaxLongitude);
           
           double rLon, rLat, a, k, z, z1;


           rLon = lng * DEG_RAD;// Math.PI / 180; 
           rLat = lat * DEG_RAD;// Math.PI / 180; 
           
           a = 6378137 ;      
           k = 0.0818191908426;

           z = Math.Tan(MathPiDiv4 + rLat / 2) / Math.Pow((Math.Tan(MathPiDiv4 + Math.Asin(k * Math.Sin(rLat)) / 2)), k);
           z1 = Math.Pow(2,23 - zoom);      
           double DX =  ((20037508.342789 + a * rLon) * 53.5865938 /  z1);      
           double DY = ((20037508.342789 - a * Math.Log(z)) * 53.5865938 / z1);
           ret.X = (int)DX;
           ret.Y = (int)DY;
           return ret;


       }

       public override PointLatLng FromPixelToLatLng(int x, int y, int zoom)
       {
           PointLatLng ret = PointLatLng.Empty;

           Size s = GetTileMatrixSizePixel(zoom);
           double mapSizeX = s.Width;
           double mapSizeY = s.Height;

           double  a, c1, c2 , c3 , c4, g, z;            
           double mercX, mercY; 
           a= 6378137;
           c1 = 0.00335655146887969;
           c2 = 0.00000657187271079536;
           c3 = 0.00000001764564338702;
           c4=0.00000000005328478445;
           double z1=(23 - zoom );
           mercX = ( x * Math.Pow ( 2,z1)) / 53.5865938 - 20037508.342789; 
           mercY = 20037508.342789 - (y *Math.Pow ( 2 ,z1)) / 53.5865938; 
           g =Math.PI /2 - 2 *Math.Atan (1 / Math.Exp (mercY /a)) ;
           z = g + c1 * Math.Sin(2 * g) + c2 * Math.Sin(4 * g) + c3 * Math.Sin(6 * g) + c4 * Math.Sin(8 * g);


           ret.Lat = z * RAD_DEG;

           ret.Lng = mercX / a * RAD_DEG; 

      
           return ret;
       }

       /// <summary>
       /// Clips a number to the specified minimum and maximum values.
       /// </summary>
       /// <param name="n">The number to clip.</param>
       /// <param name="minValue">Minimum allowable value.</param>
       /// <param name="maxValue">Maximum allowable value.</param>
       /// <returns>The clipped value.</returns>
       double Clip(double n, double minValue, double maxValue)
       {
           return Math.Min(Math.Max(n, minValue), maxValue);
       }

       public override Size GetTileMatrixMinXY(int zoom)
       {
           return new Size(0, 0);
       }

       public override Size GetTileMatrixMaxXY(int zoom)
       {
           int xy = (1 << zoom);
           return new Size(xy - 1, xy - 1);
       }
   }

Apr 7, 2010 at 1:19 PM

пока я копировал, ты уже вставил свой вариант :) У тебя круче, но подход похоже одинаковый. Спасибо.

Apr 7, 2010 at 1:26 PM

Dear radioman!

Thank you for your work!

We hope to see support for YandexMap in the following versions of your beautiful component. (translated into English bing) ;)

Apr 7, 2010 at 2:37 PM

Perfect, thank you guys! ;}

Jul 2, 2010 at 4:22 PM
Edited Mar 20, 2014 at 2:39 PM
new MapType support: YandexMapRuSatellite, YandexMapRuLabels, YandexMapRuHybrid

...

p.s. for leaflet users, use already defined CRS L.CRS.EPSG3395 // http://leafletjs.com/reference.html#icrs