第二章 自己的框架WMTS服务,下载数据集成的文章1
在构建数据源下载文件的叙述性说明第一步
如此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 = "http://tile{0}.tianditu.com/DataServer?
T=B0627_EMap1112&X={1}&Y={2}&L={3}";
string baseUrl2 = "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;
}
}
以上代码请细致阅读代码片段会陆续更新
版权声明:本文博客原创文章,博客,未经同意,不得转载。