arcgis server jsapi 地图打印:多图层打印(tiled+dynamic地图服务)

来源:互联网 发布:java第二学期考试 编辑:程序博客网 时间:2024/05/06 03:15

最近做个地图print,坐了好久,现在终于解决了。

概要:本地图中包含tiled和dynamic的图层,同时输出打印;

首先:要有esri的代理proxy。这个不多说:直接去arcgis server帮助中心http://help.arcgis.com/zh-CN/webapps/sharepoint/help/index.html#//01590000004w000000查看;

注意的是:proxyconfig文件的配置!!!!

其次:代码结构:

TestPage.htm作为主页面,点击主页中的打印会弹出Layout的打印页面。

printing.js用来获取地图服务的url信息;

MergerAndOutput.ashx是一个用于tiled地图服务的切片融合的类文件,

在printableLayer.js中使用MergerAndOutput.ashx,PrintableLayer.js文件是一个创建Dynamic图层的类,将切片的图层融合后,然后加载到这个新的Dynamic图层中;

代码:

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

PrintTableLayer.js

dojo.require("esri.utils");
dojo.declare("geoweb2book.PrintableLayer", esri.layers.DynamicMapServiceLayer, 
{
    constructor: function(url, options) 
    {
        this.layers = options.layers;
        this.initialExtent = this.fullExtent = options.extent;
        this.spatialReference = this.initialExtent.spatialReference;


        this.loaded = true;
        this.onLoad(this);
    },


    //call mergeAndOutput.*, pass request JSON.
    //process response JSON and return image url
    getImageUrl: function(extent, width, height, callback) 
    {
        try 
        {
            var layersJson = [], layerJson;


            dojo.forEach(this.layers, function(layer) 
            {
                if (layer.visible) 
                {
                    layerJson = { url: layer.url, format: (layersJson.length == 0 ? "jpg" : "png"), opacity: layer.opacity};


                    if (layer.layers) 
                    {
                        layerJson.layers = "show:" + layer.layers.join(",");
                    }
                    if (layer.layerDefs) 
                    {
                        var defs = [];
                        dojo.forEach(layer.layerDefs, function(def, index) 
                        {
                            if (def) 
                            {
                                defs.push(index + ":" + def);
                            }
                        });
                        layerJson.layerDefs = defs.join(";");
                    }


                    layersJson.push(dojo.toJson(layerJson));
                }
            });


            // JSON格式的请求
            var json = 
            {
                "width": width,
                "height": height,
                "extent": dojo.toJson(extent.toJson()),
                "layers": layersJson
            }; 
            // 将请求发送到MergeAndOutput.ashx
             var request=esri.request
             ({
                    url: this.url,
                    content: json,
                    handleAs: "json",
                    load: function(response, io) 
                    {
                        // 当得到服务器的响应后,调用callback,并传递图像URL
                        callback(response.href); 
                    },
                    error: this.onError
                });     
        }
        catch (err)
        {
            this.onError(err);
        }
    }
});

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

printing.js

dojo.require("esri.map");
dojo.require("dojo.parser");
dojo.require("dijit.layout.BorderContainer");
dojo.require("dijit.layout.ContentPane");
//serialize the map's state and call layout.* through iframe
function printMap(m) {
    var state = {
        map: getMapState(m)
    };
    var printUrl="Layout.aspx?param="+dojo.toJson(state);;
    window.open(printUrl);
}


function getMapState(m) {
    var layerStates = [], graphicsState = [], graphics = m.graphics.graphics;


    //serialize each layer's state
    dojo.forEach(m.layerIds, function(layerId) {
        layerStates.push(getLayerState(m.getLayer(layerId)));
    });


    //serialize graphics from last to first to retain graphics drawing order
    for (var i = graphics.length - 1; i >= 0; i--) {
        graphicsState.push(graphics[i].toJson());
    };


    return {
        extent: m.extent.toJson(),
        layers: layerStates,
        graphics: graphicsState
    };
}


function getLayerState(layer) {
    return {
        id: layer.id,
        type: layer.declaredClass,
        url: layer.url,
        visible: layer.visible,
        opacity: layer.opacity,
        layerDefs: layer.layerDefinitions,
        layers: layer.visibleLayers
    };
}
----------------------------------------------

MergeAndOutput.ashx

<%@ WebHandler Language="C#" Class="MergeAndOutput" %>


using System;
using System.Web;
using System.Text;
using System.Collections.Generic;
using System.Web.Script.Serialization;
using System.Drawing;
using System.Drawing.Imaging;
using System.Web.Extensions;


public class MergeAndOutput : IHttpHandler
{


    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "application/json";
        context.Response.ContentEncoding = Encoding.UTF8;


        // 得到输入JSON字符串
        //byte[] bytes = new byte[context.Request.InputStream.Length];
        //context.Request.InputStream.Read(bytes, 0, (int)context.Request.InputStream.Length);
        //string inputJson = ASCIIEncoding.ASCII.GetString(bytes);


        string inputJson = HttpUtility.UrlDecode(context.Request.Url.Query);
        inputJson = inputJson.Substring(1, inputJson.Length - 1);
        
        //inputJson = inputJson.Replace("&", ",");
        //inputJson = inputJson.Replace("=", ":");
        //inputJson = "{" + inputJson + "}";
        // 将输入JSON转换为泛型词典对象
        //Dictionary<string, object> dict = new Dictionary<string, object>();
        //dict = ReadJsonInput(inputJson);


        // 调用Blend方法融合图像
        string filename = Blend(inputJson);


        // 调用ConstructOutputJson构造输出JSON字符串
        string result = ConstructOutputJson(inputJson, filename);
        context.Response.Output.Write(result);
    }


    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
    /// <summary>
    /// Read JSON input to get map information.
    /// </summary>
    /// <param name="inputJson"></param>
    /// <returns></returns>
    //private Dictionary<string, object> ReadJsonInput(string inputJson)
    //{
    //    Dictionary<string, object> dict = new Dictionary<string, object>();
    //    JavaScriptSerializer jss = new JavaScriptSerializer();


    //    object obj = jss.DeserializeObject(inputJson);
    //    dict = (Dictionary<string, object>)obj;


    //    return dict;
    //}


    /// <summary>
    /// Contruct output JSON.
    /// </summary>
    /// <param name="dict"></param>
    /// <param name="filename"></param>
    /// <returns></returns>
    private string ConstructOutputJson(string inputJson, string filename)
    {
        //Get values persisted from the input JSON.
        char[] sep = { '&', '=' };
        string[] inputArr = inputJson.Split(sep);
        string width = inputArr[1];
        string height = inputArr[3];
        string extent = inputArr[5];


        StringBuilder sb = new StringBuilder();
        sb.Append("{");
        sb.Append("'width':" + width + ",");
        sb.Append("'height':" + height + ",");
        sb.Append("'extent':" + extent + ",");
        sb.Append("'href':'" + System.Configuration.ConfigurationManager.AppSettings["OutputUrl"] + filename + ".jpg'}");


        return sb.ToString();
    }


    /// <summary>
    /// Construct image URl to be passed to the Blend method.
    /// </summary>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <param name="extent"></param>
    /// <param name="objLayers"></param>
    /// <param name="transparency"></param>
    /// <returns></returns>
    private string ConstructImageURL(string width, string height, string extent, object objLayers)
    {
        JavaScriptSerializer jss = new JavaScriptSerializer();
        StringBuilder sb = new StringBuilder();
        string opacity = string.Empty;


        //Store extent values in dictionary object.
        Dictionary<string, object> dictExtent = new Dictionary<string, object>();
        object objExtent = jss.DeserializeObject(extent);
        dictExtent = (Dictionary<string, object>)objExtent;


        //Store layer information in dictionary object;
        Dictionary<string, object> dictLayer = new Dictionary<string, object>();
        dictLayer = (Dictionary<string, object>)objLayers;


        //Construct the image URL.
        sb.Append(dictLayer["url"].ToString());
        sb.Append("/export?f=image&bbox=");
        //sb.Append(dictExtent["xmin"].ToString() + "," + dictExtent["ymin"].ToString() + "," + dictExtent["xmax"].ToString() + "," + dictExtent["ymax"].ToString());
        sb.Append(ConvertToString(dictExtent["xmin"]) + "," + ConvertToString(dictExtent["ymin"]) + "," + ConvertToString(dictExtent["xmax"]) + "," + ConvertToString(dictExtent["ymax"]));
        sb.Append("&size=" + width + "," + height);
        sb.Append("&transparent=true");
        sb.Append("&format=" + dictLayer["format"].ToString());
        sb.Append("&layers=" + dictLayer["layers"].ToString());
        sb.Append("&layerDefs=" + ((!dictLayer.ContainsKey("layerDefs")) ? "" : HttpUtility.UrlEncode(dictLayer["layerDefs"].ToString())));


        return sb.ToString();
    }


    private string ConvertToString(object strObj)
    {
        string strStr = strObj.ToString();
        if (strStr.Contains(","))
        {
            return strStr.Replace(',', '.');
        }


        return strStr;
    }


    /// <summary>
    /// Create list of layer transparencies.
    /// </summary>
    /// <param name="objLayers"></param>
    /// <returns></returns>
    private List<double> GetTransparency(List<object> objLayers)
    {
        JavaScriptSerializer jss = new JavaScriptSerializer();
        List<double> lt = new List<double>();
        string opacity = string.Empty;
        //object objLayer = null;


        //Create list of layer transparencies.
        Dictionary<string, object> dictLayer = new Dictionary<string, object>();
        for (int i = 0; i < objLayers.Count; i++)
        {
            //objLayer = jss.DeserializeObject(objLayers[i].ToString());
            dictLayer = (Dictionary<string, object>)objLayers[i];
            opacity = dictLayer["opacity"].ToString();


            lt.Add(1 - (Convert.ToDouble(opacity)));
        }


        return lt;
    }


    #region Blend Methods


    /// <summary>
    /// Method to blend images.Dictionary<string, object> dict
    /// </summary>
    /// <param name="dict"></param>
    /// <returns></returns>
    private string Blend(string inputJson)
    {
        List<double> listTransparency = new List<double>();
        int count = 0;
        int layerIndex = -1;
        Bitmap tempimg = null;
        Bitmap baseimg = null;
        Bitmap topimg = null;
        string filename = string.Empty;
        string output = string.Empty;


        // 从JSON字符串中得到各种参数的值
        char[] sep={'&','='};
        string[] inputArr = inputJson.Split(sep);
         string width =inputArr[1];
         string height=inputArr[3];
         string extent = inputArr[5];
        List<object> objLayers =new List<object>();
        for(int i=7;i<inputArr.Length;i=i+2)
        {
            JavaScriptSerializer jss = new JavaScriptSerializer();
            object obj = jss.DeserializeObject(inputArr[i]);
            objLayers.Add(obj);
        }


        count = objLayers.Count;
        // 得到地图中各图层对应的地图图像URL
        List<string> urlList = new List<string>();
        for (int i = 0; i < count; i++)
        {
            urlList.Add(ConstructImageURL(width, height, extent, objLayers[i]));
        }


        // 得到图层的透明度
        listTransparency = GetTransparency(objLayers);


        // 创建一白色背景的图像
        Bitmap whiteImage = new Bitmap(Convert.ToInt32(width), Convert.ToInt32(height));
        Graphics objGraphics = Graphics.FromImage(whiteImage);
        objGraphics.FillRectangle(new SolidBrush(Color.White), 0, 0, Convert.ToInt32(width), Convert.ToInt32(height));


        baseimg = whiteImage;
        count = count + 1;
        while (count > 1)
        {
            topimg = Converter.ToImage(urlList[layerIndex + 1].ToString());


            // 设置输出文件名
            if (count == 2)
            {
                output = System.Configuration.ConfigurationManager.AppSettings["OutputPath"];
                filename = "export_" + Guid.NewGuid().ToString("N");
                output = output + filename + ".jpg";
            }


            // 融合图像
            tempimg = Blend(baseimg, topimg, listTransparency[layerIndex + 1], output, Convert.ToInt32(width), Convert.ToInt32(height));


            baseimg = tempimg;
            count--;
            layerIndex++;
        }


        // 释放资源
        baseimg.Dispose();
        baseimg = null;
        topimg.Dispose();
        topimg = null;
        tempimg.Dispose();
        tempimg = null;


        return filename;
    }


    /// <summary>
    /// Blend images using transparency.
    /// </summary>
    /// <param name="baseimg"></param>
    /// <param name="topimg"></param>
    /// <param name="transparency"></param>
    /// <param name="output"></param>
    /// <param name="width"></param>
    /// <param name="height"></param>
    private Bitmap Blend(Bitmap baseimg, Bitmap topimg, double transparency, string output, int width, int height)
    {
        //output image
        Bitmap newimg = new Bitmap(width, height);


        //get graphics of output image, and set size of image
        System.Drawing.Graphics imgGraphics = System.Drawing.Graphics.FromImage(newimg);


        //draw base image
        imgGraphics.DrawImage(baseimg, 0, 0,
            baseimg.Width, baseimg.Height);


        BlendNoSave(imgGraphics, topimg, transparency);


        //release resources
        // this will enable writing new image to the location of one of the component images
        baseimg.Dispose();
        baseimg = null;
        topimg.Dispose();
        topimg = null;


        //save out image and release resources
        if (!string.IsNullOrEmpty(output))
        {
            newimg.Save(output, System.Drawing.Imaging.ImageFormat.Jpeg);
        }


        return newimg;
    }


    /// <summary>
    /// Blends an image into a graphics object.
    /// </summary>
    /// <param name="baseGraphics">Graphics reference.</param>
    /// <param name="topimg">Image to blend on top.</param>
    /// <param name="transparency">Transparency from 0 (transparent) to 1 (opaque).</param>
    public static void BlendNoSave(System.Drawing.Graphics baseGraphics, Bitmap topimg, double transparency)
    {
        if (transparency > 1 || transparency < 0)
        {
            string error = "Invalid transparency value.";
            throw new ArgumentOutOfRangeException(error);
        }
        else if (transparency > 0)
        {
            ImageAttributes imgAttributes = new ImageAttributes();


            //matrix to use for alpha-blending (to achieve transparency)
            float[][] ptsArray ={ 
new float[] {1, 0, 0, 0, 0},
new float[] {0, 1, 0, 0, 0},
new float[] {0, 0, 1, 0, 0},
new float[] {0, 0, 0, 1-(float)transparency, 0}, 
new float[] {0, 0, 0, 0, 1}};


            ColorMatrix clrMatrix = new ColorMatrix(ptsArray);
            //Draw top image using matrix to apply transparency
            imgAttributes.SetColorMatrix(clrMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
            baseGraphics.DrawImage(topimg, new Rectangle(0, 0, topimg.Width, topimg.Height), 0, 0, topimg.Width, topimg.Height, GraphicsUnit.Pixel, imgAttributes);
        }
        else
        {
            baseGraphics.DrawImage(topimg, new Rectangle(0, 0, topimg.Width, topimg.Height), 0, 0, topimg.Width, topimg.Height, GraphicsUnit.Pixel);
        }
        return;
    }


    #endregion
}
 ----------------------------------

TestPage中的代码:

<head>
    <title>地图打印</title>
    <style type="text/css">
        @import "http://serverapi.arcgisonline.com/jsapi/arcgis/2.6/js/dojo/dijit/themes/tundra/tundra.css";
@import "css/RestLegends.css";
html, body, #main{
            width: 100%;
            height: 100%;
        }
    </style>


    <script type="text/javascript">
        djConfig = { parseOnLoad: true ,url:"服务器IP"};
     </script>
    <script type="text/javascript" src="http://服务器IP/jsapi/library/2.6/jsapi"></script>
    
<script type="text/javascript" src="js/printing.js">
</script>
 
<script type="text/javascript">
 
   //variable to store hidden frame
   var printingHiddenFrame;
   var map1;

   function init() {
      map1 = new esri.Map("map", {
           extent: new esri.geometry.Extent({"xmin":496369
            ,"ymin":299413
            ,"xmax":504735
            ,"ymax":312319
            ,"spatialReference":{"wkid":32601} } )
        });       
     var  tiled = new esri.layers.ArcGISTiledMapServiceLayer("http://服务器IP/ArcGIS/rest/services/baseMap/MapServer");
       map1.addLayer(tiled);
       var dyna=new esri.layers.ArcGISDynamicMapServiceLayer("http://服务器IP/ArcGIS/rest/services/YXX/MapServer");
       map1.addLayer(dyna);
       var dyna1=new esri.layers.ArcGISDynamicMapServiceLayer("http://服务器IP/ArcGIS/rest/services/PB/MapServer");
       map1.addLayer(dyna1);
   }


   dojo.addOnLoad(init); 
   function print()
   {
       var _divWidth = document.getElementById("map").style.width;
       var _divHeight =document.getElementById("map").style.height;
       var divWidth = _divWidth.substring(0,_divWidth.length-2);
       var divHeight = _divHeight.substring(0,_divHeight.length-2);
       //alert(divWidth);
       var realWidth = parseInt(divWidth);
       var realHeight = parseInt(divHeight); 
      // document.getElementById("map").style.position.s
      
   }
    </script>
  </head>
  <body class="tundra">
    <div   id="main">
        <div  style="height: 60px;">
            <h3>功能:地图打印</h3>
        </div>
        <div id="map"  ></div>
        <div
            style="height: 50px;" splitter="true">
            <button onclick="printMap(map1);"><img src="images/print.gif" />打印</button>
        </div>
    </div>
</body>
</html>

=----------------------------------------------------------

Layout文件代码:

 
    <title>地图打印</title>
    <link rel="stylesheet" type="text/css" href="http://serverapi.arcgisonline.com/jsapi/arcgis/2.6/js/dojo/dijit/themes/tundra/tundra.css" /> 
    <script type="text/javascript">
        djConfig = { parseOnLoad: true ,url:"服务器IP"};
    </script> 

    <script type="text/javascript" src="http://服务器IP/jsapi/library/2.6/jsapi"></script> 
    <script type="text/javascript" src="js/PrintableLayer.js"></script> 
    <script type="text/javascript" charset="utf-8">
        var mapParam=getParam();
        mapParam=dojo.fromJson(mapParam);
        var resizeTimer;     
        function getParam()
        {
            var obj_ArrParam = new Array();
            var str_CurrentUrl = document.URL;
            var obj_RequestUrl = str_CurrentUrl.split('?');
            var obl_Param = obj_RequestUrl[1].split('=');
return obl_Param[1];
        }
      dojo.require("esri.map");
      
//      var jsonStr = <%=Request.Params.Get("appState")%>;
      var appState = mapParam;
      var map;


      function init() 
      {


            map = new esri.Map("mapDiv", {
              extent: new esri.geometry.Extent(appState.map.extent),
              showInfoWindowOnClick: false,
              slider: false
            }); 
            //on load, add graphics to map
            dojo.connect(map, "onLoad", addGraphicsToMap); 
           
            esri.config.defaults.io.proxyUrl = "proxy.ashx";
            esriConfig.defaults.io.alwaysUseProxy = false;
            var layer = new geoweb2book.PrintableLayer("http://服务器IP/Sample3-9/MergeAndOutput.ashx", 
            {
              layers:appState.map.layers,
              extent: new esri.geometry.Extent(appState.map.extent)
            }); 
            dojo.connect(layer, "onError", errorHandler);
            map.addLayer(layer); 
            map.disableDoubleClickZoom();
            map.disableKeyboardNavigation();
            map.disableMapNavigation();
            map.disablePan();
            map.disableScrollWheelZoom();
            dojo.connect(window, 'onresize', function() { 
            //当浏览器变化的时候div也会变化,这样把地图控件重绘
            clearTimeout(resizeTimer);
            resizeTimer = setTimeout(function() {
                map.resize();
                map.reposition();
            }, 500);
        }); 
      } 
      function errorHandler(err) 
      {
        alert(err);
      } 
      //deserialize graphics and add to map
      function addGraphicsToMap() 
      {
        dojo.forEach(appState.map.graphics, function(graphic) 
        {
          map.graphics.add(new esri.Graphic(graphic));
        });
      }
      function printPage()
      {
          try
            {

             //ie中打印默认打印页眉页脚,这里做一下处理,只打印地图部分。
              var hkey_root,hkey_path,hkey_key;    
              hkey_root="HKEY_CURRENT_USER";    
              hkey_path="\\Software\\Microsoft\\Internet Explorer\\PageSetup\\";
      
              RegWsh = new ActiveXObject("WScript.Shell");
              hkey_key="header";            
              RegWsh.RegWrite(hkey_root+hkey_path+hkey_key,"");    
              hkey_key="footer";    
              RegWsh.RegWrite(hkey_root+hkey_path+hkey_key,""); 
              }    
             catch(e)
                {alert ("页面设置失败,请手动设置!");} 
             var bdhtml=window.document.body.innerHTML;   
             var sprnstr = "<!--startprint-->";   
             var eprnstr = "<!--endprint-->";    
             var frontprnhtml=bdhtml.substring(bdhtml.indexOf(sprnstr)+17);   
             var laterprnhtml=frontprnhtml.substring(0,frontprnhtml.indexOf(eprnstr));   
             window.document.body.innerHTML=laterprnhtml;    
              window.print(); 
      }
      
      dojo.addOnLoad(init);
    </script>  
    <style type="text/css">
        #btn
        {
            width: 65px;
        }
    </style>  
</head>
<body  style="text-align:center; margin-left:auto; margin-right:auto;position:relative">
<!--startprint-->
    <div id="div_print" >
        <h2>地图打印</h2>
        <div id="mapDiv" style="width: 1000px; height: 1000px; border: 1px solid #000; text-align:center; margin-left:auto; margin-right:auto;position:relative;">
        </div>
    </div>
    <!--endprint-->
    <input name="button" type="button" id="btn" onclick = "printPage();" value="打印地图" />
</body>
</html>

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

另外在website的webconfig中ps两句话:

<add key="OutputPath" value="C:\Inetpub\wwwroot\Sample3-9\output\"/>
<add key="OutputUrl" value="http://服务器IP/Sample3-9/output/"/>

整个Demo的结构就是这样的。希望对大家有用;



参考文献:ArcGIS Server JavaScript API开发GeoWeb 2.0应用/刘光           

arcgis server javascript api (arcgis resource center)

js+c#相关教程


ArcGIS Server JavaScript API开发GeoWeb 2.0应用/刘光

ArcGIS Server JavaScript API开发GeoWeb 2.0应用/刘光

ArcGIS Server JavaScript API开发GeoWeb 2.0应用/刘光

ArcGIS Server JavaScript API开发GeoWeb 2.0应用/刘光

原创粉丝点击