Select to view content in your preferred language

Download Data from ArcGIS Server using Out-of-the-Box Solution

5363
19
10-14-2010 12:31 PM
AkhilParujanwala
Regular Contributor
Hi,

I am trying to download data from my server without having to click on Manage Edits > Get Data, I want have a single click button to download the data.

The code posted below should work with a fully customized solution, but I am curious as to how I can modify the code so that it can be used to download data just like the Get Data method that ESRI provides.

MobileCache m_mobileCache1;
m_mobileCache1 = new MobileCache(@"C:\user\MobileCache");            
m_mobileCache1.Open();                        
Map mMap = new Map();
mMap.MapLayers.AddRange(m_mobileCache1);
            
if (m_mobileCache1.CacheExists)
{
   m_mobileCache1.Close();
   m_mobileCache1.DeleteCache();
}

MobileServiceConnection con = new MobileServiceConnection();
con.Url = @"http://server/arcgis/services/Test_Mobile_Services/Radiation_Data_V1_B11/MapServer/Mobileserver";
con.CreateCache(m_mobileCache1);
m_mobileCache1.Open();
            
            
MobileCacheSyncAgent agent = new MobileCacheSyncAgent(m_mobileCache1);
agent.MapDocumentConnection = con;            
agent.DownloadExtent(m_mobileCache1.GetExtent(), 0, 0);           
mMap.MapLayers.AddRange(m_mobileCache1);


Thanks,

Akhil P.
0 Kudos
19 Replies
AkhilParujanwala
Regular Contributor
mapControl1.MapLayers.AddRange(m_mobileCache1);


This line makes sense, but I can't use it because mapControl1 does not exist, I am using the out-of-the-box solution in which I don't know how to address the map or mapcontrol. That's why I thought making an instance of the map would work, but it doesn't.

Also I am using the Tablet version which is WPF.

I have tried this

using ESRI.ArcGIS.Mobile.WPF;

ESRI.ArcGIS.Mobile.WPF.Map mMap = new ESRI.ArcGIS.Mobile.WPF.Map();

mMap.MapLayers.AddRange(m_mobileCache1);   


But this still does not work, I am running out of ideas, 😞
0 Kudos
AngelGonzalez
Frequent Contributor
If you are using the out of the box solution (which ia a WPF app) see the following link:

http://help.arcgis.com/en/arcgismobile/10.0/apis/ArcGISMobile/WinApp/WinAppFramework_Using_MapPage.h...


It shows how to get a reference to the mapControl
0 Kudos
AkhilParujanwala
Regular Contributor
Thanks Angelg the link you gave me worked, but there are 2 problems and 1 more thing I need to handle.

1) The first problem is the first try and catch section, the catch is always triggered because the cache is in use. The error is "cache is in use by another process", fortunately aslong as a messagebox appears the code will continue, and works as expected.

2) If I comment out the first catch messagebox, the second try and catch section is triggered. The error states "object reference is invalid". As long as we can solve the first problem then hopefully the second try and catch will work without problems.

3) As you can see I am creating my own custom download data procedure. The goal is to trigger this procedure everytime the user creates a new point. This will allow the field collector can see everyone elses points at the immediately, rather than manually clicking on download data.

So I have managed to trigger this properly using CollectionCompletedEvent. But my data collectors may be working in a non-connected environment (no internet) and I want to handle this without some messagebox appearing everytime stating "you are not connected to the internet". I want to put an If statement, if connected to internet, then download data, otherwise don't do anything.

public static void Download_Data()
        {
            ViewMapTask viewMapTask = (ViewMapTask)MobileApplication.Current.Project.Tasks.GetFirstExtensionOfType(typeof(ViewMapTask));
            ViewMapPage viewMapPage = viewMapTask.ViewMapPage;
            MapControl mMap = viewMapTask.ViewMapPage.MapControl;

            MobileCache m_mobileCache1;
            m_mobileCache1 = new MobileCache(@"C:\Akhil\ArcGIS_Mobile_Projects\P_Radiation_Data_V1_B8\Radiation_Data_V1");

            m_mobileCache1.Open();

            if (m_mobileCache1.CacheExists)
            {
                m_mobileCache1.Close();

                try
                {
                    m_mobileCache1.DeleteCache();
                }
                catch (Exception ex)
                {
                    MessageBox.Show("FIRST ERROR " + Convert.ToString(ex));
                }
            }
            else
            {
                m_mobileCache1.Close();
            }
            
            MobileServiceConnection con = new MobileServiceConnection();            
            con.Url = @"http://emapappdev/arcgis/services/Test_Mobile_Services/Radiation_Data_V1/MapServer/Mobileserver";
            con.CreateCache(m_mobileCache1.StoragePath); // Per ESRI sample
            m_mobileCache1.Open();           

            MobileCacheSyncAgent agent = new MobileCacheSyncAgent(m_mobileCache1, con);
            agent.MapDocumentConnection = con;
            //agent.Synchronize(); // See if Synchronize works instead of line below            
            agent.DownloadExtent(m_mobileCache1.GetExtent(), 0, 0);            

            try
            {   
                mMap.Map.MapLayers.AddRange(m_mobileCache1);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Second ERROR " + ex.ToString());
            }
        }


Thanks,

Akhil P.
0 Kudos
AngelGonzalez
Frequent Contributor
Hi Akhilp,

I am in the process of converting a custom 9.3 windows laptop mobile app to 10 and I am running into some issues you are are having since I need a custom upload/download of data since editing is done in the field in a disconnected mode and uploading is done when inspectors are back in the office. My next project will be a WPF app so some of your ideas are helping me plan ahead. Fortunately the C# for a WPF and a windows app are very simliar. See below as to the changes you should try.


1) The first problem is the first try and catch section, the catch is always triggered because the cache is in use. The error is "cache is in use by another process", fortunately aslong as a messagebox appears the code will continue, and works as expected.

I modified your first try catch. I notice that I don't actually need to open and close a cache to delete it.



2) If I comment out the first catch messagebox, the second try and catch section is triggered. The error states "object reference is invalid". As long as we can solve the first problem then hopefully the second try and catch will work without problems.

See code below, I think you added an extra reference to the Map that I do not see in ESRI samples.


public static void Download_Data()
        {
            ViewMapTask viewMapTask = (ViewMapTask)MobileApplication.Current.Project.Tasks.GetFirstExtensionOfType(typeof(ViewMapTask));
            ViewMapPage viewMapPage = viewMapTask.ViewMapPage;
            MapControl mMap = viewMapTask.ViewMapPage.MapControl;

            MobileCache m_mobileCache1;
            m_mobileCache1 = new MobileCache(@"C:\Akhil\ArcGIS_Mobile_Projects\P_Radiation_Data_V1_B8\Radiation_Data_V1");

           
            if (m_mobileCache1.CacheExists)
            {
               
                try
                {
                    m_mobileCache1.DeleteCache();
                }
                catch (Exception ex)
                {
                    MessageBox.Show("FIRST ERROR " + Convert.ToString(ex));
                }
            } 
                       
            MobileServiceConnection con = new MobileServiceConnection();            
            con.Url = @"http://emapappdev/arcgis/services/Test_Mobile_Services/Radiation_Data_V1/MapServer/Mobileserver";
            con.CreateCache(m_mobileCache1.StoragePath); // Per ESRI sample
            m_mobileCache1.Open();           

            MobileCacheSyncAgent agent = new MobileCacheSyncAgent(m_mobileCache1, con);
            agent.MapDocumentConnection = con;
            //agent.Synchronize(); // See if Synchronize works instead of line below            
            agent.DownloadExtent(m_mobileCache1.GetExtent(), 0, 0);            

            try
            {   
               // mMap.Map.MapLayers.AddRange(m_mobileCache1); This line should be as follows:
                    mMap.MapLayers.AddRange(m_mobileCache1);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Second ERROR " + ex.ToString());
            }
        }




Angel
0 Kudos
AkhilParujanwala
Regular Contributor
Ah I see, I have also worked with 9.3, I used Windows Mobile, but now I am using 10.0 and working on WPF Tablet version. If I have time I will be re-writing all my code for Windows Mobile, so that our data collectors can use either types.

I have made the changes as suggested, with some modifications:

1) I have tried your suggestion to the first try and catch before and had the same problem, it always states that the cache.db file is in use.

2) I made a instance of the map so that I can use myMap.MapLayers.AddRange but it doesn't like this.

3) I went back to mMap.Map.MapLayers.AddRange and it works better, but I still get an error in the first try and catch stating that the cache.db file is in use by another process.

NOTE: The change made in #2 does work if you manually refresh the map by zooming, but the code in #3 will cause the map to refresh immediately after you click on the error presented from the first try and catch.

It make sense that the cache.db file is in use by the application, because you can phstyically see the cached radiation points on the map. I (we) assumed that mobileCache.Close should stop ArcGIS Mobile from using that folder containing the cache so that we can delete and download a new one, but this is not the case.

The only other thing I can think of is having two cache folders, one that is in use and the other isn't, and the application will switch back and forth as the download data is triggered. Mind you how does the ESRI Get Data button work around this issue, it seemlessly downloads the data and displays it on the map perfectly.


public static void Download_DataAkhil()
        {
            ViewMapTask viewMapTask = (ViewMapTask)MobileApplication.Current.Project.Tasks.GetFirstExtensionOfType(typeof(ViewMapTask));
            ViewMapPage viewMapPage = viewMapTask.ViewMapPage;
            MapControl mMap = viewMapTask.ViewMapPage.MapControl;
            ESRI.ArcGIS.Mobile.WPF.Map MyMap = mMap.Map;

            MobileCache m_mobileCache1;
            m_mobileCache1 = new MobileCache(@"C:\Akhil\ArcGIS_Mobile_Projects\P_Radiation_Data_V1_B8\Radiation_Data_V1");
            
            if (m_mobileCache1.CacheExists)
            {
                try
                {                    
                    m_mobileCache1.DeleteCache();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(Convert.ToString(ex)); //Still gives error
                }
            }
            else
            {
                m_mobileCache1.Close();
            }
            
            MobileServiceConnection con = new MobileServiceConnection();            
            con.Url = @"http://emapappdev/arcgis/services/Test_Mobile_Services/Radiation_Data_V1/MapServer/Mobileserver";
            con.CreateCache(m_mobileCache1.StoragePath); // Per ESRI sample
            m_mobileCache1.Open();           

            MobileCacheSyncAgent agent = new MobileCacheSyncAgent(m_mobileCache1, con);
            agent.MapDocumentConnection = con;
            //agent.Synchronize(); // See if Synchronize works instead of line below            
            agent.DownloadExtent(m_mobileCache1.GetExtent(), 0, 0);            

            try
            {   
                //mMap.Map.MapLayers.AddRange(m_mobileCache1);
                MyMap.MapLayers.AddRange(m_mobileCache1); //Gives error now, stating null reference
            }
            catch (Exception ex)
            {
                MessageBox.Show("This is second ERROR " + ex.ToString());
            }
        }


Thanks,

Akhil P.
0 Kudos
AkhilParujanwala
Regular Contributor
Done! Well almost, still need to put in a check if there is internet or no internet (this may not even be needed).

Ok so I got it to work:

1) As you can see I have made many attempts to make an instance of the Map, I finally got it with the first line of code.

This solved the null reference to all my other attempts of making an instance of Map or MapControl.

2) You don't have to delete the old cache, I checked the ESRI out-of-the-box Get Data button and watched the folder if it delete the cache or not and it does not.

In addition you don't even have to close the cache, based on the code below, I haven't opened the cached, so I don't need to close it. I keep the code there, but commented it out so you could see.

3) Got rid of the second try and catch, since I was making a proper reference to Map.

4) The service that I am connecting to only has 3 layers, those that will be constantly be used by data collectors. I have many other services, one for basemap vectors layers, a dummy layer for a table, a service for AGM Members and AGM Log.

Since there are only 3 operational layers and they are point layers, I didn't need to use FeatureSyncLayer to individually synchronize the layers, I am able to just synchronize the entire service, which is good.

5) The code is so short! 🙂

public static void Download_DataAkhil()
        {            
            ESRI.ArcGIS.Mobile.WPF.Map nMap = MobileApplication.Current.Map;
            
            //ViewMapTask viewMapTask = (ViewMapTask)MobileApplication.Current.Project.Tasks.GetFirstExtensionOfType(typeof(ViewMapTask));
            //ViewMapPage viewMapPage = viewMapTask.ViewMapPage;
            //MapControl mMap = viewMapTask.ViewMapPage.MapControl;
            //ESRI.ArcGIS.Mobile.WPF.Map MyMap = mMap.Map;

            MobileCache m_mobileCache1;
            m_mobileCache1 = new MobileCache(@"C:\Akhil\ArcGIS_Mobile_Projects\P_Radiation_Data_V1_B8\Radiation_Data_V1");

            //if (m_mobileCache1.CacheExists) //not needed
            //    m_mobileCache1.Close(); //not needed
                        
            MobileServiceConnection con = new MobileServiceConnection();            
            con.Url = @"http://emapappdev/arcgis/services/Test_Mobile_Services/Radiation_Data_V1/MapServer/Mobileserver";
            con.CreateCache(m_mobileCache1.StoragePath); // Per ESRI sample
            m_mobileCache1.Open();           

            MobileCacheSyncAgent agent = new MobileCacheSyncAgent(m_mobileCache1, con);
            agent.MapDocumentConnection = con; 
            agent.Synchronize(); // See if Synchronize works instead of line below             
            //agent.DownloadExtent(m_mobileCache1.GetExtent(), 0, 0);
                          
            nMap.MapLayers.AddRange(m_mobileCache1);
        }


Thanks Buddha and Angel for helping me out, now everyone can enjoy this. 🙂

Akhil P.
0 Kudos
AngelGonzalez
Frequent Contributor
Glad you figured it out. One thing I found out today regarding the line of code highlighted below. If you run this line of code in your app without deleting the current cache ESRI Mobile will literally add the same layers on top of your current layers. If this line of code is run 3 or 4 times the layer will be drawn 3 or 4 times (on top of each other). To prove this just make one of your point layer  label be visible, then run "Download_DataAkhil()" twice within the same mobile session. The label will appear twice for the same point (this is what happened to me while testing an app). I believe that "xxx.MapLayers.AddRange(m_mobileCache1);" should only be used when the mapCache is deleted first, otherwise delete this line from your code if you are using the same mapCache.

Angel




public static void Download_DataAkhil()
        {            
            ESRI.ArcGIS.Mobile.WPF.Map nMap = MobileApplication.Current.Map;
            
            //ViewMapTask viewMapTask = (ViewMapTask)MobileApplication.Current.Project.Tasks.GetFirstExtensionOfType(typeof(ViewMapTask));
            //ViewMapPage viewMapPage = viewMapTask.ViewMapPage;
            //MapControl mMap = viewMapTask.ViewMapPage.MapControl;
            //ESRI.ArcGIS.Mobile.WPF.Map MyMap = mMap.Map;

            MobileCache m_mobileCache1;
            m_mobileCache1 = new MobileCache(@"C:\Akhil\ArcGIS_Mobile_Projects\P_Radiation_Data_V1_B8\Radiation_Data_V1");

            //if (m_mobileCache1.CacheExists) //not needed
            //    m_mobileCache1.Close(); //not needed
                        
            MobileServiceConnection con = new MobileServiceConnection();            
            con.Url = @"http://emapappdev/arcgis/services/Test_Mobile_Services/Radiation_Data_V1/MapServer/Mobileserver";
            con.CreateCache(m_mobileCache1.StoragePath); // Per ESRI sample
            m_mobileCache1.Open();           

            MobileCacheSyncAgent agent = new MobileCacheSyncAgent(m_mobileCache1, con);
            agent.MapDocumentConnection = con; 
            agent.Synchronize(); // See if Synchronize works instead of line below             
            //agent.DownloadExtent(m_mobileCache1.GetExtent(), 0, 0);
                          
            nMap.MapLayers.AddRange(m_mobileCache1);
        }


Thanks Buddha and Angel for helping me out, now everyone can enjoy this. 🙂

Akhil P.
0 Kudos
AkhilParujanwala
Regular Contributor
Angel I have tried what you stated, mind you I'm on the Out of the box Tablet WPF version and there is no problem with the code as is. I don't see points layers added on top of eachother and such as you describe.

Mind you, I do understand where you are coming from, infact I just tried to remove
nMap.MapLayers.AddRange(m_mobileCache1);

Just to see if the newly downloaded points would be added to the map, and they were, but I had to manually refersh the map.
The line of code above, I would agree is not needed, what is needed is actually Map.invalidate or Map.refresh.

However my instance of the Map object doesn't allow me to directly call upon this event (refresh or invalidate) so I can't refresh my map.

In addition ran some testing on connect and disconnected from the internet scenarios, here are the results:

1) If the application starts up with an internet connection, and stays with a internet connection my download data code works as expected.

2) If the application starts up with no internet connection, and stay with no internect connection my download data code works as expected, it doesn't download, but doesn't throw and exception either.

3) If the application starts up with an internet connection and then disconnects from the internet, the download data procedure causes the table PC to not respond for roughly 90-130 secs.
Then it will respond, but if I trigger my download data  code again it will stall again, and it does not throw an error or exception.

4) If the application starts up with no internet connection but does connect to the internet at a later point, the same no response problem occurs, and it does not throw an error or exception.


In both cases 3 and 4, my try and catch segments are not working as expected and I would much rather somehow test for if there is an internet connection then download data, otherwise don't attempt to download data.

Code below:

public static void Download_DataAkhil2()
        {
            try
            {
                ESRI.ArcGIS.Mobile.WPF.Map nMap = MobileApplication.Current.Map;

                MobileCache m_mobileCache1;
                m_mobileCache1 = new MobileCache(@"C:\Akhil\ArcGIS_Mobile_Projects\P_Radiation_Data_V1_B8\Radiation_Data_V1");

                MobileServiceConnection con = new MobileServiceConnection();
                con.Url = @"http://emapappdev/arcgis/services/Test_Mobile_Services/Radiation_Data_V1/MapServer/Mobileserver";
                con.CreateCache(m_mobileCache1.StoragePath); // Per ESRI sample
                m_mobileCache1.Open();

                MobileCacheSyncAgent agent = new MobileCacheSyncAgent(m_mobileCache1, con);
                agent.MapDocumentConnection = con;
                agent.Synchronize(); // See if Synchronize works instead of line below             
                //agent.DownloadExtent(m_mobileCache1.GetExtent(), 0, 0);

                nMap.MapLayers.AddRange(m_mobileCache1);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }


Overall there still needs to be 2 or 3 fixes to make this procedure perfect.
1) Properly refresh or invalidate the map, not using .AddRange(mobilecache).

2) Handle internet connection and no internet connection better.

3) May need to swtich synchronization code to use FeatureLayerSync instead.
(Just like Buddha mentioned above)

Thanks,

Akhil P.
0 Kudos
AngelGonzalez
Frequent Contributor
I use the following in my mobile app:

To test for intranet connection:


using System.Net.NetworkInformation;
using System.Text;

Private bool PingGISServer()
        {
            try
            {
                Ping pingSender = new Ping();
                PingOptions options = new PingOptions();
                options.DontFragment = true;

                // Create a buffer of data to be transmitted.
                string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
                byte[] buffer = Encoding.ASCII.GetBytes(data);
                int timeout = 120;

                //First check if Intranetserver is available
                PingReply reply = pingSender.Send("severNameHere", timeout, buffer, options);

               
                if (reply.Status == IPStatus.Success)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                return false;
            }



if (PingGISServer())
{
        If true do something like upload & download data
}



************* Might be usefull  - Checking for network connectivity ***********************
using System.Net.NetworkInformation;

public bool isNetWorkAvailable = NetworkInterface.GetIsNetworkAvailable();


if(isNetWorkAvailable)
{
   If true do something
}...



Hope this helps
0 Kudos
AkhilParujanwala
Regular Contributor
Thanks Angel, that was exactly what I was looking for. I have placed comments on my code. Most noted would be the last line of code.
 nMap.MapLayers.AddRange(mobileCache); //refreshes the map, should use Map.Refresh or Map.Invalidate instead


For some reason this work, but I know for a fact it doesn't work at the same time. In hindsight this line of code causes the map to refresh, which is what I want it to do.

When/If I move to Window Mobile platform, the similar results as you stated previously may happen, where the layers would be on top of each other and duplicate the labels.

I know there Map class has Refresh and Invalidate which are suppose to be used to refresh the map. But for some reason that method is not available for me, I must be referencing the wrong Map object or not using the correct objects.

At the moment everything works perfectly. I have tested this code in all 4 scenarios as stated above and it handles them all perfectly.

Also it would be nice to get the application/project path from a property as opposed to me semi-hardcoding the path like I have.


 public static bool PingGISServer()
        {            
            try
            {
                Ping pingSender = new Ping(); 
                PingOptions options = new PingOptions();
                options.DontFragment = true;
                                
                // Create a buffer of data to be transmitted.
                string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
                byte[] buffer = Encoding.ASCII.GetBytes(data);
                int timeout = 2000;
                                
                //First check if Intranetserver is available
                PingReply reply = pingSender.Send("emapappdev", timeout, buffer, options);                
                               
                if (reply.Status == IPStatus.Success)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                return false;
            }
        }
       
        public static void Download_DataAkhil2()
        {
            if (PingGISServer() == true)
            {   
                try
                {
                    ESRI.ArcGIS.Mobile.WPF.Map nMap = MobileApplication.Current.Map; // instance of the Map (does not really work, but does work)                    
                    
                    string appName = MobileApplication.Current.ApplicationTitle; //gets the name of the application title to be used in cache storage path                                                           
                    appName = appName.Remove(0, 16); //removes the first 16 characters from the application title                     
                                      
                    MobileCache mobileCache = new MobileCache(@"C:\NEPRD\Mobile Radiation Applications\P_" + appName + "\\Radiation_Data_V1"); 
                    
                    MobileServiceConnection con = new MobileServiceConnection(); //connection to mobile service
                    con.Url = @"http://emapappdev/arcgis/services/Test_Mobile_Services/Radiation_Data_V1/MapServer/Mobileserver";
                    con.CreateCache(mobileCache.StoragePath); // creates the cache in the storage path
                    mobileCache.Open();

                    MobileCacheSyncAgent agent = new MobileCacheSyncAgent(mobileCache, con); //create an agent to the mobile cache and service
                    agent.MapDocumentConnection = con; //sets the map document connection
                    agent.Synchronize(); //agent.DownloadExtent(m_mobileCache1.GetExtent(), 0, 0);   - alternative for downloading and synchronizing
                                        
                    nMap.MapLayers.AddRange(mobileCache); //refreshes the map, should use Map.Refresh or Map.Invalidate instead
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            }
            //else
            //{
            //    MessageBox.Show("Ping Failed!");
            //}
        }


Thanks,

Akhil P.
0 Kudos