概述:
Arcgis Server的切片得要有Arcgis Server的支持才能使用,这样就显得比较麻烦,如果对于已经切好的切片怎么样通过自己写的程序来调用展示呢,本文讲解的内容就是这些。
Arcgis 切片简介:
Arcgis Server的切片分为两种:松散型和紧凑型。松散型,就是以单个的jpg或者png文件形式存储;紧凑型,是将多个切片文件制作成一组bundle和bundlx文件,其中bundlx存储的是切片的索引,bundle存储的是切片本身。在10的版本之前,Arcgis只支持松散型的切片方式,紧凑型是在Arcgis10的版本之后才出现的。
实现方式:
1、后台读取
后台写了一个比较简单的servlet来实现切片的读取,其中实现参考了下两篇文章中的内容,最终的实现代码如下:
package com.lzugis.servlet; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64; import com.lzugis.tile.helper.CommonConfig; /** * Servlet implementation class TileServlet */ @WebServlet(description = "get arcgis offline tile", urlPatterns = {"/agstile"}) public class AgsOffTilesServiceServlet extends HttpServlet { private static final long serialVersionUID = 1L; private String rootPath = ""; private static String base64Blank = "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NkZFQUUzNjgyRjJBMTFFNEFBQ0JGMEMyRjFFNUE0MUYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NkZFQUUzNjkyRjJBMTFFNEFBQ0JGMEMyRjFFNUE0MUYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo2RkVBRTM2NjJGMkExMUU0QUFDQkYwQzJGMUU1QTQxRiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo2RkVBRTM2NzJGMkExMUU0QUFDQkYwQzJGMUU1QTQxRiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pij7ZYAAAAJdSURBVHja7NQxAQAACMMwwL/nYQAHJBJ6tJMU8NNIAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAGAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAYABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAEABgAYAGAAgAGAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAQAGABgAYACAAYABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAAYAGABgAIABAJcVYADnygT9CIf4ngAAAABJRU5ErkJggg=="; /** * @see HttpServlet#HttpServlet() */ public AgsOffTilesServiceServlet() { super(); // TODO Auto-generated constructor stub try{ //切片存储路径 rootPath = CommonConfig.getVal("tile.agspath")+File.separator; } catch(Exception e){ e.printStackTrace(); } } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub this.doPost(request, response); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ @SuppressWarnings("resource") protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String x= request.getParameter("x"); String y= request.getParameter("y"); String z= request.getParameter("z"); String layer= request.getParameter("layer"); String type= request.getParameter("type");//file为紧凑型,image为松散型 String tilePath = rootPath+layer+"/Layers/_alllayers"; int level = Integer.parseInt(z); int row = Integer.parseInt(x); int col = Integer.parseInt(y); try { OutputStream os = response.getOutputStream(); byte[] output = null; int outlength = 0; if(type.equals("file")){//紧凑型 String l = "L" + getZero(2,z.length()) + level; int rGroup = 128 * (row / 128); String rTail = Integer.toHexString(rGroup); String r = "R" + getZero(4,rTail.length()) +rTail ; int cGroup = 128 * (col / 128); String cTail = Integer.toHexString(cGroup); String c = "C" + getZero(4,cTail.length()) + cTail; String bundleBase = String.format("%s/%s/%s%s", tilePath, l, r, c); String bundleFileName = bundleBase + ".bundle"; String bundlxFileName = bundleBase + ".bundlx"; File file = new File(bundleFileName); if(file.exists()){ int index = 128 * (col - cGroup) + (row - rGroup); FileInputStream isBundlx = new FileInputStream(bundlxFileName); isBundlx.skip(16 + 5 * index); byte[] buffer = new byte[5]; isBundlx.read(buffer); long offset = (long)(buffer[0]&0xff) + (long)(buffer[1]&0xff)*256 + (long)(buffer[2]&0xff)*65536 + (long)(buffer[3]&0xff)*16777216+ (long)(buffer[4]&0xff)*4294967296L; FileInputStream isBundle = new FileInputStream(bundleFileName); isBundle.skip(offset); byte[] lengthBytes = new byte[4]; isBundle.read(lengthBytes); int length = (int)(lengthBytes[0]&0xff)+ (int)(lengthBytes[1]&0xff)*256 + (int)(lengthBytes[2]&0xff)*65536+ (int)(lengthBytes[3]&0xff)*16777216; byte [] result = new byte[length]; isBundle.read(result); if(result.length>0){ output = result; outlength = length; } } else{ byte[] blankImg = Base64.decodeBase64(base64Blank); InputStream is = new ByteArrayInputStream(blankImg); int count=0; while ((count = is.read(blankImg)) != -1) { output = blankImg; outlength = count; } } } else{//松散型 String l = "L" + getZero(2,z.length()) + level; String strRow = Integer.toHexString(row); String r = "R" +getZero(8,strRow.length())+ strRow; String strCol = Integer.toHexString(col); String c = "C"+getZero(8,strCol.length()) + strCol; String imgfile = tilePath+l+"/"+r+"/"+c+".png"; InputStream is = null; File file = new File(imgfile); if(!file.exists()){ imgfile = tilePath+l+"/"+r+"/"+c+".jpg"; file = new File(imgfile); if(!file.exists()){ byte[] blankImg = Base64.decodeBase64(base64Blank); is=new ByteArrayInputStream(blankImg); } else{ is = new FileInputStream(imgfile); } } else{ is = new FileInputStream(imgfile); } int count = 0; byte[] buffer = new byte[1024 * 1024]; while ((count = is.read(buffer)) != -1) { output = buffer; outlength = count; } } os.write(output, 0, outlength); os.flush(); os.close(); } catch (IOException e) { e.printStackTrace(); } } private String getZero(int length,int strLength){ int zeroLength = length-strLength; String strZero = ""; for(int i=0;i<zeroLength;i++){ strZero+="0"; } return strZero; } }2、前台调用(Openlayers2)
为方便调用展示,扩展了一个Openlayers的图层类,代码如下:
OpenLayers.Layer.AgsTileLayer = OpenLayers.Class(OpenLayers.Layer.XYZ, { url: null, tileOrigin: null, tileSize: new OpenLayers.Size(256, 256), type: 'png', useScales: false, overrideDPI: false, useArcgisServer:false, cachetype:"file",//file为紧凑型,image为松散型 initialize: function(name, url, options) { OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); }, getURL: function (bounds) { var res = this.getResolution(); var originTileX = (this.tileOrigin.lon + (res * this.tileSize.w/2)); var originTileY = (this.tileOrigin.lat - (res * this.tileSize.h/2)); var center = bounds.getCenterLonLat(); var y = (Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w)))); var x = (Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h)))); var z = this.map.getZoom(); var url = this.url; var s = '' + x + y + z; if (OpenLayers.Util.isArray(url)) { url = this.selectUrl(s, url); } url = url + '?layer=${layer}&type=${type}&x=${x}&y=${y}&z=${z}'; url = OpenLayers.String.format(url, {'layer': this.name,'type': this.cachetype,'x': x, 'y': y, 'z': z}); return OpenLayers.Util.urlAppend( url, OpenLayers.Util.getParameterString(this.params) ); }, CLASS_NAME: 'OpenLayers.Layer.AgsTileLayer' });前台页面中的调用代码如下:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>openlayers map</title> <link rel="stylesheet" href="../../../plugin/OpenLayers-2.13.1/theme/default/style.css" type="text/css"> <style> html, body,#map{ padding:0; margin:0; height:100%; width: 100%; overflow: hidden; } </style> <script src="../../../plugin/OpenLayers-2.13.1/OpenLayers.js"></script> <script src="../../../plugin/jquery/jquery-1.8.3.js"></script> <script src="extend/AgsOffTileLayer.js"></script> <script> var map; var tiled; $(window).load(function() { var bounds = new OpenLayers.Bounds( 73.45100463562233, 18.16324718764174, 134.97679764650596, 53.531943152223576 ); var options = { controls: [], maxExtent: bounds, maxResolution: 0.2403351289487642, projection: "EPSG:4326", units: 'degrees' }; map = new OpenLayers.Map('map', options); OpenLayers.INCHES_PER_UNIT["千米"] = OpenLayers.INCHES_PER_UNIT["km"]; OpenLayers.INCHES_PER_UNIT["米"] = OpenLayers.INCHES_PER_UNIT["m"]; OpenLayers.INCHES_PER_UNIT["英里"] = OpenLayers.INCHES_PER_UNIT["mi"]; OpenLayers.INCHES_PER_UNIT["英寸"] = OpenLayers.INCHES_PER_UNIT["ft"]; //比例尺 map.addControl(new OpenLayers.Control.ScaleLine({topOutUnits:"米",topOutUnits:"千米"})); $(".olControlScaleLineBottom").hide(); map.addControl(new OpenLayers.Control.Zoom()); map.addControl(new OpenLayers.Control.Navigation()); tiled = new OpenLayers.Layer.AgsTileLayer( "chinashp", "http://localhost:8081/tile/agstile", { isBaseLayer: true, tileSize: new OpenLayers.Size(256, 256), resolutions: [ 0.15228550437313793, 0.07614275218656896, 0.03807137609328448, 0.01903568804664224, 0.00951784402332112, 0.00475892201166056, 0.00237946100583028 ], tileOrigin: new OpenLayers.LonLat(-400 , 400), maxExtent: bounds, projection: 'EPSG:4326' }); map.addLayers([tiled]); map.zoomToExtent(bounds); }); </script> </head> <body> <div id="map" style="width: 100%;"></div> </body> </html>`
注意:在前台页面调用的时候,有两个参数:分辨率组(resolutions)和切片原点(tileOrigin)。这两个参数是从服务的配置中获取的,你可以从服务配置文件Conf.xml中读取,Conf.xml文件位于“切片路径\Layers\”文件夹下,内容如下:
<?xml version="1.0" encoding="utf-8"?> <CacheInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:typens="http://www.esri.com/schemas/ArcGIS/10.1" xsi:type="typens:CacheInfo"> <TileCacheInfo xsi:type="typens:TileCacheInfo"> <SpatialReference xsi:type="typens:GeographicCoordinateSystem"> <WKT>GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433],AUTHORITY["EPSG",4326]]</WKT> <XOrigin>-400</XOrigin> <YOrigin>-400</YOrigin> <XYScale>11258999068426.24</XYScale> <ZOrigin>-100000</ZOrigin> <ZScale>10000</ZScale> <MOrigin>-100000</MOrigin> <MScale>10000</MScale> <XYTolerance>8.983152841195215e-009</XYTolerance> <ZTolerance>0.001</ZTolerance> <MTolerance>0.001</MTolerance> <HighPrecision>true</HighPrecision> <LeftLongitude>-180</LeftLongitude> <WKID>4326</WKID> <LatestWKID>4326</LatestWKID> </SpatialReference> <TileOrigin xsi:type="typens:PointN"> <X>-400</X> <Y>399.99999999999977</Y> </TileOrigin> <TileCols>256</TileCols> <TileRows>256</TileRows> <DPI>96</DPI> <PreciseDPI>96</PreciseDPI> <LODInfos xsi:type="typens:ArrayOfLODInfo"> <LODInfo xsi:type="typens:LODInfo"> <LevelID>0</LevelID> <Scale>64000000</Scale> <Resolution>0.15228550437313793</Resolution> </LODInfo> <LODInfo xsi:type="typens:LODInfo"> <LevelID>1</LevelID> <Scale>32000000</Scale> <Resolution>0.076142752186568963</Resolution> </LODInfo> <LODInfo xsi:type="typens:LODInfo"> <LevelID>2</LevelID> <Scale>16000000</Scale> <Resolution>0.038071376093284481</Resolution> </LODInfo> <LODInfo xsi:type="typens:LODInfo"> <LevelID>3</LevelID> <Scale>8000000</Scale> <Resolution>0.019035688046642241</Resolution> </LODInfo> <LODInfo xsi:type="typens:LODInfo"> <LevelID>4</LevelID> <Scale>4000000</Scale> <Resolution>0.0095178440233211203</Resolution> </LODInfo> <LODInfo xsi:type="typens:LODInfo"> <LevelID>5</LevelID> <Scale>2000000</Scale> <Resolution>0.0047589220116605602</Resolution> </LODInfo> <LODInfo xsi:type="typens:LODInfo"> <LevelID>6</LevelID> <Scale>1000000</Scale> <Resolution>0.0023794610058302801</Resolution> </LODInfo> </LODInfos> </TileCacheInfo> <TileImageInfo xsi:type="typens:TileImageInfo"> <CacheTileFormat>PNG</CacheTileFormat> <CompressionQuality>0</CompressionQuality> <Antialiasing>false</Antialiasing> </TileImageInfo> <CacheStorageInfo xsi:type="typens:CacheStorageInfo"> <StorageFormat>esriMapCacheStorageModeCompact</StorageFormat> <PacketSize>128</PacketSize> </CacheStorageInfo> </CacheInfo>
实现效果:
参考文献:
http://www.cnblogs.com/yuantf/p/3320876.html
http://blog.csdn.net/wankehui165/article/details/49489703
传播GIS知识 | 交流GIS经验 | 分享GIS价值 | 专注GIS发展
技术博客
http://blog.csdn.NET/gisshixisheng
在线教程
http://edu.csdn.net/course/detail/799
Github
https://github.com/lzugis/
联系方式
q q:1004740957
e-mail:niujp08@qq.com
公众号:lzugis15
Q Q 群:452117357(webgis)
337469080(Android)