This is a migrated thread and some comments may be shown as answers.

Padding for MapGeometryView

18 Answers 156 Views
Map
This is a migrated thread and some comments may be shown as answers.
Etienne
Top achievements
Rank 1
Etienne asked on 28 Jul 2015, 09:57 PM

Hi,

I have a class that inherit form MapGeometryView that create a custom PathGeometryData that is displayed on a RadMap. This shape represent a container for other item displayed on a RadMap. Since the shape is bound to a lat/long position it become very small when I zoom out. Is there a way to add a padding (in pixel) to the shape so that it keeps a reasonable size when I zoom out ?

 Thank you,

 Etienne

18 Answers, 1 is accepted

Sort by
0
Petar Mladenov
Telerik team
answered on 29 Jul 2015, 10:15 AM
Hello Etienne,

In order to add a padding to shape you can changed the default ShapeTemplate of the VisualizationLayer.
By default it is defined like so:
<Style x:Key="VisualizationLayerStyle" TargetType="maps:VisualizationLayer">
      <Setter Property="ShapeTemplate">
           <Setter.Value>
               <DataTemplate>
                   <Path x:Name="PART_RangePath"
                         Fill="{Binding ActiveShapeFill.Fill}"
                         Stroke="{Binding ActiveShapeFill.Stroke}"
                         StrokeDashArray="{Binding ActiveShapeFill.StrokeDashArray}"
                         StrokeDashCap="{Binding ActiveShapeFill.StrokeDashCap}"
                         StrokeDashOffset="{Binding ActiveShapeFill.StrokeDashOffset}"
                         StrokeEndLineCap="{Binding ActiveShapeFill.StrokeEndLineCap}"
                         StrokeLineJoin="{Binding ActiveShapeFill.StrokeLineJoin}"
                         StrokeMiterLimit="{Binding ActiveShapeFill.StrokeMiterLimit}"
                         StrokeStartLineCap="{Binding ActiveShapeFill.StrokeStartLineCap}"
                         StrokeThickness="{Binding ActiveShapeFill.StrokeThickness}"
                         Data="{Binding Path=Geometry,Mode=OneWay}"
                         RenderTransformOrigin="{Binding Path=RenderTransformOrigin,Mode=OneWay}"
                         RenderTransform="{Binding Path=Transform,Mode=OneWay}"/>
               </DataTemplate>
           </Setter.Value>
       </Setter>

You can copy this style, add it as resource in your application and add Padding / Margin on the Path.

I hope this will help you move forward.

Regards,
Petar Mladenov
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Etienne
Top achievements
Rank 1
answered on 29 Jul 2015, 04:26 PM

Hi Petar,

Thanks for the answer, I tried your solution but the "Path" class doesn't seems to have a "Padding" property and margin doesn't do what I want.

I tried to set a large stroke thickness but they overlap (the shape is a thin rectangle) when I zoom out and are too large when I zoom in. It could work if it was possible to set the stroke on the outside of the shape only (instead of centered on the path). 

If it's not possible to add a padding around the shape, is it possible to retrieve the location (lat/long) of a point that is at a distance (in pixel given the current zoom level) of an other location ? With that, I could enlarge the shape when the map is zoomed out.

Thank you!

Etienne

0
Petar Mladenov
Telerik team
answered on 31 Jul 2015, 09:22 AM
Hello Etienne,

You can try the following conversion methods in Telerik.Windows.Controls.Map.Location:

/// <summary>
    /// Gets coordinates of the point relative to the map control screen coordinates.
    /// </summary>
    /// <param name="mapControl">The map control instance.</param>
    /// <param name="point">Point.</param>
    /// <returns>Location.</returns>
    public static Location GetCoordinates(RadMap mapControl, Point point)
    {
        return GetCoordinates(mapControl, point, true);
    }
 
    /// <summary>
    /// Convert a logic point to a Pixel Point on the current screen at a particular zoom level.
    /// </summary>
    /// <param name="mapControl">The map control instance.</param>
    /// <param name="logicalPoint">The logical Point.</param>
    /// <returns>Pixel Point.</returns>
    public static Point LogicalToPixel(RadMap mapControl, Point logicalPoint)
    {
        return LogicalToPixel(mapControl, logicalPoint, true);
    }


Regards,
Petar Mladenov
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Etienne
Top achievements
Rank 1
answered on 31 Jul 2015, 09:40 PM

Hi Petar,

I did the following but it doesn't give the expected result:

private Location CalculateLocationAtPixelOffset(Location baseLocation, Point2D pixelOffset)
{
    var baseLocationInPixel = baseLocation.GetPoint(MyRadMap);
    var offsetLocationInPixel = new Point(baseLocationInPixel.X + pixelOffset.X, baseLocationInPixel.Y + pixelOffset.Y);
    return Location.GetCoordinates(MyRadMap, offsetLocationInPixel);
}

I'm not sure of what are the logical unit and what is the reference point (0,0) of pixel points so I may did something wrong.

Also, in my custom MapGeometryView, I have a DependencyProperty for the RadMap so that I can bind to ZoomChanged event to update the GeometryData when user zoom on the map, but the view is not updated when I update GeometryData. I had to add an other DependencyProperty that is bound to the ZoomLevel of the map to make it work (and that property doesn't have a change callback). So I wonder how it works and if it'a a bug or something.

Thank you,

Etienne

0
Martin Ivanov
Telerik team
answered on 05 Aug 2015, 01:31 PM
Hello Etienne,

The code snippet for retrieving the location seems correct, however, without your implementation and actual/expected result I cannot be sure why the result is not the expected one. This is why if the following information doesn't help you I would ask you to send me a runnable code that demonstrates a simplified version of your scenario and drawings of the current and the expected results.

The logical point is the location on the map in relative units (from 0 to 1). Here are several examples that explain this:
  • Location(0, 0) corresponds to the logical point (0.5, 0.5) - this is the center of the map or in other words the center of the Mercator projection.
  • Location(0, 180) corresponds to the logical point (1, 0.5)
  • Location(85, 180) corresponds to the logical point (1, 0)

As for the custom MapGeometryView, since I am not aware of the approach that you are using to update the GeometryData I am not sure what is causing the described behavior. Can you please share more information about your implementation? Also, keep in mind that the MapShapeBindableWrapper elements are not included in the visual tree, the bindings for some of their properties might not work as expected. You can try to reset the Data (the MapGeometryView object) of the wrapper and see if this works on your side.

Regards,
Martin
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Etienne
Top achievements
Rank 1
answered on 06 Aug 2015, 06:06 PM

Hi Martin,

Thanks for your reply!

I think there is a mistake in the third example : Location(85, 180) corresponds to the logical point (1, 0). I would have expected (1,1).

Also, I figure out one of the problem I had, my function CalculateLocationAtPixelOffset had a mistake. Since pixel coordinates origin is at top left of the screen it should have been this :

private Location CalculateLocationAtPixelOffset(Location baseLocation, Point2D pixelOffset)
{
    var baseLocationInPixel = baseLocation.GetPoint(MyRadMap);
    var offsetLocationInPixel = new Point(baseLocationInPixel.X + pixelOffset.X, baseLocationInPixel.Y - pixelOffset.Y);
    return Location.GetCoordinates(MyRadMap, offsetLocationInPixel);
}

The last problem I have is that the shape size is different when I zoom in then when I zoom out to the same zoom level. I joined a sample project that demonstrate the problematic behavior.

Thank you again,

Etienne

0
Petar Mladenov
Telerik team
answered on 11 Aug 2015, 11:18 AM
Hi Etienne,

I reproduced the issue with the different sizes on different zoom levels. However, I have not dived into the calculations because I gain different results when I set UseSpringAnimations = False on RadMap. Please double check your calculations with these property set to False. This removes the animations from the RadMap and will remove the possibility to refresh the geometry data by the time zoom animation is not yet completed. This is potentially an issue - you can consider disabling the animations or or refreshing the geometry somehow later.

Regards,
Petar Mladenov
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Etienne
Top achievements
Rank 1
answered on 11 Aug 2015, 03:31 PM

Hi Petar,

I have tried with UseSpringAnimations = False and the shape is rendered as expected. Since I would like to keep the zooming animation is there a way to refresh the geometry later? I tried to listen on event ZoomingFinished instead of ZoomChanged but it doesn't work : the event is raised when the animation finished (as expected), but the "GeometryData = CreateGeometry()" (in GroupView) doesn't refresh the view.

Thank you,

Etienne

0
Petar Mladenov
Telerik team
answered on 13 Aug 2015, 12:31 PM
Hi Etienne,

I checked the RadMap code for MapPathView and MapPathGeometryView (MPGV). By the time you are creating the new Geometry Data of the MPGV, the DataChanged event of the MapPathView is not triggered. Simply said , the code does not support such scenario currently.

I would suggest moving the custom code and Map properties to custom MapPathView instead of custom MapPathGeometryView. This way you will be able to create new Data (new MPGV) when the zooming is finished. This should force the UI to update properly.

Regards,
Petar Mladenov
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Etienne
Top achievements
Rank 1
answered on 14 Aug 2015, 05:40 PM

Hi Petar,

Thanks for your reply! I've been able to update my shape view when the event ZoomingFinished is raised. I simply update GeometryData in my GroupView instead of recreating it at every change. I don't understand how it could have work before since there wasn't any listener notified when GeometryData was replaced when one of the property of GroupView (in the sample I sent before) was changed. I'm interested to know how it works.

So it looks better than before, but it still behave strange, it would be better if the shape view was redraw at every step of the map zoom animation so that it's more smooth. Is there anything else that could work better?

Also, it would be nice if the method UpdateShapeData of MapShapeBindableWrapper was "protected internal" instead of "internal" so that it would be possible to inherit and create custom MapShapeView.

Thank you!

Etienne

0
Petar Mladenov
Telerik team
answered on 18 Aug 2015, 11:49 AM
Hello Etienne,

Can you send us the code which updates the GeometryData without recreating it ? This would serve as a base point for us - to investigate the difference with the previous code which was not working and to investigate if we can find a better and smoother solution. Thank you in advance for your cooperation.

You are right that the UpdateShapeData could be protected. You can submit a feature request in our feedback portal where we will later review it and most probably approve it for future development.

Regards,
Petar Mladenov
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Etienne
Top achievements
Rank 1
answered on 18 Aug 2015, 08:36 PM

Hi Petar,

Here it is!

Etienne

0
Petar Mladenov
Telerik team
answered on 20 Aug 2015, 11:32 AM
Hi Etienne,

I saw the BeginInit() and EndInit() invocations you have added. EndInit() invokes the method UpdateGeoBounds() of the PathGeometryData() which is responsible for updating the geometry. The previous approach with re-creating the PathGeometryData misses Figures dependency property in your GroupView class to listen for changes and update the geometry. You can check how the MapPathGeometry view defines such property and uses it to track changes in the geometrydata.

I can think of one last idea but I do not have currently a prepared sample. You currently have updating the geometry when the zooming is finished and the RadMap's spring animations are completed. These animations (scaling animations) act over the Map Shapes, the bindable wrappers (pathview, geometryview, rectangleview etc), shape data objects (PathData, RectangleData,EllipseData). What you can try is to use FrameworkElements over the Map and mimic the behavior from the spring animations.

Regards,
Petar Mladenov
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Etienne
Top achievements
Rank 1
answered on 21 Aug 2015, 09:05 PM

Hi Petar,

I'm not sure if I understood, is the way I did thing in the second sample project is good or bad ? I experienced some problem with this solution since I post it, I receive exception at random time when the shapes are refreshed :

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   at Telerik.Windows.Controls.Map.PathGeometryData.GetScreenGeometry(LogicalToScreenParameters logicalToScreenParameters, Point basePoint)
   at Telerik.Windows.Controls.Map.PathData.GetScreenGeometry(Int32 zoomLevel)
   at Telerik.Windows.Controls.Map.MapShapeData.CalculateScreenGeometry(Int32 zoomLevel, Boolean raisePropertyChanged)
   at Telerik.Windows.Controls.Map.ShapeCoordinatesProcessor.StartProcessing(Object paramObject)
   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.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()

I've not been able to isolate the problem neither to reproduce it with specific steps, it just happens sometimes.​ Do you have any idea of what could be the problem ?

Thank you,

Etienne

0
Etienne
Top achievements
Rank 1
answered on 21 Aug 2015, 09:09 PM

Hi Petar,

I'm not sure if I understood, is the way I did in the last sample project is alright or not ? Also, I had some issue with this solution since I post it, it throws this exception at random times when it's refreshed : 

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   at Telerik.Windows.Controls.Map.PathGeometryData.GetScreenGeometry(LogicalToScreenParameters logicalToScreenParameters, Point basePoint)
   at Telerik.Windows.Controls.Map.PathData.GetScreenGeometry(Int32 zoomLevel)
   at Telerik.Windows.Controls.Map.MapShapeData.CalculateScreenGeometry(Int32 zoomLevel, Boolean raisePropertyChanged)
   at Telerik.Windows.Controls.Map.ShapeCoordinatesProcessor.StartProcessing(Object paramObject)
   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.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()

I've not been able to isolate the error neither to reproduce it â€‹with specific steps, it just happens sometimes. Do you have any idea of what could be the problem ?

Thank you,

Etienne

0
Petar Mladenov
Telerik team
answered on 24 Aug 2015, 10:41 AM
Hi Etienne,

The code in VisualizationLayer which is responsible for building, updating, processing the geometries behind the Shape Data objects uses multiple threads. We have similar logged exceptions for places in the code where some thread tries to change a collection which is already changed by other thread. Here is a similar logged bug.

To better investigate, log, raise priority of this one, its extremely important for us to be able to reproduce it on our side. Could you please confirm you are able to reproduce this exception on some of the projects attached in this thread ? Or you reproduce it in more complex solution with much more shapes / polylines added in the layer ? We would highly appreciate if you manage to isolate this somehow. Thank you in advance for your cooperation.

Regards,
Petar Mladenov
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Etienne
Top achievements
Rank 1
answered on 24 Aug 2015, 07:56 PM

Hi Petar,

I've modified my sample project to move items randomly until it raise the exception. I've observed that it's usually faster to catch the exception when the map is zoomed in close to the shape. It takes usually around 10 seconds when it's more zoom in than the initial state.

Etienne

0
Petar Mladenov
Telerik team
answered on 26 Aug 2015, 09:20 AM
Hi Etienne,

Thank you for the project. It clearly demonstrates the issue and we logged it in in our feedback portal. We also updated your telerik account points. 
The exception occurs because multiple threads process and change the figures collection of the PathGeometryData object simultaneously. This leads to known limitation in the for-foreach C# construction - collection cannot be modified while it is being interated. Unfrtunately we were unable to find a suitable workaround for your scenario currently. If such workaround is possible or if it requires too much code, you can try switching from VisualizationLayer to InformationLayer. The map shape objects in the InformationLayer are processed synchronously and there is no multi threading code.

Regards,
Petar Mladenov
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
Tags
Map
Asked by
Etienne
Top achievements
Rank 1
Answers by
Petar Mladenov
Telerik team
Etienne
Top achievements
Rank 1
Martin Ivanov
Telerik team
Share this question
or