Archive for the ‘ArcGIS Server’ Category

AGS SOE’s without the SOAP

November 19, 2008

Vish recently wrote about calling AGS SOEs via SOAP instead of DCOM.  He doesn’t mention that AGS doesn’t make you use SOAP, nor XML for that matter. This low-tech JSON-RPC invoker shows the proof-of-concept:

public string HandleStringRequest(string Capabilities, string request){
  MethodCall mc = JavaScriptConvert.DeserializeObject<MethodCall>(request);
  object result = this.GetType().GetMethod(mc.name).Invoke(this, mc.parameters);
  return JavaScriptConvert.SerializeObject(result);
}

public string test(string str1, string str2){
  return str1 + str2;
} 

class MethodCall{
  public string name;
  public object[] parameters;
}

 Here’s an action shot…

fiddler

Using the ArcSDE Java API in .NET

November 4, 2008

I’m a .NET guy.  I have no desire to fool around with ArcSDE’s C API’s… building them, COM interop, whatever the heck would be required.  I’m also definitely reluctant to call SDE’s command-line tools from code.

Enter IKVM.  “IKVM.NET is an implementation of Java for Mono and the Microsoft .NET Framework.”  “IKVM.NET includes ikvmc, a Java bytecode to .NET IL translator.”  Run the ArcSDE Java API jars through ikmc, and you’ve got yourself a .NET ArcSDE library:

ikvmc jpe_sdk.jar -target:library
ikvmc icu4j_3_2.jar -target:library
ikvmc jsde_sdk.jar -target:library -r:jpe_sdk.dll -r:icu4j_3_2.dll
ikvmc concurrent.jar -target:library

The above .BAT file will generate 4 ArcSDE .NET dlls; giving you access to the Java API from .NET.  You also need to include the IKVM dlls in your project.  You loose some of the intellisense and error reporting goodness, but a quick test makes it look like all is working. :c)

AGS Tile Caching Explored

October 31, 2008

Recently I’ve noticed that ArcGIS does not store its tile-caching data in the standard service configuration file [Program Files\ArcGIS\server\user\cfg\myService.MapServer.cfg]. Instead these get saved to the cache directory [arcgisserver\arcgiscache\myService\Layers\conf.xml].

Why? The ArcGIS Server Manager gives us a clue if we try to edit the caching properties of a stopped service: “Caching properties and tools are available only when the service is started.”

This is interesting. The AGS Server Object Manager can edit the standard “myService.MapServer.cfg” files directly via the IServerObjectAdmin interface.  An AGS Server Object Container, or more precisely an IMapCooker2 instance, is needed to write the “conf.xml” file.  Here’s the general code:


IMapCooker2 cooker = pServerContext.CreateObject("esriCarto.MapCooker") as IMapCooker2;
cooker.Connect(physicalCacheDirectory, myMapName, myMap);
ITileCacheInfo pCacheInfo = cooker.TileCacheInfo;
ITileImageInfo2 pTileImageInfo = (ITileImageInfo2)cooker.TileImageInfo;
// do things here
cooker.WriteTilingScheme(pCacheInfo, pTileImageInfo, physicalCacheDirectory, LayerName);

Interesting, but here’s the catch:  I can’t find a way to access an AGS MapService’s IMapCooker2 instance at runtime.  My last post showed how to modify an AGS’s service layers at load.  However, this relied on runtime access to the IMap.  In essence, you can’t [AFAIK] change an AGS Mapservice’s SOC’s caching characteristics at runtime; you must restart the service to reload this data from the “conf.xml” file.

AGS + SDE – MXD + SOE

October 29, 2008

This little bit of ArcGIS Server Object Extension code can take an ArcGIS Service based on an empty MXD, and dynamically add all the feature classes from an SDE instance to it.  Its one of my first SOEs, but it seems to run just fine. Somewhat unique about this code is that this SOE has no callable methods, its mere presence is its power. :P

 

using System;
using System.Runtime.InteropServices;
using System.EnterpriseServices;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Server;

namespace com.wordpress.mapwrecker
{
    [AutomationProxy(true), ClassInterface(ClassInterfaceType.None), GuidAttribute("a9ed9e17-d2b2-461c-9da3-2915af9a3f00")]
    public class SDE_Eater : ServicedComponent, IServerObjectExtension, ILogSupport
    {
        private ESRI.ArcGIS.esriSystem.ILog2 m_log;

        private object getSingleton(string progId)
        {
            return Activator.CreateInstance(Type.GetTypeFromProgID(progId));
        }
        public void addAllSdeLayersService(IServerObjectHelper m_SOH)
        {
            IWorkspaceFactory sdeWorkFact = null;
            try
            {
                MapServer ms = m_SOH.ServerObject as MapServer;
                IMapServerObjects mso = ms as IMapServerObjects;
                IMap map = (IMap)mso.get_Map(ms.DefaultMapName);

                sdeWorkFact = (IWorkspaceFactory)getSingleton("esriDataSourcesGDB.SdeWorkspaceFactory");
                IPropertySet propertySet = getSdeProperties();
                IWorkspace pFWorkspace = sdeWorkFact.Open(propertySet, 0);
                IEnumDataset penumDataset = pFWorkspace.get_Datasets(esriDatasetType.esriDTFeatureClass);

                IDataset pDataset = penumDataset.Next();
                while (pDataset != null)
                {
                    IFeatureLayer featureLayer = new FeatureLayer();
                    featureLayer.FeatureClass = (IFeatureClass)pDataset;
                    m_log.AddMessage(3, 110000, "SDE SOE found: " + pDataset.Name);
                    featureLayer.Name = pDataset.Name;
                    map.AddLayer(featureLayer);
                    pDataset = penumDataset.Next();
                }
                mso.RefreshServerObjects();

            } catch (Exception e) {
                m_log.AddMessage(1, 110000, "SDE SOE error: " + e.StackTrace);
            } finally {
                int refsLeft = 0;
                do { refsLeft = Marshal.ReleaseComObject(sdeWorkFact); } while (refsLeft > 0);
            }
        }

        private IPropertySet getSdeProperties()
        {
            IPropertySet propertySet = new PropertySet();
            propertySet.SetProperty("SERVER", "blah");
            propertySet.SetProperty("INSTANCE", "sde:sqlserver:blah");
            propertySet.SetProperty("DATABASE", "SDE");
            propertySet.SetProperty("USER", "blah");
            propertySet.SetProperty("PASSWORD", "blah");
            propertySet.SetProperty("VERSION", "sde.DEFAULT");
            propertySet.SetProperty("AUTHENTICATION_MODE", "DBMS");
            return propertySet;
        }

        public void Init(IServerObjectHelper pSOH)
        {
            m_log.AddMessage(2, 110000, "SDE SOE is loaded");
            addAllSdeLayersService(pSOH);
        }

        public void Shutdown()
        {
            m_log = null;
        }

        public void InitLogging(ILog Log)
        {
            m_log = (ESRI.ArcGIS.esriSystem.ILog2)Log;
        }
    }
}

ArcGIS Server Virtual Earth Tile Server vs. ArcIMS

February 4, 2008

Time to change the name Dave.

     

using System;
using System.Text;
using ArcDeveloper.TileServer.Interfaces;
using System.Net;
using System.IO;
using System.Xml;
using System.Drawing;
using System.Drawing.Imaging;   

namespace ArcDeveloper.TileServer.ArcIMS
{
    public class ArcImsTileProvider : ITileProvider
    {
        ///
        /// Private member containing the web service url
        ///
        private string _webServiceUrl = "";   

        ///
        /// Constructor that takes a web service Url
        ///
        ///
        public ArcImsTileProvider(string webServiceUrl)
        {
            _webServiceUrl = webServiceUrl;
        }   

        ///
        /// Get the tile image
        ///
        ///
        ///
        ///
        /// Tile data stream
        public System.IO.MemoryStream GetTile(ITileExtent extent, int tileHeight, int tileWidth)
        {
            MemoryStream returnStream = null;   

            try
            {
                string axl = getAXL(extent, tileHeight, tileWidth);
                Stream responseStream = PostData(_webServiceUrl, axl);
                XmlDocument xd = new XmlDocument();
                xd.Load(responseStream);
                string imgURL = xd.GetElementsByTagName("OUTPUT")[0].Attributes["url"].Value;
                responseStream.Close();   

                Bitmap bm = new Bitmap(new WebClient().OpenRead(imgURL));
                returnStream = new MemoryStream();
                bm.Save(returnStream, ImageFormat.Png);
            }
            catch(Exception e)
            {
                Bitmap bm = GetMessageTile(e.Message);
                returnStream = new MemoryStream();
                bm.Save(returnStream, ImageFormat.Png);
            }   

            return returnStream;
        }   

        ///
        /// Gets the ArcIMS GetImage AXL request.
        ///
        ///
        ///
        ///
        /// GetImage ArcIMS AXL request
        private string getAXL(ITileExtent extent, int tileHeight, int tileWidth)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
            sb.Append("<ARCXML version=\"1.1\">");
            sb.Append("<REQUEST>");
            sb.Append("<GET_IMAGE>");
            sb.Append("<PROPERTIES>");
            sb.Append("<ENVELOPE minx=\"" + extent.lon2 + "\" miny=\"" + extent.lat2 + "\" maxx=\"" + extent.lon + "\" maxy=\"" + extent.lat + "\" />");
            sb.Append("<IMAGESIZE height=\"" + tileHeight + "\" width=\"" + tileWidth + "\" />");
            sb.Append("</PROPERTIES>");
            sb.Append("</GET_IMAGE>");
            sb.Append("</REQUEST>");
            sb.Append("</ARCXML>");
            return sb.ToString();
        }   

        ///
        /// Gets a tile with an error message burned into it.
        ///
        /// The message.
        /// An error message PNG
        private Bitmap GetMessageTile(string message)
        {
            Bitmap tile = new Bitmap(Properties.Resources.blankTile);
            Graphics graphics = Graphics.FromImage(tile);
            Font drawFont = new Font("Arial", 12);
            SolidBrush drawBrush = new SolidBrush(System.Drawing.Color.Black);
            SizeF len = graphics.MeasureString(message, drawFont);
            graphics.DrawString(message, drawFont, drawBrush, new PointF(10, 10));
            graphics.Dispose();
            return tile;
        }   

        ///
        /// Does an HTTP Post, returning a stream.
        ///
        /// The URL to post to
        /// The data to post
        /// The Http response stream
        private Stream PostData(string sURL, string postData)
        {
            HttpWebRequest webReq = null;
            HttpWebResponse response = null;
            Stream requestStream = null;
            StreamWriter writer = null;
            Stream responseStream = null;
            try
            {
                webReq = (HttpWebRequest)WebRequest.Create(sURL);
                webReq.Method = "POST";
                webReq.ContentType = "application/x-www-form-urlencoded";
                webReq.Timeout = 0xea60;
                webReq.AllowAutoRedirect = true;
                requestStream = webReq.GetRequestStream();
                writer = new StreamWriter(requestStream);
                writer.Write(postData);
                writer.Flush();
                writer.Close();
                requestStream.Close();
                response = (HttpWebResponse)webReq.GetResponse();
                responseStream = ((HttpWebResponse)webReq.GetResponse()).GetResponseStream();
            }
            finally
            {
                if (writer != null) writer.Close();
            }
            return responseStream;
        }   

    }
}   

Winners: Python-fearing .NET folks still using ArcIMS.  All six of you!

Losers: “ArcGIS Server Virtual Earth Tile Server” as a name.  Die.

Scrappad: ArcGIS Server 9.2 With A Reverse Proxy

April 16, 2007

There’s a nice write-up on Scrappad concerning Configuring ArcGIS Server 9.2 For .NET To Work With A Reverse Proxy .

It references an ESRI article that talks about using Apache for the reverse proxy. Scrappad recommends Microsoft’s ISA server for the same deal. If you’re in an IIS setting and that’s outside your price range, I recommend taking a look at the article I wrote on proxying ArcIMS traffic using ASP.NET HttpHandlers.

Register all the Web ADF Samples with IIS at Once

March 29, 2007

You know the drill… if you’re creating an ASP.Net web application, you need to configure its folder as an application within IIS. 

ArcGIS Server comes with 52 samples ADF web applications, 26 in C#, 26 in VB.Net.  Stick them into folders within your web root, and you’ll have 52 error messages telling you to configure your 52 folders as IIS applications.

Here’s a rough VBS script that walks your web root, wiring up an IIS application anywhere it sees web.config file:

FixAspxRoots.vbs

Dim IISroot
Dim objFSO
Set objFSO = CreateObject(“Scripting.FileSystemObject”)
Set IISroot = GetObject(“IIS://localhost/W3SVC/1/ROOT”)
objStartFolder = IISroot.path
WScript.Echo “Scanning for web.config files below ” + objStartFolder
ExamineSubfolders objFSO.GetFolder(objStartFolder)
IISroot.SetInfo()

Sub ExamineSubFolders(Folder)
 If (objFSO.FileExists(Folder.path + “\web.config”)) Then AddvirtualDir Folder.Path, Folder.name
 For Each Subfolder in Folder.SubFolders
  ExamineSubFolders Subfolder
 Next
End Sub
Sub AddVirtualDir(folderPath, folderName)

 Dim iispath
 Dim VirtualDir
 
 
 iisPath = replace(mid(folderPath, len(objStartFolder)+2), “\”, “/”)
 
 Set VirtualDir = Nothing
 On Error Resume Next
 Set VirtualDir = GetObject(“IIS://localhost/W3SVC/1/ROOT/” & iispath)
 On Error Goto 0
 If Not VirtualDir Is Nothing Then
  If (VirtualDir.class = “IISWebVirtualDir”) Then
   ’I'm assuming that this should be a webdir, not a vdir
   WScript.Echo “killing vdir ” & iispath
   Set IISParent = GetObject(VirtualDir.Parent)
   IISParent.Delete VirtualDir.class, VirtualDir.name
   IISParent.SetInfo()
   WScript.Echo “Creating IIsWebDirectory ” & iispath
   Set VirtualDir = IISroot.Create(“IIsWebDirectory”, iispath)
  Else
   WScript.Echo “updating  ” & VirtualDir.class & ” ” & iispath
  End If
 Else
  WScript.Echo “Creating IIsWebDirectory ” & iispath
  Set VirtualDir = IISroot.Create(“IIsWebDirectory”, iispath)
 End If
 ’I have no idea what half these settings mean
 VirtualDir.AppIsolated = 0
 VirtualDir.AppFriendlyName = folderName
 VirtualDir.AccessScript = true
 VirtualDir.AccessRead = true
 VirtualDir.SetInfo
 VirtualDir.AppCreate2 1
 VirtualDir.SetInfo
End Sub