ThinkGeo.com    |     Documentation    |     Premium Support

RequiredIndex on shapefiles. When and why?

We have seen a lot of recent issues with the latest versions of mapsuite when displaying maps, generally with a "The input double value is out of range." error or similar. I've discovered with recreating the index file by doing some validation on each shape before adding it to the list of features to be indexed and setting RequireIndex = true on the layer I can get these issues to go away. However, I've seen many examples set this to false. I'm curious what happens if there is an index file on a layer, but this flag is set to false. Also, should we always set it to true if it has an index file that exists? 


 


I've also seen a lot of different types of "validation" on features when generating the index, both checking points to see if they are large then double.Max and calling things like boundingbox.validate(). Is there some way to guarentee a feature is valid before adding it to the list of features to be indexed? Does the validation have to change based on the type (shape, line, point) of the layer?



To add a bit of an addendum to this, a specific issue I am unable to capture before the map is displayed is the following:


 <Note this stacktrace is coming from the sample app included with SDE, but is referencing all the latest ThinkGeo DLL's>


"Index was outside the bounds of the array."


   at ThinkGeo.MapSuite.Core.x01af2673a54dd1c0.GetBoundingBoxFromWkb(Byte[] wkb)

   at ThinkGeo.MapSuite.Core.Feature.GetBoundingBox()

   at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesInsideBoundingBoxCore(RectangleShape boundingBox, IEnumerable`1 returningColumnNames)

   at ThinkGeo.MapSuite.Core.ShapeFileFeatureSource.GetFeaturesInsideBoundingBoxCore(RectangleShape boundingBox, IEnumerable`1 returningColumnNames)

   at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesForDrawingCore(RectangleShape boundingBox, Double screenWidth, Double screenHeight, IEnumerable`1 returningColumnNames)

   at ThinkGeo.MapSuite.Core.ShapeFileFeatureSource.GetFeaturesForDrawingCore(RectangleShape boundingBox, Double screenWidth, Double screenHeight, IEnumerable`1 returningColumnNames)

   at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesForDrawing(RectangleShape boundingBox, Double screenWidth, Double screenHeight, IEnumerable`1 returningColumnNames)

   at ThinkGeo.MapSuite.Core.FeatureLayer.DrawCore(GeoCanvas canvas, Collection`1 labelsInAllLayers)

   at ThinkGeo.MapSuite.Core.Layer.Draw(GeoCanvas canvas, Collection`1 labelsInAllLayers)

   at ThinkGeo.MapSuite.DesktopEdition.LayerOverlay.DrawCore(GeoCanvas canvas)

   at ThinkGeo.MapSuite.DesktopEdition.Overlay.MainDraw(GeoCanvas canvas)

   at ThinkGeo.MapSuite.DesktopEdition.Overlay.Draw(GeoCanvas canvas)

   at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.x03e3d48bcfe7bb6c(IEnumerable`1 xa6f0db4f183189f1)

   at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.xff5b27c00f9678c2(RectangleShape x178b193eec228e6e)

   at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.xe3cee4adb9c72451()

   at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.x9ac8c50f434f4b39(Int32 xb565f4681f05557a)

   at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.Refresh()

   at HowDoISamples.ConsumingArcSDE.ConsumingArcSDE_Load(Object sender, EventArgs e) in C:\Program Files\ThinkGeo\Map Suite Spatial Data Extension Full Edition\HowDoISamples\CSharp HowDoISamples\CSharp Desktop HowDoISamples\Samples\ConsumingArcSDE.cs:line 66

   at System.Windows.Forms.UserControl.OnLoad(EventArgs e)

   at System.Windows.Forms.UserControl.OnCreateControl()

   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)

   at System.Windows.Forms.Control.CreateControl()

   at System.Windows.Forms.Control.ControlCollection.Add(Control value)

   at HowDoISamples.Samples.treeViewLeft_AfterSelect(Object sender, TreeViewEventArgs e) in C:\Program Files\ThinkGeo\Map Suite Spatial Data Extension Full Edition\HowDoISamples\CSharp HowDoISamples\CSharp Desktop HowDoISamples\Samples.cs:line 68

   at System.Windows.Forms.TreeView.OnAfterSelect(TreeViewEventArgs e)

   at System.Windows.Forms.TreeView.TvnSelected(NMTREEVIEW* nmtv)

   at System.Windows.Forms.TreeView.WmNotify(Message& m)

   at System.Windows.Forms.TreeView.WndProc(Message& m)

   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)

   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)

   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

   at System.Windows.Forms.UnsafeNativeMethods.SendMessage(HandleRef hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)

   at System.Windows.Forms.Control.SendMessage(Int32 msg, IntPtr wparam, IntPtr lparam)

   at System.Windows.Forms.Control.ReflectMessageInternal(IntPtr hWnd, Message& m)

   at System.Windows.Forms.Control.WmNotify(Message& m)

   at System.Windows.Forms.Control.WndProc(Message& m)

   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)

   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)

   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)

   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

   at System.Windows.Forms.UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)

   at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)

   at System.Windows.Forms.Control.DefWndProc(Message& m)

   at System.Windows.Forms.TreeView.WmMouseDown(Message& m, MouseButtons button, Int32 clicks)

   at System.Windows.Forms.TreeView.WndProc(Message& m)

   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)

   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)

   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)

   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)

   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)

   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)

   at System.Windows.Forms.Application.Run(Form mainForm)

   at HowDoISamples.Program.Main() in C:\Program Files\ThinkGeo\Map Suite Spatial Data Extension Full Edition\HowDoISamples\CSharp HowDoISamples\CSharp Desktop HowDoISamples\Program.cs:line 17

   at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)

   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)

   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()

   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)

   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)

   at System.Threading.ThreadHelper.ThreadStart()


 


This is on a relatively simple shape file. If I create an index file and require index on the shapefile, it works fine, but not all customers have the shape file generated. I tried doing the following validation, but it does not catch any bad shapes:


 


static void fLayer_DrawingFeatures( object sender, DrawingFeaturesEventArgs e )
      {
         Collection<Feature> featuresToDraw = new Collection<Feature>();
         foreach( Feature feature in e.FeaturesToDraw )
         {
            try
            {
               if( feature.IsValid() )
               {
                  if( feature.GetShape().Validate( ShapeValidationMode.Advanced ).IsValid )
                  {
                     featuresToDraw.Add( feature );
                  }
               }
            }
            catch
            {
               //ignore feature
            }
            
         }

         e.FeaturesToDraw.Clear();
         foreach( Feature f in featuresToDraw )
         {
            e.FeaturesToDraw.Add( f );
         }
}


Kevin,


Thanks for 

Try following code, hope it is what we are seeking:



void worldLayer_DrawingFeatures(object sender, DrawingFeaturesEventArgs e)
{
    Collection<Feature> featuresToDraw = new Collection<Feature>();
    foreach (Feature feature in e.FeaturesToDraw)
    {
       try
       {
          if (feature.GetShape().Validate(ShapeValidationMode.Simple).IsValid)
          {
              featuresToDraw.Add(feature);
          }
       }
       catch
       {
           //ignore feature
       }
    }

    e.FeaturesToDraw.Clear();
    foreach (Feature f in featuresToDraw)
    {
        e.FeaturesToDraw.Add(f);
    }
}

About the  RequireIndex, the default value is true and it will throw exception if the target index file was not found. While this exception can be removed by setting the value to false, this way the performance will be much slower because it will read each shape every time extent changes.


Besides, Kevin, I think this code should work for your application ,while it will be slow, because every time we need to validate the shapes. If possible, a better solution is to create a index file only contains those validated shapes without this event invloved.


Any more questions just feel free to let us know.


Thanks.


Yale

 



Yale, 



Thanks for following up with me. I have a couple of additional questions relating to this. First is the concept of index files and shapefiles. 



We have a customer that rebuilds their shapefiles nightly. These rebuilds always change data associated with the shapes, not the shapes or identifiers themselves (i.e. the ownername on a parcel). This should have no effect on the index itself. Is there a way to use an existing index file for a new shape file? This way the customer could drop in the new shapefile in with the existing index file and not have to rebuild it. 



Alternatively, they'd like to be able to use their maps without them failing without the index. I am trying to get this to work on a much simpler map with no success whatsoever. I cannot get the DrawingFeatures to fire before the map error's on Map.Refresh. I am even trying this in a simple ThinkGeo sample, and it still bombs before I can loop through the features. 



On top of that, I do something like this: 



 


ShapeFileFeatureLayer layer = new ShapeFileFeatureLayer( "\\myShapeFile.shp" );
           layer.RequireIndex = false;
           
           layer.FeatureSource.Open();
           foreach( Feature feature in layer.FeatureSource.GetAllFeatures( ReturningColumnsType.AllColumns ) )
           {
              if( feature.IsValid() )
              {
                 if( feature.GetShape().Validate( ShapeValidationMode.Advanced ).IsValid )
                    continue;
              }
               layer.FeatureSource.FeatureIdsToExclude.Add( feature.Id );
           }

           layer.FeatureSource.Close();

To which nothing actually gets caught and put in the FeatureIdsToExclude. This map still bombs on map.Refresh with an "index out of the bounds of the array" error. I've tried both simple and advanced validation. When I rebuild the index and require it, all is well, but I'd rather not have to use the index file every time.


I guess I'll open a ticket with the map data.


 



 


Kevin,
 
Thanks for your post.
 
Yes, it is possible to use specify the index file if new shape file comes in. Two ways:
1)       Specifies the index file name in the constructor.
2)       Use the property IndexPathFileName to set the index file name.
 
 
About the second problem, I suggest you use a very simple data which is attached in our package like Countries02.shp and try to load it into the map control without using the RtreeIndex(set the RequireIndex = false).
 
Besides, following post shows you how to validate each record in the shape file feature source:
gis.thinkgeo.com/Support/Dis...fault.aspx
 
Any more questions just feel free to let me know.
 
Thanks.
 
Yale