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

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

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



lunedì 24 dicembre 2012

La segmentazione dinamica logora chi non ce l’ha!

La segmentazione dinamica è il processo per calcolare la shape delle posizioni route al volo lungo feature lineari calibrate basandosi su tabelle eventi per le quali la misura (m) delle distanze è disponibile. Una feature lineare calibrata o route è una feature di tipo polyline che ha i valori di m (misura) ed un identificatore. Pertanto una route è semplicemente una qualsiasi feature lineare sulla quale possono essere localizzati eventi.

Si possono organizzare le posizioni route in tabelle basate su un tema comune, le tabelle eventi. Per esempio, cinque tabelle eventi contenenti informazioni su limiti di velocità, anno di riasfaltamento, stato della pavimentazione, segnali stradali e frequenza incidenti possono riferire ad una route feature class rappresentante il reticolo stradale.
 
Una tabella eventi è una qualsiasi tabella che contenga un campo di identificazione della route ed almeno un campo per la misura (m). Le tabelle contenenti posizioni route puntuali hanno un campo di misura, mentre le tabelle contenenti posizioni route lineari ne hanno due. Il campo che identifica la route si 'aggancia' all’identificatore di route nella route feature class (non è necessario che questi abbiano lo stesso nome).

Una route event source fornisce una tabella eventi come una feature class dinamica. Ogni riga della tabella è rappresentata come una feature la cui shape è calcolata al volo ogni volta che è richiesto: questa è la segmentazione dinamica.

Nella seguente illustrazione mostriamo il concetto di segmentazione dinamica:



In una route event source, c’è una feature per ogni riga della tabella eventi originale. A volte, comunque, le feature hanno shape vuote. Ciò accade perché si è verificata una qualche ragione per la quale l’evento non è stato localizzato propriamente. In alcuni ad esempio, si può localizzare un evento solo parzialmente (solo con eventi lineari). La seguente illustrazione mostra alcuni tipi di errore:



La seguente illustrazione mostra il modello a oggetti per la segmentazione dinamica:



Per quel che riguarda lo sviluppo, abbiamo a disposizione il Locator, una classe astratta che specifica tutti i tipi di oggetti locator. Tra i tipi di locator ci sono: quelli per indirizzi, route (quello che siamo illustrando), coordinate x y e nomi di località. Il locator combina dati di riferimento con un metodo di posizionamento.

L’interfaccia ILocator fornisce accesso alle proprietà di un locator.

Il RouteLocator è una classe astratta che trasforma una posizione route in una shape visualizzabile su mappa. Le classi astratte Locator e RouteLocator sono mostrate nella seguente illustrazione:



La posizione route descrive una posizione lungo una route o una porzione di route tra un DA e un A di misure (m). L’interfaccia IRouteLocator2 eredita da IRouteLocator ed è utile per restituire le proprietà di un oggetto RouteLocator e per determinare la shape delle posizioni route ed eventi. Il metodo Identify è utile per identificare le posizioni di route utilizzando un envelope. Quando utilizziamo ArcMap, un metodo consigliato è utilizzare la tolleranza del map document:

            IEnvelope envelope = mxDoc.CurrentLocation.Envelope;
            envelope.Expand(mxDoc.SearchTolerance, mxDoc.SearchTolerance, false); 

Per route locator, la proprietà RouteFeatureClass può essere uno shapefile PolylineM o una feature class Polyline (con valori m) in un personal, file o ArcSDE geodatabase.
Questo significa che le route sono memorizzate in una feature class dove:

IGeometryDef.GeometryType = esriGeometryPolyLine e IGeometryDef.HasM = true

RouteMeasureLocator è un tipo di RouteLocator. Esso determina la shape di una posizione route associando i valori di misura delle posizioni route a quelli memorizzati nella feature route.
Il RouteMeasureLocator è creato tramite la controparte del suo oggetto nome, RouteMeasureLocatorName. L’oggetto RouteMeasureLocator è mostrato nella seguente figura:



La classe concreta RouteMeasureLocatorName è mostrato nella seguente illustrazione:



L’interfaccia IRouteLocatorName è utilizzata per restituire le proprietà di un oggetto RouteLocator.

Il LocatorName è una classe astratta che può essere utilizzata per far riferimento ad un oggetto Locator.
Il RouteLocatorName è una classe astratta che può essere utilizzata per riferirsi ad un oggetto RouteLocator.
RouteMeasureLocatorName è una classe concreta che può essere utilizzata per riferirsi ad un oggetto RouteMeasureLocator ed è una specifica implementazione del LocatorName e del RouteLocatorName.
Tutte le classi di RouteLocatorName implementano l’interfaccia IRouteLocatorName. Tale interfaccia si usa per impostare e restituire le proprietà dell’oggetto RouteLocatorName (classe concreta: RouteMeasureLocatorName).

Seguono alcune precisazioni circa le proprietà di questa interfaccia:

  • RouteFeatureClassName: feature class polyline con valori m.
  • RouteIDFieldName: qualsiasi campo numerico o testuale contenente l’identificatore di route. Questo campo si relazionerà ad un campo della stessa natura nella tabella eventi.
  • RouteMeasureUnit: unità di misura dei valori m memorizzati nelle route. Il valore di default è esriUnknownUnits.
  • RouteWhereClause: condizione che limita il noumero di routes sulle quali le posizioni possono essere trovate.

Il seguente esempio di codice mostra come creare un RouteMeasureLocator via RouteMeasureLocatorName:

            IDataset dS = (IDataset)routeFC; // A polylineM feature class.
            IName name = dS.FullName;
            IRouteLocatorName rtLocatorName = new RouteMeasureLocatorNameClass();
            rtLocatorName.RouteFeatureClassName = name;
            rtLocatorName.RouteIDFieldName = "routeId";
            rtLocatorName.RouteMeasureUnit = esriUnits.esriMeters;
            name = (IName)rtLocatorName;
            IRouteLocator2 rtLocator = (IRouteLocator2)name.Open();

Una RouteMeasureLocation descrive una porzione di route o una singola posizione lungo una route. L’interfaccia IRouteLocation ti permette di definire le proprietà di una posizione route, ad esempio le posizioni route che si trovano lungo una singola route; perciò, impostane qui il valore. In aggiunta, identifica le unità di misura nelle quali la posizione route è stata raccolta e specifica se lo desideri che la shape della posizione di route abbia un offset dalla route quando è posizionata.

Gli offset sono nelle unità di misura del riferimento spaziale della route feature class (e non necessariamente nelle stesse unità delle misure della feature class). Quindi, un offset su dati di route memorizzati in unità geografiche potrebbe produrre risultati non corretti. Gli offset vengono utilizzati esclusivamente per scopi di visualizzazione degli eventi sulla mappa.

Impostare la proprietà IRouteLocation.MeasureUnit ti consente di eseguire al volo conversioni di misure. Tale proprietà corrisponde a IRouteLocatorName.RouteMeasureUnit. Ad esempio, potresti conoscere la collocazione di una posizione route in miglia, ma la tua route feature class ha le sue misure memorizzate in metri. Impostando i valori di conseguenza, puoi ottenere la conversione della misura.

RouteMeasureLineLocation è una classe che descrive porzioni di una route utilizzando posizioni di misura DA - A. L’oggetto RouteMeasureLineLocation è mostrato nella seguente illustrazione:



L’interfaccia IRouteMeasureLineLocation è dove puoi impostare i valori di misura Da – A delle posizioni route, ad esempio, trovare una posizione a partire da 2500 metri a 3500 metri lungo la route 10 e impostare un offset di 25 metri dalla route:

            IRouteLocation routeLoc = new RouteMeasureLineLocationClass();
            routeLoc.MeasureUnit = esriUnits.esriMeters;
            routeLoc.RouteID = 10;
            routeLoc.LateralOffset = 25;
            IRouteMeasureLineLocation rMLineLoc = (IRouteMeasureLineLocation)routeLoc;
            rMLineLoc.FromMeasure = 2500;
            rMLineLoc.ToMeasure = 3500;

RouteMeasurePointLocation è una classe che utilizza un singolo valore di m per rappresentare una singola posizione lungo la route.

L’interfaccia IRouteMeasurePointLocation è dove puoi impostare i valori m delle posizioni. Ad esempio per trovare la posizione a 565.5 metri lungo la route 10:

            IRouteLocation routeLocation = new RouteMeasurePointLocationClass();
            routeLocation.MeasureUnit = esriUnits.esriMeters;
            routeLocation.RouteID = 10;
            routeLocation.LateralOffset = 0;
            IRouteMeasurePointLocation rMPointLoc = (IRouteMeasurePointLocation)routeLocation;
            rMPointLoc.Measure = 565.5;

Una volta che hai creato la posizione di route, determini la sua geometria chiamando il metodo IRouteLocator.Locate come mostrato nel seguente codice di esempio:

            IGeometry geom;
            esriLocatingError locError;
            rtLocator.Locate((IRouteLocation)rMPointLoc, out geom, out locError);

Qui potete scaricare una SOE (10 e 10.1) che ho sviluppato che permette interrogare la M lungo una route, impostare una posizione puntuale e lineare via rest mediante un servizio ArcGIS Server, utilizzando i metodi qui presentati.



Anche ESRI ha sviluppato una SOE che potete trovare qui tra i samples di arcgis.com. Una volta disegnata la polyline la SOE posiziona i marker ogni miglia o chilometro. In questo caso la creazione della route è fatta al volo impostando i valori di m con la lunghezza cumulata a partire dall'origine della geometria (il parametro ratio = false indica che si vuole il valore assoluto delle m e non quello relativo rispetto alla lunghezza complessiva della polyline).


            IMSegmentation mSeg = (IMSegmentation)pointColl;
            mSeg.SetMsAsDistance(false);





Per dettagli su come impostare le route event source lineari e puntuali (come già detto: feature class dinamiche basate su tabelle eventi), come creare route, come calibrare le misure route, come geoprocessare eventi, come localizzare feature lungo le route e come etichettare le feature lineari con m (hatching) seguite il seguente link.

sabato 17 novembre 2012

Smack...! Bacini idrografici

L’extension Spatial Analyst, una delle più utilizzate in ArcGIS, permette un’innumerevole serie di analisi. Tra queste troviamo anche quelle relative all’idrologia. In questo caso, ArcGIS ci mette a disposizione uno strumento per lo sviluppo di soluzioni e la gestione delle risorse idriche. Gli idrologi e gli idrogeologi possono utilizzare la tecnologia GIS per integrare vari tipi di dati e applicazioni in un unico sistema centralizzato. L’insieme degli strumenti contenuti, ad esempio, in ArcHydro, facilita la creazione, la gestione e la visualizzazione degli oggetti idrologici all’interno dell’ambiente di ArcGIS.
I seguenti tool ci sono messi a disposizione per la gestione delle nostre analisi: Basin, Fill, Flow Accumulation, Flow Direction, Flow Length, Sink, Snap Pour Point, Stream Link, Stream Order, Stream To Feature e Watershed
E’ possibile, ad esempio, con questi tool delineare in modo automatico il bacino imbrifero a partire dal modello digitale del terreno, determinare il reticolo idrografico e l’ordine delle aste fluviali.
Ad esempio, per determinare un bacino idrografico a partire da una sezione di chiusura, si procede creando innanzitutto un raster che rappresenta la direzione di flusso di ogni cella ad una delle otto celle adiacenti verso la quale ha la pendenza maggiore. A seconda delle direzione la cella sarà impostata con i valori 1,2,4,8,16,32,64 o 128.
I valori di output sono derivati da una rappresentazione binaria dei risultati dell’analisi e, come possiamo osservare dall’immagine, se il flusso è diretto a destra verrà impostato 1, se il flusso è diretto in basso a destra verrà impostato 2 e così via fino a 128. Il focal flow che è un filtro focale può utilizzare operatori bitwise; così, ad esempio, se da una cella il flusso è in tutte le direzioni verrà impostato il valore 128+64+32+16+8+4+2+1 = 255, mentre se dovesse essere nella direzione 3 e 1 sarà 4+1 = 5.
 

L’approccio utilizzato è quello di un modello di flusso di otto direzioni (D8) e segue l’approccio presentato da Jensen and Domingue (1988).

Il calcolo effettuato per determinare la direzione con la pendenza più ripida per ogni cella è:




ΔZ / distanza


La distanza è deteminata tra i centri delle celle, pertanto la cella centrale rispetto alle celle poste sulla diagonale avrà un fattore 1,4142.. (le celle sono quadrate) mentre le altre avranno un fattore 1.
Nell’esempio seguente la dimensione della cella è 1 unità.
 

Se tutte le celle adiacenti sono più alte di quella centrale, la cella è un sink (pozzo) ed avrà una direzione di flusso non definita. Celle con direzione di flusso non definito possono essere marcate come sink utilizzando la funzione Sink. Per ottenere una rappresentazione accurata della direzione di flusso sulla superficie, i sink possono essere riempiti con la funzione Fill utilizzando il DEM.
Una volta determinata la direzione di flusso a partire dal DEM, occorre determinare gli accumuli di flusso. La flow accumulation è determinata a partire dalla flow direction.
Questa funzione calcola il flusso accumulato come peso accumulato di tutte le celle che scorrono verso ogni cella. Se non è fornito un peso sul raster, ad ogni cella è applicato un peso pari a 1 e quindi il numero rappresentato sarà il numero di celle nel quale il flusso arriva alla cella.
 
 In questo esempio è stato dato peso 1 ad ogni cella.
 
 
Celle con un elevato accumulo di flusso sono aree dove l’acqua converge in modo massiccio e possono essere utilizzate per identificare canali di flusso, mentre celle con accumulo di flusso pari a zero possono essere utilizzate per identificare gli spartiacque.
Un esempio di utilizzo di un raster pesato nella funzione di Flow Accumulation è determinare l’ammontare di pioggia caduta all’interno di un dato bacino imbrifero. In questo caso il raster pesato potrebbe essere un raster che rappresenta la precipitazione media durante un dato temporale. Il risultato della funzione di Flow Accumulation rappresenterà la quantità di pioggia che defluisce in ogni cella, assumendo che tutta la pioggia defluisce senza altri fattori che possano influenzarla (evapotraspirazione, infiltrazione nel sottosuolo ecc.).
Ora, per determinare i bacini idrografici relativi a determinate sezioni di chiusura, possiamo utilizzare la funzione Watershed che richiede come input la flow direction e i punti rappresentanti le sezione di chiusura.
Il modello seguente determina un watershed a partire da una flow direction, una flow accumulation e da uno o più punti che si posizioneranno mediante la distanza di snap alle celle con il più alto valore di flusso di accumulo (Snap Pour Point).
 
Utilizzando gli ArcObjects, l’interfaccia da utilizzare è la IHydrologyOp2 della library Spatial Analyst che ci mette a disposizione tutte le funzioni elencate precedentemente.

Per determinare un watershed scriveremo:

            try
            {
                IFeatureWorkspace featureWorkspace = Helper.CreateInMemoryWorkspace() as IFeatureWorkspace;
                IFeatureClass featureClass = this.CreateFeatureClass(location, featureWorkspace);
 
                IFeature feature = featureClass.CreateFeature();
                feature.Shape = location;
                feature.set_Value(featureClass.FindField(SAUtility.FieldNameIdWatershed), (int)idWatershed.Value);
                feature.Store();
 
                IHydrologyOp hydrologyOp = new RasterHydrologyOp() as IHydrologyOp;
 
                IGeoDataset accumulation = this.GetGeodataset((int)idAccumulation.Value);
                IGeoDataset direction = this.GetGeodataset((int)idDirection.Value);
 
                IFeatureClassDescriptor featureClassDescriptor = new FeatureClassDescriptorClass();
                featureClassDescriptor.Create(featureClass, nullSAUtility.FieldNameIdWatershed);
                IGeoDataset pourPoint = featureClassDescriptor as IGeoDataset;
 
                IRasterAnalysisEnvironment rasterAnalysisEnvironment = new RasterAnalysisClass();
                object extentProvider = Type.Missing;
                object snapRasterData = Type.Missing;
                rasterAnalysisEnvironment.SetExtent(esriRasterEnvSettingEnum.esriRasterEnvMaxOf, ref extentProvider, ref snapRasterData);
 
                IGeoDataset snapRaster = hydrologyOp.SnapPourPoint(pourPoint, accumulation, snapDistance.Value);
 
                IGeoDataset watershed = hydrologyOp.Watershed(direction, snapRaster);
 
                IConversionOp conversionOp = new RasterConversionOpClass() as IConversionOp;
                IGeoDataset featureClassWatershed = conversionOp.RasterDataToPolygonFeatureData(watershed, featureWorkspace as IWorkspace"WatershedPolygon"true);
 
                IRecordSetInit recordset = new RecordSetClass();
                recordset.SetSourceTable(featureClassWatershed as ITablenull);
 
                byte[] recorset = Conversion.ToJson(recordset as IRecordSet);
                this.logger.LogMessage(ServerLogger.msgType.infoDetailed, methodName, SAUtility.MessageCodeSOE, string.Format("Watershed created with succcess. IdWatershed {0}", (int)idWatershed.Value));
                return recorset;
            }
            catch (Exception ex)
            {
                this.logger.LogMessage(ServerLogger.msgType.error, methodName, SAUtility.MessageCodeSOE, ex.Message);
                return new ObjectError("error create watershed").ToJsonObject().JsonByte();
            }

Per puro divertimento (come dice il mio amico Erik) ho creato una SOE da utilizzate in ArcGIS Server 10.1 per poter creare bacini idrografici online. Potete scaricarla qui.



Per i non sviluppatori gli stessi risultati possono essere ottenuti pubblicando il modello (che potete scaricare qui mentre il tutorial lo trovate qui) visto precedentemente.

giovedì 11 ottobre 2012

ArcGIS for Server 10.1: RESTful administrative API

ArcGIS for Server espone via Rest API per l'amministrazione. Questo significa che possiamo gestire completamente il nostro server creando tool che fanno richieste http.
Le API sono organizzate in Resources e Operations: le resource sono entità in ArcGIS for Server che hanno delle informazioni ed uno stato ben definito, mentre le operation si attuano ed aggiornano le informazioni o lo stato delle resource. Le resource e le operation sono gerarchiche ed hanno un URL univoco.
Le API per l'amministrazione supportano l'autenticazione basata su token. Per utilizzare le API occorre fornire un token che sia stato acquisito fornendo credenziali amministrative. Ovviamente, data la natura delle informazione trasmesse tra le API per l'amministrazione e l'utente delle stesse,  la comunicazione dovrebbe avvenire su una connessione SSL.
Per poter utilizzare le API per l'amministrazione occorre essere un PSA (Primary Site Administrator) o avere  privilegi amministrativi.
 
ESRI ha già pubblicato dei tool utlizzando Python, ma nulla vieta di utilizzare il linguaggio a noi più familiare (Java, JavaScript, PowerShell, C#, Ruby, Scala, Perl, ecc.).
 
Qui potete trovare la guida in linea che descrive tramite esempi le varie funzionalità delle API.
 
Interessante notare come sia possibile registrare macchine e aggiungere, unire ed eliminare site; ciò permette, schedulando task, di distribuire un numero maggiore di istanze su più macchine se le richieste ai servizi dovessero aumentare. Ma possiamo anche semplicemente schedulare lo stato di salute dei nostri servizi ed inviare un'email se ci sono dei problemi od esaminare le statistiche per un determinato servizio.
 
Alcune delle operazioni di amministrazione possono richiedere un po' di tempo. Per prevenire il blocco del client mentre l'operazione è in esecuzione, è possibile eseguire queste operazioni in modalità asincrona passando come parametro alla richiesta runAsync  uguale a true. A questo punto, il sistema risponderà con un jobID e il client dovrà seguire l'esecuzione dalla resource jobs ed attendere che lo status sia  COMPLETED per avere la response dell'operazione.
 
Qui vediamo un esempio utilizzando c#:
 
namespace Studioat.ArcGIS.Server.Administrator
{
    using System;
    using Studioat.ArcGIS.Rest;
 
    class Program
    {
        static void Main(string[] args)
        {
            AGSAdmin agsAdmin = new AGSAdmin("myserver", 6080, "admin""secret");
            
            string serviceName = "SampleWorldCities";
            bool result = agsAdmin.StopService(serviceName, ServiceType.MapServer);
            Console.WriteLine("stop service {0}, result: {1}", serviceName, result);
 
            //string serviceName = "SampleWorldCities";
            result = agsAdmin.StartService(serviceName, ServiceType.MapServer);
            Console.WriteLine("stop service {0}, result: {1}", serviceName, result);
 
            string folder = "Demo1";
            result = agsAdmin.CreateServerFolder(folder, "Prova 1");
            Console.WriteLine("create folder {0}, result: {1}", folder, result);
 
            string physicalPath;
            string virtualPath;
            result = agsAdmin.GetServerDirectory("arcgisoutput"out physicalPath, out virtualPath);
            Console.WriteLine("physicalPath {0}, virtualPath {1}, result: {2}", physicalPath, virtualPath, result);
 
            agsAdmin.ListServices();
            Console.Read();
            
 
            agsAdmin.CreateService();
        }
    }
}
 
namespace Studioat.ArcGIS.Rest
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Text;
    using System.Web;
    using ESRI.ArcGIS.SOESupport;
 
    /// <summary>
    /// tipi di servizio arcgis server (mappa la tabella servizio tipo)
    /// </summary>
    public enum ServiceType
    {
        MapServer,
        GeocodeServer,
        SearchServer,
        IndexingLauncher,
        IndexGenerator,
        GeometryServer,
        GeoDataServer,
        GPServer,
        GlobeServer,
        ImageServer
    }
 
    /// <summary>
    /// Load Balancing
    /// </summary>
    public enum LoadBalancing
    { 
       ROUND_ROBIN,
       FAIL_OVER 
    }
 
    /// <summary>
    /// isolation level
    /// </summary>
    public enum IsolationLevel
    {
        LOW,
        HIGH
    }
 
    /// <summary>
    /// administrative API Rest
    /// </summary>
    public class AGSAdmin
    {
        private string username;
        private string password;
        private string urlRestAdmin;
        private string urlRestServer;
 
        /// <summary>
        /// Initializes a new instance of the <see cref="AGSAdmin"/> class.
        /// </summary>
        /// <param name="serverName">server name</param>
        /// <param name="port">port of server</param>
        /// <param name="username">username administrator</param>
        /// <param name="password">password administrator</param>
        public AGSAdmin(string serverName, int port, string username, string password)
        {
            this.username = username;
            this.password = password;
            string url = string.Format("http://{0}:{1}/arcgis", serverName, port.ToString());
            this.urlRestAdmin = url + "/admin";
            this.urlRestServer = url + "/server";
        }
 
        /// <summary>
        /// Prevents a default instance of the <see cref="AGSAdmin"/> class from being created.
        /// </summary>
        private AGSAdmin()
        { 
        }
 
        /// <summary>
        /// Create arcgis server folder
        /// </summary>
        /// <param name="folderName">Folder name</param>
        /// <param name="description">Description of the folder</param>
        /// <returns>True if successfully created</returns>
        public bool CreateServerFolder(string folderName, string description)
        {
            try
            {
                string token = this.GenerateAGSToken();
                string folderUrl = this.urlRestAdmin + "/services/" + folderName + "?f=json&token=" + token;
                string resultExistsFolder = this.GetResult(folderUrl);
                if (!this.HasError(resultExistsFolder))
                {
                    return true// exists
                }
                else
                {
                    string createFolderUrl = this.urlRestAdmin + "/services/createFolder";
                    string postContent = string.Format("folderName={0}&description={1}&f=json&token={2}", folderName, description, token);
                    string result = this.GetResult(createFolderUrl, postContent);
                    return this.HasSuccess(result);
                }
            }
            catch
            {
                return false;
            }
        }
 
        /// <summary>
        /// Get physical Path and virtual Path from directory ags
        /// </summary>
        /// <param name="directory">directory ags</param>
        /// <param name="physicalPath">physical Path</param>
        /// <param name="virtualPath">virtual Path</param>
        /// <returns>True if successfully return path</returns>
        public bool GetServerDirectory(string directory, out string physicalPath, out string virtualPath)
        {
            physicalPath = null;
            virtualPath = null;
            try
            {
                string token = this.GenerateAGSToken();
                string directoryUrl = this.urlRestAdmin + "/system/directories/" + directory + "?f=json&token=" + token;
 
                string result = this.GetResult(directoryUrl);
 
                JsonObject jsonObject = new JsonObject(result);
                if (!jsonObject.Exists("physicalPath") || !jsonObject.TryGetString("physicalPath"out physicalPath))
                {
                    throw new Exception();
                }
 
                jsonObject = new JsonObject(result);
                if (!jsonObject.Exists("virtualPath") || !jsonObject.TryGetString("virtualPath"out virtualPath))
                {
                    throw new Exception();
                }
 
                return true;
            }
            catch
            {
                return false;
            }
        }
 
        /// <summary>
        /// Delete Service
        /// </summary>
        /// <param name="serviceName">Service Name</param>
        /// <param name="serviceType">Server Type</param>
        /// <returns>True if successfully deleted</returns>
        public bool DeleteService(string serviceName, ServiceType serviceType)
        {
            try
            {
                string token = this.GenerateAGSToken();
                string serviceUrl = this.urlRestAdmin + "/services/" + serviceName + "." + Enum.GetName(typeof(ServiceType), serviceType) + "/delete";
                string result = this.GetResult(serviceUrl, "f=json&token=" + token);
                return this.HasSuccess(result);
            }
            catch
            {
                return false;
            }
        }
 
        /// <summary>
        /// Start Service
        /// </summary>
        /// <param name="serviceName">Service Name</param>
        /// <param name="serviceType">Server Type</param>
        /// <returns>True if successfully started</returns>
        public bool StartService(string serviceName, ServiceType serviceType)
        {
            try
            {
                string token = this.GenerateAGSToken();
                string serviceUrl = this.urlRestAdmin + "/services/" + serviceName + "." + Enum.GetName(typeof(ServiceType), serviceType) + "/start";
                string result = this.GetResult(serviceUrl, "f=json&token=" + token);
                return this.HasSuccess(result);
            }
            catch
            {
                return false;
            }
        }
 
        /// <summary>
        /// Stop Service
        /// </summary>
        /// <param name="serviceName">Service Name</param>
        /// <param name="serviceType">Server Type</param>
        /// <returns>True if successfully stopped</returns>
        public bool StopService(string serviceName, ServiceType serviceType)
        {
            try
            {
                string token = this.GenerateAGSToken();
                string serviceUrl = this.urlRestAdmin + "/services/" + serviceName + "." + Enum.GetName(typeof(ServiceType), serviceType) + "/stop";
                string result = this.GetResult(serviceUrl, "f=json&token=" + token);
                return this.HasSuccess(result);
            }
            catch
            {
                return false;
            }
        }
 
        /// <summary>
        /// list of services
        /// </summary>
        public void ListServices()
        {
            this.ListServices(null);
        }
 
        /// <summary>
        /// list of services in folder
        /// </summary>
        /// <param name="folder">name of folder</param>
        public void ListServices(string folder)
        {
            try
            {
                string token = this.GenerateAGSToken();
                string serviceUrl = this.urlRestAdmin + "/services/" + folder;
                string postcontent = "f=json&token=" + token;
                string result = this.GetResult(serviceUrl, postcontent);
 
                JsonObject jsonObject = new JsonObject(result);
                object[] folders = null;
                if (jsonObject.Exists("folders") && jsonObject.TryGetArray("folders"out folders))
                {
                    foreach (string subfolder in folders)
                    {
                        this.ListServices(subfolder);
                    }
                }
 
                object[] services = null;
                if (jsonObject.Exists("services") && jsonObject.TryGetArray("services"out services))
                {
                    IEnumerable<JsonObject> jsonObjectService = services.Cast<JsonObject>();
                    jsonObjectService.ToList().ForEach(jo =>
                        {
                            string serviceName;
                            jo.TryGetString("serviceName"out serviceName);
                            string folderName;
                            jo.TryGetString("folderName"out folderName);
                            Console.WriteLine(folderName + "/" + serviceName);
                        });
                }
            }
            catch
            {
                throw;
            }
        }
 
        /// <summary>
        /// create service type MapServer
        /// </summary>
        /// <returns>>True if successfully created</returns>
        public bool CreateService()
        {
            try
            {
                string token = this.GenerateAGSToken();
                string serviceUrl = this.urlRestAdmin + "/services/createService";
 
                JsonObject jsonObject = new JsonObject();
                jsonObject.AddString("serviceName""Test");
                jsonObject.AddString("type"Enum.GetName(typeof(ServiceType), ServiceType.MapServer));
                jsonObject.AddString("description""This is an example");
                jsonObject.AddString("capabilities""Map,Query,Data");
                jsonObject.AddString("clusterName""default");
                jsonObject.AddLong("minInstancesPerNode", 1);
                jsonObject.AddLong("maxInstancesPerNode", 2);
                jsonObject.AddLong("maxWaitTime", 60); 
                jsonObject.AddLong("maxStartupTime", 300); 
                jsonObject.AddLong("maxIdleTime", 1800);
                jsonObject.AddLong("maxUsageTime", 600);
                jsonObject.AddLong("recycleInterval", 24);
                jsonObject.AddString("loadBalancing"Enum.GetName(typeof(LoadBalancing), LoadBalancing.ROUND_ROBIN));
                jsonObject.AddString("isolationLevel"Enum.GetName(typeof(IsolationLevel), IsolationLevel.HIGH));
 
                JsonObject jsonObjectProperties = new JsonObject();
 
                // see for a list complete http://resources.arcgis.com/en/help/server-admin-api/serviceTypes.html
                jsonObjectProperties.AddLong("maxBufferCount", 100); // optional 100
                jsonObjectProperties.AddString("virtualCacheDir"this.urlRestServer + "/arcgiscache"); // optional
                jsonObjectProperties.AddLong("maxImageHeight", 2048); // optional 2048
                jsonObjectProperties.AddLong("maxRecordCount", 1000); // optional 500
 
                // Starting at ArcGIS 10.1, Map Server Definition ( .msd ) files have been
                // replaced with Service Definition Draft ( .sddraft ) and Service Definition ( .sd ) files. 
                // In the case of a map service, you must specify a map service definition (MSD) file in your JSON. 
                // This file synthesizes information from your ArcMap document (MXD) in a format that can be understood and 
                // drawn by ArcGIS Server. You must use the arcpy.mapping module to analyze your map and create the MSD before 
                // you can go ahead with creating the service. This part requires a machine licensed for ArcGIS for Desktop. 
                // Other service types do not require you to use arcpy.mapping or create an MSD.
                jsonObjectProperties.AddString("filePath"@"C:\AvGis\Test\mappa\UTM_ReteFognaria.msd"); // required
                
                jsonObjectProperties.AddLong("maxImageWidth", 2048); // optional 2048
                jsonObjectProperties.AddBoolean("cacheOnDemand"false); // optional false
                jsonObjectProperties.AddString("virtualOutputDir"this.urlRestServer + "/arcgisoutput");
                jsonObjectProperties.AddString("outputDir"@"C:\arcgisserver\directories\arcgisoutput"); // required
                jsonObjectProperties.AddString("supportedImageReturnTypes""MIME+URL"); // optional MIME+URL
                jsonObjectProperties.AddBoolean("isCached"false); // optional false
                jsonObjectProperties.AddBoolean("ignoreCache"false); // optional false 
                jsonObjectProperties.AddBoolean("clientCachingAllowed"false); // optional true 
                jsonObjectProperties.AddString("cacheDir"@"C:\arcgisserver\directories\arcgiscache"); // optional
 
                jsonObject.AddJsonObject("properties", jsonObjectProperties);
 
                string result = this.GetResult(serviceUrl, "service=" + HttpUtility.UrlEncode(jsonObject.ToJson()) + "&f=json&token=" + token);
                return this.HasSuccess(result);
            }
            catch
            {
                return false;
            }
        }
 
        /// <summary>
        /// check is status is equal success
        /// </summary>
        /// <param name="result">result of request</param>
        /// <returns>True if status is equal success</returns>
        private bool HasSuccess(string result)
        {
            JsonObject jsonObject = new JsonObject(result);
            string status = null;
            if (!jsonObject.Exists("status") || !jsonObject.TryGetString("status"out status))
            {
                return false;
            }
 
            return status == "success";
        }
 
        /// <summary>
        /// check is status is equal error
        /// </summary>
        /// <param name="result">result of request</param>
        /// <returns>True if status is equal error</returns>
        private bool HasError(string result)
        {
            JsonObject jsonObject = new JsonObject(result);
            string status = null;
            if (!jsonObject.Exists("status") || !jsonObject.TryGetString("status"out status))
            {
                return false;
            }
 
            return status == "error";
        }
 
        /// <summary>
        /// Get request rest
        /// </summary>
        /// <param name="url">url of request</param>
        /// <returns>return response</returns>
        private string GetResult(string url)
        {
            try
            {
                WebRequest request = WebRequest.Create(url);
                WebResponse response = request.GetResponse();
                using (Stream responseStream = response.GetResponseStream())
                {
                    using (StreamReader reader = new StreamReader(responseStream))
                    {
                        return reader.ReadToEnd();
                    }
                }
            }
            catch
            {
                throw;
            }
        }
 
        /// <summary>
        /// Post request rest
        /// </summary>
        /// <param name="url">url of request</param>
        /// <param name="postContent">content of post</param>
        /// <returns>return response</returns>
        private string GetResult(string url, string postContent)
        {
            try
            {
                WebRequest request = WebRequest.Create(url);
                byte[] content = Encoding.UTF8.GetBytes(postContent);
                request.ContentLength = content.Length;
                request.ContentType = "application/x-www-form-urlencoded";
                request.Method = WebRequestMethods.Http.Post;
                using (Stream requestStream = request.GetRequestStream())
                {
                    requestStream.Write(content, 0, content.Length);
                    requestStream.Close();
                    WebResponse response = request.GetResponse();
                    using (Stream responseStream = response.GetResponseStream())
                    {
                        using (StreamReader reader = new StreamReader(responseStream))
                        {
                            return reader.ReadToEnd();
                        }
                    }
                }
            }
            catch
            {
                throw;
            }
        }
 
        /// <summary>
        /// Generate a token
        /// </summary>
        /// <returns>A token that has default expiration time</returns>
        private string GenerateAGSToken()
        {
            try
            {
                string urlGenerateToken = string.Format("{0}/generateToken"this.urlRestAdmin);
                string credential = string.Format("username={0}&password={1}&client=requestip&expiration=&f=json"this.username, this.password);
                string result = this.GetResult(urlGenerateToken, credential);
 
                JsonObject jsonObject = new JsonObject(result);
                string token = null;
                if (!jsonObject.Exists("token") || !jsonObject.TryGetString("token"out token))
                {
                    throw new Exception("Token not found!");
                }
 
                return token;
            }
            catch
            {
                return string.Empty;
            }
        }
    }
}