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

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

I migliori software GIS, il miglior supporto tecnico!

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

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



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

mercoledì 20 gennaio 2010

SOE to REST

Proseguendo il post del  23 giugno 2009, per esporre la SOE (Server Object Extension), potrebbe essere comodo utilizzare uno stile architetturale REST.
Per l'implementazione usiamo ad esempio WCF che nello specifico, poichè si serve dello stile architetturale REST, sarà definito un servizio RESTful. Qui potete trovare una introduzione all'argomento.

Se il servizio WCF può accedere alla SOM tramite DCOM, possiamo fare la connessione direttamente dal servizio, altrimenti dobbiamo esporre la nostra SOE via SOAP e solo poi il servizio WCF potrà comunicare via http alle funzionalià esposte dalla SOE.

Vediamo il caso in cui possiamo comunicare alla SOM direttamente tramite DCOM. Per non stare ad inventarci niente di nuovo, possiamo utilizzare l'esempio della ESRI (ArcGIS Spatial Query Server Object Extension) che usa i dati in -ArcGIS install location-\DeveloperKit\SamplesNet\Server\data\Yellowstone.
In sintesi, questa SOE riceve in input le coordinate di un punto ed una distanza; in seguito, in funzione del layer X (tipo poligonale) e del campo Y impostato nelle property della SOE (configurabili con ArcCatalog), vengono restituite le somme delle aree in funzione del valori nel campo Y all'interno del buffer indicato dai dati di input.


[ErrorBehavior(typeof(YellowstoneServiceErrorHandler))]
    public sealed class YellowstoneService : IYellowstoneService
    {
        public List<VegetationSumType> GetSumVegetationType(double x, double y, double distance)
        {
            IServerContext serverContext = null;
            List<VegetationSumType> vegetationSumType = new List<VegetationSumType>();
            try
            {
                // Connection to ArcGIS Server
                Identity identity = new Identity();
                identity.UserName = ConfigurationManager.AppSettings.Get("userAGS");
                identity.Password = ConfigurationManager.AppSettings.Get("passwordAGS");


                using (AGSServerConnection agsServerConnection = new AGSServerConnection(ConfigurationManager.AppSettings.Get("hostnameAGS"), identity))
                {
                    try
                    {
                        agsServerConnection.Connect();
                    }
                    catch (ServerHostNullException)
                    {
                        throw new FaultException(YellowstoneRes.ServerHostNullException);
                    }
                    catch
                    {
                        throw;
                    }

                    // Reference SOM
                    IServerObjectManager serverObjectManager = agsServerConnection.ServerObjectManager;
                    serverContext = serverObjectManager.CreateServerContext(ConfigurationManager.AppSettings.Get("ServiceName"), "MapServer");

                    IMapServer mapServer = (IMapServer)serverContext.ServerObject;
                    IServerObjectExtensionManager serverObjectExtensionManager = (IServerObjectExtensionManager)mapServer;
                    IServerObjectExtension serverObjectExtension = serverObjectExtensionManager.FindExtensionByTypeName(ConfigurationManager.AppSettings.Get("SOEName"));
                    IVegUtilsSOE vegutils = (IVegUtilsSOE)serverObjectExtension;

                    IVegResultsSOE result = vegutils.sumVegetationType(x, y, distance);

                    IRecordSet stats = result.Stats;

                    using (ComReleaser comReleaser = new ComReleaser())
                    {
                        ICursor cursor = stats.get_Cursor(true);
                        comReleaser.ManageLifetime(cursor);

                        IRow row = cursor.NextRow();

                        while (row != null)
                        {
                            vegetationSumType.Add(new VegetationSumType() { Type = Convert.ToString(row.get_Value(0)), Area = Convert.ToDouble(row.get_Value(1)) });

                            row = cursor.NextRow();
                        }

                    }
                }
            }
            catch (Exception ex)
            {
                throw new FaultException(ex.Message);
            }
            finally
            {

                if (serverContext != null)
                {
                    serverContext.ReleaseContext();
                }
            }

            return vegetationSumType;
        }

    }

Nel Web.Config memorizzeremo le impostazioni per connettersi ad ArcGIS Server (deve essere un utente che appartiene al gruppo degli utenti agsUser)

<appSettings>
    <!-- user ArcGIS Server group agsUser-->
    <add key="userAGS" value="youragsUser"/>
    <add key="passwordAGS" value="xxxxxxxx"/>
    <add key="domainAGS" value=""/>
    <!--**********************-->
    <add key="hostnameAGS" value="localhost"/>
    <add key="ServiceName" value="YellowStone"/>
    <add key="SOEName" value="VegUtilitiesSOE_CSharp"/>
  </appSettings>


Il nostro service contract sarà:

namespace Studioat.Samples
{
    [ServiceContract(Namespace = "http://Studioat.Samples")]
    public interface IYellowstoneService
    {

        [OperationContract]
        [WebGet(UriTemplate = "Vegetation?x={x}&y={y}&distance={distance}", ResponseFormat = WebMessageFormat.Json,BodyStyle= WebMessageBodyStyle.WrappedResponse)]
        List<VegetationSumType> GetSumVegetationType(double x, double y, double distance);

    }

    [DataContract]
    public class VegetationSumType
    {

        [DataMember]
        public string Type
        {
            get;
            set;
        }

        [DataMember]
        public double Area
        {
            get;
            set;
        }

    }
}


A questo punto possiamo fare un test e, servendoci ad esempio del fiddler oppure direttamente del browser, verificare il risultato. Eseguiamo una request Builder di tipo GET da fiddler: http://localhost/wsYellowStone/YellowstoneService.svc/Vegetation?x=541131&y=4908614&distance=5500.0



In questo caso la risposta è in formato JSON ma, se volete la risposta in Xml, potete semplimente modificarla utilizzando ResponseFormat = WebMessageFormat.Xml.

Nel caso in cui abbiate già esposto la vostra SOE via SOAP, potete far riferimento nel servizio WCF al servizio SOAP senza dovervi connettere alla SOM e quindi evitando anche la comunicazione DCOM.


Scarica qui la soluzione

giovedì 30 luglio 2009

SQL Server 2008 Layer

This is an example of how to visualize spatial data stored in SQL Server 2008 (and Express) using Silverlight ESRI API. In a nutshell, we create a WCF service acting as a connection to database SQL Server which reads spatial data in a table with a geography field and converts SQL geography format into geometry ESRI.
In WFC service, in the reference we use the WPF Silverlight ESRI API library containing the definition of geometry class which can be used with the same contract as far as the silverlight client part is concerned.
Calling the WFC Service from client silverlight allows you to catch objects (including geometry ESRI) to be visualized on the map.
With this code you can load points, lines and polygons but geography not valid data have not still been checked.

From sqlgeography to geometry ESRI:


public static Geometry CreateGeography(SqlGeography geometry)

        {

 

            string geometryType = geometry.STGeometryType().Value;

 

            if (string.Compare(geometryType, "Point") == 0)

                return geometry.GetPointN(1);

            else if (string.Compare(geometryType, "MultiPoint") == 0)

            {

                MultiPoint multipoint = new MultiPoint();

                geometry.PopulatePointCollection(multipoint.Points);

                return multipoint;

            }

            else if ((string.Compare(geometryType, "Polygon") == 0) || (string.Compare(geometryType, "MultiPolygon") == 0))

            {

                return geometry.BuildPoly<Polygon>((Polygon p, PointCollection t1) => p.Rings.Add(t1));

            }

            else if ((string.Compare(geometryType, "LineString") == 0) || (string.Compare(geometryType, "MultiLineString") == 0))

            {

                return geometry.BuildPoly<Polyline>((Polyline p, PointCollection t1) => p.Paths.Add(t1));

            }           

            return null;

        }



In order to visualize your own data, in the WFC Service insert your connection string to database.


public System.Collections.Generic.List<Feature> GetSpatialData(Query query)

        {

            try

            {

 

 

                ///////////////////////////////////////////////////////////////////////////////////////////////////////////

                string connString = @"Data Source=PC-HOME\SQLEXPRESS;Initial Catalog=SpatialData;Integrated Security=True";

                //For instance @"Data Source=MyServerName\sqlexpress;Initial Catalog=SpatialData;Integrated Security=True"

                ///////////////////////////////////////////////////////////////////////////////////////////////////////////

 

                string where = null;

                if (query.Geometry != null)

                {

                    SqlGeography spatialFilter = Utility.CreateGeography(query.Geometry);

                    where = string.Format("({0}.STIntersects(geography::STGeomFromText('{1}',{2})) = 1)", query.GeometryColumnName, spatialFilter.ToString(), spatialFilter.STSrid.Value.ToString());

                }

 

                if ((where != null) && (!string.IsNullOrEmpty(query.Where)))

                   where += " AND ";





In the XAML page you have to set the name of the field containing geography (GeometryColumnName), the name of the table (Table) and the name of the service WFC (Service) for GeoDataLayer.


<samples:GeoDataLayer x:Name="GeoDataLayer" GeometryColumnName="GEOM"

                 Table="[SpatialData].[dbo].[LimitiComunali]" MaxRecordCount="10"

                                  Service="http://localhost:3088/ISpatialData.svc">




An option is adding a filter (Where), a max record count (MaxRecordCount) and a spatial filter (Geometry).
Since a map tip is created for every visualized feature, it is possible to point out the fields you want to visualize. If you do not point them out, all will be visualized.


<samples:GeoDataLayer.OutFields>

    <!--Empty for all fields-->

    <!--<sys:String>IdCameretta</sys:String>

                    <sys:String>NomeCameretta</sys:String>-->

  </samples:GeoDataLayer.OutFields>







Download here

lunedì 15 giugno 2009

SQL Server 2008 - Virtual Earth

Nella versione 2008 di SQL Server sono stati introdotti due nuovi tipi di dati: Geometry e Geography.
Per capire la differenza tra i due tipi di dati potete trovare la documentazione su MSDN.

Vediamo ora come caricare uno shapefile da ArcGIS Desktop (mediante l'extension Data Interoperability) in SQL Server 2008.

Per questo esempio potete anche utilizzare la versione SQL Server 2008 Express SP1 e il tool di Morten Nielsen.

Come si può vedere dalla figura qui sotto, dagli Arctoolbox si seleziona 'Quick Export' e dalla lista dei formati si seleziona Microsoft SQL Server Spatial. Lo shapefile è stato preventivamente proiettato in wgs84.








Una volta caricati, è possibile visualizzarli in Virtual Earth. In questo momento non abbiamo un metodo diretto per poterli visualizzare poichè gli output da SQL Server 2008 sono il WKT e il GML e il formato nativo che nei client .NET si traduce nei tipi .NET SqlGeometry e SqlGeography; mentre Virtual Earth necessita di GeoRSS, KML, xml personalizzato da processare con javascript personalizzato o stringhe Javascript da passare ad eval().


In questo esempio creiamo una stored procedure (GetLimiteComunale) che chiamiamo tramite un servizio WCF utilizzando Linq to SQL. La stored procedure restituisce la versione GML (nell'esempio eliminiamo la parte di tag di apertura e chiusura) della feature richiamata. Nel servizio utilizziamo poi l'utility GeoRssUtils per pubblicare i dati su Virtual Earth.







In Visual Studio 2008 trasciniamo le stored procedure sul design della classe Linq to SQL



e questo è il metodo nel servizio WCF

public SyndicationFeedFormatter GetLimiteComunale(string id)
    {
        var geoData = new GeoRssData("GetLimiteComunale", "Seleziona comune");
        using (var db = new SpatialDataDataContext())
        {
            var result = db.GetLimiteComunale(int.Parse(id)).First();
            geoData.AddPolygon(result.Name, string.Format("Comunità Montana:{0}", result.Comunità_Montana), result.Polygon);
        }
 
        return new Rss20FeedFormatter(GeoRssFeedBuilder.GetFeed(geoData));
    }


sulla parte client richiamiamo il servizio WCF:

$(document).ready(function() {
 
    map = new VEMap("myMap");
    map.LoadMap();
    map.SetCenterAndZoom(new VELatLong(45.132, 7.438), 10);
 
    $("#btnIdGetLimiteComunale").click(function() {
        var request = "SpatialData.svc/GetLimiteComunale/" + $("#txtIdComune").val();
 
        var layer = new VEShapeLayer();
        var layerSpec = new VEShapeSourceSpecification(
                                    VEDataType.GeoRSS,
                                    request,
                                    layer);
 
        map.ImportShapeLayerData(layerSpec);
    });



Stessa cosa facciamo con la stored procedure GetLimitiComunaliLimitrofi. In questo caso viene mostrato come utilizzare le funzioni spaziali messe a disposizione da SQL Server (vedi http://msdn.microsoft.com/en-us/library/bb895266.aspx ). Nello specifico selezioniamo i comuni confinanti con il comune cercato.




Con la terza stored procedure (GetLimiteComunaleJSON) restituiamo il dato al servizio WCF in formato WKT e sul client inviamo i dati in formato GeoJSON che verranno poi processati mediante l'helper javascript per VE.


Sul client:
$("#btnIdAddPointGeoJSON").click(function() {
        var v = new VEGeoJSON(map);
        $.ajax({ type: "POST",
            url: "SpatialData2.svc/GetLimiteComunaleJSON",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            data: '{"id":"' + $("#txtIdComune").val() + '"}',
            success: function(data) {
 
                var geoJSON = eval('(' + data.d + ')');
                v.addGeoJSON(geoJSON);
            }
        });



Per la parte javascript ho utilizzato la libreria javascript jquery.

Scarica qui il codice della soluzione.