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

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ì 12 dicembre 2011

Fidarsi è bene ma con checksum è meglio!

Con la versione 10 il software ESRI è distribuito tramite la CDN di Akamai. Sebbene sia ancora disponibile la possibilità di richiedere il software su supporto DVD, è innegabile il vantaggio di poter scaricare il software direttamente sul proprio computer. E' altrettanto vero che l'operazione di scaricamento potrebbe durare anche molte ore se non si ha a disposizione una connessione veloce; qui infatti ci viene incontro il Download Manager che ci permette di mettere in pausa o fermare il download e riprenderlo più tardi, scaricare più file simultaneamente e recuperare il download corrente su una perdita di connessione o da crash di sistema.
Se tuttavia non si vuole utilizzare il Download Manager (ad esempio non accettiamo di installarlo), è possibile scaricare direttamente l'immagine del software. Così facendo è consigliabile, una volta scaricato il file, controllarne la checksum.


La checksum, cioè somma di controllo, viene utilizzata per verificare l'integrità di un dato o di un messaggio che potrebbe subire alterazioni. Una somma di controllo potrebbe sommare tutti i bit del dato e verificare se la somma dei bit trasmessi corrisponda a quella calcolata prima del trasferimento.  Se due o più input hanno la stessa somma, quindi la stessa checksum, si parla di collisione. Minore è la probabilità di collisione, migliore è la qualità dell'algoritmo di controllo per verificare l'integrità del dato in sicurezza. Pertanto è importante utilizzare algoritmi con strutture matematiche complesse per impedire a malintenzionati di calcolare checksum che determinino collisioni. Quando scarichiamo file da internet, è buona pratica verificare  la checksum per poter assicurare che i dati scaricati siano corretti e per garantirne l'autenticità. In genere per la checksum (digest nel caso specifico) vengono utilizzati algoritmi di hash crittografati, come MD5 e SHA-1 che comunque non sono esenti da collisioni.
Qui possiamo vedere una panoramica sulle funzioni hash e degli attacchi ai quali potrebbero essere soggetti: collisione hash, attacco del compleanno, attacco preimmagine, tabella arcobaleno, attacco del canale laterale e attacco a forza bruta.

Con c# possiamo facilmente calcolare la checksum (MD5) di un file:

            using (FileStream file = new FileStream(txtFile.Text, FileMode.Open))
            {
                    byte[] retVal;    
                    using (MD5 md5 = new MD5CryptoServiceProvider()) 
                    {
                             retVal = md5.ComputeHash(file);
                             txtMD5.Text= BitConverter.ToString(retVal).Replace("-"""); // hex string
                    }     
            }

Qui potete scaricare la soluzione.
Controllo di un file scaricato con verifica del checksum (MD5) dichiarato:

Esito positivo


Esito negativo:

mercoledì 30 novembre 2011

Deserializzazione JSON: polimorfismo

Quando si ha a che fare con il formato JSON, ad esempio quando richiamiamo i servizi rest SOE ESRI, .NET ci mette a disposizione DataContractJsonSerializer che ci consente di serializzare direttamente oggetti di tipo .NET in dati JSON e di deserializzare tali dati in istanze di tipi .NET. Tuttavia, uno dei problemi sorge quando il servizio REST ci restituisce istanze di oggetti diversi o derivati. Javascript è un linguaggio con tipizzazione debole e l'identità del tipo non rappresenta solitamente un problema. Il problema si presenta quando si utilizza JSON per le comunicazioni tra un sistema fortemente tipizzato (.NET) e uno con tipizzazione debole (Javascript): è utile allora mantenere l'identità del tipo.

Prendiamo questo esempio: i tipi con nomi di contratto dati "Envelope" e "Point" derivano da un tipo con nome di contratto dati "Geometry". Se "Point" viene inviato da .NET a Javascript e viene quindi restituito ad un metodo .NET che prevede "Geometry", è utile per .NET sapere che l'oggetto in questione è stato originariamente un "Point"; in caso contrario, tutte le informazioni specifiche del tipo derivato (ad esempio i membri dati "x" e "y" su "Point") potrebbero andare perdute.
Per mantenere l'identità del tipo, durante la serializzazione di tipi complessi in JSON, può essere aggiunto un suggerimento; il deserializzatore riconosce tale suggerimento e agisce di conseguenza. Il suggerimento relativo ai tipi è rappresentato da una coppia chiave/valore JSON dove il nome della chiave è "__type" (due caratteri di sottolineatura seguiti dalla parola "type"). Il valore è rappresentato invece da una stringa JSON con forma 'DataContractName:DataContractNamespace' (tutto il testo che precede i due punti rappresenta il nome).

Per ridurre le dimensioni dei messaggi JSON, il prefisso dello spazio dei nomi del contratto dati predefinito viene sostituito dal carattere "#". Per rendere reversibile questa sostituzione, viene utilizzata una regola di escape: se il primo carattere nello spazio dei nomi è "#" o "\", verrà aggiunto un carattere "\"). Pertanto, se "Point" è un tipo nello spazio dei nomi .NET "Studioat.Geometry", il relativo spazio dei nomi del contratto dati sarà: http://schemas.datacontract.org/2004/07/Studioat.Geometry. Le forme e la rappresentazione JSON appaiono nel modo seguente.

//prima forma
string a1 = "{\"__type\":\"Point:http:\\/\\/schemas.datacontract.org\\/2004\\/07\\/Studioat.Geometry\",\"X\":10.256,\"Y\":80.729}";
//seconda forma
string a2 = "{\"__type\":\"Point:#Studioat.Geometry\",\"X\":10.256,\"Y\":80.729}";

Sia i nomi troncati (#Studioat.Geometry) che quelli completi vengono riconosciuti durante la deserializzazione. Inoltre il suggerimento relativo ai tipi deve essere visualizzato all'inizio nella rappresentazione JSON. Questo è il solo caso in cui l'ordine delle coppie chiave/valore è importante nell'elaborazione di JSON.
Di seguito viene riportato un esempio di modalità non valida per specificare un suggerimento relativo ai tipi.
string a2 = "{\"X\":10.256,\"Y\":80.729,\"__type\":\"Point:#Studioat.Geometry\"}";



using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
 
namespace Studioat.ScratchCode
{
 class Program
 {
  static void Main(string[] args)
  {
     string a1 = "{\"__type\":\"Point:http:\\/\\/schemas.datacontract.org\\/2004\\/07\\/Studioat.Geometry\",\"X\":10.256,\"Y\":80.729}";
     string a2 = "{\"__type\":\"Point:#Studioat.Geometry\",\"X\":10.256,\"Y\":80.729}";
 
     string b1 = "{\"__type\":\"Envelope:http:\\/\\/schemas.datacontract.org\\/2004\\/07\\/Studioat.Geometry\",\"XMin\":10.256,\"YMin\":80.729,\"XMax\":21.956,\"YMax\":34.712}";
     string b2 = "{\"__type\":\"Envelope:#Studioat.Geometry\",\"XMin\":10.256,\"YMin\":80.729,\"XMax\":21.956,\"YMax\":34.712}";
 
     byte[] rawstring = System.Text.Encoding.Unicode.GetBytes(a1);
     MemoryStream stream = new MemoryStream(rawstring);
 
     DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Geometry.Geometry), new Type[] { typeof(Geometry.Envelope), typeof(Geometry.Point) });
 
     Geometry.Geometry o = serializer.ReadObject(stream) as Geometry.Geometry;
     if (o is Geometry.Point)
     {
      Geometry.Point j = o as Geometry.Point;
      Console.WriteLine("Point x:{0} y:{1}", j.X, j.Y);
     }
     else if (o is Geometry.Envelope)
     {
      Geometry.Envelope j = o as Geometry.Envelope;
      Console.WriteLine("Envelope xmin:{0} ymin:{1} xmax:{2} ymax:{3}", j.XMin, j.YMin, j.XMax, j.YMax);
     }
     Console.Read();
  }
 }
}
 
namespace Studioat.Geometry
{
 [DataContract]
 public abstract class Geometry : ICloneable
 {
    
  protected Geometry()
  {
  }
 
  public abstract object Clone();
 }
 
 [DataContract]
 public class Point : Geometry
 {
 
  private double x;
  private double y;
 
 
  public Point()
  {
   this.x = double.NaN;
   this.y = double.NaN;
 
  }
 
  public Point(double x, double y)
  {
   this.x = x;
   this.y = y;
  }
 
  public override object Clone()
  {
   Studioat.Geometry.Point point = base.MemberwiseClone() as Studioat.Geometry.Point;
   return point;
  }
 
  [DataMember]
  public double X
  {
   get;
   set;
  }
 
  [DataMember]
  public double Y
  {
   get;
   set;
  }
 }
 
 [DataContract]
 public class Envelope : Geometry
 {
 
  private double maxx;
  private double maxy;
  private double minx;
  private double miny;
 
  public Envelope()
   : this(double.NaN, double.NaN, double.NaN, double.NaN)
  {
  }
 
  public Envelope(Point minPoint, Point maxPoint)
   : this(minPoint.X, minPoint.Y, maxPoint.X, maxPoint.Y)
  {   
  }
 
  public Envelope(double minx, double miny, double maxx, double maxy)
  {
   this.minx = minx;
   this.miny = miny;
   this.maxx = maxx;
   this.maxy = maxy;
  }
 
  public override object Clone()
  {
   Envelope envelope = base.MemberwiseClone() as Envelope;
   return envelope;
  }
 
  [DataMember]
  public double XMax
  {
   get;
   set;
  }
 
  [DataMember]
  public double XMin
  {
   get;
   set;
  }
 
  [DataMember]
  public double YMax
  {
   get;
   set;
  }
 
  [DataMember]
  public double YMin
  {
   get;
   set;
  }
 }
}

sabato 29 ottobre 2011

... a qualcuno piace versionato!

Il versioning è il meccanismo che abilita più utenti all'editing contemporaneo in geodatabase ArcSDE.
Esso utilizza un modello di concorrenza ottimistica di lock dei dati che, in parole povere, significa che non sono applicati blocchi sulle feature e sulle row durante lunge transazioni.
Il versioning registra e gestisce gli stati delle singole feature e row così come sono modificate mentre preserva l'integrità nel database. Esso è la base per l'accesso multiutente e l'editing simultaneo nei geodatabase Enterprise e non crea copie di dati. Una versione si riferisce ad uno specifico stato del geodatabase. Esso contiene tutti i dataset nel geodatabase ed evolve nel tempo. Gli utenti accedono ai dati attraverso una versione. Dietro le quinte, vengono utilizzate delle query nell'RDBMS  per visualizzare o lavorare con uno stato che si riferisce ad un specifico segmento di tempo o per vedere le attuali modifiche del singolo utente. Il versioning permette di gestire complessi workflow di editing che normalmente sono richiesti dai sistemi GIS di fascia Enterprise. Occorre però sottolineare che la maggior parte delle transazioni nei database avvengono nell'ordine dei secondi. Lo stato è l'unità dei cambiamenti (ad esempio una modifica) che è fatta sui dati di un geodatabase;  se un cambio è stato fatto esso rappresenta uno snapshot del database.
E' importante però capire perchè le versioni sono importanti: nei geodatabase di tipo enterprise in molti casi molti utenti necessitano di modificare gli stessi dati contemporaneamente. La natura delle relazioni spaziali e della connettività che definisce i dati geografici richiede che le sessioni di editing per dati geospaziali sia dell'ordine delle ore, giorni o settimane. Queste sessioni possono richiedere lunghe transazioni nel DBMS. Inoltre l'utente richiede di poter anche annullare o rifare i cambi apportati, di sviluppare proposte senza intaccare i geodatabase pubblicati e i meccanismi per gestire i cambiamenti nel tempo come i dati e il geodatabase.

Tutti i geodatabase ArcSDE hanno una versione di default denominata DEFAULT che è di proprietà dell'amministratore ArcSDE. Questa versione esiste sempre e non può essere cancellata. Essa è la radice di tutte le versioni. Normalmente è la versione pubblicata del geodatabase che rappresenta la vista corrente per l'utente finale del geodatabase. La versione di DEFAULT è tipicamente mantenuta e aggiornata nel tempo incorporando in sè le modifiche dalle altre versioni. Come le altre versioni può essere modifica direttamente.
Un geodatabase ArcSDE può avere molte versioni. Una nuova versione (versione figlio) è creata da una versione esistente (versione padre). Quando una versione viene creata è identica alla versione padre. Comunque nel tempo padre e figlio possono divergere a causa delle modifiche effettuate in ogni versione.

Vediamo come creare due versioni figlio della versione di DEFAULT


Innanzitutto occorre versionare i dataset abilitati alle modifiche.


Quando registriamo il nostro dataset nel geodatabase ci viene chiesto se desideramo spostare le modifiche nella tabella base: questa possibilità ci permette di lavorare con l'editing versionato ma supporta anche alcune funzionalità dell'editing non versionato. Quest'ultimo abilita gli utenti ad effettuare modifiche direttamente nelle tabelle base dei geodatabase analogamente ai database classici. Se si seleziona questa funzionalità, essa funziona come l'editing versionato tranne quando si effettuano modifiche alla versione di DEFAULT. Quando si modifica la versione di DEFAULT le modifiche vengono apportate direttamente nella tabella base, anche quando le versioni  sono riconciliate e postate (vedremo più avanti questi concetti) alla versione di DEFAULT. Chiaramente sia il modello versionato con spostamento delle modifiche nella tabella base che il modello non versionato presentano delle limitazioni rispetto al modello versionato: ad esempio non è supportata la replica o non tutti i tipi di oggetti possono essere modificati.


Quando modifichiamo il dataset versionato nell'ambiente di editing ogni versione ci sembra abbia una propria copia dei dati. Infatti quando visualizzamo i dati di una versione piuttosto che un'altra il dataset ci può apparire differente. Il dato invece è memorizzato una sola volta del DBMS e, come abbiamo detto precedentemente, dietro le quinte ArcGIS lascia il dataset nel suo originale stato durante l'editing. Le modifiche vengono memorizzate in tabelle associate al dataset dette 'delta table' o tabella A (aggiungi) e tabella D (cancella). Ogni dataset avrà associato una coppia di queste tabelle quando sono registrate come versionate in ArcCatalog.


Ogni versione ha un proprietario, una descrizione, una versione padre, associato uno stato del database e un livello di accesso. I livelli di accesso sono:
  • Private: solo il proprietario può vedere e effettuare modifiche;
  • Protected: tutti gli utenti possono vedere ma soltanto il proprietario può effettuare modifiche;
  • Public: tutti gli utenti possono vedere ed effetture modifiche.
 Il livello di accesso predefinito per la versione di DEFAULT è public. Normalmente è raccomandato di impostare il livello a protected per assicurare che i dati nel geodatabase ArcSDE non siano accidentalmente persi o corrotti. Questo significa che solo l'amministratore ArcSDE può modificare o postare le modifiche alla versione DEFAULT.

Il versioning, come detto, si presta molto bene a gestire complessi workflow così come progetti GIS a fasi (ogni fase è rappresentata da una versione) e modelli a scenari what-if senza intaccare i dataset originali. Qui potete vedere degli esempi.
Fornisce un framework per gestire sicurezza e assicurare qualità nei dati in editing e supporta archiving e repliche.
Ogni organizzazione può adattare o personalizzare il proprio workflow, visti i moltissimi scenari che il versioning permette di fare.
Il più semplice workflow è avere utenti concorrenti direttamente nella versione di DEFAULT.
Un'altra opzione è creare una versione separata per ogni utente editor. Spesso per proteggere la versione di default si crea una versione di qualità del dato dalla versione di DEFAULT. Questa versione dovrebbe essere gestita da un utente che controlla i dati prima di propagarli alla versione di DEFAULT. Ogni workflow ha pro e contro, pertanto è importante che ognuno utilizzi la miglior strategia atta a soddisfare i propri requisiti nel workflow di business.

Nel nostro esempio creiamo un albero a due livelli (DEFAULT e due figli: VersioneA e VersioneB):

Dal Version Manager selezioniamo la versione padre (in questo caso la versione DEFAULT) e con il tasto destro creiamo la versione versioneA e la versione versioneB.


Visualizzazione del Version Manager dopo che abbiamo aggiunto le due versioni.


Per creare la versione lato programmazione utilizzare l'interfaccia IVersion2

Qui vediamo un esempio di creazione di due versioni figlio per la versione versioneQA.
public void CreateChildExample(IWorkspace workspace)
{
  IVersionedWorkspace versionedWorkspace = (IVersionedWorkspace)workspace;
  IVersion2 qaVersion = (IVersion2)versionedWorkspace.FindVersion("versioneQA");
  IVersion2 editorAVersion = (IVersion2)qaVersion.CreateChild("VersioneB",qaVersion);
  IVersion2 editorBVersion = (IVersion2)qaVersion.CreateChild("VersioneB", qaVersion);

}


Una versione come detto si riferisce ad uno specifico stato del database, un'unità di modifica che avviene nel database. Ogni operazione di modifica eseguita nel geodatabase crea un nuovo stato del database. Un'operazione di modifica è un qualsiasi task o insieme di task (aggiunte, eliminazioni o modifiche) sulle feature o sulle row.  I valori di ID dello stato si applicano a qualsiasi modifica fatta nel geodatabase. Inizialmente la versione di DEFAULT punta allo stato 0. Con le modifiche al geodatabase l'ID dello stato inizierà ad incrementarsi. Normalmente l'ID dello stato si incrementa di un'unità per ogni operazione di editing. Comunque, ci sono delle eccezioni: dove l'ID dello stato si incrementa con un valore più grande dell'unità, così ad esempio durante un'operazione di riconcilia.



Facciamo ad esempio 4 modifiche a due feature class accedendo alla versione di default.

Prima delle modifiche:



Dopo le modifiche (eliminato un poligono, aggiunti due punti e modificato un attributo di un poligono):
Ora se andiamo a vedere la tabella degli stati:

Come possiamo notare lo state_id ora è 4 (avendo fatto 4 modifiche). I singoli stati dall'1 al 3 sono stati eliminati.

Nella tabella delta D (cancellazione) della feature poligonale possiamo notare due record perchè uno si riferisce all'eliminazione di una feature (con ObjectID 8) con riferimento allo stato Id 4 mentre il primo record è stato aggiunto perchè abbiamo fatto una modifica al poligono con ObjectID 3. La modifica aggiunge un record nella tabella delta A con i nuovi valori e contemporaneamente uno nella tabella D per eliminare il vecchio record.

Ora vediamo uno scenario a due livelli: creiamo due versioni (versioneA e versioneB) dalla versione di DEFAULT. In questo scenario l'ID dello stato crescerà con il numero delle modifiche effettuate nelle sessioni di editing nelle varie versioni. Inizialmente le versioni A e B partiranno con lo stesso ID di stato (stato 0) perchè sono state derivate dalla DEFAULT. Ora l'utente A che opera sulla versione A aggiunge una nuova feature e così l'ID di stato si incrementa di 1. Quando l'utente B inizia una sessione di editing, un nuovo ramo separato è creato dalla DEFAULT per registrare le modifiche. In questo scenario abbiamo ad esempio le seguenti operazioni:
  • Utente A aggiunge una feature;
  • utente B cancella una feature;
  • utente A fonde due feature in una singola;
  • utente B aggiunge una feature.
L'ordine di queste operazioni è registrato con il corrispondente ID di stato che rappresenta ogni cambio fatto al geodatabase.

Gli ID di stato nel geodatabase possono essere concepiti come una struttura ad albero. Questa struttura, chiamata diagramma ad albero degli stati, è una mappa logica degli stati in un geodatabase.
Man mano che si modifica nel tempo il geodatabase, una traccia (lineage) degli stati è mantenuta così da identificare  tutte le modifiche apportate in una versione. Per determinare la lineage per una specifica versione, si seleziona il percorso più diretto nell'albero degli stati allo stato 0.

ID dello stato a 0 (dalla versione DEFAULT)



Sequenza di editing in multiutenza.

Utente A in versione A: aggiunta di un poligono


Utente B in versione B: cancellazione poligono


Utente A in versione A: fusione di 2 poligoni


Utente B in versione B: aggiunta di un poligono


Con il GDBT possiamo visualizzare l'albero degli stati per questa sequenza di editing


Qui vediamo le due lineage delle due versioni:

Le relazioni padre-figlio possono essere derivate dalle lineage dello stato. Entrambe le versioni A e B fanno riferimento al loro nuovo stato 3 e 4 a differenza della DEFAULT e la loro lineage contiene l'ID dello stato al quale riferisce la DEFAULT ovverosia lo stato 0. La lineage della versione A è 3,1,0 mentre quella della versione B è 4,2,0 mentre la DEFAULT punta all'ID  dello stato 0.
Ciò significa che la DEFAULT è una versione antenata alle versioni A e B. Mentre la versione DEFAULT è la versione padre per le versioni A e B.

Tutte le versioni che esistono nel geodatabase ArcSDE possono essere viste nel Version Manager ad esclusione di quelle marcate private che sono solo visibili dai loro rispettivi proprietari.
In questa finestra di dialogo possono essere create e cancellate. Come precedentemente detto, è importate implementare la strategia di workflow che si adatti meglio al proprio business poichè la complessità della gestione delle versioni cresce in base al numero di versioni create.

Le modifiche apportate in una versione rimangono isolate in quella versione fino a quando il proprietario non decide di fonderle con un'altra versione. L'eccezione riguarda il cambio di schema di un dataset (ad esempio aggiunta di un campo in una tabella): questo cambio sarà applicato a tutte le versioni.

Il compito di fondere in modo appropriato le varie versioni si ottiene in ArcGIS grazie a due operazioni: il reconciling e il posting. Queste due operazioni in genere si eseguono l'una dopo l'altra (per esempio il reconciling seguito dal posting) per combinare le modifiche da una versione all'altra.

Reconcile
L'operazione di reconciling è il primo passo per fondere le modifiche tra due versioni. In questo processo le modifiche di una versione antenata (chiamata versione target) sono portate nella versione che è in modifica in una sessione di editing di ArcMap (chiamata versione edit). Una versione target può essere una qualsiasi versione nella lineage della versione che si sta modificando. Ritornando all'esempio precedente, sia la versione A che la versione B possono riconciliare con la DEFAULT perchè entrambe hanno nella loro lineage lo stato con ID 0 che è puntato dalla DEFAULT. Il processo di reconciling fonde le modifiche dalla versione target alla versione edit.

Per eseguire un'operazione di reconcile ci può essere solamente un utente a modificare la versione di edit. Dal momento che una versione mette tutti gli oggetti versionati nel geodatabase, qualsiasi feature modificata nella versione target verrà inserita nella versione di edit. Dal momento che la maggioranza di queste feature non si troverà in conflitto, si inseriranno senza problemi nella versione di edit. Ad esempio: se un poligono fosse aggiunto alla versione target dopo il processo di reconcile, il poligono apparirebbe anche nella versione di edit. L'utente potrebbe allora decidere se salvare o meno le modifiche nella versione di edit.
Ad un livello concettuale, un processo di reconcile comporta l'unione di modifiche relative ad un ramo dell'albero degli stati con un altro ramo.

Riprendiamo l'esempio precedente: dalla versione B eliminiamo un poligono.


Riconciliamo e postiamo con la versione antenata (DEFAULT):


Ora passiamo alla versione A: se riconciliamo con la versione DEFAULT scatta il conflitto perchè lo stesso poligono eliminato nella versione B e propagato alla versione DEFAULT esiste nella versione A.


Nella finestra di dialog Conflits possiamo gestire i conflitti alfanumeri e geografici.



Il processo di reconcile può essere implicito od esplicito.

Implicito: un'operazione di reconcile è implicita quando ci sono diversi utenti che modificano la stessa versione. Ogni utente mantiene il suo ramo per la durata della sessione di editing. Quando un utente cerca di salvare le modifiche della sua sessione avviene un'operazione di reconcile per mettere le modifiche al ramo dell'utente nel ramo al quale si riferisce la versione. Con più utenti in una versione, ogni volta che si salvano le modifiche viene eseguito il processo di reconcile. Non si può scegliere quando esso avviene: succede sempre quando si salvano le modifiche.

Esplicito: Quando si esegue un'operazione di reconcile tra diverse versioni, un utente sceglie quando eseguire il processo di reconcile. Questo è diverso dal processo implicito, che avviene quando si salvano le modifiche. Indipendentemente dal tipo di reconcile, il meccanismo è lo stesso.

La differenza tra processo implicito ed esplicito è quando avviene il processo di reconcile e come vengono specificate le opzioni di individuazione del conflitto.

Possibili conflitti durante il reconciling

In alcuni casi, una piccola percentuale di feature ed oggetti può essere in conflitto quando si confrontano la versione target e la versione edit. I conflitti possono scattare in due scenari di editing:
  • quando la stessa feature è aggiornata in entrambe le versioni (target e edit);
  • quando la stessa feature è aggiornata in una versiona e cancellata nell'altra.
In pratica, i conflitti non scatteranno frequentemente per la maggior parte dei processi di reconcile, perchè in molti workflow di business, le versioni tipicamente rappresentano differenti progetti con aree geografiche distinte. Pertanto la probabilità che avvengano dei conflitti è rara. I conflitti di solito sorgono quando gli utenti modificano feature in zone di confine.

Quando si eseguono operazioni di reconcile, ArcGIS trova i conflitti in due modi: per object ID o per attributo.
Conflitti per object ID significa che si individua una feature in conflitto quando qualsiasi parte di essa (geometria o attributi) è stata modificata sia nella versione di target che in quella di edit.
Conflitto per attributo significa che si individua una feature in conflitto solo quando lo stesso attributo è stato modificato sia nella versione di target che in quello di edit. 

Le regole delle risoluzioni dei conflitti si possono impostare automaticamente sia a favore della versione di target che di edit. C'è anche l'opzione di far risolvere manualmente i conflitti individuati all'utente della versione di edit rivedendo ogni conflitto con la finestra di dialogo Conflicts Resolution in ArcMap.


Si può esaminare dettagliatamente ogni conflitto e l'utente decide se applicare la modifica della versione target, mantenere la modifica della versione di edit o riconvertire la feature a com'era all'inizio della sessione di edit. Dopo la risoluzione di tutti i conflitti (se ce ne sono), il processo di reconcile può considerarsi completato e l'utente può salvare le modifiche e continuare a modificare o procedere con un'operazione di post.

Post

Il post è il secondo passo quando si uniscono le modifiche di due versioni. Questo processo deve sempre seguire un'operazione di riconciliazione. Il processo di post sincronizza la corrente versione di edit con la versione di target. Tutte le modifiche fatte nella versione di edit sono salvate nella versione target facendo entrambe le versioni identiche.
A differenza del processo di reconcile, il posting non può essere annullato una volta che è stato eseguito perchè le modifiche sono applicate ad una versione al di fuori di una sessione di editing.
A questo punto, l'utente della versione di edit ha l'opzione di continuare a fare modifiche in una sessione di editing e poi eseguire un'altro processo di reconcile e di post per sincronizzare le due versioni o semplicemente salvare le modifiche e fermare la sessione di editing nella versione di edit.

Qui potete vedere come riconciliare, ascoltare gli eventi delle versioni, trovare le differenze tra versioni e risolvere i conflitti, tutto via codice tramite gli  ArcObjects.

Compress
Nel tempo un'attività di editing in un geodatabase ArcSDE Enterprise accumula centinaia di migliaia di ID di stato (rappresentanti le modifiche memorizzate nella tabella delta). Questo può impattare negativamente nelle prestazioni. Periodicamente l'amministratore ArcSDE deve comprimere il geodatabase ArcSDE rimuovendo gli stati non più riferiti da una versione.  Un'operazione di compress può ridurre la profondità dell'albero degli stati e aiuta nelle prestazioni del sistema.
La compressione non rimuove dati che sono accessibili attraverso la lineage della versione, ma solamente i dati non utilizzati. Un'operazione di compress è implementata come una serie di transazioni che rimuovono e rinominano gli stati, il tutto all'interno di una transazione nel database per assicurare che il DBMS possa ripristinare il geodatabase ad uno stato consistente.

Albero degli stati prima di un'operazione di compress.




Albero degli stati dopo un'operazione di compress.

martedì 20 settembre 2011

Non ti scordar mai del result map service...

I servizi di geoprocessing possono essere creati pubblicando due differenti risorse ArcGIS Desktop: un toolbox o un documento di ArcMap contenente layer di tool.

  • quando pubblichiamo un toolbox, tutti i tools nel toolbox diventano task di geoprocessing nel servizio di geoprocessing;
  • quando pubblichiamo un documento di mappa, tutti i layer di tool nel documento di mappa diventano task di geoprocessing del servizio di geoprocessing di ArcGIS Server. I layer di tool sono creati trascinando i tool nella tabella dei contenuti (TOC) di ArcMap;
  • quando pubblichiamo un documento di mappa contenente layer di tool, possiamo anche specificare se desideriamo che il documento di mappa diventi un servizio di mappa da utilizzare per generare l'output dei task. Un servizio di mappa che genera gli output dei task è chiamato result map service. 
Ma vediamo nel dettaglio i result map service.

I servizi di geoprocessing possono avere un result map service per creare un'immagine della mappa come risultato dei task. Le immagini sono facili da gestire, trasferire e visualizzare su web.
Le immagini della mappa sono create dal servizio di mappa di ArcGIS Server e sono il risultato della pubblicazione del documento mappa (.mxd). Ma perchè creare un'immagine come risultato del task?
I motivi possono essere molteplici:
  • il risultato del task potrebbe essere potenzialmente un dataset di grosse dimensioni;
  • il tipo di dato restituito in output potrebbe non essere supportato dal client (esempio raster in ArcGIS Explorer). In questo caso utilizziamo il result map service per visualizzare l'output;
  • desideriamo proteggere i dati del dataset e consentiamo solo la visualizzazione dei risultati;
  • abbiamo una complessa cartografia da visualizzare ed il client non ha le capabilities per poterlo fare;
  • abbiamo la necessità di visualizzare immagini in report (ad esempio in report services);
  • per generare tematizzazioni lato server.
Quando utilizziamo i result map service è importante sottolineare che avremo due servizi: il servizio di geoprocessing e il result map service. Questi due servizi sono eseguiti indipendentemente l'uno dall'altro. Quando si invoca il task, ArcGIS Server esegue prima il task di geoprocessing e poi esegue il result map service per generare l'output del servizio di geoprocessing utilizzando la mappa del result map service. Questo significa che l'output dei task nel servizio di geoprocessing deve essere un dataset su disco e non un dataset in memoria perchè il result map service deve poter accedere al dato elaborato (GPRasterDataLayer o GPFeatureRecordSetLayer).

Uno degli errori più comuni è quello però di utilizzare il result map service come servizio di base della mappa dell'applicazione. L'utente clicca ad esempio su una particella, il nostro task ci restituisce la particella selezionata con un colore in funzione, ad esempio di un attributo. A questo punto, perchè non utilizzare il result map service come servizio di base della mappa? Lo abbiamo già a disposizione senza dover creare un altro servizio di mappa.
I motivi sono due:
  • quando un result map service è aggiunto ad una applicazione, tutti i layer del servizio sono disponibili alla visualizzazione. Questi layer includono i layer di tool di geoprocessing utilizzati per la generazione dell'output, layer che possono contenere dati sensibili o layer utilizzati dal servizio di geoprocessing ma che non ha senso visualizzare per l'utente (tipo i  layer di tool);
  • le mappe di base sono multiscala e multirisoluzione (costruite per mostrare dettagli alle grandi scale  e generalizzazioni alle piccole scale). Creare mappe di base multiscala e multirisoluzione che vengono generate velocemente è una caratteristica che il nostro result map service non necessita di avere poichè il suo lavoro è quello di generare un output. 

Passiamo ora ad un esempio pratico.

Creiamo un modello che calcola le service area (utilizziamo i dati di questo esempio):



Come potete vedere, abbiamo come input i valori di break (impostiamo un default: 1 2 3) e la posizione, mentre come output la service area.



E' importante salvare l'output su disco poichè successivamente il servizio di mappa ArcGIS Server dovrà utilizzarlo per generare la mappa.
Quando il task del servizio di geoprocessing viene invocato, ArcGIS Server imposta al volo la variabile %scratchworkspace% al percorso fisico della cartella di nome 'scratch' contenuta all'interno della cartella univoca del job generato. Ogni volta che si esegue un task, una cartella univoca di job è creata all'interno della cartella del servizio contenuta all'interno della cartella 'jobs' di arcgis server. Qui sotto è illustrato lo schema delle cartelle e dei file gestiti da ArcGIS Server durante la creazione dei job.



Pertanto abbiamo la possibilità di memorizzare l'output in un file geodatabase già pronto creato dal sistema (scratch.gdb) o salvarlo nella cartella scratch come file (ad esempio come shapefile).
Nel primo caso il percorso di output sarà:
%scratchworkspace%\scratch.gdb\SAPolygon
Nel secondo caso sarà:
%scratchworkspace%\SAPolygon.shp

Optiamo per il primo caso e quindi nel modello impostiamo per l'ouput:


A questo punto creiamo la mappa che utilizzeremo come result map service. Trasciniamo nella TOC (tabella dei contenuti) di ArcMap il tool e carichiamo il network dataset utilizzato per il calcolo della service area. E' importante caricare in mappa il network dataset perchè utilizzare layer come sorgenti migliora le prestazioni del modello. Difatti, se si dovesse fare riferimento ad un network dataset su disco ogni volta che viene eseguito il task, il sistema dovrebbe leggere la struttura dei file; mentre, se è già caricato, vengono sfruttate la lettura iniziale e le proprietà del dataset in cache.



Ora, dal menu di Geoprocessing di ArcMap, occorre selezionare Enviroments ... ed impostare nella sezione Workspace il valore per la proprietà 'Scratch Workspace'. E' importante indicare una cartella dove l'utente SOC abbia i privilegi per leggere; poichè, una volta eseguito il modello per creare il layer di output, il documento di mappa verrà pubblicato ed i layer in esso contenuti dovranno essere accessibili.
In questo esempio ho creato nella cartella 'Progetti' (dove l'utente SOC può leggere) una cartella 'Test' con all'interno una cartella di nome 'Scratch' che a sua volta contiene un file geodatabase di nome 'scratch', così il modello potrà trovare il file geodatabase (visto come ho impostato l'output nel modello).



A questo punto eseguiamo il modello da ArcMap: selezioniamo il layer di tool dalla TOC e clicchiamo con il tasto destro del mouse, dal context menu visualizzato selezioniamo la voce Open.
Viene così aperta la maschera di input del modello che ci consente di selezionare un punto sulla mappa e generare la service area.







A questo punto selezioniamo il tool sulla mashera e clicchiamo un punto sulla mappa. Il modello verrà eseguito e verrà creata la feature class nel file geodatabase indicato in output (la variabile %scratchworkspace% viene risolta da quello che è stato impostato in Enviroments in ArcMap).



Una volta generate le service area, possiamo creare la nostra legenda direttamente in ArcMap o fare riferimento direttamente ad un layer, se nel modello abbiamo impostato il Layer Symbology dell'output.
Infine creiamo il servizio di mappa dall'mxd indicando nelle capabilities anche Geoprocessing. Verranno creati, come precedentemente detto, due servizi.







Se diamo un'occhiata alle proprietà del servizio di geoprocessing, noteremo che la proprietà Result Map Service punta al servizio di mappa DrivePolygon.


Ora vediamo come chiamare il task da client. In questo esempio utilizziamo le API Javascript ma potete utilizzare gli analoghi metodi per Flex e Silverlight.

Con il metodo getResultImageLayer ci facciamo restituire l'output del task come immagine che carichiamo come layer sulla mappa.

 <!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,IE=9" />
  
   <!--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>Service Area Task</title>
  
   <link rel="stylesheet" type="text/css" href="http://serverapi.arcgisonline.com/jsapi/arcgis/2.4/js/dojo/dijit/themes/claro/claro.css">
  
   <script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=2.4"></script>
  
   <script type="text/javascript">
  
     dojo.require("esri.map");
  
     dojo.require("esri.tasks.gp");
  
     var map, gp;
  
     var driveTimes = "1 2 3";
  
     /*Initialize map, GP and image params*/
  
     function init() {
  
       var startExtent = new esri.geometry.Extent({ "xmin": -122.673387, "ymin": 37.635131, "xmax": -122.157408, "ymax": 37.918426, "spatialReference": { "wkid": 4326} });
  
       map = new esri.Map("mapDiv", { extent: startExtent });
  
       var imageParameters = new esri.layers.ImageParameters();
  
       imageParameters.format = "jpeg"; //set the image type to PNG24, note default is PNG8. 
  
       //Takes a URL to a non cached map service. 
  
       var dynamicMapServiceLayer = new esri.layers.ArcGISTiledMapServiceLayer("http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer", { "imageParameters": imageParameters });
  
       map.addLayer(dynamicMapServiceLayer);
  
       gp = new esri.tasks.Geoprocessor("http://localhost/ArcGIS/rest/services/DrivePolygon/GPServer/PolygonDriver");
  
       gp.setOutputSpatialReference({ wkid: 4326 });
  
       dojo.connect(map, "onClick", computeServiceArea);
  
     }
  
     function computeServiceArea(evt) {
  
       map.graphics.clear();
  
       var pointSymbol = new esri.symbol.SimpleMarkerSymbol();
  
       pointSymbol.setOutline = new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new dojo.Color([255, 0, 0]), 1);
  
       pointSymbol.setSize(5);
  
       pointSymbol.setColor(new dojo.Color([0, 255, 0, 0.25]));
  
       var graphic = new esri.Graphic(evt.mapPoint, pointSymbol);
  
       map.graphics.add(graphic);
  
       var features = [];
  
       features.push(graphic);
  
       var featureSet = new esri.tasks.FeatureSet();
  
       featureSet.features = features;
  
       var params = { "Input_locations": featureSet, "Default_break_values": driveTimes };
  
       gp.submitJob(params, completeCallback, statusCallback);
  
     }
  
     function statusCallback(jobInfo) {
  
       console.log(jobInfo.jobStatus);
  
     }
  
     function completeCallback(jobInfo) {
  
       var imageParams = new esri.layers.ImageParameters();
  
       imageParams.imageSpatialReference = map.spatialReference;
  
       gp.getResultImageLayer(jobInfo.jobId, "SAPolygons", imageParams, function (gpLayer) {
  
         gpLayer.setOpacity(0.5);
  
         map.addLayer(gpLayer)
  
       });
  
     }
  
     dojo.addOnLoad(init);
  
   </script>
  
  </head>
  
  <body class="claro">
  
   <div id="mapDiv" style="width:800px; height:600px; border:1px solid #000;"></div>
  
   Zoom in to a US city and single click the map. A service area GP task will be executed synchronously and on
  
   completion the results will be drawn as graphic features to the map. The drive time polygons are 1, 2,
  
   and 3 minutes.
  
  </body>
  
 </html>  



Come possiamo vedere da Fiddler alla riga 21 viene invocato il job, dalla 22 alla 26 viene controllato se il job ha terminato con successo ed infine alla riga 27 viene fatta la richiesta dell'immagine del job.



Come possiamo vedere nel dettaglio la richiesta REST dell'immagine è:


In sintesi, all'url del geoprocessing si appendono 'jobs', l'id del job, 'results' ed il nome del parametro di output e si concatenano i parametri di export map per l'immagine.

giovedì 18 agosto 2011

Archiving: questo sconosciuto ...

Quante volte ti hanno chiesto di tenere traccia di tutte le modifiche apportate nel tempo al geodatabase? Spesso la gestione della history dei dati è un’operazione che non viene effettuata. Ma, così facendo, perdiamo moltissime informazioni. Ad esempio, a domande di questo tipo non potremmo rispondere:
-  che valore aveva uno specifico attributo in un determinato periodo di tempo?
-  come si è modificata nel tempo una specifica feature o row?
-  come è cambiata nel tempo la geometria di un poligono? 
Quante operazioni di analisi spaziale eseguiamo senza considerare le differenze temporali relative ai dati: capite che l’elaborazione potrebbe non dare risultati corretti.  Il caso più evidente, per fare un esempio pratico, sono le particelle catastali che hanno una dinamicità nel tempo abbastanza elevata (frazionamenti, fusioni, cambi di proprietà ecc.) ma se ci pensate bene anche dati come i comuni o le province cambiano nel tempo (esempio: formazione della provincia di Monza e Brianza o - voci di questi giorni - l’eliminazione di provincie con meno di 300000 abitanti).
Archiving in ArcGIS, introdotto dalla versione 9.2, fornisce la funzionalità di registrare ed accedere ai cambiamenti apportati a tutti i dati o ad un sottoinsieme di essi di un geodatase versionato. Archiving è il meccanismo per catturare, gestire ed analizzare i dati che vengono modificati nel tempo.
Il geodatabase archiving mantiene i cambi dal momento in cui viene abilitato a farlo fino a quando l’archiving è disabilitato.
Precedentemente all’introduzione dell’archiving, una delle soluzioni alternative per catturare le modifiche nel tempo era quella di creare delle versioni, che fungevano da snapshots, per l’intero geodatabase. Comunque, una soluzione di questo tipo non era completamente soddisfacente perché, una volta che i dati venivano deversionati o la versione veniva eliminata, si perdeva la rappresentazione storica dei dati. L’archiving avviene parallelamente alle modiche dei dati non appesantendo il sistema e richiede meno consumo di risorse quando si utilizza rispetto all’equivalente classe versionata.
Il Geodatabase archiving introduce la versione historical in aggiunta a quella già esistente ovvero la versione transactional.
L’utente può connettersi o ad una versione transactional o ad una versione historical.
Una versione transactional consente ad un utente di modificare i dati. Una versione historical rappresenta i dati ad uno specifico momento nel tempo e fornisce una rappresentazione in sola lettura del geodatabase.
Un utente può connettersi ad una versione historical utilizzando un riferimento storico (historical marker) o specificando un momento ben preciso. In sostanza con il riferimento storico nominiamo un tempo (esempio: “incendio boschivo zona Spinelli” è il nostro riferimento storico per fare riferimento alla data 14:00 PM Giugno 2003).
Il modello archiving supporta completamente il modello dati del geodatabase: pertanto stand-alone feature classes, feature datasets, tables, relationship classes, networks, topologies, e terrains possono partecipare all’archiving.
L’archiving richiede che i dati siano registrati come versionati e con l’opzione “Register the selected objects with the option to move edits to base.” non selezionata.
Una volta che l’archiving è abilitato, tutti i cambi salvati o postati alla versione di DEFAULT sono registrati nella corrispondente classe archive. La classe archive è una completa copia della classe abilitata più tutte le modifiche che sono stata salvate o postate alla versione di DEFAULT.
Una cosa importate da capire è però come ArcGIS rappresenti il tempo quando i cambi vengono effettuati. La history può essere registrata o con un valido momento temporale o con un riferimento temporale transaction. La differenza tra i due è che il primo rappresenta il momento temporale in cui i cambi sono avvenuti nel mondo reale e normalmente vengono registrati dall’utente che applica le modifiche mentre il secondo è il momento in cui il dato è stato modificato nel database. I riferimenti temporali transaction sono generati automaticamente dal sistema e sono quelli utilizzati da ArcGIS. Pertanto occorre prestare molta attenzione per non avere ad esempio modifiche nel geodatabase nell’ordine non corretto rispetto al mondo reale.
Illustriamo ora come abilitare e gestire l’archiving. In questo caso utilizzo la licenza ArcEditor con installato un personal SDE geodatabase memorizzato in SQL Server Express 2008 R2 e copiamo la feature class Trees che trovate nel file geodatabase GDBTrees.gdb  della cartella C:\Programmi\ArcGIS\ DeveloperKit10.0\Samples\data\Trees.

 

 
Da ArcCatalog seleziono la feature class che desidero abilitare all’archiving.
Come detto precedentemente, per poter abilitare la feature class all’archiving, devo in via preliminare versionarla con l’opzione “Register the selected objects with the option to move edits to base.” non abilitata.

 

 

 
Un dataset abilitato all’archiving abiliterà tutte le feature class contenute in esso.
Lato programmazione: se la proprietà moveEditsToBase è abilitata sulla nostra object class, occorre chiamare dall’interfaccia IVersionedObject3 il metodo UnRegisterAsVersioned3(false) per deregistrare la classe come versionata disabilitando il ‘moving of edits to the base table’ e poi chiamare sempre dalla IVersionedObject3  il metodo RegisterAsVersioned3(false) per registrare l’object come versionato con l’opzione moveEditsToBase a false.
A questo punto possiamo abilitare l’archiving:

 
 
Nel momento in cui si abilita l’archiving nel geodatabase viene creata una classe archiving che tiene traccia di tutte le modiche effettuare sull’ object class (in questo esempio la feature class Trees). Nel geodatabase la classe archiving è una tabella con il nome della feature class con l’aggiunta del suffisso ‘_H’
 

Oltre ai campi dell’object class (nel nostro esempio: ObjectID, Year_Planted e Shape) all’archiving class vengono aggiunti anche i campi: GDB_FROM_DATE, GDB_TO_DATE e GDB_ARCHIVE_OID.

 
L’GDB_ARCHIVE_OID funge da OBJECTID nella classe archiving (Trees_H).

 

 
E’ possibile modificare il nome della classe archiving ed i nomi dei campi aggiunti per l’archiving utilizzando la classe ArchiveRegistrationInfo.

 

 

 
namespace Studioat.Samples.Archiving
{
    using System;
    using System.Collections.Generic;
    using ESRI.ArcGIS.esriSystem;
    using ESRI.ArcGIS.Geodatabase;
 
    internal class Program
    {
        private static LicenseInitializer licenseInitializer = new LicenseInitializer();
    
        [STAThread]
        internal static void Main(string[] args)
        {
            ////ESRI License Initializer generated code.
            licenseInitializer.InitializeApplication(new esriLicenseProductCode[] { esriLicenseProductCode.esriLicenseProductCodeArcEditor }, new esriLicenseExtensionCode[] { });
 
            ////connessione ad una versione transactional
            IWorkspace workpace = Archive.GetPersonalOrWorkgroupArcSdeWorkspace("studioat40_sqlexpress""sde:sqlserver:studioat40\\sqlexpress""OSA""History"Archive.TypeVersion.VERSION, "dbo.DEFAULT");
 
            IFeatureWorkspace fw = workpace as IFeatureWorkspace;
            IObjectClass oc = fw.OpenFeatureClass("Trees"as IObjectClass;
 
            ////facoltativo il passaggio di un set di ArchiveRegistrationInfo per indicare dei nomi diversi
            ////rispetto a quelli di default (impostati ad esempio in dbtune)
            ISet setArchiveInfo = new SetClass();
            setArchiveInfo.Add(new ArchiveRegistrationInfoClass() { ArchiveTableName = "TreesHistory", FromFieldName = "Da", ToFieldName = "A", OIDFieldName = "OIDHistory", DatasetName = "Trees" });
 
            ////imposto ad esempio un secondo ArchiveRegistrationInfo se è una classe relazionata a Trees (se utilizzo il 3° parametro true nell'abilitazione all'archiving)
            ////setArchiveInfo.Add(new ArchiveRegistrationInfoClass() { ArchiveTableName = "TreesMaintenanceHistory", FromFieldName = "Da", ToFieldName = "A", OIDFieldName = "OIDHistory", DatasetName = "TreesMaintenance" });
            
            ////ISet setArchiveInfo = null;
            
            oc.EnableArchiving(setArchiveInfo);
            
            ////determino il numero di differenze (inserimenti) tra l'historical name DEFAULT e l'historical name di nome 'Test'
            ////IFIDSet fids = FindHistoricalDifferences(workpace, "Test", "Trees", esriDifferenceType.esriDifferenceTypeInsert);
            ////Console.WriteLine("Insert:" + fids.Count().ToString());
 
            ////ESRI License Initializer generated code.
            ////Do not make any call to ArcObjects after ShutDownApplication()
            licenseInitializer.ShutdownApplication();
        } 
    }
 
    public static class Archive
    {
        public enum TypeVersion
        { 
             /// <summary>
             /// Version Transactional
             /// </summary>
             VERSION,
             /// <summary>
             /// Version Historical Name
             /// </summary>
             HISTORICAL_NAME,
             /// <summary>
             /// Version Historical Timestamp
             /// </summary>
             HISTORICAL_TIMESTAMP
        }
 
        public static IWorkspace GetPersonalOrWorkgroupArcSdeWorkspace(string server, string instance, string authenticationMode, string database, TypeVersion typeVersion, string version)
        {
            IPropertySet propertySet = new PropertySetClass();
            propertySet.SetProperty("SERVER", server);
            propertySet.SetProperty("INSTANCE", instance);
            propertySet.SetProperty("DATABASE", database);
            propertySet.SetProperty("AUTHENTICATION_MODE", authenticationMode);
            object o = version;
            if (typeVersion == TypeVersion.HISTORICAL_TIMESTAMP)
            {
                o = Convert.ToDateTime(version);
            }
            propertySet.SetProperty(Enum.GetName(typeof(TypeVersion), typeVersion), o);
 
            Type factoryType = Type.GetTypeFromProgID("esriDataSourcesGDB.SdeWorkspaceFactory");
            IWorkspaceFactory workspaceFactory = (IWorkspaceFactory)Activator.CreateInstance(factoryType);
            return workspaceFactory.Open(propertySet, 0);
        }
 
        public static ITable GetArchiveTable(this IObjectClass objectClass)
        {
            IArchivableClass archivableClass = objectClass as IArchivableClass;
            return archivableClass.Archive;
        }
 
        public static void EnableArchiving(this IObjectClass objectClass, ISet setArchiveInfo)
        {
            IArchivableObject archivableObjecting = objectClass as IArchivableObject;
            if (!archivableObjecting.IsArchiving)
            {
                ////time stamp inizializzato dal sistema (imposto null)
                ////se l'object class ha relationship è possibile impostare l'ultimo parametro a true per automaticamente
                ////abilitare l'archiving sulle object class relazionate
                archivableObjecting.EnableArchiving(setArchiveInfo, nullfalse);
            }
        }
 
        public static IWorkspace ConnectoToVersion(string server, string instance, string user, string password, string database, TypeVersion typeVersion, string version)
        {
            IPropertySet propertySet = new PropertySetClass();
            propertySet.SetProperty("SERVER", server);
            propertySet.SetProperty("INSTANCE", instance);
            propertySet.SetProperty("DATABASE", database);
            propertySet.SetProperty("USER", user);
            propertySet.SetProperty("PASSWORD", password);
            object o = version;
            if (typeVersion == TypeVersion.HISTORICAL_TIMESTAMP)
            {
                o = Convert.ToDateTime(version);
            }
            propertySet.SetProperty(Enum.GetName(typeof(TypeVersion), typeVersion), o);
 
            Type factoryType = Type.GetTypeFromProgID("esriDataSourcesGDB.SdeWorkspaceFactory");
            IWorkspaceFactory workspaceFactory = (IWorkspaceFactory)Activator.CreateInstance(factoryType);
            return workspaceFactory.Open(propertySet, 0);
        }
 
        public static IFIDSet FindHistoricalDifferences(IWorkspace workspace, string historicalMarkerName, string tableName, esriDifferenceType differenceType)
        {
            IHistoricalWorkspace historicalWorkspace = (IHistoricalWorkspace)workspace;
            IHistoricalVersion defaultVersion = historicalWorkspace.FindHistoricalVersionByName(historicalWorkspace.DefaultMarkerName);
            IHistoricalVersion historicalVersion = historicalWorkspace.FindHistoricalVersionByName(historicalMarkerName);
 
            IFeatureWorkspace defaultFWS = (IFeatureWorkspace)defaultVersion;
            IFeatureWorkspace historicalFWS = (IFeatureWorkspace)historicalVersion;
            ITable defaultTable = defaultFWS.OpenTable(tableName);
            ITable historicalTable = historicalFWS.OpenTable(tableName);
 
            IVersionedTable versionedTable = (IVersionedTable)defaultTable;
            IDifferenceCursor differenceCursor = versionedTable.Differences(historicalTable, differenceType, null);
 
            IFIDSet fidSet = new FIDSetClass();
            IRow differenceRow = null;
            int objectID = -1;
 
            differenceCursor.Next(out objectID, out differenceRow);
            while (objectID != -1)
            {
                fidSet.Add(objectID);
                differenceCursor.Next(out objectID, out differenceRow);
            }
 
            fidSet.Reset();
            return fidSet;
        }
 
        public static IList<IPair<stringobject>> GetHistoricalMarkers(IWorkspace workspace)
        {
            IList<IPair<stringobject>> list = new List<IPair<stringobject>>();
            IHistoricalWorkspace historicalWorkspace = (IHistoricalWorkspace)workspace;
            IEnumHistoricalMarker enumHistoricalMarker = historicalWorkspace.HistoricalMarkers;
            enumHistoricalMarker.Reset();
 
            IHistoricalMarker historicalMarker = null;
            while ((historicalMarker = enumHistoricalMarker.Next()) != null)
            {
                list.Add(new Pair<stringobject>() { First = historicalMarker.Name, Second = historicalMarker.TimeStamp }); 
            }
 
            return list;
        }
    }
 
    /// <summary>
    /// classe generica per gestire coppie di oggetti
    /// </summary>
    /// <typeparam name="T">tipo del primo oggetto</typeparam>
    /// <typeparam name="U">tipo del secondo oggetto</typeparam>
    public struct Pair<T, U> : IPair<T, U>
    {
        private T first;
        private U second;
 
        public T First
        {
            get
            {
                return this.first;
            }
 
            set
            {
                this.first = value;
            }
        }
 
        public U Second
        {
            get
            {
                return this.second;
            }
 
            set
            {
                this.second = value;
            }
        }
    }
 
    /// <summary>
    /// interfaccia per coppie di oggetti generici
    /// </summary>
    /// <typeparam name="T">tipo del primo oggetto</typeparam>
    /// <typeparam name="U">tipo del secondo oggetto</typeparam>
    public interface IPair<T, U>
    {
        T First
        {
            get;
            set;
        }
 
        U Second
        {
            get;
            set;
        }
    }
}

 

Una volta che si abilita l’archiving, la classe archiving si presenta con tutti i record dell’object class presenti nella versione di DEFAULT. Come si può notare, la data GDB_FROM_DATE è stata inizializzata nel momento in cui è stato abilitato l’archiving (13/7/2011 alle 20.59.19), mentre il GDB_FROM_TO è lasciato aperto (data impostata ad infinito) .
 
 
Facendo una connessione alla versione historical con una data precedente al 13/7/2011 alle 20.59.19 non vediamo nessuna feature perché prima di questa data non era ancora abilitato l’archiving:


Ma se effettuiamo una connessione indicando una data posteriore al 13/7/2011 alle 20.59.19 vedremo tutte le feature della feature class (nel nostro caso 5 trees).


Da notare che viene creato anche in automatico il riferimento temporale di abilitazione dell’archiving denominato DEFAULT.
Per ogni operazione di archive, il riferimento temporale DEFAULT è aggiornato con il valore dell’operazione di archive. Ciò consente che, quando si sceglie il riferimento temporale DEFAULT e si lavora con una versione historical,  la corrente rappresentazione della classe archive è equivalente alla rappresentazione della classe versionata nella versione DEFAULT di tipo transactional.

Il riferimento temporale di abilitazione dell’archiving può anche essere visto nelle proprietà della feature class:


Ma ora vediamo cosa succede all’archiving class quando si aggiunge, modifica o cancella una feature nella feature class.

Lato programmazione: l’evento che cattura il momento dell’operazione di archive è OnArchiveUpdated dell’interfaccia IVersionEvents2.

Sull’aggiunta di una feature in ArcMap verrà compilato in automatico con la data di sistema il campo GDB_FROM_DATE mentre il campo GDB_TO_DATE verrà lasciato aperto (data impostata ad infinito). I valori nei campi che rappresentano la feature class (OBJECTID, YEAR_PLATED e SHAPE) sono uguali ai corrispondenti della feature class.


Ora modifichiamo la posizione spaziale da ArcMap della feature con OBJECID uguale ad 1.



Come possiamo notare la feature con OBJECTID uguale ad 1 e GDB_ARCHIVE_OID uguale ad 1 viene chiusa con la data della modifica della feature class (13/08/2011 alle 20:41.28) col campo GDB_TO_DATE e viene creato un nuovo record con i dati della feature modificati (in questo caso solo il campo shape mentre OBJECTID e YEAR_PLANTED rimangono invariati) e con il valore della data di modifica nel campo GDB_FROM_DATE mentre GDB_TO_DATE rimane aperto (data impostata ad infinito).

Infine vediamo la cancellazione di una feature con OBJECTID uguale ad 3. In questo caso viene solo compilato il campo GDB_TO_DATE con la data di cancellazione della feature.


Per la gestione della history in ArcMap abbiamo a disposizione la toolbar “Geodatabase History”


Il primo pulsante apre il “Geodatabase History Viewer” che permette di visualizzare i dati selezionando un riferimento temporale precedentemente creato (Historical marker) od uno specifico momento temporale senza modificare la connessione. Comodo è l’Auto Apply che, se abilitato, permette sulla modifica dei riferimenti temporali di visualizzare automaticamente i cambiamenti in mappa senza dover cliccare ulteriori pulsanti di conferma.


Il secondo pulsante consente di caricare in mappa la classe di archiving della feature class selezionata in TOC.



In questo modo l’utente potrà effettuare le proprie interrogazioni, ad esempio utilizzando il Select By Attributes.

Il terzo pulsante consente di creare i riferimenti temporali (Historical Marker). Una volta selezionata la connessione sulla TOC è possibile creare i riferimenti temporali per poi poterli successivamente utilizzare nella visualizzazione temporale dei dati.


E’ possibile disabilitare l’archiving di un object class. In questo caso tutte le modifiche salvate o postate alla versione di DEFAULT non verranno più registrate nella relativa classe di archivio.
Quando si disabilita l’archiving, viene presentato un messaggio che chiede se si vuole mantenere o cancellare la classe archive. Se si sceglie di non eliminare la classe archive associata possiamo preservarla come tabella temporale nel geodatabase.
Consigli sulla disabilitazione dell’archiving
  • L’archiving deve essere disabilitato su una classe prima che possa essere deversionata.
  • Eliminando un feature dataset od una classe con archiving abilitato preserviamo le classi archive.
Dalla versione 10 nel geodatabase replication è possibile utilizzare la classe archive per tenere traccia delle modifiche invece di utilizzare le tabelle delta delle versioni. Questa possibilità è disponibile solo per le repliche one-way. In tal modo i riconcilia, post e compress non sono influenzati rendendo la gestione delle versioni e delle repliche indipendenti. Ciò consente anche che la schedulazione della sincronizzazione sia più flessibile.
Infine illustriamo uno scenario in cui si potrebbe creare un salto temporale nella classe archive:
Un utente effettua modifiche direttamente nella versione di DEFAULT ed elimina una feature in una sessione di editing. Infine, l’utente salva le modifiche.  L’attributo GDB_TO_DATE nella classe archive viene aggiornato con la data di sistema della cancellazione dell’oggetto.
Se lo stesso oggetto è aggiornato da una versione figlia e riconciliata con la versione di DEFAULT, scatta il conflitto.
Se durante il processo di risoluzione del conflitto l’utente sceglie di sostituire il conflitto con la rappresentazione modificata nella versione figlia, la riga verrà sostituita nella versione di DEFAULT quando la versione stessa verrà postata. L’operazione di archive inserisce una nuova riga nella classe archive e imposta l’attributo GDB_FROM_DATE alla data di sistema e l’attributo GDB_TO_DATE ad infinito (12/31/9999).
Pertanto, quando l’utente osserva il lineage dell’oggetto temporalmente, le date conterranno un salto tra il GDB_TO_DATE e il GDB_FROM_DATE per il tempo in cui l’oggetto non esiste nella versione di DEFAULT.