ThinkGeo.com    |     Documentation    |     Premium Support

Exception in RotationProjection trying to find point clicked by user

I’m setting up my layers using an instance of your RotationProjection class, to allow rotating our map (assuming your map ever supports also rotating the satellite image in sync).  The setup is quite simple as seen below.




InMemoryFeatureLayer layer = new InMemoryFeatureLayer( );
layer.FeatureSource.Projection = rotateProjection;





While trying to use your sample code to provide functions that would allow our users to click on and select dynamic point objects added to

instances of InMemoryFeatureLayer, I added the following code based on your sample “GetFeatureClickedOnWithProjection”.  


Collection<Feature> features = featureLayer.QueryTools.GetFeaturesWithinDistanceOf( new PointShape( e.WorldX, e.WorldY ), DesktopMap.MapUnit, DistanceUnit.Feet, distanceBuffer, ReturningColumnsType.NoColumns );



However, when this line executes, we get the following exception regarding your projection object’s InternalGeographyUnit.  


System.InvalidOperationException was unhandled
  _HResult=-2146233079
  _message=Cannot get the projection’s InternalGeographyUnit, please provide it by overriding the Projection’s GetInternalGeographyUnitCore() method.
  HResult=-2146233079
  IsTransient=false
  Message=Cannot get the projection’s InternalGeographyUnit, please provide it by overriding the Projection’s GetInternalGeographyUnitCore() method.
  Source=MapSuiteCore
  StackTrace:
       at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesWithinDistanceOf(BaseShape targetShape, GeographyUnit unitOfData, DistanceUnit distanceUnit, Double distance, IEnumerable`1 returningColumnNames)
       at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesWithinDistanceOf(BaseShape targetShape, GeographyUnit unitOfData, DistanceUnit distanceUnit, Double distance, ReturningColumnsType returningColumnNamesType)
       at ThinkGeo.MapSuite.Core.QueryTools.GetFeaturesWithinDistanceOf(BaseShape targetShape, GeographyUnit unitOfData, DistanceUnit distanceUnit, Double distance, ReturningColumnsType returningColumnNamesType)
       at Toro.Client.Shared.Map.LynxMap.DesktopMapOnMapClick(Object sender, MapClickWinformsMapEventArgs e) in d:\Toro\Map\Prototypes\LynxMapPrototype\LynxMap\LynxMap.cs:line 77
       at System.EventHandler`1.Invoke(Object sender, TEventArgs e)
       at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.OnMapClick(MapClickWinformsMapEventArgs e)
       at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.5xM=(InteractionArguments iBc=)
       at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.0hM=(Object dhc=, 0xU= dxc=)
       at ThinkGeo.MapSuite.DesktopEdition.MouseEventAnalyzer.OnMouseEvent(0xU= e)
       at ThinkGeo.MapSuite.DesktopEdition.MouseEventAnalyzer.yBQ=(Object yRQ=, EventArgs yhQ=)
       at System.Windows.Forms.Timer.OnTick(EventArgs e)
       at System.Windows.Forms.Timer.TimerNativeWindow.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(IntPtr 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 ShapesDisplay.Program.Main() in d:\Toro\Map\Prototypes\LynxMapPrototype\DriverForm\Program.cs:line 16
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly 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.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 



My requirement is simply to loop through a collection of simple layers (from top to bottom) that contain nothing but a number of points within the layers, looking to try to find the first layer (and associated feature) that is within the buffer range of the click point.  It does not care about “columns to return” or anything of the like, I just need the point feature they tried to click, and it has to work with a rotation projection.  



Can you please provide a sample of how can this be done?







I just realized that the rotation projection appears to not be working with the InMemoryFeatureLayer.  My shape file layers rotate as desired, but the overlay above it with InMemoryFeatureLayer(s) does not rotate with it.

Hi Russ, 
  
 Actually, InMemoryFeatureLayer also can work with projection such as RotationProjection. As the issue you occurred, I’m wondering how you add features to InmemoryFeatureLayer, and below code is our suggest way: 
 
           InMemoryFeatureLayer inLayer = new InMemoryFeatureLayer();
            inLayer.FeatureSource.Projection = new RotationProjection(90);

            inLayer.Open();
            inLayer.FeatureSource.BeginTransaction();
            inLayer.FeatureSource.AddFeature(new Feature(0, 2));
            inLayer.FeatureSource.CommitTransaction();
 
 
 Would you please have a try? And if the RotationProjection still not work, please provide us some related code snippet. 
  
  
 Thanks, 
  
 Kevin

I was adding the objects to layer.InternalFeatures, something I got from one of the samples. 
  
 I’ve changed to use the Open/Begin/Add/Commit/Close.  I no longer get an exception when I have the RotationProjection set on the layer, but sync rotation (or any rotation at all) with the shape layers in the shape overlay is still not working.  The shape layers in the static map overlay rotate, the InMemoryLayers using the same RotationProjection in the domain specific overlay do not rotate.  Likewise the state objects in the InMemoryLayers of the Dynamic overlay (using the same projection instance) do not rotate either.  Only the shape layers are rotating. 
  
 Rotation is not a hard requirement, just very desirable, so I’ll revisit and explore this option further in the future.

Oops, replied too soon.  I forgot that I needed to perform operations to invoke the featureLayer.QueryTools.GetFeaturesWithinDistanceOf code.  When I did so, after changing my loading code to use the FeatureSource rather than InternalFeatures, when I set a projection on the layer, I get the following exception.  I’ve reverted back to not using a projection until I find out how to address this issue.  But this will work for my demo later today. 





System.InvalidOperationException was unhandled 

_HResult=-2146233079 

_message=Cannot get the projection’s InternalGeographyUnit, please provide it by overriding the Projection’s GetInternalGeographyUnitCore() method. 

HResult=-2146233079 

IsTransient=false 

Message=Cannot get the projection’s InternalGeographyUnit, please provide it by overriding the Projection’s GetInternalGeographyUnitCore() method. 

Source=MapSuiteCore 

StackTrace: 

at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesWithinDistanceOf(BaseShape targetShape, GeographyUnit unitOfData, DistanceUnit distanceUnit, Double distance, IEnumerable`1 returningColumnNames) 

at ThinkGeo.MapSuite.Core.FeatureSource.GetFeaturesWithinDistanceOf(BaseShape targetShape, GeographyUnit unitOfData, DistanceUnit distanceUnit, Double distance, ReturningColumnsType returningColumnNamesType) 

at ThinkGeo.MapSuite.Core.QueryTools.GetFeaturesWithinDistanceOf(BaseShape targetShape, GeographyUnit unitOfData, DistanceUnit distanceUnit, Double distance, ReturningColumnsType returningColumnNamesType) 

at Toro.Client.Shared.Map.LynxMap.GetFeature(FeatureLayer featureLayer, Double worldX, Double worldY, Double distanceBuffer) in d:\Toro\Map\Prototypes\LynxMapPrototype\LynxMap\LynxMap.cs:line 156 

at Toro.Client.Shared.Map.LynxMap.DesktopMapOnMapClick(Object sender, MapClickWinformsMapEventArgs e) in d:\Toro\Map\Prototypes\LynxMapPrototype\LynxMap\LynxMap.cs:line 121 

at System.EventHandler`1.Invoke(Object sender, TEventArgs e) 

at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.OnMapClick(MapClickWinformsMapEventArgs e) 

at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.5xM=(InteractionArguments iBc=) 

at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.0hM=(Object dhc=, 0xU= dxc=) 

at ThinkGeo.MapSuite.DesktopEdition.MouseEventAnalyzer.OnMouseEvent(0xU= e) 

at ThinkGeo.MapSuite.DesktopEdition.MouseEventAnalyzer.yBQ=(Object yRQ=, EventArgs yhQ=) 

at System.Windows.Forms.Timer.OnTick(EventArgs e) 

at System.Windows.Forms.Timer.TimerNativeWindow.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(IntPtr 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 ShapesDisplay.Program.Main() in d:\Toro\Map\Prototypes\LynxMapPrototype\DriverForm\Program.cs:line 16 

at System.AppDomain._nExecuteAssembly(RuntimeAssembly 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.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 

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

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

at System.Threading.ThreadHelper.ThreadStart() 

InnerException:  


Hi Russ, 
  
 Looks like we are missing the SourceUnit property of RotationProjection, try to specify an unit like meter should avoid this exception, it should be the same as your featuresource coordinate unit. Comparing with other projection type like ManageredProjection, they can get its unit from itself, but for RotationProjection, we need to specify it in some query operations. 
  
 Hope it helps. 
 Troy

It’s the same RotationProjection instance that is working for the shape layers.  But I just hard coded it to “Feet” for a test, which happens to be the units used in my test data.  This did stop the exception, but my InMemoryFeatureLayer data points still do not rotate along with the shape file layers (in a different overlay, if that matters).   
  
 Since the same rotation projection had been working for quite some time with the shape file layers, I did not expect something like this (configuration of the rotation projection itself) to be at the heart of the problem.  Also, the exception message itself suggest a need for overriding methods of the projection as though I were providing a custom derived class, which I was not.  So between the two, my expectation was that it was an internal problem of some sort. 
  
 As I said earlier, this is not a huge issue since we will not be able to make the rotation feature available to our customers until/unless your component supports also rotating the satellite background image in sync with all the overlay data.  But for that hopeful eventuality, and for my evolving understanding, I want to be able to make this work as it seems it should.  Perhaps you could provide a simple functional sample where a shape file layer in one overlay, and an InMemoryFeatureLayer in another overlay get rotated in sync?  If so, then perhaps that will provide for me the information to determine what I am doing wrong.

I believe I have this mostly sorted.  The dynamic overlay, the one that rotation was not working for, was actually rotating but didn’t update the image.  I found that the refresh after rotation was only refreshing the static (shape files) overlay using map.Refresh(staticOverlay).  So I added code to update the dynamic layer overlay as well.  This caused and undesirable “two-step” where you see the static shapes rotate, then the dynamic shapes follow.  I also tried just using map.Refresh(), but was surprised that this seems not to work at all for updating the rotation.  It thought perhaps I might find a Begin/EndUpdate construct to help here, but was not successful.  Still investigating how to get this to rotate all overlays smoothly.



Looks like I also need to find a way to deal with identifying an object selected by clicking in the dynamic overlay, and then adding a matching feature to the state feedback layer (in a separate state overlay) to show up in the correct place.  The state overlay layers now rotate with the other 2 overlays just fine other than the jerky multi-update redraw.  However, once rotated, the click selects the appropriate point in the dynamic overlay, but when using that point to add a new matching feature in the “selected” layer of the state overlay, it shows up in the wrong position.  Subsequent rotates move it along, but it’s not in the right place relative to previously rotated objects.  Looks like I need to figure out how to get the original location rather than rotated location of the clicked PointShape feature to use for inserting a matching PointShape feature into the state layer.  Not yet sure how that is done.

Hi Russ,



For the RotationProjection unit, I just think there might be some enhancements need us to add. As for why the the points still do not along with shape file rotating, as we don’t have any codes or data, it is impossible for us to recreate it. But we created a sample and hope it is what you are looking for. Please check it.



Actually, I didn’t get the whole of your requirements and issues, would you mind to take some minuets to build a sample which includes your issues and requirements? That will help us a lot to towards to the right direction.



Thanks,

Troy








001_Post12404.zip (103 KB)

Never mind, I sorted it out. 
  
 Not sure why, but your sample seems to work fine just calling Map.Refresh with no arguments.  When I apply a rotation, that overload does nothing at all, and the map does not rotate.  However, I noticed an overload that takes a collection of overlays.  So instead of calling refresh on each overlay one at a time (which gave the obvious single overlay at a time rotate and draw), I loaded a collection and used the collection Refresh overload.  This overload seems to have access to what I was looking for earlier, namely a Begin/EndUpdate logic.  In any case, using that overload updates all overlays at the same moment.  But for future reference, providing a public Begin/EndUpdate pattern for users would be useful.

Hi Russ, 
  
 Glad to hear that the issue has been resolved and thanks for your suggestion. As far as I know, there isn’t any differences between the 2 overloads of “Refresh” method, they are calling the same core method.I’m not sure whether there is any missing we have, could you please update your demo to us for a further debug? 
  
 I guess making the Refresh called like “Begin/EndUpdate” pattern is more obvious for use, but a penitential problem is that is a bit more complicated and not too straight forward, just like when we are using EnterLock/ExiteLock for multiple threads, I will show your suggestion to development guys to see if they have any ideas. 
  
 Thanks, 
 Johnny

Not sure why the difference.  I can only say that when I call it separately for each overlay, I see significant incremental updates.  When I pass an array of the overlays, I see a single update all at once.  And though I’ve not timed it, the perception is that the process completes faster.

Hi Russ, 
  
 That’s true if call the refresh separately for each overlay will incremental update and take more time, because only when one overlay is completely drawn and update the map, the next overlay can be drawn. But if refresh an array of overlay, the map drawn these overlay to one canvas then update to map only once, so it will be faster.   
  
 As for the refresh overload, would you please add below code before you call Map.Refresh() and see if the returned value is true?  
  
 The parameter is overlay that passing into Refresh method.  
 
Bool isContains = Map.Overlays.Contains(overlayToRefresh);
 
 
  
 I’m guessing the reference of overlay to refresh have changed and no longer belongs to Map.Overlays, so when call refresh without parameter, the map don’t changes.  
  
 Thanks, 
  
 Kevin