ThinkGeo.com    |     Documentation    |     Premium Support

How can I place empty layers on a form?

Based upon prior discussion with Moritz today, I am trying to add custom feature layers to map as place holders.   These layers do not have a FeatureSource, and the IsVisible property of the layer is set to false.    However, when I try to start my program, and the map gets resized as part of setting its size property, an null reference exception is generated in the base FeatureLayer.OpenCore event.   I assumed that there was something in that function that is failing because there is no FeatureSource, but that leaves two questions:


1) If the IsVisible property of the FeatureLayer is false, then why are we even trying to open the FeatureSource?


2) I have overridden the OpenCore method in my own classes, but my own function is never getting called... or at least the breakpoint is not being hit.   I thought that if I overrode the methods in the base class, the code in the base class would never be executed?


Is there some other step that I should take in order to place a layer w/o a FeatureSource onto the map as a placeholder layer?



Ted, 
  
   I have some help for you on this. 
  
   For the first issue with IsVisible, this is a good observation.  We implemented the check for IsVisible in the Draw method before the DrawCore, when you call the Draw we check and if it is not visible then we never call the DrawCore method.  I agree that there could be an optimization higher up to prevent opening of the layer. 
  
   For the second item I think the issue is a little more subtle.  I created a PhantonFeatureLayer and overrode the OpenCode just as you mentioned.  I also got a null exception however it was not on the OpenCore but the IsOpenCore.  Before we open we check if it is already open and this method by default calls the FeatureSource.IsOpen.  Since you do not have a feature source this will cause the exception.  I suggest that you override the IsOpenCore as well and always pass back false.  Once I overrode that other method the sample worked fine.  In summary I think you need to override both the OpenCore and the IsOpenCore. 
  
   You are right in that if you override a method then the bases implementation doesn’t get called.  I think in this case the name between the two methods were very similar and misled you into thinking that the OpenCore was throwing an exception.  The reason your OpenCore never got called is because it was failing on the IsOpenCode before it ever got to your code. 
  
 I hope this helps and thanks for the suggestion on the IsVisible, I think we will put that in. 
  
 David

Ah... I missed the stack trace after my inherit said IsOpenCore. You are right. Sorry about that. 



Thanks!


BTW:  I was surprised that I could not override IsVisible.   I had to use "New", and then proxy through to the base.IsVisible.   Just curious what is going on under that hood that makes it not overridable.



Ted, 
  
 Just wonder why you need to override this, what’s your scenario? As IsVisible is a simple read / write property, every object can easily set it directly, I’m not sure what’s the scenario we need to make it overridable.  Please let us know and thanks in advance. 
  
 BTW: thanks for pointing out the performance issue about IsVisilbe(the 1st issue in the first thread), we’ve add this to our tracking system. 
  
 Thanks, 
  
 Ben

It would be cleaner for us to implement IsVisible in the Get logic when needed than for us to try to maintain the proper state of the object in the base class.   It isn't a big deal, but as I try and gets my hands around MapSuite and its custom extensions, I was just curious why something so simple couldn't be overridden. 



Ted, 
  
   I am not exactly sure that I follow you.  It is ok though because I am sure I am just not fully understanding your master plan here.  I tend to agree with Ben on that the property is a get and set so you have full control.  I am also sure though that I am totally missing something here and that you have a good reason to do so.  What do you pan to write in the IsVisible property implmentation, I am really curious? 
  
   To answer your question about why we did not allow IsVisible to be overridden.  We use pretty strict Api design guidelines about extensibility and the number one rule is that we have a real concrete case and need for offering the Core method.  This means that every Core method we actually override somewhere in the framework or in a suite of client scenarios we maintain with an actual alternate implementation.  The reason for this is to ensure the override is useful.  It would be easy to just make everything Core and have loads of protected methods however it is sloppy and once we add a protected method we can never change it or we break compatibility.  It is amazing how too many protected method can cause confusion.   
  
   Another key guideline is to try and isolate and simplify the code you have to write in an override to ensure that you have the best possible chance to get it right.  Notice that if you create your own FeatureSource that you don’t have to bother with projection, messing with the transaction buffer when getting features, checking the IsVisible, raising events etc.  These are pieces of functionality that we want to provide and let you focus on what we intended the override to do.  This keeps things internally coherent and give you a good chance at getting it right.  Sometimes like in your case it is just not enough h and we need to consider expanding our extension model. 
  
   In your situation if you can explain to us really well why you need it we are more than happy to provide it.  We just need a little convincing.  When we better understand your scenario we may find there is a better way to handle it as well.  I hope this makes sense, providing an extensible framework is a tough job for us to have it make sense and even a tougher job for you to understand it.  We try extra hard to help people who are extending our stuff to better understand their needs to make our framework better.  We also like extenders because you guys typically take the framework to all kinds of territory we never imagined. 
  
 Ok, sorry for the rant here, I just love framework design and am always a bit long winded! 
  
 David

David, I'm kind of new to the inheritance logic, so I'm exploring as well.   From what I have been exposed to it, the whole *Core thing is different than I have seen, but it makes sense as it still allows the base class to have logic that runs, and then optionally run related logic from the deriving class.  This protects you from a user that doesn't call base.foo in their overriding implementation?   In other applications, I've seen notes in the help system that warn a user that they should call the base method in their overriding implementation.   You've protected us from an failure to do so.  Thank you :)


Please understand that I'm not pushing for an overridable IsVisible, or complaining that it is not overridable.   It's as much an academic question as anything, as I try and get a sense for the extensibility features of MapSuite.   It is this extensibility that will determine if we select MapSuite as our package going forward, or keep exploring (and right now, you are looking very good).


So, here is my "IsVisible" scenario:


I use layers as a placeholder on the map, so that if I know that I always want layers loaded as streams on the bottom, roads over the top of them, and utilities on top of those, I can order the layers into an overlay one time.    Sometimes I have road data, and sometimes I don't.   But by keeping an empty layer there, all I have to do is plug in a roads datasource to the layer and refresh.  I don't have to worry about reordering the layers after adding a new roads layer.    So, if there is no data source, the layer is forced to IsVisible = false, even if the user has checked the "Show Roads" checkbox.   There are other data triggers for visibility, as well as the obvious IsVislble selection by the user.


In my existing code, whenever one of the four things that set visibility changes, I call this.IsVisible and in that setter, I check the state of all four things and then call the base.IsVisible setter with the appropriate value.    In my this.IsVisible getter, I just return the base.IsVisible.


If I could override IsVIsible, I would not need to have the calls to this.IsVisible in the four locations where visibility status might be changed.  Rather, the this.IsVisible getter would return true or false upon demand, based upon the state of the four visibility dependency items.


None of this is a big deal.   I just feel that you have to be very careful when you are overriding base functionality with a "New" attribute.   Makes me feel like the base author didn't think that method or property should be overridden, and raises a red flag for me.   I could also add a Visible property, rather than overriding the base IsVisible, but the intended purpose is the same, so it seams like it would be a mistake to use a differently named property in the deriving class.


Thanks for the discussion.


 



Ted, 
  
   Great, I love academic discussions, maybe a little too much! :-) 
  
   I now see what you are trying to do.  That is really a good idea the way you use placeholder layers.  Moving layers in and out is a big hassle and though we try to make easy Apis on the GeoCollection it is stuff a big mess with few good solutions. 
  
   Do you always know that you will have x layers?  I mean you may not have x at any given time but x is the maximum number of layers or at least in a set?  Are they all FeatureLayers or do you mix and match Raster ones as well?  In either case one kinda cool solution would be this.  Create a new Layer type called StandardSetLayer and make sure to inherit from Layer.  Then on this class create properties for each of the different kinds of stock layers you have so you might have a RoadLayer property that is a type FeatureLayer and a LakesLayer that is type FeatureLayer etc.  By default these can start as null.  Then when you override the Layer.DrawCore you simple in order check if the property is null and if not then you call the Draw method of the property.  The nice thing about this is that it make you code look better because you can use code like Map1.StaticLayers[“StockLayers”].Roads…   
  
   Another approach you could consider and I am not sure it will work is to let the visible be true  and override the DrawCore.  In there just check your four parameters and if you don’t want it to draw do not call the base class’s draw, if you do then call it’s draw. 
  
   One other approach would be to create a FeatureSource called EmptyFeatureSource.  Override all of the required methods and just always return back nothing.  This way even if it is visible nothing will draw and won’t have that much overhead.  Then you can swap out the FeatureSource anytime with a good one.  Just some ideas. 
  
   I like you suspicion surrounding the “New” keyword.  It is useful but can be dangerous.  The guidance we follow is that we never provide extensibility if we do not have a concrete case that is coded.  This doesn’t mean it is not a good idea to allow the method to be extended but it does mean we have not written a solid example of how it would be useful.  One little phrase that has stuck with me for year is “By definition something has to first be usable before it can be reusable!.”.  If this is a topic that interestes you you can send an e-mail to support@thingeo.com and have then forward it to David and I can send you some really great material on extensibility and inheritence. 
  
   The Core pattern is an extensibility pattern designed by Microsoft for .net.  What you do is whenever you need to provide extensibility through a protected method you always have a concrete version without the Core like GetCount and then a protected virtual or abstract version called GetCountCore.  In the concrete GetCount by default you may simply call GetCountCore method.  Though this seems a little useless it is giving us place for future growth.  Remember a user can override the GetCountCore and replace it with their logic.  Imagine if later in V2 we wanted to add an event every time a person got a count.  If I didn’t have the Core pattern I would have no place to put the code to raise the event.  Since we have the pattern we can place the event in the GetCount right before or after we call the GetCountCore.   
  
   In the concrete method we also put the parameter validation.  I am sure you are a responsible programmer and never forget to validate each variable that gets passed to you for null or an invalid state.  Surprisingly not all developers do so we place validation at that higher level to help you.  It also allows us to do thing like pre or post processing of the data that comes out of methods you override.  For example if you have a FeatureSource and you override the GetFeatureById for a shape file that is in decimal degrees.  You override the Core method and put in code to read the shape and pass it back as a feature.  Simple hugh?  Well remember that there may be projections that need to be applied.  Don’t worry in the GetFeatureById we will call the core and get your value then project your data based on the projection object for the FeatureSource.  This means when you override you don’t have to worry about that.  We can reliably handle this no matter what kind of implementation you use.  When there are cases we are 100% sure we can do the work then we do it and leave the real customization work to you. 
  
   Having the user call the base class is not correct in all circumstances.  Typically you call the base when you are overriding a method where you want to tack on some functionality but still want the main classes version to run.  In our documentation if it is required to call the base we should say that.  The problem with inheritance is that is really a bit complex and relies heavily on understanding of the intention of the method being overridden.  It is very powerful when it works and a frustrating mess when it does not.  
  
 David

I do see the value of the *Core methods, as you have explained them.    Probably not as important within my own little world, where the same development team is building the base and derived objects.  Would seem very important when you are building base clases to be used by third-party developers. 
  
 In my world, I have a variable number of “base data” layers, where one layer of base-data has four shapefiles for a county an aerial image, etc.   We are going to build a custom overlay to handle this. 
  
 Then I have a known number of layers that are always the same, but may or may not exist.   I think we might build an overlay to hold these named layers, or maybe do the single “multi-layer” as you have described.   Not quite sure of the pros and cons of either of those approaches.  I’ll start another thread on that question after I get some more experience. 
  
 And, finally, we have a variable number of user defined layers that sit on the top of the map… and we’ll use an overlay for those. 
  
 I have stayed away from any of the Draw methods so far.  I’m not sure what a GeoCanvas is, and I didn’t really see any examples that helped me understand the significance of implementing the draw stuff on our own.    What I would really like to see is a document that describes the map “Refresh” process.   Something that describes what methods get called on what objects, and in what order, along with key decision point criteria within each method that determines if a layer gets drawn, etc.    When I first saw the original “Draw” methods on styles, layers, etc, I thought that was really getting into the nitty gritty of the process, and we should stay out.   As I get more experience, it appears that these methods might actually be places to accomplish some pretty powerful things.

Ted,  
  
   I agree on all points.  The Core pattern is really more for us not to get hedged in when user override our stuff and to help you get things write when you do overload. 
  
   I will see about at least some kind of flow chart or list of operations and in what order the occur.  Kind of like a refreshing life cycle.  Let me see if I can find someone to whip this up.  We will need it anyway for our documentation wiki. 
  
 David