• 自己写一个读取Arcgis Server切片的后台服务


    概述:

    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%;
                 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=" 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)


  • 相关阅读:
    PHP与Web页面的交互
    PHP数组和数据结构(下)未完。。。。
    PHP数组和数据结构(上)
    网络
    单列模式(饿汉模式和懒汉模式)
    C++动态内存管理
    基础I/O
    进程程序替换(自主实现shell)
    进程控制
    调研task_struct结构体
  • 原文地址:https://www.cnblogs.com/lzugis/p/6539769.html
Copyright © 2020-2023  润新知