1. 程式人生 > 其它 >ArcGIS 切片的三種儲存形式

ArcGIS 切片的三種儲存形式

ArcGIS 切片的三種儲存形式

來源

鬆散型

也就是我們常見的檔案式的切片管理方式,將 Arcgis Server 切出來的切片圖片按照行列號的規範,儲存在相應的資料夾中。

早期緊湊型

將切好的切片轉化成.bundle.bundlex的兩種檔案格式儲存。

  • 其中bundle檔案用以儲存切片資料,bundlx 是 bundle 檔案中切片資料的索引檔案

  • 一個 bundle 檔案中最多可以儲存128×128(16384)個切片;

  • 在 bundlx 檔案中用固定的位元組數量(5位元組)標識一個切片在 bundle 檔案中的狀態(偏移量);每個 bundlx 檔案都是一樣的大小:81952位元組,起始16位元組和檔案結束16位元組與索引無關,剩餘的81920位元組資料以5個位元組

    的頻率重複,構成了一個對bundle檔案的索引【注:16384 * 5 = 81920】;這5個位元組標示了切片資料的偏移量。

  • 在 bundle 檔案中,每2個切片資料之間相隔了4個位元組;這4個位元組正好是以低位到高位的方式標示了後續這個切片資料的長度。

  • 切片資料的長度(4位元組)和資料偏移(5位元組)是無符號的整數。

  • bundlx 檔案的檔名,包含了切片的行列資訊,而它所在的資料夾名稱(目錄名稱),則包含切片的級別資訊。

因此,我們如果知道了一個切片的級別、行號、列號,就可以找到相應的 bundlx 檔案,並通過 bundlx 首先找到bundle中切片內容的偏移,然後從bundle檔案中取出4個位元組的長度資料,再隨後根據這個長度讀取真實的切片資料。

假設已知切片的級別、行號和列號,求對應的 bundlx 檔案:

目錄名:L開頭,並加上級別,級別不足2位的,高位補0,例如:L01,L19等。

檔名:R開頭,加上4位16進位制數(行號組最小行號),
	   再加上字母C,最後加上4位16進位制數(列號組最小列號),
	   例如:R0080C0080.bundlx
	   
行號組最小行號、列號組最小列號 的計算:
(1)因為一個 bundle 檔案中最多可以儲存 128×128 個切片,所以,
	行號/128,向下取整,得到切片所在的“行號組的序數 r”(即第 r 組);
	列號/128,向下取整,得到切片所在的“列號組的序數 c”(即第 c 組);
	r * 128,得到所在行組的最小行號 rrrr;
	c * 128,得到所在列組的最小列號 cccc;
(2)將 rrrr 和 cccc 轉成 16 進位制數,並轉化為長度為4的字串(不足4位時,高位補0);

找到 bundlx 檔案:
(3)拼接出檔名:R{rrrr}C{cccc}.bundlx
(4)拼接出路徑:_alllayers\level\R{rrrr}C{cccc}.bundlx

--------------------------------

假設已知切片的級別、行號和列號,求對應的切片資料:

(1)求切片的序數:
行號 - 行號組最小行號(即:行號-rrrr),得到切片在當前行號組的序數 m;
列號 - 列號組最小列號(即:列號-cccc),得到切片在當前列號組的序數 n;
128 * m + n,得到切片在 bundle 檔案中的序數 index(即:切片是 bundle 中總共的16384 張切片中的第 index 張切片);

(2)求切片的位置(偏移量):
因為在 bundlx 檔案中,每張切片的位置資訊用5位元組表示,且頭部有16個起始位元組,所以,前(16 + 5 * index)個位元組需要忽略;之後的5位元組是切片的位置資訊,如下解析切片的位置資訊:
偏移量 offset = 第0位元組 + 第1位元組 * 256 + 第2位元組 * 256 * 256 + 第3位元組 * 256 * 256 * 256 + 第4位元組 * 256 * 256 * 256 * 256。

(3)求切片的長度:
因為切片之前4個位元組,是切片的長度資訊,所以 用 offset 之後的四個位元組來計算切片的長度,如下:
切片長度 length = 第0位元組 + 第1位元組 * 256 + 第2位元組 * 65536 + 第3位元組 * 16777216

(4)讀取切片
offset 之後 length 個位元組,就是切片的圖片流。

示例

using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

private string _layerPath;

public void Draw()
{
	_layerPath = @"D:\arcgisserver\directories\arcgiscache\ZJXZQ\圖層\_alllayers\L00"; 
	if (!Directory.Exists(_layerPath))
	{
		throw new DrawWatermarkException("路徑不存在或無效:" + _layerPath);
	}

	//http://localhost:6080/arcgis/rest/services/ZJXZQ/MapServer/tile/0/201/168
	//http://localhost:6080/arcgis/rest/services/ZJXZQ/MapServer/tile/0/203/170

	int level = 0;
	int startTileRow = 168;
	int endTileRow = 170;
	int startTileCol = 201;
	int endTileCol = 203;

	string levelStr = level.ToString();
	if(levelStr.Length ==1)
	{
		levelStr = "0" + levelStr;
	}
	levelStr = "L" + levelStr;

	string targetPath = @"D:\Targets\" + levelStr;
	if (!Directory.Exists(targetPath))
	{
		Directory.CreateDirectory(targetPath);
	}

	int rowLimit = endTileRow + 1, colLimit = endTileCol + 1;
	for (int row = startTileRow; row < rowLimit; row++)
	{
		for (int col = startTileCol; col < colLimit; col++)
		{
			int r = (int)(row / 128);
			int c = (int)(col / 128);
			int rrrr = r * 128;
			int cccc = c * 128;

			string rHex = rrrr.ToString("x");
			if (rHex.Length < 4)
			{
				int temp = 4 - rHex.Length;
				for (int i = 0; i < temp; i++)
				{
					rHex = "0" + rHex;
				}
			}
			string cHex = cccc.ToString("x");
			if (cHex.Length < 4)
			{
				int temp = 4 - cHex.Length;
				for (int i = 0; i < temp; i++)
				{
					cHex = "0" + cHex;
				}
			}

			FileStream bundlxFs = GetFile(rHex, cHex, true);
			if (bundlxFs != null)
			{
				int m = row - rrrr;
				int n = col - cccc;
				int index = m * 128 + n;
				long offset1 = 16 + 5 * index;
				if (offset1 > bundlxFs.Length)
				{
					continue;
				}

				byte[] bytesIndex = new byte[5];
				bundlxFs.Seek(offset1, SeekOrigin.Begin);
				bundlxFs.Read(bytesIndex, 0, 5);

				long offset = 0;
				for (int i = 0; i < bytesIndex.Length; i++)
				{
					long temp = bytesIndex[i] & 0xff;
					if (temp != 0)
					{
						for (int j = 0; j < i; j++)
						{
							temp *= 256;
						}
						offset += temp;
					}
				}

				FileStream bundleFs = GetFile(rHex, cHex, false);

				byte[] bytesLength = new byte[4];
				bundleFs.Seek(offset, SeekOrigin.Begin);
				bundleFs.Read(bytesLength, 0, 4);

				int length = 0;
				for (int i = 0; i < bytesLength.Length; i++)
				{
					int temp = bytesLength[i] & 0xff;
					if (temp != 0)
					{
						for (int j = 0; j < i; j++)
						{
							temp *= 256;
						}
						length += temp;
					}
				}

				if (length > 0)
				{
					byte[] bytesImage = new byte[length];
					bundleFs.Read(bytesImage, 0, length);
					MemoryStream ms = new MemoryStream(bytesImage);
					Image image = Image.FromStream(ms);
					image.Save(targetPath + "\\" + row + col + ".png", ImageFormat.Png);
					image.Dispose();
					ms.Close();
				}
			}

		}
	}
}

private Dictionary<string, FileStream> _bundlxFiles = new Dictionary<string, FileStream>();
private Dictionary<string, FileStream> _bundleFiles = new Dictionary<string, FileStream>();

private FileStream GetFile(string rHex, string cHex, bool isBundlx)
{
	string extension = isBundlx ? "bundlx" : "bundle";
	Dictionary<string, FileStream> temp = isBundlx ? _bundlxFiles : _bundleFiles;

	string fileName = string.Format("R{0}C{1}.{2}", rHex, cHex, extension);
	if (temp.ContainsKey(fileName))
	{
		return temp[fileName];
	}

	string file = string.Format("{0}\\{1}", _layerPath, fileName);
	if (File.Exists(file))
	{
		FileStream fileStream = new FileStream(file, FileMode.Open);
		temp.Add(fileName, fileStream);
		return fileStream;
	}

	return null;
}

10.3以後的緊湊型

將切好的切片轉化成.bundle的格式來儲存。

  • 切片的索引、切片的偏移和切片的圖片流都必然包含在這一.bundle檔案中;
  • 頭資訊:.bundle檔案起始 64 位元組 是 bundle 的檔案頭資訊;
  • 位置資訊:頭資訊之後,記錄了 16384 張切片的位置;每個位置資訊,用8個位元組表示;僅前4位元組有用,從低位到高位;後4位元組可忽略;
  • 圖片流資訊:位置資訊之後,是圖片流資訊;每個切片圖,先以4位元組記錄切片的長度,而後緊跟圖片的流資訊。
  • .bundle檔案的檔名以及所在資料夾的名稱,包含切片的級別、行列資訊。
假設已知切片的級別、行號和列號,求對應的 bundle 檔案:
求取方式同 “假設已知切片的級別、行號和列號,求對應的 bundlx 檔案”。

--------------------------------

假設已知切片的級別、行號和列號,求對應的切片資料:

(1)求切片的序數:
行號 - 行號組最小行號(即:行號-rrrr),得到切片在當前行號組的序數 m;
列號 - 列號組最小列號(即:列號-cccc),得到切片在當前列號組的序數 n;
128 * m + n,得到切片在 bundle 檔案中的序數 index(即:切片是 bundle 中總共的16384 張切片中的第 index 張切片);

(2)求切片的位置(偏移量):
因為每張切片的位置用8位元組表示,且頭資訊佔64位元組,所以,前(64 + 8 * index)個位元組需要忽略;之後的8位元組是切片的位置資訊(僅前4位元組有用),如下解析切片的位置資訊:
偏移量 offset = 第1位元組 + 第2位元組 * 256 + 第3位元組 * 65536 + 第4位元組 * 16777216。

(3)求切片的長度:
因為切片之前4個位元組,是切片的長度資訊,所以 用(offset-4)之後的四個位元組來計算切片的長度,如下:
切片長度 length = 第0位元組 + 第1位元組 * 256 + 第2位元組 * 65536 + 第3位元組 * 16777216

(4)讀取切片
offset 之後 length 個位元組,就是切片的圖片流。