第二章 自己的框架WMTS服务,下载数据集成的文章1

时间:2024-09-26 09:34:15

在构建数据源下载文件的叙述性说明第一步

如此XML结构体

<?xml version="1.0" encoding="utf-8"?>

<onlinemapsources>

 

 <onlineMapSource>

  <name>GaoDeDiTuImage</name>

  <url><![CDATA[http://webst0{$s}.is.autonavi.com/appmaptile?style=6&x={$x}&y={$y}&z={$z}]]></url>

  <servers>1,2,3,4</servers>

 </onlineMapSource>

 

</onlinemapsources>

当中NAME节点描写叙述下载地图数据源名称

url为须要下载数据源切片的訪问方式的URL

servers为在线地图的服务器个数。

以下我们以天地图为例

首先构建DATASROUS对象 全部数据源的基类

/// <summary>

    /// 全部数据缓存下载转换的基类

    /// </summary>

    public abstract class DataSourceBase

    {

        ~DataSourceBase()

        {

            if (_connLocalCacheFile != null)

                _connLocalCacheFile.Close();

            _connLocalCacheFile = null;

        }

        /// <summary>

        /// 提前定义或者在线地图

        /// </summary>

        public string Type { get; set; }

        /// <summary>

        /// 数据服务地址

        /// </summary>

        public string Path { get; set; }

        /// <summary>

        /// 服务的元数据信息

        /// </summary>

        public TilingScheme TilingScheme { get; set; }

        /// <summary>

        /// 表述该地图源头为在线地图

        /// 非常多方法数据的下载取决于这个属性

        /// </summary>

        public bool IsOnlineMap { get; set; }

/// <summary>

        /// 初始化 对象生成下载和转换方案

        /// </summary>

        protected virtual void Initialize(string path)

        {

            

            this.Path = path;

            TilingScheme ts;

            try

            {

                ReadTilingScheme(out ts);

            }

            catch (Exception e)

            {

                throw new Exception("读取瓦片的元数据失败!\r\n" + e.Message+"\r\n"+e.StackTrace);

            }

            TilingScheme = ts;

            IsOnlineMap = IsOnlineMaps(Type);

        }

/// <summary>

        /// TODO: 读取相应数据源的元数据信息

        /// </summary>

        /// <param name="tilingScheme"></param>

        /// <param name="lodsJson"></param>

        protected abstract void ReadTilingScheme(out TilingScheme tilingScheme);

/// <summary>

        /// 生成JSON字符串

        /// </summary>

        /// <param name="tilingScheme"></param>

        /// <returns></returns>

        protected TilingScheme TilingSchemePostProcess(TilingScheme tilingScheme)

        {

            #region ArcGIS REST Service Info

            string pjson = @"{

  ""currentVersion"" : 10.01,

  ""serviceDescription"" : ""This service is populated from PortableBasemapServer by diligentpig. For more information goto
http://newnaw.com"",

  ""mapName"" : ""Layers"",

  ""description"" : ""none"",

  ""copyrightText"" : ""IMapGeoServer by diligentpig, REST API by Esri"",

  ""layers"" : [

    {

      ""id"" : 0,

      ""name"" : ""YourServiceNameHere"",

      ""parentLayerId"" : -1,

      ""defaultVisibility"" : true,

      ""subLayerIds"" : null,

      ""minScale"" : 0,

      ""maxScale"" : 0

    }

  ],

  ""tables"" : [

   

  ],

  ""spatialReference"" : {

    ""wkid"" : " + tilingScheme.WKID + @"

  },

  ""singleFusedMapCache"" : true,

  ""tileInfo"" : {

    ""rows"" : " + tilingScheme.TileRows + @",

    ""cols"" : " + tilingScheme.TileCols + @",

    ""dpi"" : " + tilingScheme.DPI + @",

    ""format"" : """ + tilingScheme.CacheTileFormat + @""",

    ""compressionQuality"" : " + tilingScheme.CompressionQuality + @",

    ""origin"" : {

      ""x"" : " + tilingScheme.TileOrigin.X + @",

      ""y"" : " + tilingScheme.TileOrigin.Y + @"

    },

    ""spatialReference"" : {

      ""wkid"" : " + tilingScheme.WKID + @"

    },

    ""lods"" : [" + tilingScheme.LODsJson + @"

    ]

  },

  ""initialExtent"" : {

    ""xmin"" : " + tilingScheme.InitialExtent.XMin + @",

    ""ymin"" : " + tilingScheme.InitialExtent.YMin + @",

    ""xmax"" : " + tilingScheme.InitialExtent.XMax + @",

    ""ymax"" : " + tilingScheme.InitialExtent.YMax + @",

    ""spatialReference"" : {

      ""wkid"" : " + tilingScheme.WKID + @"

    }

  },

  ""fullExtent"" : {

    ""xmin"" : " + tilingScheme.FullExtent.XMin + @",

    ""ymin"" : " + tilingScheme.FullExtent.YMin + @",

    ""xmax"" : " + tilingScheme.FullExtent.XMax + @",

    ""ymax"" : " + tilingScheme.FullExtent.YMax + @",

    ""spatialReference"" : {

      ""wkid"" : " + tilingScheme.WKID + @"

    }

  },

  ""units"" : ""esriMeters"",

  ""supportedImageFormatTypes"" : ""PNG24,PNG,JPG,DIB,TIFF,EMF,PS,PDF,GIF,SVG,SVGZ,AI,BMP"",


  ""documentInfo"" : {

    ""Title"" : ""none"",

    ""Author"" : ""none"",

    ""Comments"" : ""none"",

    ""Subject"" : ""none"",

    ""Category"" : ""none"",

    ""Keywords"" : ""none"",

    ""Credits"" : ""diligentpig""

  },

  ""capabilities"" : ""Map,Query,Data""

}

";

            #endregion

            tilingScheme.RestResponseArcGISPJson = pjson;

            tilingScheme.RestResponseArcGISJson = pjson.Replace("\r\n", "").Replace("\n", "");

            return tilingScheme;

        }

/// <summary>

        /// TODO: 获取瓦片的的二进制文件

        /// </summary>

        /// <param name="level"></param>

        /// <param name="row"></param>

        /// <param name="col"></param>

        /// <returns></returns>

        public abstract byte[] GetTileBytes(int level, int row, int col);

        /// <summary>

        /// 当瓦片開始载入的时候运行

        /// </summary>

        public EventHandler<TileLoadEventArgs> TileLoaded;

#region 读取瓦片的的元数据

        protected void ReadSqliteTilingScheme(out TilingScheme tilingScheme, SQLiteConnection sqlConn)

        {

            tilingScheme = new TilingScheme();

            StringBuilder sb;

            #region 读取MBtile中的元数据信息

            tilingScheme.Path = "N/A";

            bool isMACFile = false;

            using (SQLiteCommand cmd = new SQLiteCommand(sqlConn))

            {

                cmd.CommandText = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='info'";

                long i = (long)cmd.ExecuteScalar();

                if (i == 0)

                    isMACFile = false;

                else

                    isMACFile = true;

              

                if (!isMACFile)

                {

                   

                    cmd.CommandText = string.Format("SELECT value FROM metadata WHERE name='format'");

                    object o = cmd.ExecuteScalar();

                    if (o != null)

                    {

                        string f = o.ToString();

                        tilingScheme.CacheTileFormat = f.ToUpper().Contains("PNG") ?

ImageFormat.PNG : ImageFormat.JPG;

                    }

                    else

                    {

                        tilingScheme.CacheTileFormat = ImageFormat.JPG;

                    }

                }

            }

            tilingScheme.CompressionQuality = 75;

            tilingScheme.DPI = 96;

           

            tilingScheme.LODs = new LODInfo[20];

            const double cornerCoordinate = 20037508.342787;

            double resolution = cornerCoordinate * 2 / 256;

            double scale = 591657527.591555;

            for (int i = 0; i < tilingScheme.LODs.Length; i++)

            {

                tilingScheme.LODs[i] = new LODInfo()

                {

                    Resolution = resolution,

                    LevelID = i,

                    Scale = scale

                };

                resolution /= 2;

                scale /= 2;

            }

           

            sb = new StringBuilder("\r\n");

           

            foreach (LODInfo lod in tilingScheme.LODs)

            {

                sb.Append(@"      {""level"":" + lod.LevelID + "," + @"""resolution"":" + lod.Resolution + "," + @"""scale"":" + lod.Scale + @"}," + "\r\n");

            }

            tilingScheme.LODsJson = sb.ToString().Remove(sb.ToString().Length - 3);


            try

            {

                using (SQLiteCommand sqlCmd = new SQLiteCommand(sqlConn))

                {

                    sqlCmd.CommandText = string.Format("SELECT value FROM metadata WHERE name='bounds'");


                    object o = sqlCmd.ExecuteScalar();

                    if (o != null)

                    {

                        string[] bounds = o.ToString().Split(new char[] { ',' });

                        Point leftBottom = new Point(double.Parse(bounds[0]), double.Parse(bounds[1]));

                        Point rightTop = new Point(double.Parse(bounds[2]), double.Parse(bounds[3]));

                        leftBottom = Utility.GeographicToWebMercator(leftBottom);

                        rightTop = Utility.GeographicToWebMercator(rightTop);

                        tilingScheme.InitialExtent = new Envelope(leftBottom.X, leftBottom.Y, rightTop.X, rightTop.Y);

                        tilingScheme.FullExtent = tilingScheme.InitialExtent;

                    }

                    else

                    {

                        throw new Exception();

                    }

                }

            }

            catch (Exception)

            {

                tilingScheme.InitialExtent = new Envelope(-cornerCoordinate, -cornerCoordinate, cornerCoordinate, cornerCoordinate);

                tilingScheme.FullExtent = tilingScheme.InitialExtent;

            }

            tilingScheme.PacketSize = 0;

            tilingScheme.StorageFormat = StorageFormat.esriMapCacheStorageModeExploded;

            tilingScheme.TileCols = tilingScheme.TileRows = 256;

            tilingScheme.TileOrigin = new Point(-cornerCoordinate, cornerCoordinate);

            tilingScheme.WKID = 3857;

            tilingScheme.WKT = @"PROJCS[""WGS_1984_Web_Mercator_Auxiliary_Sphere"",GEOGCS[""GCS_WGS_1984"",DATUM[""D_WGS_1984"",SPHEROID[""WGS_1984"",6378137.0,298.257223563]],PRIMEM[""Greenwich"",0.0],UNIT[""Degree"",0.0174532925199433]],PROJECTION[""Mercator_Auxiliary_Sphere""],PARAMETER[""False_Easting"",0.0],PARAMETER[""False_Northing"",0.0],PARAMETER[""Central_Meridian"",0.0],PARAMETER[""Standard_Parallel_1"",0.0],PARAMETER[""Auxiliary_Sphere_Type"",0.0],UNIT[""Meter"",1.0],AUTHORITY[""ESRI"",""3857""]]";

            #endregion

        }

/// <summary>

        ///

        /// </summary>

        /// <param name="path">切片方案的路径而不是数据源头的路径</param>

        /// <param name="tilingScheme"></param>

        protected void ReadArcGISTilingSchemeFile(string path, out TilingScheme tilingScheme)

        {

            tilingScheme = new TilingScheme();

            StringBuilder sb;

            #region 读取arcgis缓存的描写叙述文件

            if (!System.IO.File.Exists(path))//当数据产生缓存时候path是一个文件夹

            {

                path += "\\conf.xml";

            }

            tilingScheme.Path = path;//构建完整的文件夹地址

          //读取配置描写叙述文件

            XElement confXml = XElement.Load(System.IO.Path.GetDirectoryName(path) + @"\\conf.xml");

            tilingScheme.WKT = confXml.Element("TileCacheInfo").Element("SpatialReference").Element("WKT").Value;

            if (confXml.Element("TileCacheInfo").Element("SpatialReference").Element("WKID") != null)

                tilingScheme.WKID = int.Parse(confXml.Element("TileCacheInfo").Element("SpatialReference").Element("WKID").Value);

            else

                tilingScheme.WKID = -1;

            tilingScheme.TileOrigin = new Point(

                double.Parse(confXml.Element("TileCacheInfo").Element("TileOrigin").Element("X").Value),

                double.Parse(confXml.Element("TileCacheInfo").Element("TileOrigin").Element("Y").Value));

            tilingScheme.DPI = int.Parse(confXml.Element("TileCacheInfo").Element("DPI").Value);

           

            int lodsCount = confXml.Element("TileCacheInfo").Element("LODInfos").Elements().Count();

            tilingScheme.LODs = new LODInfo[lodsCount];

            for (int i = 0; i < lodsCount; i++)

            {

                tilingScheme.LODs[i] = new LODInfo()

                {

                    LevelID = i,

                    Scale = double.Parse(confXml.Element("TileCacheInfo").Element("LODInfos").Elements().ElementAt(i).Element("Scale").Value),

                    Resolution = double.Parse(confXml.Element("TileCacheInfo").Element("LODInfos").Elements().ElementAt(i).Element("Resolution").Value)

                };

            }

            sb = new StringBuilder("\r\n");

            

            foreach (LODInfo lod in tilingScheme.LODs)

            {

                sb.Append(@"      {""level"":" + lod.LevelID + "," + @"""resolution"":" + lod.Resolution + "," + @"""scale"":" + lod.Scale + @"}," + "\r\n");

            }

            tilingScheme.LODsJson = sb.ToString().Remove(sb.ToString().Length - 3);//remove last "," and "\r\n"

            tilingScheme.TileCols = int.Parse(confXml.Element("TileCacheInfo").Element("TileCols").Value);

            tilingScheme.TileRows = int.Parse(confXml.Element("TileCacheInfo").Element("TileRows").Value);

            tilingScheme.CacheTileFormat = (ImageFormat)Enum.Parse(typeof(ImageFormat), confXml.Element("TileImageInfo").Element("CacheTileFormat").Value.ToUpper());

            tilingScheme.CompressionQuality = int.Parse(confXml.Element("TileImageInfo").Element("CompressionQuality").Value);

            tilingScheme.StorageFormat = (StorageFormat)Enum.Parse(typeof(StorageFormat), confXml.Element("CacheStorageInfo").Element("StorageFormat").Value);

            tilingScheme.PacketSize = int.Parse(confXml.Element("CacheStorageInfo").Element("PacketSize").Value);

            //read conf.cdi

            XElement confCdi = XElement.Load(System.IO.Path.GetDirectoryName(path) + @"\\conf.cdi");

            try

            {

                tilingScheme.FullExtent = new Envelope(

                double.Parse(confCdi.Element("XMin").Value),

                double.Parse(confCdi.Element("YMin").Value),

                double.Parse(confCdi.Element("XMax").Value),

                double.Parse(confCdi.Element("YMax").Value));

            }

            catch (Exception e)

            {

                throw new Exception("the content of conf.cdi file is not valid!" + e.Message);

            }

            tilingScheme.InitialExtent = tilingScheme.FullExtent;

            #endregion

        }

/// <summary>

        /// 创建TilingScheme解析ArcGISTiledMapService信息

        /// </summary>

        /// <param name="ht">哈希表包括全部ArcGISTiledMapService信息解析JSON util类.</param>

        /// <param name="tilingScheme"></param>

        protected void ReadArcGISTiledMapServiceTilingScheme(Hashtable ht, out TilingScheme tilingScheme)

        {

            tilingScheme = new TilingScheme();

            StringBuilder sb;

            #region 读取缓存的元数据

            tilingScheme.Path = "N/A";

            tilingScheme.WKT = (ht["spatialReference"] as Hashtable)["wkt"] == null ?

"Absent" : (string)(ht["spatialReference"] as Hashtable)["wkt"];

            tilingScheme.WKID = int.Parse((ht["spatialReference"] as Hashtable)["wkid"].ToString());

            tilingScheme.TileOrigin = new Point(

                (double)((ht["tileInfo"] as Hashtable)["origin"] as Hashtable)["x"],

                (double)((ht["tileInfo"] as Hashtable)["origin"] as Hashtable)["y"]);

            tilingScheme.DPI = int.Parse((ht["tileInfo"] as Hashtable)["dpi"].ToString());

            //LODInfos

            int lodsCount = ((ht["tileInfo"] as Hashtable)["lods"] as ArrayList).Count;

            tilingScheme.LODs = new LODInfo[lodsCount];

            for (int i = 0; i < lodsCount; i++)

            {

                tilingScheme.LODs[i] = new LODInfo()

                {

                    LevelID = i,

                    Scale = (double)(((ht["tileInfo"] as Hashtable)["lods"] as ArrayList)[i] as Hashtable)["scale"],

                    Resolution = (double)(((ht["tileInfo"] as Hashtable)["lods"] as ArrayList)[i] as Hashtable)["resolution"]

                };

            }

            sb = new StringBuilder("\r\n");

           

            foreach (LODInfo lod in tilingScheme.LODs)

            {

                sb.Append(@"      {""level"":" + lod.LevelID + "," + @"""resolution"":" + lod.Resolution + "," + @"""scale"":" + lod.Scale + @"}," + "\r\n");

            }

            tilingScheme.LODsJson = sb.ToString().Remove(sb.ToString().Length - 3);//remove last "," and "\r\n"

            tilingScheme.TileCols = int.Parse((ht["tileInfo"] as Hashtable)["cols"].ToString());

            tilingScheme.TileRows = int.Parse((ht["tileInfo"] as Hashtable)["rows"].ToString());

            tilingScheme.CacheTileFormat = (ImageFormat)Enum.Parse(typeof(ImageFormat), (ht["tileInfo"] as Hashtable)["format"].ToString().ToUpper());

            tilingScheme.CompressionQuality = int.Parse((ht["tileInfo"] as Hashtable)["compressionQuality"].ToString());

            tilingScheme.StorageFormat = StorageFormat.unknown;//ArcGISTiledMapService doesn't expose this property

            tilingScheme.PacketSize = int.MinValue;

tilingScheme.FullExtent = new Envelope(

            (double)(ht["fullExtent"] as Hashtable)["xmin"],

            (double)(ht["fullExtent"] as Hashtable)["ymin"],

            (double)(ht["fullExtent"] as Hashtable)["xmax"],

            (double)(ht["fullExtent"] as Hashtable)["ymax"]);

            tilingScheme.InitialExtent = new Envelope(

                (double)(ht["initialExtent"] as Hashtable)["xmin"],

                (double)(ht["initialExtent"] as Hashtable)["ymin"],

                (double)(ht["initialExtent"] as Hashtable)["xmax"],

                (double)(ht["initialExtent"] as Hashtable)["ymax"]);

            #endregion

        }

protected void ReadArcGISTilePackageTilingSchemeFile(string path, out TilingScheme tilingScheme)

        {           

            tilingScheme = new TilingScheme();

            StringBuilder sb;

            #region 读取缓存的元数据

            tilingScheme.Path = "v101/Layers/conf.xml";

            using (Stream streamConfxml = new MemoryStream(Utility.GetEntryBytesFromZIPFile(path, "v101/Layers/conf.xml")))

            {

                if (streamConfxml == null)

                {

                    throw new Exception("conf.xml not found in " + "v101/Layers/conf.xml of " + System.IO.Path.GetFileName(path));

                }

                //read conf.xml

                XElement confXml = XElement.Load(streamConfxml);

                tilingScheme.WKT = confXml.Element("TileCacheInfo").Element("SpatialReference").Element("WKT").Value;

                if (confXml.Element("TileCacheInfo").Element("SpatialReference").Element("WKID") != null)

                    tilingScheme.WKID = int.Parse(confXml.Element("TileCacheInfo").Element("SpatialReference").Element("WKID").Value);

                else

                    tilingScheme.WKID = -1;

                tilingScheme.TileOrigin = new Point(

                    double.Parse(confXml.Element("TileCacheInfo").Element("TileOrigin").Element("X").Value),

                    double.Parse(confXml.Element("TileCacheInfo").Element("TileOrigin").Element("Y").Value));

                tilingScheme.DPI = int.Parse(confXml.Element("TileCacheInfo").Element("DPI").Value);

                //LODInfos

                int lodsCount = confXml.Element("TileCacheInfo").Element("LODInfos").Elements().Count();

                tilingScheme.LODs = new LODInfo[lodsCount];

                for (int i = 0; i < lodsCount; i++)

                {

                    tilingScheme.LODs[i] = new LODInfo()

                    {

                        LevelID = i,

                        Scale = double.Parse(confXml.Element("TileCacheInfo").Element("LODInfos").Elements().ElementAt(i).Element("Scale").Value),

                        Resolution = double.Parse(confXml.Element("TileCacheInfo").Element("LODInfos").Elements().ElementAt(i).Element("Resolution").Value)

                    };

                }

                sb = new StringBuilder("\r\n");

                //{"level" : 0, "resolution" : 156543.033928, "scale" : 591657527.591555},


                foreach (LODInfo lod in tilingScheme.LODs)

                {

                    sb.Append(@"      {""level"":" + lod.LevelID + "," + @"""resolution"":" + lod.Resolution + "," + @"""scale"":" + lod.Scale + @"}," + "\r\n");

                }

                tilingScheme.LODsJson = sb.ToString().Remove(sb.ToString().Length - 3);//remove last "," and "\r\n"

                tilingScheme.TileCols = int.Parse(confXml.Element("TileCacheInfo").Element("TileCols").Value);

                tilingScheme.TileRows = int.Parse(confXml.Element("TileCacheInfo").Element("TileRows").Value);

                tilingScheme.CacheTileFormat = (ImageFormat)Enum.Parse(typeof(ImageFormat), confXml.Element("TileImageInfo").Element("CacheTileFormat").Value.ToUpper());

                tilingScheme.CompressionQuality = int.Parse(confXml.Element("TileImageInfo").Element("CompressionQuality").Value);

                tilingScheme.StorageFormat = (StorageFormat)Enum.Parse(typeof(StorageFormat), confXml.Element("CacheStorageInfo").Element("StorageFormat").Value);

                tilingScheme.PacketSize = int.Parse(confXml.Element("CacheStorageInfo").Element("PacketSize").Value);

            }

            using (Stream streamConfcdi = new MemoryStream(Utility.GetEntryBytesFromZIPFile(path, "v101/Layers/conf.cdi")))

            {

                if (streamConfcdi == null)

                {

                    throw new Exception("conf.cdi not found in " + path);

                }

                //read conf.cdi

                XElement confCdi = XElement.Load(streamConfcdi);

                try

                {

                    tilingScheme.FullExtent = new Envelope(

                    double.Parse(confCdi.Element("XMin").Value),

                    double.Parse(confCdi.Element("YMin").Value),

                    double.Parse(confCdi.Element("XMax").Value),

                    double.Parse(confCdi.Element("YMax").Value));

                }

                catch (Exception e)

                {

                    throw new Exception("the content of conf.cdi file is not valid!" + e.Message);

                }

                tilingScheme.InitialExtent = tilingScheme.FullExtent;

            }

            #endregion

        }

protected void ReadGoogleMapsTilingScheme(out TilingScheme tilingScheme)

        {

            tilingScheme = new TilingScheme();

            StringBuilder sb;

            #region 谷歌必应地图的元数据

            tilingScheme.Path = "N/A";

            tilingScheme.CacheTileFormat = ImageFormat.JPG;

            tilingScheme.CompressionQuality = 75;

            tilingScheme.DPI = 96;

            

            tilingScheme.LODs = new LODInfo[20];

            const double cornerCoordinate = 20037508.342787;

            double resolution = cornerCoordinate * 2 / 256;

            double scale = 591657527.591555;

            for (int i = 0; i < tilingScheme.LODs.Length; i++)

            {

                tilingScheme.LODs[i] = new LODInfo()

                {

                    Resolution = resolution,

                    LevelID = i,

                    Scale = scale

                };

                resolution /= 2;

                scale /= 2;

            }

            //create json

            sb = new StringBuilder("\r\n");

            //{"level" : 0, "resolution" : 156543.033928, "scale" : 591657527.591555},


            foreach (LODInfo lod in tilingScheme.LODs)

            {

                sb.Append(@"      {""level"":" + lod.LevelID + "," + @"""resolution"":" + lod.Resolution + "," + @"""scale"":" + lod.Scale + @"}," + "\r\n");

            }

            tilingScheme.LODsJson = sb.ToString().Remove(sb.ToString().Length - 3);//remove last "," and "\r\n"

            //two extent

            tilingScheme.InitialExtent = new Envelope(-cornerCoordinate, -cornerCoordinate, cornerCoordinate, cornerCoordinate);

            tilingScheme.FullExtent = tilingScheme.InitialExtent;

            tilingScheme.PacketSize = 0;

            tilingScheme.StorageFormat = StorageFormat.esriMapCacheStorageModeExploded;

            tilingScheme.TileCols = tilingScheme.TileRows = 256;

            tilingScheme.TileOrigin = new Point(-cornerCoordinate, cornerCoordinate);

            tilingScheme.WKID = 3857;//102100;

            tilingScheme.WKT = @"PROJCS[""WGS_1984_Web_Mercator_Auxiliary_Sphere"",GEOGCS[""GCS_WGS_1984"",DATUM[""D_WGS_1984"",SPHEROID[""WGS_1984"",6378137.0,298.257223563]],PRIMEM[""Greenwich"",0.0],UNIT[""Degree"",0.0174532925199433]],PROJECTION[""Mercator_Auxiliary_Sphere""],PARAMETER[""False_Easting"",0.0],PARAMETER[""False_Northing"",0.0],PARAMETER[""Central_Meridian"",0.0],PARAMETER[""Standard_Parallel_1"",0.0],PARAMETER[""Auxiliary_Sphere_Type"",0.0],UNIT[""Meter"",1.0],AUTHORITY[""ESRI"",""3857""]]";

            #endregion

        }

protected void ReadTianDiTuTilingScheme(out TilingScheme tilingScheme)

        {

           

            tilingScheme = new TilingScheme();

            StringBuilder sb;

            #region 天地图的元数据

            tilingScheme.Path = "N/A";

            tilingScheme.CacheTileFormat = ImageFormat.JPEG;

            tilingScheme.CompressionQuality = 75;

            tilingScheme.DPI = 96;

            //LODs

            tilingScheme.LODs = new LODInfo[17];

            double resolution = 90 / 256d;

            double scale = 147914677.73;//147748799.285417;

            for (int i = 0; i < tilingScheme.LODs.Length; i++)

            {

                tilingScheme.LODs[i] = new LODInfo()

                {

                    Resolution = resolution,

                    LevelID = i,

                    Scale = scale

                };

                resolution /= 2;

                scale /= 2;

            }

            //create json

            sb = new StringBuilder("\r\n");

            //{"level" : 0, "resolution" : 156543.033928, "scale" : 591657527.591555},


            foreach (LODInfo lod in tilingScheme.LODs)

            {

                sb.Append(@"      {""level"":" + lod.LevelID + "," + @"""resolution"":" + lod.Resolution + "," + @"""scale"":" + lod.Scale + @"}," + "\r\n");

            }

            tilingScheme.LODsJson = sb.ToString().Remove(sb.ToString().Length - 3);//remove last "," and "\r\n"

            //two extent

            tilingScheme.InitialExtent = new Envelope(-180, -90, 180, 90);

            tilingScheme.FullExtent = tilingScheme.InitialExtent;

            tilingScheme.PacketSize = 0;

            tilingScheme.StorageFormat = StorageFormat.esriMapCacheStorageModeExploded;

            tilingScheme.TileCols = tilingScheme.TileRows = 256;

            tilingScheme.TileOrigin = new Point(-180, 90);

            tilingScheme.WKID = 4326;

            tilingScheme.WKT = @"GEOGCS[""WGS 84"",DATUM[""WGS_1984"",SPHEROID[""WGS 84"",6378137,298.257223563,AUTHORITY[""EPSG"",""7030""]],AUTHORITY[""EPSG"",""6326""]],PRIMEM[""Greenwich"",0,AUTHORITY[""EPSG"",""8901""]],UNIT[""degree"",0.01745329251994328,AUTHORITY[""EPSG"",""9122""]],AUTHORITY[""EPSG"",""4326""]]";

            #endregion

        }

        #endregion

public static bool IsOnlineMaps(string type)

        {

            bool isPredefinedDataSource = false;

            foreach (var str in Enum.GetValues(typeof(DataSourceTypePredefined)))

            {

                if (string.Equals(type, str.ToString()))

                {

                    isPredefinedDataSource = true;

                    break;

                }

            }

            return !isPredefinedDataSource;

        }

protected byte[] HttpGetTileBytes(string uri)

        {           

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);

            request.Accept = "*/*";

            request.KeepAlive = true;

            request.Method = "GET";

         

            if (this.Type == DataSourceTypePredefined.ArcGISTiledMapService.ToString())

            {

                request.Referer = this.Path + "?f=jsapi";

            }

           

            request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4";

           

            request.Proxy = null;//==no proxy

            request.Timeout = 20000;

            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())

            {

                if (!response.ContentType.ToLower().Contains("image"))

                    throw new Exception("download(http get) result is not image");

                return Util.Utility.StreamToBytes(response.GetResponseStream());

            }

        }

protected byte[] HttpPostTileBytes(string url, string queryData)

        {

            HttpWebRequest request;

            request = (HttpWebRequest)WebRequest.Create(url);

            request.Method = "POST";

            request.ContentType = "application/x-www-form-urlencoded";

            request.Accept = "text/html, image/png, image/jpeg, image/gif, */*;q=0.1";

            request.KeepAlive = true;

            request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4";

            request.Proxy = null;//GlobalProxySelection.GetEmptyWebProxy();  

            byte[] data = Encoding.UTF8.GetBytes(queryData);

            request.ContentLength = data.Length;

            using (Stream stream = request.GetRequestStream())

            {

                stream.Write(data, 0, data.Length);

            }

            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())

            {

                if (!response.ContentType.ToLower().Contains("image"))

                    throw new Exception("download(http post) result is not image");

                return Util.Utility.StreamToBytes(response.GetResponseStream());

            }

        }

#region Local File Cache

        /// <summary>

        /// by checking keyword to determin if sqlite file is IMapGeoServer created local cache file.

        /// </summary>

        private string constIMapGeoServerFileCacheKeyword = "___IMapGeoServerLOCALCACHE___";

        private string _localCacheFileName;

public string LocalCacheFileName

        {

            get { return _localCacheFileName; }

            set { _localCacheFileName = value; }

        }

        private SQLiteConnection _connLocalCacheFile;

        private ConcurrentDictionary<string, byte[]> _dictTilesToBeLocalCached = new ConcurrentDictionary<string, byte[]>();

        /// <summary>

        /// 检查本地缓存文件相应onlinemap是有效的。

假设无效或不存在,创建新的。

        /// </summary>

        /// <param name="localCacheFileName">本地缓存文件的名称(.cache)</param>

        protected void ValidateLocalCacheFile(string localCacheFileName)

        {

            _localCacheFileName = localCacheFileName;

            try

            {

                if (File.Exists(localCacheFileName))

                {

                    #region 推断文件是否有效

                    using (SQLiteConnection conn = new SQLiteConnection("Data source = " + localCacheFileName))

                    {

                        conn.Open();

                        using (SQLiteCommand cmd = new SQLiteCommand(conn))

                        {

                            cmd.CommandText = "SELECT value FROM metadata WHERE name='name'";

                            object o = cmd.ExecuteScalar();

                            if (o != null && o.ToString().Equals(constIMapGeoServerFileCacheKeyword))

                            {

                                _connLocalCacheFile = new SQLiteConnection("Data Source=" + _localCacheFileName);

                                _connLocalCacheFile.Open();

                                return;

                            }

                            else

                            {

                                try

                                {

                                    File.Delete(localCacheFileName);

                                }

                                catch (Exception e)

                                {

                                    throw new Exception("Init online maps local cache file failed.\r\n" + e.Message);

                                }

                            }

                        }

                    }

                    #endregion

                }

                #region 创建sqllite数据库

                SQLiteConnection.CreateFile(localCacheFileName);

                using (SQLiteConnection conn = new SQLiteConnection("Data source = " + localCacheFileName))

                {

                    conn.Open();

                    using (SQLiteTransaction transaction = conn.BeginTransaction())

                    {

                        using (SQLiteCommand cmd = new SQLiteCommand(conn))

                        {

                            #region 创建table和索引

                            //ref http://mapbox.com/developers/mbtiles/

                            //metadata table

                            cmd.CommandText = "CREATE TABLE metadata (name TEXT, value TEXT)";

                            cmd.ExecuteNonQuery();

                            //images table

                            cmd.CommandText = "CREATE TABLE images (tile_data BLOB, tile_id TEXT)";

                            cmd.ExecuteNonQuery();

                            //map table

                            cmd.CommandText = "CREATE TABLE map (zoom_level INTEGER, tile_column INTEGER, tile_row INTEGER, tile_id TEXT)";

                            cmd.ExecuteNonQuery();

                            //tiles view

                            cmd.CommandText = @"CREATE VIEW tiles AS SELECT

    map.zoom_level AS zoom_level,

    map.tile_column AS tile_column,

    map.tile_row AS tile_row,

    images.tile_data AS tile_data

FROM map JOIN images ON images.tile_id = map.tile_id";

                            cmd.ExecuteNonQuery();

                            //indexes

                            cmd.CommandText = "CREATE UNIQUE INDEX images_id on images (tile_id)";

                            cmd.ExecuteNonQuery();

                            cmd.CommandText = "CREATE UNIQUE INDEX map_index on map (zoom_level, tile_column, tile_row)";

                            cmd.ExecuteNonQuery();

                            cmd.CommandText = @"CREATE UNIQUE INDEX name ON metadata (name)";

                            cmd.ExecuteNonQuery();

                            #endregion

                            #region 写入元数据信息

                            //name

                            cmd.CommandText = @"INSERT INTO metadata(name,value) VALUES (""name"",""" + constIMapGeoServerFileCacheKeyword + @""")";

                            cmd.ExecuteNonQuery();

                            //type

                            cmd.CommandText = "INSERT INTO metadata(name,value) VALUES ('type','baselayer')";

                            cmd.ExecuteNonQuery();

                            //version

                            cmd.CommandText = "INSERT INTO metadata(name,value) VALUES ('version','1.2')";

                            cmd.ExecuteNonQuery();

                            //no description

                            //format

                            string f = TilingScheme.CacheTileFormat.ToString().ToUpper().Contains("PNG") ?

"png" : "jpg";

                            cmd.CommandText = "INSERT INTO metadata(name,value) VALUES ('format','" + f + "')";

                            cmd.ExecuteNonQuery();

                            //no bounds

                            //no attribution

                            #endregion

                        }

                        transaction.Commit();

                    }

                }

                #endregion

                _connLocalCacheFile = new SQLiteConnection("Data Source=" + _localCacheFileName);

                _connLocalCacheFile.Open();

            }

            catch (Exception e)

            {

                throw new Exception("Validating local cache file error!\r\n" + e.Message);

            }

        }

/// <summary>

        /// 从本地尝试检索瓷砖byte[]。缓存文件。返回byte[]假设成功,null假设失败了。

/// </summary>

        /// <param name="level"></param>

        /// <param name="row"></param>

        /// <param name="col"></param>

        /// <returns></returns>

        public byte[] GetTileBytesFromLocalCache(int level, int row, int col)

        {

            if (!IsOnlineMap && !(this is DataSourceRasterImage))

                return null;

            int tmsCol, tmsRow;

            Utility.ConvertGoogleTileToTMSTile(level, row, col, out tmsRow, out tmsCol);

            string commandText = string.Format("SELECT {0} FROM tiles WHERE tile_column={1} AND tile_row={2} AND zoom_level={3}", "tile_data", tmsCol, tmsRow, level);

            if (_connLocalCacheFile.State.ToString() == "Closed")

            {

                _connLocalCacheFile.Open();

            }

            using (SQLiteCommand sqlCmd = new SQLiteCommand(commandText, _connLocalCacheFile))

            {

                object o = sqlCmd.ExecuteScalar();

                if (o != null)

                {

                    return (byte[])o;

                }

                return null;

            }

        }

      

       

       

        /// <summary>

        /// 发生在瓦载入和瓷砖保存到本地文件缓存

        /// </summary>

        /// <param name="o"></param>

        /// <param name="a">TileLoadEventArgs</param>

        protected void InternalOnTileLoaded(object o, TileLoadEventArgs a)

        {

            if (a.GeneratedMethod != TileGeneratedSource.DynamicOutput)

                return;

            if (o is DataSourceRasterImage && !ConfigManager.App_AllowFileCacheOfRasterImage ||

                IsOnlineMap && !ConfigManager.App_AllowFileCacheOfOnlineMaps)

                return;

int tmsRow, tmsCol;

            Utility.ConvertGoogleTileToTMSTile(a.Level, a.Row, a.Column, out tmsRow, out tmsCol);

            string key = string.Format("{0}/{1}/{2}", a.Level, tmsCol, tmsRow);

            if (_dictTilesToBeLocalCached.ContainsKey(key))

                return;

            _dictTilesToBeLocalCached.TryAdd(key, a.TileBytes);

            if (_dictTilesToBeLocalCached.Count ==1000)

            {

                WriteTilesToLocalCacheFile(_dictTilesToBeLocalCached);

                _dictTilesToBeLocalCached.Clear();

            }

        }

        /// <summary>

        /// 当下载在线地图或者栅格图片把瓦片数据下乳缓存文件

        /// </summary>

        /// <param name="dict"></param>

        protected void WriteTilesToLocalCacheFile(ConcurrentDictionary<string, byte[]> dict)

        {

            lock (_locker)

            {

                try

                {

                    using (SQLiteConnection conn = new SQLiteConnection("Data source = " + _localCacheFileName))

                    {

                        conn.Open();

                        using (SQLiteTransaction transaction = conn.BeginTransaction())

                        {

                            using (SQLiteCommand cmd = new SQLiteCommand(conn))

                            {

                                foreach (KeyValuePair<string, byte[]> kvp in dict)

                                {

                                    //key = "level/col/row"

                                    int level = int.Parse(kvp.Key.Split(new char[] { '/' })[0]);

                                    int col = int.Parse(kvp.Key.Split(new char[] { '/' })[1]);

                                    int row = int.Parse(kvp.Key.Split(new char[] { '/' })[2]);

                                    string guid = Guid.NewGuid().ToString();

                                    cmd.CommandText = "INSERT INTO images VALUES (@tile_data,@tile_id)";

                                    cmd.Parameters.AddWithValue("tile_data", kvp.Value);

                                    cmd.Parameters.AddWithValue("tile_id", guid);

                                    cmd.ExecuteNonQuery();

                                    cmd.CommandText = "INSERT INTO map VALUES (@zoom_level,@tile_column,@tile_row,@tile_id)";

                                    cmd.Parameters.AddWithValue("zoom_level", level);

                                    cmd.Parameters.AddWithValue("tile_column", col);

                                    cmd.Parameters.AddWithValue("tile_row", row);

                                    cmd.Parameters.AddWithValue("tile_id", guid);

                                    cmd.ExecuteNonQuery();

                                }

                            }

                            transaction.Commit();

                        }

                    }

                }

                catch (Exception e)

                {

                    throw new Exception("Writing tiles to local .cache file error!\r\n" + e.Message);

                }

            }

        }

        #endregion

#region 转换状态

        private ConvertStatus _convertingStatus;

        /// <summary>

        /// 缓存转换状态

        /// </summary>

        public ConvertStatus ConvertingStatus

        {

            get { return _convertingStatus; }

            protected set { _convertingStatus = value; }

        }

        #region ToMBTiles

        private string _outputFile;

      

        private static readonly object _locker = new object();

        private long _completeCount, _levelCompleteCount, _totalCount, _levelTotalCount, _errorCount, _levelErrorCount, _completeTotalBytes;

        /// <summary>

        /// constrain the tile downloading to the extent of an envelope or boundary shape of a polygon.

        /// </summary>

        private Geometry _downloadGeometry;

        private CancellationTokenSource _cts = new CancellationTokenSource();

        /// <summary>

        /// Do convert jobs from various datasource to MBTiles. Datasource must be in 3857 tilingscheme.

        /// </summary>

        /// <param name="outputPath">full output file name and path.</param>

        /// <param name="name">optional by mbtiles.</param>

        /// <param name="description">optional by mbtiles.</param>

        /// <param name="attribution">optional by mbtiles.</param>

        /// <param name="levels">tiles in which levels to convert to mbtiles.</param>

        /// <param name="geometry">convert/download extent, sr=3857. If this is Envelope, download by rectangle, if this is polygon, download by polygon's shape.</param>

        /// <param name="doCompact">implementing the reducing redundant tile bytes part of MBTiles specification?</param>

        protected void DoConvertToMBTiles(string outputPath, string name, string description, string attribution, int[] levels, Geometry geometry, bool doCompact)

        {

            _outputFile = outputPath;

            _downloadGeometry = geometry;

            _convertingStatus.IsInProgress = true;

            try

            {

                CreateMBTilesFileAndWriteMetaData(outputPath, name, description, attribution, geometry);

                #region calculate startCol/Row and endCol/Row and tiles count of each level

                int constTileSize = 256;

                _convertingStatus.TotalCount = 0;

                string[] keyTileInfos = new string[levels.Length];

                int[] tilesCountOfLevel = new int[levels.Length];

                for (int i = 0; i < levels.Length; i++)

                {

                    LODInfo lod = TilingScheme.LODs[levels[i]];

                    double oneTileDistance = lod.Resolution * constTileSize;

                    int startTileRow = (int)(Math.Abs(TilingScheme.TileOrigin.Y - geometry.Extent.YMax) / oneTileDistance);

                    int startTileCol = (int)(Math.Abs(TilingScheme.TileOrigin.X - geometry.Extent.XMin) / oneTileDistance);

                    int endTileRow = (int)(Math.Abs(TilingScheme.TileOrigin.Y - geometry.Extent.YMin) / oneTileDistance);

                    int endTileCol = (int)(Math.Abs(TilingScheme.TileOrigin.X - geometry.Extent.XMax) / oneTileDistance);

                    keyTileInfos[i] = string.Format("{0},{1},{2},{3}", startTileRow, startTileCol, endTileRow, endTileCol);

                    tilesCountOfLevel[i] = Math.Abs((endTileCol - startTileCol + 1) * (endTileRow - startTileRow + 1));

                    _convertingStatus.TotalCount += tilesCountOfLevel[i];

                }

                _totalCount = _convertingStatus.TotalCount;

                _completeCount = _errorCount = 0;

                #endregion

                for (int i = 0; i < levels.Length; i++)

                {

                    int level, startR, startC, endR, endC;//startTileRow,startTileCol,...

                    level = TilingScheme.LODs[levels[i]].LevelID;

                    startR = int.Parse(keyTileInfos[i].Split(new char[] { ',' })[0]);

                    startC = int.Parse(keyTileInfos[i].Split(new char[] { ',' })[1]);

                    endR = int.Parse(keyTileInfos[i].Split(new char[] { ',' })[2]);

                    endC = int.Parse(keyTileInfos[i].Split(new char[] { ',' })[3]);

                    _convertingStatus.Level = level;

                    _convertingStatus.LevelTotalCount = tilesCountOfLevel[i];

                    _convertingStatus.LevelCompleteCount = _convertingStatus.LevelErrorCount = 0;

                    _levelTotalCount = _convertingStatus.LevelTotalCount;

                    _levelCompleteCount = _levelErrorCount = 0;          

                    SaveOneLevelTilesToMBTiles(level, startR, startC, endR, endC);

                    if (_convertingStatus.IsCancelled)

                    {

                        _convertingStatus.IsCompletedSuccessfully = false;

                        break;

                    }

                }

                if (doCompact)

                {

                    _convertingStatus.IsDoingCompact = true;

                    _convertingStatus.SizeBeforeCompact = new FileInfo(_outputFile).Length;

                    CompactMBTiles(_outputFile);

                    _convertingStatus.IsDoingCompact = false;

                    _convertingStatus.SizeAfterCompact = new FileInfo(_outputFile).Length;

                }

                if (!_convertingStatus.IsCancelled)

                    _convertingStatus.IsCompletedSuccessfully = true;

            }

            finally

            {

                _convertingStatus.IsInProgress = false;

                _convertingStatus.IsCommittingTransaction = false;

                _convertingStatus.IsDoingCompact = false;

            }

        }

/// <summary>

        /// process all tiles in a level and save them in sqlite db, with multi threads

        /// </summary>

        /// <param name="level">level number</param>

        /// <param name="startRowLevel">start row number of full extent of this level</param>

        /// <param name="startColLevel"></param>

        /// <param name="endRowLevel"></param>

        /// <param name="endColLevel"></param>

        private void SaveOneLevelTilesToMBTiles(int level, int startRowLevel, int startColLevel, int endRowLevel, int endColLevel)

        {

            int bundleSize = IsOnlineMap ? 16 : 128;

            Bundle startBundle = new Bundle(bundleSize, level, startRowLevel / bundleSize, startColLevel / bundleSize, TilingScheme);

            Bundle endBundle = new Bundle(bundleSize, level, endRowLevel / bundleSize, endColLevel / bundleSize, TilingScheme);

            List<Bundle> allBundles = new List<Bundle>();

            for (int bRow = startBundle.Row; bRow <= endBundle.Row; bRow++)

            {

                for (int bCol = startBundle.Col; bCol <= endBundle.Col; bCol++)

                {

                    Bundle b = new Bundle(bundleSize, level, bRow, bCol, TilingScheme);

                    if (_downloadGeometry is Polygon)

                    {

                        bool bPolygonTouchesWithBundle = false;

                        Polygon polygon = _downloadGeometry as Polygon;

                        if (polygon.ContainsPoint(b.Extent.LowerLeft) || polygon.ContainsPoint(b.Extent.LowerRight) || polygon.ContainsPoint(b.Extent.UpperLeft) || polygon.ContainsPoint(b.Extent.UpperRight))

                            bPolygonTouchesWithBundle = true;

                        if (b.Extent.ContainsPoint(polygon.Extent.LowerLeft) && b.Extent.ContainsPoint(polygon.Extent.LowerRight) && b.Extent.ContainsPoint(polygon.Extent.UpperLeft) && b.Extent.ContainsPoint(polygon.Extent.UpperRight))

                            bPolygonTouchesWithBundle = true;

                        if (polygon.IsIntersectsWithPolygon(b.Extent.ToPolygon()))

                            bPolygonTouchesWithBundle = true;

                        if (!bPolygonTouchesWithBundle)

                            continue;

                    }

                    allBundles.Add(b);

                }

            }

           

            int maxThreadCount = IsOnlineMap ? 50 : 5;

            int queueCount = allBundles.Count % maxThreadCount == 0 ?

allBundles.Count / maxThreadCount : allBundles.Count / maxThreadCount + 1;

            for (int queue = 0; queue < queueCount; queue++)

            {

                int startBundleIndex = maxThreadCount * queue;

                int endBundleIndex = startBundleIndex + maxThreadCount - 1;

                endBundleIndex = endBundleIndex > allBundles.Count ? allBundles.Count - 1 : endBundleIndex;

                List<Task> tasks = new List<Task>();

                for (int i = startBundleIndex; i <= endBundleIndex; i++)

                {

                    if (_convertingStatus.IsCancelled)

                        return;

                    Bundle b = allBundles[i];

                    int startR = startRowLevel > b.StartTileRow ? startRowLevel : b.StartTileRow;

                    int startC = startColLevel > b.StartTileCol ? startColLevel : b.StartTileCol;

                    int endR = endRowLevel > b.EndTileRow ?

b.EndTileRow : endRowLevel;

                    int endC = endColLevel > b.EndTileCol ? b.EndTileCol : endColLevel;

                    Task t = Task.Factory.StartNew(() => { WriteTilesToSqlite(GetTilesByExtent(level, startR, startC, endR, endC)); }, _cts.Token);

                    tasks.Add(t);

                }

                _convertingStatus.ThreadCount = tasks.Count;

                try

                {

                    Task.WaitAll(tasks.ToArray());

                }

                catch (AggregateException)

                {

                }

            }

        }

/// <summary>

        /// Notify all converting tasks to cancel. The db transaction has began will still need sometime to be completed.

        /// </summary>

        protected void CancelDoConvertToMBTiles()

        {

            _convertingStatus.IsCancelled = true;

            _cts.Cancel();

        }

/// <summary>

        /// Get tiles of specified extent.

        /// </summary>

        /// <param name="level"></param>

        /// <param name="startRow"></param>

        /// <param name="startCol"></param>

        /// <param name="endRow"></param>

        /// <param name="endCol"></param>

        /// <returns>A Dictionary which stores the tiles bytes. key patten is "level/col/row".</returns>

        private Dictionary<string, byte[]> GetTilesByExtent(int level, int startRow, int startCol, int endRow, int endCol)

        {

            Dictionary<string, byte[]> dict = new Dictionary<string, byte[]>();

            for (int r = startRow; r <= endRow; r++)

            {

                for (int c = startCol; c <= endCol; c++)

                {

                    byte[] bytes = GetTileBytes(level, r, c);

                    if (bytes != null)

                    {

                        dict.Add(string.Format("{0}/{1}/{2}", level, c, r), bytes);

                        try

                        {

                            _convertingStatus.LevelCompleteCount = Interlocked.Increment(ref _levelCompleteCount);

                            _convertingStatus.CompleteCount = Interlocked.Increment(ref _completeCount);

                            _convertingStatus.CompleteTotalBytes = Interlocked.Add(ref _completeTotalBytes, bytes.Length);

                        }

                        catch (Exception ex)

                        {

                           

                            throw;

                        }

                      

                    }

                    else

                    {

                        _convertingStatus.LevelErrorCount = Interlocked.Increment(ref _levelErrorCount);

                        _convertingStatus.ErrorCount = Interlocked.Increment(ref _errorCount);

                    }

#if Debug

                    System.Diagnostics.Debug.WriteLine(_convertingStatus.LevelCompleteCount + " / " + _convertingStatus.LevelTotalCount + "  |||  " + level + "/" + r + "/" + c + "thread:" + Thread.CurrentThread.ManagedThreadId);

#endif

                }

            }

            return dict;

        }

/// <summary>

        /// Write tiles in Dictionary to Sqlite file.

        /// </summary>

        /// <param name="dict">the Dictionary which contains tiles bytes to write. key patten is "level/col/row".</param>

        private void WriteTilesToSqlite(Dictionary<string, byte[]> dict)

        {

            lock (_locker)

            {

                using (SQLiteConnection conn = new SQLiteConnection("Data source = " + _outputFile))

                {

                    conn.Open();

                    SQLiteTransaction transaction = conn.BeginTransaction();

                    try

                    {

                        using (SQLiteCommand cmd = new SQLiteCommand(conn))

                        {

                            foreach (KeyValuePair<string, byte[]> kvp in dict)

                            {

                                int level = int.Parse(kvp.Key.Split(new char[] { '/' })[0]);

                                int col = int.Parse(kvp.Key.Split(new char[] { '/' })[1]);

                                int row = int.Parse(kvp.Key.Split(new char[] { '/' })[2]);

                                int tmsRow, tmsCol;

                                Utility.ConvertGoogleTileToTMSTile(level, row, col, out tmsRow, out tmsCol);

                                string guid = Guid.NewGuid().ToString();

                                cmd.CommandText = "INSERT INTO images VALUES (@tile_data,@tile_id)";

                                cmd.Parameters.AddWithValue("tile_data", kvp.Value);

                                cmd.Parameters.AddWithValue("tile_id", guid);

                                cmd.ExecuteNonQuery();

                                cmd.CommandText = "INSERT INTO map VALUES (@zoom_level,@tile_column,@tile_row,@tile_id)";

                                cmd.Parameters.AddWithValue("zoom_level", level);

                                cmd.Parameters.AddWithValue("tile_column", tmsCol);

                                cmd.Parameters.AddWithValue("tile_row", tmsRow);

                                cmd.Parameters.AddWithValue("tile_id", guid);

                                cmd.ExecuteNonQuery();

                            }

                        }

                        _convertingStatus.IsCommittingTransaction = true;

                        transaction.Commit();

                        _convertingStatus.IsCommittingTransaction = false;

                    }

                    catch (Exception e)

                    {

                       

                       

                        }

                    finally

                    {

                        if (transaction != null)

                            transaction.Dispose();

                    }

                }

            }

        }

/// <summary>

        /// implementing the reducing redundant tile bytes part of MBTiles specification.

        /// </summary>

        /// <param name="fileName">MBTiles file name.</param>

        private void CompactMBTiles(string fileName)

        {

            try

            {

                using (SQLiteConnection conn = new SQLiteConnection("Data source = " + fileName))

                {

                    conn.Open();

                    List<int> duplicateLength = new List<int>();

                    using (SQLiteCommand cmd = new SQLiteCommand(conn))

                    {

                        cmd.CommandText = "SELECT tile_data,count(*) as counts FROM images GROUP BY tile_data HAVING count(*) > 1";

                        SQLiteDataReader dr = cmd.ExecuteReader();

                        while (dr.Read())

                        {

                            if (!duplicateLength.Contains(((byte[])dr[0]).Length))

                                duplicateLength.Add(((byte[])dr[0]).Length);

                        }

                        dr.Close();

                        foreach (int length in duplicateLength)

                        {

                            cmd.CommandText = "SELECT tiles.zoom_level as zoom_level,tiles.tile_column as tile_column,tiles.tile_row as tile_row,tiles.tile_data as tile_data,map.tile_id as tile_id FROM tiles JOIN map ON map.zoom_level=tiles.zoom_level AND map.tile_column=tiles.tile_column
AND map.tile_row=tiles.tile_row WHERE length(tile_data)=" + length;

                            SQLiteDataReader row = cmd.ExecuteReader();

                            Dictionary<string, byte[]> uniqueBytes = new Dictionary<string, byte[]>();                 


                            using (SQLiteTransaction transaction = conn.BeginTransaction())

                            {

                                using (SQLiteCommand cmd1 = new SQLiteCommand(conn))

                                {

                                    while (row.Read())

                                    {

                                        bool isRedundant = false;

                                        if (uniqueBytes.Count == 0)

                                        {

                                            uniqueBytes.Add(row["tile_id"].ToString(), (byte[])row["tile_data"]);

                                            continue;

                                        }

                                        foreach (KeyValuePair<string, byte[]> kvp in uniqueBytes)

                                        {

                                            if (IsByteArrayEquivalent((byte[])row["tile_data"], kvp.Value))

                                            {

                                                isRedundant = true;

cmd1.CommandText = string.Format("UPDATE map SET tile_id = '{0}' WHERE zoom_level = {1} AND tile_column = {2} AND tile_row = {3} ", kvp.Key, int.Parse(row["zoom_level"].ToString()), int.Parse(row["tile_column"].ToString()),
int.Parse(row["tile_row"].ToString()));

                                                cmd1.ExecuteNonQuery();

                                                cmd1.CommandText = "DELETE FROM images WHERE tile_id='" + row["tile_id"].ToString() + "'";

                                                cmd1.ExecuteNonQuery();

break;

                                            }

                                        }

                                        if (!isRedundant)

                                            uniqueBytes.Add(row["tile_id"].ToString(), (byte[])row["tile_data"]);

                                    }

                                }

                                row.Close();

                                transaction.Commit();

                            }

                        }

                        cmd.CommandText = "VACUUM";

                        cmd.ExecuteNonQuery();

                    }

                }

            }

            catch (Exception e)

            {

                throw new Exception("compacting MBTiles error!\r\n" + e.Message);

            }

        }

private bool IsByteArrayEquivalent(byte[] bytes1, byte[] bytes2)

        {

            if (bytes1.Length != bytes2.Length)

                return false;

            for (int i = 0; i < bytes1.Length; i++)

            {

                if (bytes1[i] != bytes2[i])

                    return false;

            }

            return true;

        }

/// <summary>

        /// create the .mbtiles file and initialize tables, views and write metadata.

        /// </summary>

        /// <param name="outputPath"></param>

        /// <param name="name"></param>

        /// <param name="description"></param>

        /// <param name="attribution"></param>

        private void CreateMBTilesFileAndWriteMetaData(string outputPath, string name, string description, string attribution, Geometry geometry)

        {

            //check the wkid

            if (!TilingScheme.WKID.Equals(102100) && !TilingScheme.WKID.Equals(3857))

            {

                throw new Exception("The WKID of ArcGIS Cache is not 3857 or 102100!");

            }

            //check the numbers of lods

            if (TilingScheme.LODs.Length != 20)

            {

                throw new Exception("The count of levels must be 20! Current levels count = " + TilingScheme.LODs.Length);

            }

            //check tiling scheme origin

            if (Math.Abs(TilingScheme.TileOrigin.X + 20037508.342787) > 0.1)

            {

                throw new Exception("The tiling scheme origin is not correct!");

            }

            //check the tile dimension

            if (!TilingScheme.TileCols.Equals(256) || !TilingScheme.TileRows.Equals(256))

            {

                throw new Exception("The size of a tile is not 256*256!");

            }

            //create new sqlite database

            try

            {

                SQLiteConnection.CreateFile(outputPath);

            }

            catch (Exception ex)

            {

               

                throw;

            }

          

            using (SQLiteConnection conn = new SQLiteConnection("Data source = " + outputPath))

            {

                this._connLocalCacheFile = conn;

                conn.Open();

             

                using (SQLiteTransaction transaction = conn.BeginTransaction())

                {

                    using (SQLiteCommand cmd = new SQLiteCommand(conn))

                    {

                        #region create tables and indexes

                        //ref http://mapbox.com/developers/mbtiles/

                        //metadata table

                        cmd.CommandText = "CREATE TABLE metadata (name TEXT, value TEXT)";

                        cmd.ExecuteNonQuery();

                        //images table

                        cmd.CommandText = "CREATE TABLE images (tile_data BLOB, tile_id TEXT)";

                        cmd.ExecuteNonQuery();

                        //map table

                        cmd.CommandText = "CREATE TABLE map (zoom_level INTEGER, tile_column INTEGER, tile_row INTEGER, tile_id TEXT)";

                        cmd.ExecuteNonQuery();

                        //tiles view

                        cmd.CommandText = @"CREATE VIEW tiles AS SELECT

    map.zoom_level AS zoom_level,

    map.tile_column AS tile_column,

    map.tile_row AS tile_row,

    images.tile_data AS tile_data

FROM map JOIN images ON images.tile_id = map.tile_id";

                        cmd.ExecuteNonQuery();

                        //indexes

                        cmd.CommandText = "CREATE UNIQUE INDEX images_id on images (tile_id)";

                        cmd.ExecuteNonQuery();

                        cmd.CommandText = "CREATE UNIQUE INDEX map_index on map (zoom_level, tile_column, tile_row)";

                        cmd.ExecuteNonQuery();

                        cmd.CommandText = @"CREATE UNIQUE INDEX name ON metadata (name)";

                        cmd.ExecuteNonQuery();

                        #endregion

                        #region write metadata

                        //name

                        cmd.CommandText = @"INSERT INTO metadata(name,value) VALUES (""name"",""" + name + @""")";

                        cmd.ExecuteNonQuery();

                        //type

                        cmd.CommandText = "INSERT INTO metadata(name,value) VALUES ('type','baselayer')";

                        cmd.ExecuteNonQuery();

                        //version

                        cmd.CommandText = "INSERT INTO metadata(name,value) VALUES ('version','1.2')";

                        cmd.ExecuteNonQuery();

                        //description

                        cmd.CommandText = @"INSERT INTO metadata(name,value) VALUES (""description"",""" + description + @""")";

                        cmd.ExecuteNonQuery();

                        //format

                        string f = TilingScheme.CacheTileFormat.ToString().ToUpper().Contains("PNG") ?

"png" : "jpg";

                        cmd.CommandText = "INSERT INTO metadata(name,value) VALUES ('format','" + f + "')";

                        cmd.ExecuteNonQuery();

                        //bounds

                        Point bottomLeft = Utility.WebMercatorToGeographic(new Point(geometry.Extent.XMin, geometry.Extent.YMin));

                        Point upperRight = Utility.WebMercatorToGeographic(new Point(geometry.Extent.XMax, geometry.Extent.YMax));

                        cmd.CommandText = "INSERT INTO metadata(name,value) VALUES ('bounds','" + string.Format("{0},{1},{2},{3}", bottomLeft.X.ToString(), bottomLeft.Y.ToString(), upperRight.X.ToString(), upperRight.Y.ToString()) + "')";

                        cmd.ExecuteNonQuery();

                        //attribution

                        cmd.CommandText = @"INSERT INTO metadata(name,value) VALUES (""attribution"",""" + attribution + @""")";

                        cmd.ExecuteNonQuery();

                        #endregion

                    }

                    transaction.Commit();

                }

            }

        }

        #endregion

        #endregion

    }

/// <summary>

    ///地图数据源类型

    /// </summary>

    public enum DataSourceTypePredefined

    {

        MobileAtlasCreator,

        MBTiles,

        ArcGISCache,

        ArcGISTilePackage,

        ArcGISDynamicMapService,

        ArcGISTiledMapService,

        ArcGISImageService,

        RasterImage,

        OGCWMSService,

        AutoNaviCache,       

        TianDiTuAnnotation,

        TianDiTuMap,

    }

public class TilingScheme

    {

        /// <summary>

        /// tiling scheme file path

        /// </summary>

        public string Path { get; set; }

        public string RestResponseArcGISJson { get; set; }

        public string RestResponseArcGISPJson { get; set; }

        public int WKID { get; set; }

        public string WKT { get; set; }

        public int TileCols { get; set; }

        public int TileRows { get; set; }

        /// <summary>

        /// PNG/PNG32/PNG24/PNG8/JPEG/JPG/Mixed

        /// </summary>

        public ImageFormat CacheTileFormat { get; set; }

        /// <summary>

        /// png=0,jpg=XX

        /// </summary>

        public int CompressionQuality { get; set; }

        public Point TileOrigin { get; set; }

        public LODInfo[] LODs { get; set; }

        public string LODsJson { get; set; }

        public Envelope InitialExtent { get; set; }

        public Envelope FullExtent { get; set; }

        public StorageFormat StorageFormat { get; set; }

        public int PacketSize { get; set; }

        public int DPI { get; set; }

    }

public class LODInfo

    {

        public int LevelID { get; set; }

        public double Scale { get; set; }

        public double Resolution { get; set; }

    }

    /// <summary>

    /// 存储格式

    /// </summary>

    public enum StorageFormat

    {

        esriMapCacheStorageModeExploded,

        esriMapCacheStorageModeCompact,

        unknown//ArcGISTiledMapService doesn't expose this property

    }

    /// <summary>

    /// 图片类型

    /// </summary>

    public enum ImageFormat

    {

        PNG,

        PNG32,

        PNG24,

        PNG8,

        JPG,

        JPEG,

        MIXED

    }

public class TileLoadEventArgs : EventArgs

    {

        public int Level { get; set; }

        public int Column { get; set; }

        public int Row { get; set; }

        public byte[] TileBytes { get; set; }

        public TileGeneratedSource GeneratedMethod { get; set; }

    }

public enum TileGeneratedSource

    {

        DynamicOutput,

        FromMemcached,

        FromFileCache

    }

然后构建天地图数据实体对象取名为DataSourceTianDiTuMap.cs

DataSourceBase

    {

        public DataSourceTianDiTuMap()

        {

            Initialize("N/A");

        }

protected override void Initialize(string path)

        {

            this.Type = DataSourceTypePredefined.TianDiTuMap.ToString();

            base.Initialize(path);

        }

protected override void ReadTilingScheme(out TilingScheme tilingScheme)

        {

            ReadTianDiTuTilingScheme(out tilingScheme);

            this.TilingScheme = TilingSchemePostProcess(tilingScheme);

        }

public override byte[] GetTileBytes(int level, int row, int col)

        {

            string baseUrl = string.Empty;

            string[] subDomains = null;

            string subdomain = string.Empty;

            string uri = string.Empty;

            string baseUrl0 = "http://tile{0}.tianditu.com/DataServer?T=A0512_EMap&X={1}&Y={2}&L={3}";

            string baseUrl1 = "

T=B0627_EMap1112&X={1}&Y={2}&L={3">http://tile{0}.tianditu.com/DataServer?

T=B0627_EMap1112&X={1}&Y={2}&L={3

}";

            string baseUrl2 = "

T=siwei0608&X={1}&Y={2}&L={3">http://tile{0}.tianditu.com/DataServer?

T=siwei0608&X={1}&Y={2}&L={3

}";

            subDomains = new string[] { "0", "1", "2", "3", "4", "5", "6", "7" };

            subdomain = subDomains[(level + col + row) % subDomains.Length];

            if (level + 2 < 11)

                uri = string.Format(baseUrl0, subdomain, col, row, level + 2);

            else if (level + 2 < 13)

                uri = string.Format(baseUrl1, subdomain, col, row, level + 2);

            else if (level + 2 < 19)

                uri = string.Format(baseUrl2, subdomain, col, row, level + 2);

            try

            {

                return HttpGetTileBytes(uri);

            }

            catch (Exception e)

            {

                //if server has response(not a downloading error) and tell IMapGeoServer do not have the specific tile, return null

                if (e is WebException && (e as WebException).Response != null && ((e as WebException).Response as HttpWebResponse).StatusCode == HttpStatusCode.NotFound)

                    return null;

string suffix = this.TilingScheme.CacheTileFormat.ToString().ToUpper().Contains("PNG") ? "png" : "jpg";

                Stream stream = this.GetType().Assembly.GetManifestResourceStream("IMapGeoServer.Assets.badrequest" + this.TilingScheme.TileCols + "." + suffix);

                byte[] bytes = new byte[stream.Length];

                stream.Read(bytes, 0, bytes.Length);

                return bytes;

            }

        }

以上代码请细致阅读代码片段会陆续更新



版权声明:本文博客原创文章,博客,未经同意,不得转载。