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

Acquista i software ArcGIS tramite Studio A&T srl, rivenditore autorizzato dei prodotti Esri.

I migliori software GIS, il miglior supporto tecnico!

I migliori software GIS, il miglior supporto tecnico!
Azienda operante nel settore GIS dal 2001, specializzata nell’utilizzo della tecnologia ArcGIS e aderente ai programmi Esri Italia Business Network ed Esri Partner Network

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



Visualizzazione post con etichetta dojo. Mostra tutti i post
Visualizzazione post con etichetta dojo. Mostra tutti i post

domenica 9 gennaio 2011

jsonp nothing to do with pjson

A chi non è capitato sviluppando con javascript di doversi imbattere nel famoso limite imposto dalla same domain policy.
In sintesi questa policy permette di eseguire scripts su pagine che hanno origine dallo stesso sito ed accedere ai metodi/proprietà di queste senza particolari restrizioni, ma impedisce di accedere alla maggior parte dei metodi/proprietà di pagine che hanno origine da siti diversi.
Questa policy è presente sin dai tempi di Netscape 2.0 ed è stata introdotta per motivi di security.
In questi link potete farvi un'idea dei possibili attacchi che sono legati a questa restrizione.
Cross Site Scripting (XSS)
SQL Injection
Cross-Site Request Forgery (CSRF)

Ritornando a noi, il problema è che oramai le nostre mashup application richiedono sempre più dati da terze parti (Flickr, Yahoo, Google ecc.) e la same domain policy ostacolerebbe non poco lo sviluppo della nostra applicazione.

Per bypassare la same domain policy è necessario crearsi una pagina di proxy sul server e delegarlo nella richiesta ad un altro sito così noi rispettiamo la same domain policy perchè chiamiamo il nostro server oppure possiamo utilizzare l'IFrame.

Ma c'è un'alternativa ovverosia utilizzare JSONP.
L'idea è quella di sfruttare il tag script html e utilizzare l'attributo src, che non è soggetto alla restrizione della same domain policy.

Vediamo un esempio:
Richiamiamo il metodo getData e passiamo come parametro una funzione definita nella nostra pagina (in questo caso il nome della funzione è theResponse).

 <script type="text/javascript">
  
   src="http://www.example.com/getData?id=2&callback=theResponse">
  
 </script>  

Se la risposta di questa chiamata riempie il payload JSON con il nome della funzione theResponse passata, il metodo theResponse verrà eseguito:

payload JSON: {Name: "Marco", Surname: "Rossi"}
risposta del servizio: theResponse({Name: "Marco", Surname: "Rossi"})

Funzione definita sul client:
function theResponse(data){
   alert("Name: " + data.Name + ", Surname: " + data.Surname);
}

Comunque, ci sono alcuni limiti tra i quali quello di supportare solo richieste GET e quello di non avere un error handler: se fallisce qualcosa esso lo farà silenziosamente. Normalmente si tende a rispondere sempre con successo e ad inserire nel payload l'errore così da poter essere opportunamento trattato.
Inoltre abbiamo il problema, come abbiamo anticipato all'inizio, della sicurezza e quindi andrebbe utilizzato con servizi trusted o sotto il proprio controllo.

Lato client Dojo ci facilita un po' le cose mettendoci a disposizione dojo.io.script.get

 dojo.require("dojo.io.script");
  
 dojo.io.script.get({
  
 callbackParamName : "callback",
  
 url: "http://example.com/getData?id=2",
  
 load : function(response, ioArgs) {
  
 console.log(response);
  
 return response;
  
 },
  
 error : function(response, ioArgs) {
  
 console.log(response);
  
 return response;
  
 }
  
 });  

Dietro le quinte Dojo gestisce la callback creando una funzione temporanea e mette la risposta nella funzione load. In sintesi, Dojo rimuove il padding al tuo posto e indirizza il risultato nella funzione di load. Attenzione quindi che a callbackParamName occorre indicare il nome del parametro della callback che in questo esempio si chiama 'callback' e non il nome della funzione.

Dal server potremmo anche farci restituire in alternativa anche un javascript puro. Ad esempio una stringa del tipo:

return "var p = {Name: "Marco", Surname: "Rossi"}"

In questo caso lato client utilizziamo il checkString:

 dojo.require("dojo.io.script");
  
 dojo.io.script.get({
  
 checkString : "p",
  
 timeout : 2000,
  
 url : "http://www.example.com/GetData?Id=2",
  
 load : function(response, ioArgs) {
  
 console.log(p);
  
 console.log(response)
  
 },
  
 error : function(response, ioArgs) {
  
 console.log("error", response, ioArgs);
  
 return response;
  
 }
  
 });  

Lato server per creare un servizio WCF REST JSONP in NET 4 potete impostare crossDomainScriptAccessEnabled a true. Qui i dettagli.


Con le esri API js puoi utilizzare esri.request, un wrapper intorno a dojo.io.script.get e dojo.xhrPost che ti consente anche di indicare se usare il proxy per richieste 'corpose'.

Vediamo un esempio:

 dojo.byId("url").value = "http://api.flickr.com/services/feeds/photos_public.gne?tags=earthquakes,us&tagmode=all&format=json";
  
 var requestHandle = esri.request({
  
  url: url.path,
  
  content: url.query,
  
  callbackParamName: "jsoncallback",
  
  load: requestSucceeded,
  
  error: requestFailed
  
 });  

In questo caso il contenuto è scaricato in JSONP. Nell'esempio è stata fatta una chiamata Flickr e Flickr vuole che il nome del parametro di callback utilizzato per passare la funzione si chiami 'jsoncallback'

Nel caso utilizzassimo ESRI REST API il nome del parametro si chiamerebbe 'callback'.

Vediamo un esempio (vedi online):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <meta http-equiv="X-UA-Compatible" content="IE=7" />
  <!--The viewport meta tag is used to improve the presentation and behavior of the samples 
    on iOS devices-->
  <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
  <title>JSONP Content</title>
  <link rel="stylesheet" type="text/css" href="http://serverapi.arcgisonline.com/jsapi/arcgis/2.1/js/dojo/dijit/themes/claro/claro.css" />
  <link rel="stylesheet" type="text/css" href="http://serverapi.arcgisonline.com/jsapi/arcgis/2.1/js/dojo/dojox/widget/Toaster/Toaster.css" />
  <script type="text/javascript">
    var djConfig = { isDebug: true, parseOnLoad: true };
  </script>
  <script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=2.1"></script>

  <style type="text/css">
    #content { 
      width400pxheight350pxpadding5pxoverflowauto;
      bordersolid 2px #AAAAAAbackground-color#FFFFFF;
      -moz-border-radius5px-webkit-border-radius5px-o-border-radius5pxborder-radius5px;
      -moz-box-shadow0 0 0.5em black-webkit-box-shadow0 0 0.5em black-o-box-shadow0 0 0.5em blackbox-shadow0 0 0.5em black;
    }
    .failure { colorred; }
    #status { font-size12px; }
  </style>
 
  <script type="text/javascript">
    dojo.require("esri.map");
    dojo.require("dojox.widget.Toaster");

    
    dojo.addOnLoad(function() {
      dojo.byId("url").value = "http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer/project?inSR=4326&outSR=102113&geometries=%7B%0D%0A%22geometryType%22%3A%22esriGeometryPoint%22%2C%0D%0A%22geometries%22%3A%5B%7B%22x%22%3A-117%2C%22y%22%3A34%7D%5D%0D%0A%7D&f=json";
      dojo.byId("content").value = "";
      
    });
    
    function getContent() {
      dojo.byId("content").value = "";
      dojo.removeClass(dojo.byId("content"), "failure");
      dojo.byId("status").innerHTML = "Downloading...";
      
      var url = esri.urlToObject(dojo.byId("url").value);
      
 
      var requestHandle = esri.request({
        url: url.path,
        content: url.query,
        callbackParamName: "callback",
        load: requestSucceeded,
        error: requestFailed
      },{useProxy:false});
      
    }
    
    function requestSucceeded(response, io) {
      console.log("Succeeded: ", response);
      requestCompleted();
      dojo.publish("msgTopic", [{
          message: "Succeeded!",
          type: "message",
          duration: 1000
      }]);      

      // dojo.toJson method converts the given JavaScript object
      // and its properties and values into simple text.
      dojo.toJsonIndentStr = "  ";
      dojo.byId("content").value = dojo.toJson(response, true);
      
 
    }
    
    function requestFailed(error, io) {
      console.log("Failed: ", error);
      dojo.addClass(dojo.byId("content"), "failure");
      requestCompleted();
      dojo.publish("msgTopic", [{
          message: "'Error!",
          type: "error",
          duration: 500
      }]); 
      dojo.toJsonIndentStr = "  ";
      dojo.byId("content").value = dojo.toJson(error, true);      
    }
    
    function requestCompleted() {
      dojo.byId("status").innerHTML = "Done.";
      var reset = function() {
        dojo.byId("status").innerHTML = "";
      };
      setTimeout(reset, 2000);
    }
    
  </script>
</head>
 
<body style="font-familyArial Unicode MS,Arial,sans-serif;">
  <p>
    Use esri.request to download content available in <b>JSONP</b> format.
  </p>
 
  <p>
    Enter URL here: <input type="text" id="url" size="75" />
    <input type="button" value="GO" onclick="getContent();" />
 
    <span id="status"></span>
  </p>
  
  <p>
    <p>Content:</p>
    <textarea id="content"></textarea>
  </p>
  <div dojoType="dojox.widget.Toaster" id="msgToaster" positionDirection="br-left" messageTopic = "msgTopic"></div>
</body>
</html>

domenica 19 dicembre 2010

Autocomplete con dojo API

Con le ArcGIS JavaScript API potrebbe essere utile utilizzare l'Autocomplete. Per questo ci viene incontro il dojox.data.QueryReadStore. In dojo campus possiamo vedere degli esempi.





Innanzitutto creiamo il servizio lato server che verrà chiamato lato client. In questo esempio che vi illustro usiamo il template WCF Rest per il Framework 4.0 (CS) , ci connettiamo ad un servizio Geodata e ci facciamo restituire i dati richiesti.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using ESRI.ArcGIS.ADF;
using ESRI.ArcGIS.ADF.Connection.AGS;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.GeoDatabaseDistributed;
using ESRI.ArcGIS.Server;
 
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class GeoData : IGeoData
{
 
    #region IGeoDataService Members
 
    [WebGet(UriTemplate = "/Comune?q={filter}&limite={limit}", ResponseFormat  = WebMessageFormat.Json)]
    public ListaComuni GetListaComuni(string filter, int limit)
    {
        List<Comune> items = new List<Comune>();
 
        if (string.IsNullOrEmpty(filter))
            return new ListaComuni() { Items = items };
 
        filter = filter.Substring(0, filter.Length - 1);
 
        if (filter.Length <= limit)
            return new ListaComuni() { Items = items };
        
 
 
        IServerContext serverContext = null;
        try
        {
            // Connessione ad ArcGIS Server
            Identity identity = new Identity();
            identity.UserName = ConfigurationManager.AppSettings.Get("utenteAGS");
            identity.Password = ConfigurationManager.AppSettings.Get("passwordAGS");
 
            string domain = ConfigurationManager.AppSettings.Get("domainAGS");
 
 
            if (!string.IsNullOrEmpty(domain))
              identity.Domain = domain;
            
 
            using (AGSServerConnection agsServerConnection = new AGSServerConnection(ConfigurationManager.AppSettings.Get("hostnameAGS"), identity))
            {
                try
                {
                    agsServerConnection.Connect();
                }
                catch
                {
                    throw;
                }
 
                // get a server context
                IServerObjectManager som = agsServerConnection.ServerObjectManager;
                serverContext = som.CreateServerContext(ConfigurationManager.AppSettings.Get("serviceGeoData"), "GeodataServer");
 
                // get the workspace from the GeodataServer
                IGeoDataServer gds = serverContext.ServerObject as IGeoDataServer;
                IGeoDataServerObjects geoDataServerObjects = gds as IGeoDataServerObjects;
                IWorkspace workspace = geoDataServerObjects.DefaultWorkingWorkspace;
 
 
                IFeatureWorkspace featureWorkspace = workspace as IFeatureWorkspace;
                IFeatureClass featureClass = featureWorkspace.OpenFeatureClass("Comuni");
                string nomeCampo = "COMUNE";
                int idxField = featureClass.FindField(nomeCampo);
 
 
                IQueryFilter queryFilter = serverContext.CreateObject("esriGeodatabase.QueryFilter"as IQueryFilter;
 
 
                queryFilter.SubFields = nomeCampo;
 
                if (!string.IsNullOrEmpty(filter))
                {
                    ISQLSyntax sqlSyntax = workspace as ISQLSyntax;
                    string wildcardMany = sqlSyntax.GetSpecialCharacter(esriSQLSpecialCharacters.esriSQL_WildcardManyMatch);
                    string prefixDI = sqlSyntax.GetSpecialCharacter(esriSQLSpecialCharacters.esriSQL_DelimitedIdentifierPrefix);
                    string suffixDI = sqlSyntax.GetSpecialCharacter(esriSQLSpecialCharacters.esriSQL_DelimitedIdentifierSuffix);
                    
                    string where = string.Format("{0}{1}{2} LIKE '{3}{4}'", prefixDI, nomeCampo, suffixDI, filter.ToUpper(), wildcardMany);
                    queryFilter.WhereClause = where;
                }
 
                using (ComReleaser comReleaser = new ComReleaser())
                {
                    IFeatureCursor featureCursor = featureClass.Search(queryFilter, true);
                    comReleaser.ManageLifetime(featureCursor);
                    IFeature feature = null;
                    string item = null;
                    while ((feature = featureCursor.NextFeature()) != null)
                    {
                        item = Convert.ToString(feature.get_Value(idxField));
                        items.Add(new Comune() { Name = item });
                    }
                }
 
                items = items.OrderBy(x => x.Name).ToList();
 
                //if (limit > 0)
                //  items = items.Take(limit).ToList();
 
            }
            return new ListaComuni() { Items = items };
        }
        catch
        {
            return new ListaComuni() { Items = new List<Comune>() };
        }
        finally
        {
            if (serverContext != null) serverContext.ReleaseContext();
        }
    }
 
    #endregion
}

using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;


[ServiceContract]
public interface IGeoData
{

    [OperationContract]
    ListaComuni GetListaComuni(string filter,int limit);

}


[DataContract]
public class ListaComuni
{
    [DataMember(Name = "items")]
    public List<Comune> Items
    {
        get;
        set;
    }
}

[DataContract]
public class Comune
{

    [DataMember(Name = "name")]
    public string Name
    {
        get;
        set;
    }
}



Dalla nostra pagina che esegue codice lato client utilizziamo il QueryReadStore e richiamiamo il nostro servizio REST.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
       <meta http-equiv="X-UA-Compatible" content="IE=7" />
       <!--The viewport meta tag is used to improve the presentation and behavior of the samples 
        on iOS devices-->
       <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>


        <link rel="stylesheet" type="text/css" href="http://serverapi.arcgisonline.com/jsapi/arcgis/2.1/js/dojo/dijit/themes/claro/claro.css">

        <script type="text/javascript">
            var djConfig = {
                parseOnLoad: true, isDebug: true, baseUrl: './', modulePaths: { 'custom''custom' }
            };
        </script> 
        <script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=2.1"></script>

<script type="text/javascript">

    dojo.require("dijit.dijit"); // optimize: load dijit layer
    dojo.require("dijit.layout.BorderContainer");
    dojo.require("dijit.layout.ContentPane");
    dojo.require("dijit.layout.TabContainer");
    dojo.require("dijit.Tooltip");
    dojo.require("dijit.TitlePane");
    dojo.require("esri.map");

    dojo.require("dojox.data.QueryReadStore");
    dojo.require("dijit.form.ComboBox");
    dojo.require("custom.ComboBoxReadStore");




    var map;


    function Init() {

        dojo.style(dojo.byId("map"), { width: dojo.contentBox("map").w + "px", height: (esri.documentBox.h - dojo.contentBox("navTable").h - 40) + "px" });
        var initExtent = new esri.geometry.Extent({ "xmin": 6.55059928850006, "ymin": 45.0006208151578,
            "xmax": 8.22229873550003, "ymax": 45.6702338525465, "spatialReference": { "wkid": 4326 }
        });
        map = new esri.Map("map", { extent: initExtent });


        var layer = new esri.layers.ArcGISDynamicMapServiceLayer("http://localhost/ArcGIS/rest/services/LimitiComuni/MapServer", { "opacity": 0.5 });
        var layerBaseMap = new esri.layers.ArcGISTiledMapServiceLayer("http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer");
        map.addLayer(layerBaseMap);
        map.addLayer(layer);

        var resizeTimer;
        dojo.connect(map, 'onLoad'function (theMap) {

            dojo.connect(dijit.byId('map'), 'resize'function () {
                clearTimeout(resizeTimer);
                resizeTimer = setTimeout(function () {
                    map.resize();
                    map.reposition();
                }, 500);
            });
        });




    }
    dojo.addOnLoad(Init);

</script>
</head>
<body class="tundra">

<table style="width:100%">
<tr>
<td>
<table id="navTable" width="100%">
<tbody>
<tr valign="top">
<td id="breadcrumbs">
ArcGIS JavaScript API: LimitiComuni
</td>

<td align="right" id="help">

<div dojoType="custom.ComboBoxReadStore" jsId="theStore" url="http://localhost:1385/GeoData/Comune?"
               requestMethod="get">
</div>

<input dojoType="dijit.form.ComboBox"
        searchAttr="name" name="theSelect" id="theSelect" 
        store="theStore" placeHolder="Cerca comune" hasDownArrow="false">
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
<div id="map" style="margin:auto;width:97%;border:1px solid #000;"></div>
</body>
</html>



In custom/ComboBoxReadStore.js subclassiamo dojox.data.QueryReadStore per poter modificare eventualmente il passaggio dei parametri dal client al server:

dojo.provide("custom.ComboBoxReadStore");
 
dojo.require("dojox.data.QueryReadStore");
 
dojo.declare("custom.ComboBoxReadStore", dojox.data.QueryReadStore, {
  fetch:function(request) {
    request.serverQuery = {q:request.query.name, limite:2};
    // Call superclasses' fetch
    return this.inherited("fetch", arguments);
  }
});

domenica 28 novembre 2010

SQL Server Reporting services Map & ArcGIS Server

Con la nuova versione di SQL Server 2008 R2, possiamo visualizzare mappe nelle report che distribuiamo con RS (anche con la versione Express).

Questo è possibile grazie al nuovo controllo map. Qui potete trovare maggiori dettagli su tale controllo.

Contestualmente all'aggiunta in ArcGIS Server dei query layer,  possiamo effettuare una sorta di integrazione tra ArcGIS Server ed il controllo di mappa di RS senza utilizzare gli shapefile.

In questo video illustro una possibile integrazione:


Code:

<%@ WebHandler Language="C#" Class="rsMap" %>
 
using System;
using System.Web;
//Author: Domenico Ciavarella
//www.studioat.it
public class rsMap : IHttpHandler {
 
 public void ProcessRequest (HttpContext context) {
  
  bool isJson = false;
  bool isPrettyJson = false;
  string format = null;
  if (context.Request.QueryString["f"] != null)
  {
   format = context.Request.QueryString["f"].ToLower();
   if (format == "json" || format == "pjson")
   {
    isJson = true;
    isPrettyJson = format == "pjson";
   }
  }
 
  
  
  reportExecution.ReportExecutionService rs = new reportExecution.ReportExecutionService();
  rs.Credentials = new System.Net.NetworkCredential(System.Configuration.ConfigurationManager.AppSettings.Get("userReport"), System.Configuration.ConfigurationManager.AppSettings.Get("passwordReport"), System.Configuration.ConfigurationManager.AppSettings.Get("domain"));
  
  // Render arguments
  byte[] result = null;
  string reportPath = "/rsMappe/rsMap";
  string formatOutput = "PDF";
  string historyID = null;
 
  string encoding;
  string mimeType;
  string extension;
  reportExecution.Warning[] warnings = null;
  string[] streamIDs = null;
 
 
  
 
 
  reportExecution.ExecutionInfo execInfo = new reportExecution.ExecutionInfo();
  reportExecution.ExecutionHeader execHeader = new reportExecution.ExecutionHeader();
 
  rs.ExecutionHeaderValue = execHeader;
 
  execInfo = rs.LoadReport(reportPath, historyID);
  
  String SessionId = rs.ExecutionHeaderValue.ExecutionID;
 
  string oidValue = context.Server.HtmlEncode(context.Request.QueryString["oid"]);
  
  if (string.IsNullOrEmpty(oidValue))
  {
   context.Response.Write("Parametri non passati!");
   return;
  }
  
 
 
 
  reportExecution.ParameterValue[] parameters = new reportExecution.ParameterValue[1 ];
 
    
  parameters[0] = new reportExecution.ParameterValue();
  parameters[0].Name = "pFID";
  parameters[0].Value = oidValue;
  
  rs.SetExecutionParameters(parameters, "it-IT");
   
  result = rs.Render(formatOutput, nullout extension, out encoding, out mimeType, out warnings, out streamIDs);
 
 
  if (isJson)
  {
   string fileName = "_ags_" + Guid.NewGuid().ToString() + ".pdf";
   string pathfileName = @"c:\arcgisserver\arcgisoutput\" + fileName;
   string urlfileName = "http://localhost/arcgisoutput/" + fileName;
   using (System.IO.FileStream stream = System.IO.File.OpenWrite(pathfileName))
   {
    stream.Write(result, 0, result.Length);
   }
 
   GenerateJson(context, new Result() { url = urlfileName }, isPrettyJson);
   return;
  }
  
  
  context.Response.ClearContent();
  context.Response.AppendHeader("content-length", result.Length.ToString());
  context.Response.ContentType = "application/pdf";
  context.Response.BinaryWrite(result);
  context.Response.Flush();
  context.Response.Close();
 }
 
 public bool IsReusable {
  get {
   return false;
  }
 }
 
 
 private static void GenerateJson(HttpContext context, Result result, bool isPrettyJson)
 {
  context.Response.ContentType = "text/plain";
  string newLine = "\n";
  string tab = " ";
  if (!isPrettyJson)
  {
   newLine = tab = "";
  }
  context.Response.Write(string.Format("{{\"results\": {0}", newLine, tab));
 
  context.Response.Write(string.Format("{2}{1}{2}{2}{{\"url\":\"{0}\"}}{1}", result.url, newLine, tab));
    
  context.Response.Write(string.Format("{0}}}", newLine, tab));
 }
 
 public struct Result {
  public string url;
 }
 
 
}


page HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=7" />
  <title>ArcGIS JavaScript API: LimitiComuni</title>
<style type="text/css">
  @import "http://serverapi.arcgisonline.com/jsapi/arcgis/2.1/js/dojo/dijit/themes/tundra/tundra.css";
htmlbody { height100%width100%margin0padding0; }
      .tundra .dijitSplitContainer-dijitContentPane.tundra .dijitBorderContainer-dijitContentPane#navtable { 
        PADDING-BOTTOM5pxMARGIN0px 0px 3pxPADDING-TOP0pxBORDER-BOTTOM#000 1px solidBORDER-TOP#000 1px solidBACKGROUND-COLOR#E5EFF7;
      }
      .tundra .dijitSplitContainer-dijitContentPane.tundra .dijitBorderContainer-dijitContentPane#map {
        overflow:hiddenborder:solid 1px blackpadding0;
      }
      #breadcrumbs {
        PADDING-RIGHT0pxPADDING-LEFT11pxFONT-SIZE0.8emFONT-WEIGHTboldPADDING-BOTTOM5pxMARGIN0px 0px 3pxPADDING-TOP0px;
      }
      #help {
        PADDING-RIGHT11pxPADDING-LEFT0pxFONT-SIZE0.70emPADDING-BOTTOM5pxMARGIN0px 0px 3pxPADDING-TOP3px</style>
<script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis?v=2.1"></script>
<script type="text/javascript">
    dojo.require("esri.map");
    dojo.require("dijit.layout.ContentPane");
    dojo.require("dijit.layout.BorderContainer");
    dojo.require("dijit.Dialog");
 
    var map;
    var printDlg;
 
    function Init() {
        dojo.style(dojo.byId("map"), { width: dojo.contentBox("map").w + "px", height: (esri.documentBox.h - dojo.contentBox("navTable").h - 40) + "px" });
        var initExtent = new esri.geometry.Extent({ "xmin": 6.55059928850006, "ymin": 45.0006208151578,
            "xmax": 8.22229873550003, "ymax": 45.6702338525465, "spatialReference": { "wkid": 4326 }
        });
        map = new esri.Map("map", { extent: initExtent });
        
        printDlg = new dijit.Dialog({
            title: "Stampa",
            style: "width: 825px; height: 650px"
        });
   
        var layer = new esri.layers.ArcGISDynamicMapServiceLayer("http://localhost/ArcGIS/rest/services/LimitiComuni/MapServer", { "opacity": 0.5 });
        var layerBaseMap = new esri.layers.ArcGISTiledMapServiceLayer("http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer");
        map.addLayer(layerBaseMap);
        map.addLayer(layer);
        var resizeTimer;
        dojo.connect(map, 'onLoad'function (theMap) {
            dojo.connect(map, "onClick", doIdentify);
 
            identifyTask = new esri.tasks.IdentifyTask("http://localhost/ArcGIS/rest/services/LimitiComuni/MapServer");
 
            identifyParams = new esri.tasks.IdentifyParameters();
            identifyParams.tolerance = 3;
            identifyParams.returnGeometry = false;
            identifyParams.layerIds = [0];
            identifyParams.layerOption = esri.tasks.IdentifyParameters.LAYER_OPTION_ALL;
            identifyParams.width = map.width;
            identifyParams.height = map.height;
            
            dojo.connect(dijit.byId('map'), 'resize'function () {
                clearTimeout(resizeTimer);
                resizeTimer = setTimeout(function () {
                    map.resize();
                    map.reposition();
                }, 500);
            });
        });
    }
    dojo.addOnLoad(Init);
 
 
    function doIdentify(evt) {
        identifyParams.geometry = evt.mapPoint;
        identifyParams.mapExtent = map.extent;
        identifyTask.execute(identifyParams, function (idResults) { addToMap(idResults, evt); });
    }
    function addToMap(idResults, evt) {
        var idResult = idResults[0];
        print(idResult.feature.attributes.FID);
    }
 
    function print(id) {
   var url = esri.urlToObject("http://localhost/rsMap/rsMap.ashx?f=json&oidIndirizzo=" + id);

            var requestHandle = esri.request({
                url: url.path,
                content: url.query,
                handleAs: "json",
                load: function (data) {
                    printDlg.attr("content", "<div><object data='" + data.results.url + "' type='application/pdf' width='800' height='600'></object></div>");
                    printDlg.show();
                },
                error: function (error) {
                    alert("Errore di stampa");
                }
            });
}
 
 
</script>
</head>
<body class="tundra">
 
<table style="width:100%">
<tr>
<td>
<table id="navTable" width="100%">
<tbody>
<tr valign="top">
<td id="breadcrumbs">
ArcGIS JavaScript API: LimitiComuni
</td>
<td align="right" id="help">
Built using the  <a href="http://resources.esri.com/arcgisserver/apis/javascript/arcgis">ArcGIS JavaScript API</a>
</td>
</tr>
</tbody>
</table>
 
</td>
</tr>
</table>
<div id="map" style="margin:auto;width:97%;border:1px solid #000;"></div>
</body>
</html>

domenica 6 giugno 2010

Deferreds

Javascript non supporta il concetto di threads ma offre la possibilità di eseguire richieste asincrone con l'oggetto XMLHttpRequest e attraverso i ritardi con la funzione setTimeout.

Dojo mette a disposizione una classe chiamata Deferred che aiuta a gestire la complessità degli eventi asincroni astraendola con una interfaccia consistente.

Vediamo un esempio

 var d = new dojo.Deferred(/* Optional cancellation function goes here */);
  
                //aggiungi una funzione di callback
  
                d.addCallback(function(response) {
  
                  console.log("La response e': ", response);
  
                  return response +1;
  
                });
  
                //Aggiungi un'altra callback che viene eseguita dopo la precedente
  
                d.addCallback(function(response) {
  
                  console.log("La response e': ", response);
  
                  return response + 1;
  
                });
  
                d.addErrback(function(response) {
  
                  console.log("C'è un errore: ", response);
  
                  return response;
  
                });
  
                d.callback(100);  

Questo è quello che vedremo in console:


Deferreds consente di concatenare insieme multiple callback e errbacks così da essere eseguite in ordine sequenziale. Inoltre il Deferreds consente di fornire una funzione di annullamento così da eseguire un'operazione di abort in richieste asincrone.

Tutte le chiamate di input/output alla rete restitutiscono un Deferred perchè offre una maggiore flessibilità nella gestione delle chiamate asincrone.

Vediamo un esempio applicato ad una funzione XHR.

 var d = dojo.xhrGet({
  
        url : "/legend.json",
  
        handleAs : "json", 
  
                 load : function(response, ioArgs) {
  
                  return response; // passa al prossimo callback
  
                 },
  
                 error : function(response, ioArgs) {
  
                  return response; //passa al prossimo errback
  
                 }
  
                });
  
                //chiamato una volta che il load è completato
  
                d.addCallback(function(response) {
  
                      console.log("Il response 1 e': ", response);
  
                      //passato al prossimo callback
  
                   return response;
  
                });
  
                d.addCallback(function(response) {
  
                      console.log("Il response 2 e': ", response);
  
                   return response;
  
                });
  
                d.addErrback(function(response) {
  
                      console.log("C'è un errore: ", response);
  
                   return response;
  
                });  

Questo è il risultato in console:




Per annullare un'operazione asincrona 'iniettiamo' un deferred negli eventi load ed error:

 var d = new dojo.Deferred;
  
                     //aggiungi una funzione di callback
  
                     d.addCallback(function(response){
  
                          console.log("Callback 1: ", response);
  
                          return response;
  
                     });
  
                     //Aggiungi un'altra callback che viene eseguita dopo la precedente
  
                     d.addCallback(function(response){
  
                          console.log("Callback 2: ", response);
  
                          return response;
  
                     });
  
                     d.addErrback(function(response){
  
                          console.log("Errback 1: ", response);
  
                          return response;
  
                     });
  
                     d.addErrback(function(response){
  
                          console.log("Errback 2: ", response);
  
                          return response;
  
                     });
  
                     request = dojo.xhrGet({
  
                          url: "/legend.json",
  
                          handleAs: "json",
  
                          timeout: 5000,
  
                          load: function(response, ioArgs){
  
                               console.log("load: ", response);
  
                               d.callback(response, ioArgs);
  
                               return response; // passa al prossimo callback
  
                          },
  
                          error: function(response, ioArgs){
  
                               console.log("error: ", response);
  
                               d.errback(response, ioArgs);
  
                               return response; //passa al prossimo errback
  
                          }
  
                     });  

Se non annulliamo avremo:

se annulliamo con un: request.cancel() avremo



Infine vi segnalo il DeferredList che fa parte del core che facilita la gestione di multipli Deferreds.

Vediamo un esempio pratico con le API Javascript ESRI. Tenete presente che dalla versione 1.4 i task restituiscono un oggetto dojo.Deferred che permette di monitorare lo stato della richiesta e gestire la comunicazione di thread asincroni.

 var mapDeferred = esri.arcgis.utils.createMap(itemInfo, "map", {
  
       mapOptions: {
  
        slider: true
  
       },
  
       bingMapsKey: bingMapsKey,
  
 geometryServiceURL: "http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer"
  
  });
  
 mapDeferred.addCallback(function(response) {
  
       map = response.map;
  
       dojo.connect(dijit.byId('map'), 'resize', resizeMap);
  
 });
  
 mapDeferred.addErrback(function(error) {
  
      console.log("Map creation failed: ", dojo.toJson(error));
  
 });
  

Il metodo esri.arcgis.utils.createMap che crea una mappa utilizzando informazioni da arcgis.com restituisce un dojo.Deferred con response:
{
map: esri.Map,
itemInfo:{
item: Object,
itemData:Object
}
}

che utilizziamo per aggiungere una funzione di callback per, ad esempio, impostare il resize della mappa.