Subscribe Now: Feed Icon

Monday, December 26, 2011

ArcObjects: TOC

List of ArcObjects posts:

  1. Introduction
  2. Getting Started
  3. Extending the Framework
  4. Workspace
  5. Workspace Provider
  6. Workspace is Down
  7. Licensing
  8. Com Releaser
  9. Cursors and IsRecycling
  10. WorkspaceUtils
  11. Multithreading
  12. Mocking

 

IceRocket Tags:

ArcObjects: Workspace is Down

This post is the sixth post in my ArcObjects series.

This is a direct continuation from my post ArcObjects: Workspace Provider.

 

Back in March our DB was down over the night and unlike all the other systems ours didn't just resume work after the DB was brought up, it needed the team’s involvement.

The Exception we got was:

System.Runtime.InteropServices.COMException was unhandled

  HelpLink=esri_csGeoDatabase.hlp

  Message=Failure to access the DBMS server [Microsoft SQL Server Native Client 10.0: Communication link failure] [SERVER.SCHEMA.LAYER_NAME]

  Source=esriDataSourcesGDB.SdeWorkspace.1

  ErrorCode=-2147155559

  StackTrace:

       at ESRI.ArcGIS.Geodatabase.IFeatureClass.Search(IQueryFilter filter, Boolean Recycling)

  InnerException:

 

So I recreated the error by restarting the "SQL Server (MSSQLSERVER)" service in our DB with some special code.

 

Symptoms:

1.       The IWorkspace status:

((IWorkspace) _workspace).Exists();

                Returns true

2.       Then I started looking at other places like ESRI’s diagrams. One of the diagrams:

image

(Not mine EDN's)

IWorkspaceStatus sounded a bit off to me that it is under WorkspaceFactory and not under Workspace but the real problem was what was written in the documentation. There are no implementations of this according to EDN…

But I found another interface IWorkspaceFactoryStatus, managed to point it to my workspace and got back:

((ESRI.ArcGIS.Geodatabase.IWorkspaceFactoryStatus  )workspaceFactoryTemp).PingWorkspaceStatus((ESRI.ArcGIS.Geodatabase.IWorkspace)_workspace)

{System.__ComObject}

    [System.__ComObject]: {System.__ComObject}

    ConnectionStatus: esriWCSAvailable

    Workspace: {System.__ComObject}

 

I just thought to myself how can it possibly be Available, until I look at the documentation at esriWorkspaceConnectionStatus:

image

Available doesn't mean it's up - just that it was lost and now is available. And I was sure it meant something else… (Couldn’t they name it something like LostAndNowAvailable or AvailableToReconnect? – Available just does not cut it!!!).

 

The end result looks like this:

  1. if (!IsWorkspaceRunning(_workspace))
  2. {
  3.     ReactivateWorkspace();
  4.     InitializeWorkspaceUtils(_workspace);
  5. }
  6. return _workspaceUtils;

Checking if the workspace is running:

  1. private bool IsWorkspaceRunning(IWorkspace workspace)
  2. {
  3.     IWorkspaceFactoryStatus workspaceFactory = new SdeWorkspaceFactoryClass();
  4.     return workspaceFactory.PingWorkspaceStatus(workspace).ConnectionStatus == esriWorkspaceConnectionStatus.esriWCSUp && workspace.Exists();
  5. }

If it’s down recreate it (and if it can’t be recreated throw an exception):

  1. private void ReactivateWorkspace()
  2. {
  3.     IWorkspaceFactoryStatus workspaceFactory = new SdeWorkspaceFactoryClass();
  4.     var status = workspaceFactory.PingWorkspaceStatus(_workspace);
  5.     if (status.ConnectionStatus != esriWorkspaceConnectionStatus.esriWCSDown)
  6.     {
  7.         _workspace = workspaceFactory.OpenAvailableWorkspace(status);
  8.     }
  9.  
  10.     //try to create it again:
  11.     _workspace = CreateWorkspace(_connectionString);
  12.     status = workspaceFactory.PingWorkspaceStatus(_workspace);
  13.  
  14.     if (status.ConnectionStatus == esriWorkspaceConnectionStatus.esriWCSDown)
  15.     {
  16.         throw new ApplicationException("The Connection to the database was lost. Please contact your system administrators.");
  17.     }
  18. }

You can also look at the full code here.

 

Hopefully next time the DB is down it will just recreate IWorksapce…

 

Resources:

The forum post I wrote

ArcObjects: Workspace Provider

This post is the fifth post in my ArcObjects series.

As I mentioned in my previous post you cannot create a workspace you have to receive it from some place. That place is called the WorkspaceFactory (or as usual IWorkspaceFactory).  Now since getting the workspace is useful and usually only done once we put it in a singleton class named WorkspaceProvider.

As can be seen in the IWorkspaceFactory documentation there are many implementation of this interface. The implementation differ in the Space where the workspace is doing it’s work:

  1. SdeWorkspaceFactory – works on a Sde DB
  2. CadWorkspaceFactory – works on CAD files
  3. AccessWorkspaceFactory and FileGDBWorkspaceFactory – work on File based Geodatabases

These classes provide the interface to connect to the Space the same way ArcDesktop tools do so by the Open method. You can either use a file containing the connection (created in ArcCatalog), use IPropertySet a list containing the connection information (see here for details) or by using connection string (the other options just use this option behind the scene).

Sde connection string are separated to two categories (more details in one of my previous posts: ArcSDE: Connection to the Geodatabase):

  • Regular connection: "SERVER={0};INSTANCE={4};USER={2};PASSWORD={3};Database={1};VERSION=SDE.Default"
  • Direct connection: "SERVER={0};INSTANCE=sde:{5}:{0};USER={2};PASSWORD={3};Database={1};VERSION=SDE.Default"

Where:

  • {0} – Server name
  • {1} – Database name
  • {2} – User name
  • {3} – password
  • {4} – Sde service number
  • {5} – Type of the server (i.e. sqlserver)
  • If you need to you can also add the Sde version

As you can see direct connection doesn’t use the Sde service number, that is because it doesn’t use the Sde service (it does all the work at the client). Most times you won’t need to change the Server type so that’s out as well. My code only uses Direct Connection string (since ESRI recommends using it).

 

In my experience most applications will use only one IWorkspace – the one connecting them to the application Sde. For that reason WorkspaceProvider uses DefaultDbConfigSection which is both a Configuration Section and a Connection String builder (I use it for both the Sde and regular DB Connection string). To prevent unnecessary creation of IWorkspace objects WorkspaceProvider will also store the last IWorkspace created and the connection string used to create it.

As it is the only public methods needed for this class are:

1. Using Sde Connection String from web/app.config file:

  1. public WorkspaceUtils GetWorkspace()
 

2. Using Sde Connection String provided:

  1. public WorkspaceUtils GetWorkspace(string sdeConnectionString)

3. Using a Cad file:

  1. public CadWorkspaceUtils GetCadWorkspace(string path)

4. Using either GDB or MDB file paths:

  1. public FileWorkspaceUtils GetFileWorkspace(string filePath)

The full code can be found here.

 

Most of the code is self-explanatory except one part – getting a cached Workspace. Now you might think this is the most simple part but when taking into account that sometimes connections fail, servers are down and Murphy is constantly prowling than you must be ready for that “What If…”. I will write more on this in one of the next posts.

The rest of the code is pretty much self-explanatory and can be found in my CodePlex Project, or directly here.

 

Resources:

MSDN: How to: Create Custom Configuration Sections Using ConfigurationSection

Sunday, December 18, 2011

ArcSDE: Connection to the Geodatabase

When connection to a database SDE there are two ways to achieve that:

(From ESRI site the ArcCatalog connection screen)

1. Regular connection using Server, Service (enter the SDE service number), database, user(optional), password(optional)

The regular way uses a service on the database machine which must be running. The service can be found in:

Right click on My Computer –> Manage:

image

Computer Management –> Services And Applications –> Service:

image

By Default the service will be called “ArcSde Service(esri_sde)”:

image

As you can see I am not using the regular way (the service is not marked as started). For both 10 and 9.3.1 ESRI advices not to use it this way since it does all it’s work on the DB machine.

Note: if you are using several sde services than the Service port will differ than the default of 5151. If you are unsure about the number than look in the DB server at services file (located at: c:\Windows\System32\drivers\etc and has no extension). The file contains lines for services, their port and connection protocol:

esri_sde    5151/tcp    #ArcSDE for SqlServer

2. Direct Connection using Server, Service, database, user(optional), password(optional)

The difference in the connection is only in Service, for example in SQL Server: “sde:sqlserver:<sql_server_instance>” (for any other server use this ESRI reference the forth step).

This connection doesn’t use the SDE service. It connects directly to the DB and does all it’s work on the client. If you are using only this connection type then I advise you to change the “ArcSde Service(esri_sde)” to run Manually instead of Automatic – it just means that at startup the service won’t be turned on.

 

Managing SDE connection in a developer team

I have created all my SDE connection but now my team mate Bob needs a connection, What do I do?

Well I usually save the connections in one of the team’s network folders with a bat file called install connections. Here is how I do that:

  1. Copy all the connections from %APPDATA%\ESRI\Desktop10.0\ArcCatalog to your network folder.
  2. Create a bat file with:

xCopy *.sde %APPDATA%\ESRI\Desktop10.0\ArcCatalog /I

 

(The /I is for creating the sub folder when they don’t exist (like when you never opened ArcCatalog…))

 

Resources:

ESRI: Geodatabase connections in ArcGIS Desktop

ESRI: Preparing database connection files

ESRI: What is a direct connection to a geodatabase in DB2?

Help about xCopy

MSD tools: Changing Connection and Publishing

Last month I got a new assignment to develop a tool that changes the layers’ connection in a MXD file and publish it in ArcGis Server. It is fairly easy to do it using ArcMap but we have too many environments/servers and whatever we can automate should be automated.

Since then the team went to a Systematics Event (ESRI local provider) where they talked about the new stuff in version 10.1, namely that publishing will only support MSD files (not MXD files) – so the task got changed a bit. For more information on MSD functionality and Performance considerations.

Finding how to change the connection of the layers was easy enough with the IMSDHelper interface:

  1. private const bool UseRelativePath = true;
  2. private const bool ValidateLayerConnection = false;
  3.  
  4. /// <summary>
  5. /// Changes the SDE connection string for all the layers in the MSD file
  6. /// </summary>
  7. /// <param name="msdFilePath"></param>
  8. /// <param name="connectionString"></param>
  9. /// <param name="resultMsdFile">result file is relative path</param>
  10. public static void ChangeConnection(string msdFilePath, string connectionString, string resultMsdFile)
  11. {
  12.     IMSDHelper helper = new MSDHelper();
  13.     helper.Open(msdFilePath);
  14.     var maps = helper.GetMaps();
  15.     for (var i = 0; i < maps.Count; i++)
  16.     {
  17.         var layers = helper.GetLayers(maps.Element[i]);
  18.         for (var j = 0; j < layers.Count; j++)
  19.         {
  20.             helper.PutWorkspaceConnectionStringInLayer(layers.Element[j], connectionString, ValidateLayerConnection);
  21.         }
  22.     }
  23.     helper.SaveAs(resultMsdFile, UseRelativePath);
  24. }

We simply iterate through all the maps and all the inner layers and change the connection to the given one.

 

But what about Publishing the MSD automatically? This proved to be a bit more problematic. It was fairly easy to find a Python script using arcPy that publishes MSD files. But less so on ArcObjects code, in the end I found this forum post which led me to this code (just run it through Google Translate since it’s in Chinese). The code looks like this:

  1. public static void PublishToServer(string mapFilePath, string serverName, string serviceName)
  2. {
  3.     IGISServerConnection gisServerConnection = new GISServerConnectionClass();
  4.     gisServerConnection.Connect(serverName);
  5.     var serverObjectAdmin = gisServerConnection.ServerObjectAdmin;
  6.     var configuration = (IServerObjectConfiguration2)serverObjectAdmin.CreateConfiguration();
  7.  
  8.     // release the name of the Service, required
  9.     configuration.Name = serviceName;
  10.     // release the type of service, such as: MapServer, GeocodeServer
  11.     configuration.TypeName = ServiceConfigurationTypeName;
  12.     configuration.Description = serviceName;
  13.  
  14.     // Service Description
  15.     SetDefaultServerConfigurations(configuration, mapFilePath, serverName);
  16.  
  17.     // add service to the Server
  18.     serverObjectAdmin.AddConfiguration(configuration);
  19.  
  20.     // start the service
  21.     serverObjectAdmin.StartConfiguration(configuration.Name, configuration.TypeName);
  22. }

This is to set the default values of my organization:

private static void SetDefaultServerConfigurations(IServerObjectConfiguration2 configuration, string mapFilePath, string serverName)
{
    var properties = configuration.Properties;
    // Set the path of the file
    properties.SetProperty("FilePath", mapFilePath);
    // image output directory
    properties.SetProperty("outputdir", "c:\\arcgisserver\\arcgisoutput");
    // image output virtual path
    properties.SetProperty("VirtualOutPutDir", "http://" + serverName + "/arcgisoutput");
    // image type supported by
    properties.SetProperty("SupportedImageReturnTypes", "URL");

(there are more settings that can be found here)

 

I found that it is best to look at existing services, for that I added the capability of getting your service settings in XML format:

  1. public static string GetServiceConfiguration(string serverName, string serviceName)
  2. {
  3.     IGISServerConnection gisServerConnection = new GISServerConnectionClass();
  4.     gisServerConnection.Connect(serverName);
  5.     var serverObjectAdmin = gisServerConnection.ServerObjectAdmin;
  6.  
  7.     var configuration = (IServerObjectConfiguration4)serverObjectAdmin.GetConfiguration(serviceName, ServiceConfigurationTypeName);
  8.     
  9.     return configuration.Serialize();
  10. }

The output of this method looks like this:

  1. <ServerObjectConfiguration>
  2.   <Description>PublishDemoIi</Description>
  3.   <Properties>
  4.     <FilePath>C:\Install\MsdUtils\Demo.msd</FilePath>
  5.     <outputdir>c:\arcgisserver\arcgisoutput</outputdir>
  6.     <VirtualOutPutDir>http://server/arcgisoutput</VirtualOutPutDir>
  7.     <SupportedImageReturnTypes>URL</SupportedImageReturnTypes>
  8.     <MaxImageHeight>2048</MaxImageHeight>
  9.     <MaxRecordCount>50000</MaxRecordCount>
  10.     <MaxBufferCount>100</MaxBufferCount>
  11.     <MaxImageWidth>2048</MaxImageWidth>
  12.     <IsCached>false</IsCached>
  13.     <CacheOnDemand>false</CacheOnDemand>
  14.     <IgnoreCache>false</IgnoreCache>
  15.     <ClientCachingAllowed>true</ClientCachingAllowed>
  16.     <CacheDir>c:\arcgisserver\arcgiscache\GtmPublishDemoIi</CacheDir>
  17.     <SOMCacheDir>c:\arcgisserver\arcgiscache</SOMCacheDir>
  18.     <UseLocalCacheDir>true</UseLocalCacheDir>
  19.   </Properties>
  20.   <Extension>
  21.     <TypeName>FeatureServer</TypeName>
  22.     <Enabled>true</Enabled>
  23.     <Properties>
  24.       <EnableZDefaults>false</EnableZDefaults>
  25.       <ZDefaultValue>0</ZDefaultValue>
  26.     </Properties>
  27.     <Info>
  28.       <WebEnabled>true</WebEnabled>
  29.       <SupportsMSD>true</SupportsMSD>
  30.       <WebCapabilities>Query</WebCapabilities>
  31.     </Info>
  32.   </Extension>
  33.  
  34.   <Extension>
  35.     <TypeName>...</TypeName>
  36.     <Enabled>false</Enabled>
  37.     <Properties>
  38.     </Properties>
  39.     <Info>
  40.     </Info>
  41.   </Extension>
  42.  
  43.   <Info>
  44.     <WebEnabled>true</WebEnabled>
  45.     <WebCapabilities>Map,Query,Data</WebCapabilities>
  46.   </Info>
  47.   <IsPooled>true</IsPooled>
  48.   <MinInstances>1</MinInstances>
  49.   <MaxInstances>2</MaxInstances>
  50.   <InstancesPerContainer>1</InstancesPerContainer>
  51.   <WaitTimeout>60</WaitTimeout>
  52.   <IdleTimeout>-1</IdleTimeout>
  53.   <UsageTimeout>600</UsageTimeout>
  54.   <CleanupTimeout>30</CleanupTimeout>
  55.   <ServiceKeepAliveInterval>-1</ServiceKeepAliveInterval>
  56.   <StartupTimeout>300</StartupTimeout>
  57.   <Isolation>high</Isolation>
  58.   <StartupType>automatic</StartupType>
  59. </ServerObjectConfiguration>

As you can see the properties in the XML correspond perfectly with the properties settings.

 

To test the creation of the service you can look at ArcGis Server Manager:

http://ServerName/ArcGIS/manager/default.aspx

ArcGisServerManagementService

Where you can tweak the settings (as you can see I had 5 tries till I got it right…).

Also you can test the service using the REST API :

http://ServerName/ArcGIS/rest/services

ArcGisRestService

Looking into the FeatureServer link you will find in the bottom “Supported Operations: Query” where you can test the Feature service queries:

FeatureServiceQuery

 

Some points:

  1. Using PublishToServer needs MSD file located on the server.
  2. In order to use PublishToServer user must be an admin on the server (otherwise the call gisServerConnection.ServerObjectAdmin would fail)
  3. You must clear ArcGis rest API cache before using the service (or you won’t see it in the rest/services url) – for doing that automatically see my previous post: Clearing ArcGIS Server REST API Cache

I have posted the complete tool in my Codeplex project: ArcGisServerTools, MsdUtils, ArcGisServerUtils.

 

Resources:

ArcGis Server Forum: Promote map service from one server stage to another in automated fashion?

Findleaf - Chinese Blog code

ArcObjects Forum: Publish MXD to server in ArcObjects C#

 

Keywords: MSD, ArcObjects, ArcGis Server, ESRI

Sunday, December 11, 2011

Active Window Loses Focus problem (Full Screen minimizes)

For the past several years I have been having a problem with my home computer (Windows XP), the problem is not so severe just annoying (though I guess for some people it will be much more severe). My problem? from time to time the application I work on losses it’s focus for a second and then either:

a. gets it back (when it’s in windowed state) 

b. another application gets the focus (when it’s full screen applications).

My solution to the problem was using only windowed applications but it is too annoying when watching movies or sometimes impossible when playing games.

From time to time I would search the web for solutions and find forums that advise people to open Task Manager (or other similar applications) and just see what application is stealing the focus – the problem is you need to be fairly fast to catch the bugger (especially when games and movies demand your full attention). Other solutions were with specific applications that caused the problem like AVG anti virus (in one of its old versions). I have tried formatting the computer but the problem returned (meaning it must be some application I always use).

So I have decided to fix (or at least find the problematic application) myself. I first found a windows function that partially does what I wanted – GetForegroundWindow, then searching for it with C# I found a solution at StackOverflow site a bunch of invoke methods that return the current focused window information, all that was left was wrapping it in a console application and using it.

In the end running the little app at home found the problem:

27/11/2011 20:22:10:C:\WINDOWS\System32\rasautou.exe -> Connecting Cables012...

For the past several years my computer has been the dial up computer for the internet. Cables012 is that connection. Thinking to solve connection problems I enabled “Establish a dial-up connection whenever a computer on my network attempts to access the Internet”:

BeforeInternetConnection

I thought to myself that this option will just reconnect to the internet when the connection is down, well it does that and also steals the windows focus whenever it feels like and at exactly midnight it also activate what I like to call “Blinking Window Mode” when at least one window will appear in the general top left area of the screen and disappear before you can read what it says…

Well the fix was easy enough just uncheck this option and restart the connection. This whole thing was a surprise since I was sure I had some kind of funky spyware app installed which caused all of this…

I have added the source code for this little app to my Codeplex project, and also a release exec (for the non programmers who reach this site).

 

Keywords: Windows, Full Screen, Focus

IceRocket Tags: ,,

Wednesday, November 23, 2011

Clearing ArcGIS Server REST API Cache

Whenever you change a layer in the DB or publish a new server you might notice that the REST API will not automatically update. In order for it to update you might need to clear the REST API cache.

There are several ways to do so:

The default way:

ArcGis Server comes with a REST API admin page, which can be found here:

http://SERVERNAME/ArcGIS/rest/admin

Logging in to this page gives you the Clear Cache Options:

The page has a bunch of options for clearing the catch semi-automatically:

What you need to do is simply click on the "Clear Cache Now" several times (at some version of the Server one time was not enough…).

Disadvantages:

1. You need to have the admin user/password in order to clear the cache
2. The process needs to be done manually.
3. If you have multiple server you will have to do so for each server

 

The token way:

ESRI gives another way which is to use a generated token and then you can simply call a link whenever you want to clear the cache. More on that here (ESRI refers to this method as “the easy way”).

Disadvantages:

1. Different token for each server
2. The process needs to be manually set at each server

 

With Console Application (with code):

The problem with all of these methods is that you can’t really use them to install servers, at my company we have more servers then flies and having to clear the cache after each installation is tedious work that is often forgotten (and when it is forgotten I find myself wasting hours trying to debug the “problem”). So I decided to write a console application that does the clearing of the cache for me. This idea actually came from this forum post, the only problem was that the code didn’t really work for me…

So I tweaked it a bit with WebRequest and the end result is this:

  1. public static string ClearCache(string serverName, string userName, string password)
  2. {
  3.     var result = HttpUtils.PostRequest("http://" + serverName + "/ArcGIS/rest/admin/login",
  4.                             new List<KeyValuePair<string, string>>
  5.                                 {
  6.                                     new KeyValuePair<string, string>("username", userName),
  7.                                     new KeyValuePair<string, string>("password", password),
  8.                                     new KeyValuePair<string, string>("redirect", String.Format("http%3a%2f%2f{0}%2fArcGIS%2frest%2fadmin%2fcache%2fclear", serverName)),
  9.                                 }, String.Format("http://srvp7d-gtm/ArcGIS/rest/admin/login?redirect=http%3a%2f%2f{0}%2fArcGIS%2frest%2fadmin%2fcache%2fclear", serverName));
  10.  
  11.     //The response is in html, only return the response if its not a success
  12.     return result.Contains("Cache Cleared.") ? "Cache Cleared." : result;
  13. }

 

And HttpUtils.PostRequest looks like:

  1. public static string PostRequest(string url, List<KeyValuePair<string, string>> paramters, string referer)
  2. {
  3.     var req = WebRequest.Create(url);
  4.     req.ContentType = "application/x-www-form-urlencoded";
  5.     req.Method = "POST";
  6.  
  7.     var parameters = GetParameters(paramters);
  8.  
  9.     //We need to count how many bytes we're sending.
  10.     var bytes = Encoding.ASCII.GetBytes(parameters);
  11.     req.ContentLength = bytes.Length;
  12.  
  13.     ((HttpWebRequest)req).CookieContainer = new CookieContainer();
  14.     if (!String.IsNullOrEmpty(referer))
  15.         ((HttpWebRequest)req).Referer = referer;
  16.  
  17.     var os = req.GetRequestStream();
  18.     os.Write(bytes, 0, bytes.Length); //Push it out there
  19.     os.Close();
  20.     var resp = req.GetResponse();
  21.     if (resp == null)
  22.         return null;
  23.     var sr = new StreamReader(resp.GetResponseStream());
  24.     return sr.ReadToEnd().Trim();
  25. }

I think that this way is the best since all I need is the server name, the admin user and it’s password. I can do it remotely and I can do it automatically after each deploy. The only thing that is not so good is the using of the clear password (and lets be honest here the next step is putting the call to the console application in a batch file for the development environment and giving that file to the rest of the team so that a hacker…).

Source code can be found at my Codeplex project, here (the code has a few more useful commands like publishing a MSD file, changing the connection of MSD file etc.).

 

Resources:

Clearing the ArcGIS Services Directory cache “the easy way” – ArcGIS Server Blog

Clear cache through code by demand – ArcGis Server Forum

Hanselman: HTTP POSTs and HTTP GETs with WebClient and C# and Faking a PostBack

 

Keywords: ESRI,ArcGIS Server,REST API

IceRocket Tags: ,,

Tuesday, November 15, 2011

ArcObjects: Workspace

This post is the forth post in my ArcObjects series.

As I have written in ArcObjects: Introduction, using ArcObjects is one way of performing CRUD operations against a Spatial DB (with SDE layer on top) and the way to do that is by using the intefaces of WorkspaceClass.

ESRI definition for the Workspace is this:

A Workspace is a container of spatial and non-spatial datasets such as feature classes, raster datasets and tables. It provides methods to instantiate existing datasets and to create new datasets.

Mine is a bit more simple, the WorkspaceClass is the class that does all the “work” within the “space” it is defined to work at. The Space section of the definition can be a DB Sde, File Geodatabase, Cad files and anything else ArcDesktop applications can open. The Work section refers to any basic action that can be done with ArcDesktop applications.

For example: IFeatureWorkspace allows you to create a new Feature Table, open an existing Feature Table and with the help of IFeatureClass perform CRUD operations on that table.

One important note however is that you cannot create a new instance of it, it must be returned for you – the next post will be on getting the workspace.

WorkspaceClass-Object

As you can see the WorkspaceClass implements a lot of interfaces, the good news is you don’t need most of them (I have only found usage for two of them).

What do you need then?

  1. IFeatureWorkspace – probably the most important interface, will allow you to edit features in the Space section
  2. IWorkspaceDomains – allows the creation, deletion of domains (important only if you want to automate the deployment process and using domains)

So how do we use it?

  1. protected internal readonly IFeatureWorkspace _workspace;

  1. protected IFeatureClass GetFeatureClass(string layerName)
  2. {
  3.     return _workspace.OpenFeatureClass(layerName);
  4. }

This actually the only thing you can do with just the IFeatureWorkspace, but if you actually want to do something like  return all the feature in a layer you need to use the IFeatureClass:

  1. private const bool IsRecyclingCursorInGetFeatures = false;

  1. private IQueryFilter GetQueryFilter(string whereClause)
  2. {
  3.     return new QueryFilter {WhereClause = whereClause};
  4. }
  1. protected List<IFeature> GetLayerFeatures(string layerName)
  2. {
  3.     var result = new List<IFeature>();
  4.     var filter = GetQueryFilter(EmptyWhereClause);
  5.     var featureClass = GetFeatureClass(layerName);
  6.     using (var comReleaser = new ComReleaser())
  7.     {
  8.         var cursor = featureClass.Search(filter, IsRecyclingCursorInGetFeatures);
  9.         comReleaser.ManageLifetime(cursor);
  10.  
  11.         var feature = cursor.NextFeature();
  12.         while (feature != null)
  13.         {
  14.             result.Add(feature);
  15.             feature = cursor.NextFeature();
  16.         }
  17.     }
  18.     return result;
  19. }

So what do we have here?

line 4: creates the IQueryFilter – this goes into the where clause of the SQL we will send to DB.

line 5: we get the IFeatureClass of the layer

line 6 and 9: this is important ArcObjects use Com which is a resource, every resource we use must be disposed. At kine 6 we create the disposing class and in line 9 we attach the cursor to that disposing class, when line 17 arrives the using statement will call the Dispose method for ComReleaser  and automatically dispose our cursor. More on cursor and disposing of the resources will be written in a later post.

line 8: creates the cursor that we will use to iterate the rows of the DB, IsRecyclingCursorInGetFeatures defines the way the cursor works (more on that on a later post)

lines 12-16: iterating the data

 

And that’s it.

IceRocket Tags: ,

Monday, October 10, 2011

Simple Web Mapping

A friend of mine called me a few months ago and asked for advice on implementing a simple mapping application on the web. He remembered I worked on a mapping project and wanted my help.

His client wanted him to implement a web based site with a map of the client stores. The map should be customizable, meaning you could add items and remove them. And when I write map I mean a map of the interior of the store not it’s location in the world.

Now all the mapping options I know of are bulky and not quite simple but I promised him I will look it up for him.

The first thing I did was Googling “web simple map” got myself this results:

But the options were neither simple nor easy to implement.

Then I got back to the requirements – mapping a store. That is almost like my B.A. final project of internal design for apartments, that had a 3D look to it but the design mode was at it’s base a 2D painter. And that is what the requirements wanted a Painter that has only basic drawing tools like placing certain items on the canvas.

The background for this application will be an image of the store. Saving the canvas will save the items’ locations in the canvas, while loading will repaint them in place.

And the best part there is already an open source Painter application around in Java Script Called Canvas Paint (a copy of Microsoft’s Paint application in the Web) the script is also included here:

web-painter

The File->Save option allow saving the image on the server…

 

In the end my friend decided to create a demo using this site, which allows you to place points on an image:

mapsalive-floor-plan

(when the mouse is over a point you get a tooltip with an image)

If the client likes it he will actually program something similar.

 

So, I guess what I am saying is check out the requirements before you jump to implementing a GIS solution. Mapping for the client doesn’t always mean using world coordinates (and often the client won’t even know what world coordinates mean).

 

Keywords: mapping, simple, paint

IceRocket Tags: ,,

Special Achievement in GIS (SAG) 2011

I know its been a while…

The past month or so I have been mostly working on bug fixes since we were deploying a new version. But enough about that, ESRI gave us this little award for Special Achievement in GIS 2011.

SAG-1

 

We haven’t gone to the award ceremony but we did a little photo session of our own:

SAG-2

From left to right: Michael Halperin (the GIS team leader), Gur Hanan (GIS specialist), Vered Kestenbaum (GIS specialist, in charge of all installations with ArcGis Desktop), Me (mostly server side GIS programming), Felix Shkolnik (our UI GURU, since he arrived the UI has changed colors and shapes more times than I though possible – now it looks great), Eyal Perez (our manager, also the name on the award).

 

Remember I wrote UI GURU? Well it took less than an hour for Felix to create the real photo:

RealBetterPlace

I am told this photo has made its rounds in Systematics (the local vendor for ESRI in Israel).

 

IceRocket Tags:

Tuesday, August 23, 2011

List Foreach+Add = InvalidOperationException (Collection was modified)

One of the projects I work on is a windows service that listens to the Tibco bus and handles messages. Since it always runs we decided to use it for another purpose – periodic calls. Calls that need to be done every few seconds/minutes.

One of those calls is for handling traffic load reports and another for handling some messages in bulk (we receive thousands of those messages every minute and required to handle them every 15 seconds). The traffic load synching is added on startup but the bulk handler is added later.

For that purpose I created a nice little class called PeriodicCaller that has a List of PeriodicCall (Action, when was last called, TimeSpan between calls). PeriodicCaller starts a new thread that calls the list using foreach.

The problem we faced though was that sometimes the Add operation failed, and we were unlucky enough to receive this exception:

System.InvalidOperationException was unhandled
  Message=Collection was modified; enumeration operation may not execute.
  Source=mscorlib
  StackTrace:
       at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
       at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
       at System.Collections.Generic.List`1.Enumerator.MoveNext()
       at ListAddAndForEach.PeriodicCaller.Caller()

Why is luck a factor? Because this only happens when the Add is called in the middle of the foreach loop.

The fix is quite easy instead of using a foreach loop use a for loop (and just ignore Resharper’s warning:

For-resharper-warning

because sometimes they are just wrong…).

Tuesday, July 12, 2011

Silverlight: Adding Google Streets View, part 2

Continued from Adding Google Streets View, using Google API V3 instead of V2

Or actually “ops…”

During the testing stage for our application my Team Leader installed the application on another server and tried to run it. He got this pop-up for his trouble:

Google-API-V2-key-warning

Message from webpage

This web site needs a different Google Maps API key. A new key can be
generated at http://code.google.com/apis/maps/signup.html.

Now while using the original code I had a problem with one line of code:

  1. <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=DioG342lPJG3WTn3zmQqebsjVg" type="text/javascript"></script>

More precisely the part of: key=DioG342lPJG3WTn3zmQqebsjVg

But I thought to myself – “It works…”

Well the bite in the ass has come…

So what is this key?

It seems Google Map API V2 requires each domain to be registered and then to use an API key. I didn’t want to do that because we had too many servers with different domains (with new environments added all the time).

Getting such a key though is as easy as registering here, but be sure to first read the FAQ (more on that in my previous post).

 

Google API V3 to the rescue…

In Google API V3 they removed the need for API key, but they also changed the API so that the current JavaScript code doesn’t work.

So I rewrote it to this:

Code Snippet
  1. <script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>
  2. <script type="text/javascript">
  3.  
  4.     function streetView(lat, lng) {
  5.         var client = new google.maps.StreetViewService();
  6.         var streetViewLocation = new google.maps.LatLng(lat, lng);
  7.         client.getPanoramaByLocation(streetViewLocation, 50, function (result,
  8.             status) {
  9.             if (status == google.maps.StreetViewStatus.OK) {
  10.                 var panoramaOptions = {
  11.                     position: streetViewLocation,
  12.                     pov: {
  13.                         heading: 34,
  14.                         pitch: 10,
  15.                         zoom: 1
  16.                     },
  17.                     visible: true,
  18.                     addressControl: true
  19.                 };
  20.                 document.getElementById("GoogleStreetViewContainer").style.display = 'block';
  21.                 var panorama = new google.maps.StreetViewPanorama(document.getElementById("GoogleStreetviewObject"), panoramaOptions);
  22.             }
  23.         });
  24.     }
  25. </script>

Much more simple code:

getPanoramaByLocation – returns status OK for coordinates that have Street View 50 (set in a parameter) meters from them.

Be sure though to place (line 20):

  1. document.getElementById("GoogleStreetViewContainer").style.display = 'block';

Before calling StreetViewPanorama or you will get an “Out of stack space” error (I opened a bug on this but since the work around as fairly simple it probably won’t be fixed any time soon…).

One last warning the functionality is almost the same. The only difference I could find is that V3 hasn’t implemented Full Screen mode yet (unknown when it will be added or if it will) where V2 has it out of the box.

V2:

GoogleStreetViewV2

V3:

GoogleStreetViewV3

Seems almost the same…

 

Code sample can be found in my CodePlex Project, or directly here.

 

Edit (13/09/2011): Fixed Firefox bug in JavaScript

 

Keywords: Silverlight, Map, Google Street View, Google Map API