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

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

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



domenica 17 ottobre 2010

Routing utilizzando javascript API

Siamo arrivati alla versione 2.1 delle API Javascript ESRI ma sin dalla versione 1.4 le API Javascript permettevano di lavorare con i servizi di network analysis di ArcGIS Server.


Dalla versione 2.0 inoltre è stato aggiunto il supporto per le closest facility e le service area.

Il RouteTask di ArcGIS JavaScript API ti consente di trovare i percorsi tra due o più posizioni ed eventualmente di ricevere indicazioni di percorso. Il RouteTask usa i servizi della network analysis di ArcGIS Server per calcolare i percorsi. I servizi della network analysis services ti permettono di risolvere semplici problemi di routing, ma anche più complessi che comprendono fermate multiple, barriere e vincoli temporali.

Nota: la capacità di esporre servizi di network analysis services attraverso REST (e quindi ArcGIS JavaScript API) inizia a partire dalla versione 9.3.1. di ArcGIS Server. I servizi di network analysis pubblicati dalle precedenti versioni di ArcGIS Server non si potevano utilizzare con ArcGIS JavaScript API.


Come creare un servizio di network analysis

Per disporre di un servizio di network analysis dovrai seguire le seguenti fasi:

1. Creare un documento ArcMap (MXD) ed aggiungere un layer di network analysis che faccia riferimento ad un network dataset.

2. Pubblicare il documento di mappa in ArcGIS Server come servizio di mappa.

3. Abilitare la capability Network Analysis nel servizio di mappa.

Le fasi sopra menzionate mostrano che un servizio di network analysis in realtà nasce da un servizio di mappa ed un documento di mappa. E’ una best practice, per le prestazioni, mantenere il servizio di network analysis separato da altri servizi di mappa nella tua applicazione Web. In altre parole, non mettere il tuo layer di network analysis nella mappa che verrà usata principalmente per la visualizzazione.


Se utilizzi per la prima volta ESRI network analysis, la seguente lettura ti fornirà dettagli ulteriori su Network Analyst ed i servizi di network:

An overview of Network Analyst

Network analysis workflow

Network analysis services


Cosa può fare il RouteTask?


Il RouteTask ti aiuta a risolvere problem di routing, cioè permette di trovare il percorso ottimale tra posizioni multiple che si trovano su una network. Il costo è calcolato basandosi su un attributo di impedenza che definisci nel network dataset. L’impedenza può essere un tempo o una distanza, o anche una variabile più astratta che la rappresenta.

Il RouteTask ti può aiutare a trovare la miglior sequenza delle fermate. Ad esempio, se il costo di un viaggio dalla fermata A alla fermata B è maggiore rispetto alla direzione opposta a causa delle vie a senso unico, troverai la soluzione dove la fermata B è visitata prima. Inoltre, se hai fermate multiple che possono essere visitate in qualsiasi ordine, il RouteTask troverà la sequenza più efficiente. Puoi scegliere eventualmente se mantenere la prima o l’ultima fermata fisse in caso di esigenze di percorso per iniziare o finire ad un determinato punto.

Il RouteTask può anche lavorare con finestre temporali, che sono intervalli fissati di tempo nei quali bisogna effettuare le fermate (ad esempio dalle 3:00 alle 4:00 AM per la fermata A). Se le tue fermate hanno finestre di tempo definite, il RouteTask troverà una sequenza di fermate dove cerca di rispettare le finestre di tempo minimizzando il costo totale. Inoltre, il RouteTask può considerare sia le barriere che bloccano particolari segmenti di strada, sia le restrizioni, per esempio i sensi unici.

Il RouteTask può restituire un route con la sua geometria, costo, direzioni di viaggio ed altri valori che indichi da accumulare nel corso del viaggio.

Passiamo ora alla parte pratica.


Per istanziare un RouteTask dobbiamo fornire l'URL della nostra risorsa network layer

routeTask = new esri.tasks.RouteTask("http://myServer/ArcGIS/rest/services/MyMapDoc/NAServer/MyRoute");

MyMapDoc è il nome del nostro servizio di mappa, mentre MyRoute è il nome del nostro layer di network analysis.

Ora, per configurare i parametri del routeTask ovverosia fermate, barriere, impedenza ecc., utilizziamo esri.tasks.RouteParameters.

Una volta istanziato il RouteTask ed impostato il RouteParameters, risolviamo mediante il solve. Ad esempio, per aggiungere due fermate:


routeParams = new esri.tasks.RouteParameters();


var stop1 = new esri.Graphic(new esri.geometry.Point(-117.21,34.065), stopSymbol);
var stop2 = new esri.Graphic(new esri.geometry.Point(-117.185,34.05), stopSymbol);
routeParams.stops = new esri.tasks.FeatureSet();
routeParams.stops.features.push(stop1);
routeParams.stops.features.push(stop2);

Analogo discorso può essere fatto per le barriere:

routeParams.barriers = new esri.tasks.FeatureSet();
var barrier1 = new esri.Graphic(new esri.geometry.Point(-23.185,42.05), barrierSymbol);
routeParams.barriers.features.push(barrier1);

Possiamo specificare gli attributi che dovranno essere accumulati durante l'analisi. Dovranno essere attributi specificati come costi nel network dataset

routeParams.accumulateAttributes = ["Costo1","Costo2"];

Fate attenzione che questo, come altri parametri, se non vengono impostati, prenderanno le impostazioni del network dataset / layer del servizio.

Un'altra considerazione da fare è che le fermate/barriere che abbiamo inserito verranno riferite ad edge delle route in base alle impostazioni di snap presenti nel layer di network analysis.


Se volessimo invece riferirle ad edge specifici della route, abbiamo la possibilità di utilizzare le network location tramite SourceID, SourceOID, PosAlong, SideOfEdge e CurbApproach.

In questo caso negli attributi delle fermate indicheremo:

var stop1 = new esri.Graphic(new esri.geometry.Point(-117.21, 34.065), stopSymbol);
stop1.attributes = new Object();
stop1.attributes.SourceID =1;
stop1.attributes.SourceOID =2367;
stop1.attributes.PosAlong =0.55467;
stop1.attributes.SideOfEdge =1;
stop1.attributes.CurbApproach =0;

Abbiamo la possibilità di cercare più route contemporaneamente impostando la proprietà RouteName. Inoltre possiamo impostare la proprietà Name utilizzabile, ad esempio, per 'riconoscere' la fermata quando ci verrà restituita dal RouteResult.

var stop1 = new esri.Graphic(new esri.geometry.Point(-117.21, 34.065), stopSymbol);
stop1.attributes = new Object();
stop1.attributes.Name = "A";
stop1.attributes.RouteName = "Route A";


var stop2 = new esri.Graphic(new esri.geometry.Point(-117.185, 34.05 ), stopSymbol);
stop2.attributes = new Object();
stop2.attributes.Name = "B";
stop2.attributes.RouteName = "Route A";


routeTask.solve(routeParams);


Inoltre dobbiamo dichiarare i due eventi del RouteTask per prendere i risultati o gli eventuali errori.

dojo.connect(routeTask, "onSolveComplete", showRoute);
dojo.connect(routeTask, "onError", errorHandler);


function showRoute(routeResults) {
    for (var i = 0; i < routeresults.messages.length; i++)
       alert(routeResults.message[i].type + ": " + routeResults.message[i].description);
   
    map.graphics.add(routeResults[0].route.setSymbol(routeSymbol));
}

function errorHandler(err) {
   if (err.message)
      alert("An error occured\n" + err.message + "\n" + err.details.join("\n"));
   else
     alert("Description: " + err.description + "\n" + "Type:" + err.type);
}


Come abbiamo precedentemente accennato, per avere la migliore sequenza per le fermate occorre impostare le seguenti proprietà del RouteParameters:

routeParams.findBestSequence=true;
routeParams.preserveFirstStop=false;
routeParams.preserveLastStop=false;
routeParams.returnStops = true;

Impostando a returnStops = true possiamo poi, dopo aver risolto con solve, prendere le fermate nella sequenza ottimizzata:

for (var i = 0; i < routeResults[0].stops.length; i++){
   alert("Fermata " + routeResults[0].stops[i].attributes.Name + " visita n. " + routeResults[0].stop.[i].attributes.Sequence);
}

Come potete notare routeResults è un array poichè, come abbiamo visto precedentemente, possiamo risolvere più di una route in una volta sola utilizzando la proprietà di RouteName.

function showRoute(solveResult) {
   var routeResults = solveResult.routeResults;
...

Per avere indicazioni sulle direzioni possiamo impostare la proprietà returnDirections (RouteParameters.returnDirections = true). In questo caso se non strettamente necessario (ad esempio abbiamo bisogno di alcuni attributi del routeResults) è opportuno non caricare troppo il sistema facendosi anche restituire le route ( RouteParameters.returnRoutes = false).

function showRoute(routeResults) {
   var directions = routeResults[0].directions;
   ...
}

directions restitutisce DirectionsFeatureSet una subclass di esri.tasks.FeatureSet.

Infine, per chiudere un'interessante caratteristica del RouteTask, vi è la possibilità di vincolare il passaggio alle fermate in certe ore del giorno, anche se si tratta di vincoli 'soft' perchè, anche se il solver cerca di rispettare tali vincoli, alcuni vincoli temporali potrebbero essere violati per raggiungere comunque le fermate.

routeParams.useTimeWindows = true;
var stop1 = new esri.Graphic(new esri.geometry.Point(-117.21, 34.065), stopSymbol);
stop1.attributes = new Object();
stop1.attributes.Name = "A";
stop1.attributes.TimeWindowStart = "8:00 AM";
stop1.attributes.TimeWindowEnd = "8:05 AM";

var stop2 = new esri.Graphic(new esri.geometry.Point(-117.185, 34.05 ), stopSymbol);
stop2.attributes = new Object();
stop2.attributes.Name = "B";
stop2.attributes.TimeWindowStart = "8:10 AM";
stop2.attributes.TimeWindowEnd = "8:15 AM";

Tramite le seguenti proprietà è possibile vedere il tempo violato o il tempo d'attesa.

stop.attributes["Cumul_Time"];
stop.attributes["Wait_Time"];
stop.attributes["CumulWait_Time"];
stop.attributes["Violation_Time"];
stop.attributes["CumulViolation_Time"];

Nessun commento: