Push pin visuals

Developer
Sep 29, 2008 at 1:52 AM
I made a few quick push pin visuals that can be used in the project. I was going to have these in a resource dictionary (app.xaml at first, until a better solution came about) and have a string property in the pushpin.cs class that would hold onto the key value of the resource. Then in CreateElement I was finding the resource by using Application.Current.Resources[]. When the UpdateElement method returned (in ShapeController.cs) and the visual was about to be added to the canvas (line 96 in ShapeController.cs) I was getting an exception: 'Value does not fall within the expected range.' I could not figure out what was going on and have given up for the time.

I would like to have some way of defining these pins in a resource dictonary and easily telling the pushpin.cs class which one to use. This way users can use multiple different pushpins for all sorts of things easily. Below is the xaml for my 4 push pins:

<Path x:Key="PinMagnifyBlue" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Width="41" Height="41" Stretch="Fill" StrokeLineJoin="Round" Stroke="#FF17265B" Data="F1 M 22.7222,4.94446C 32.5406,4.94446 40.5,12.9038 40.5,22.7222C 40.5,32.5406 32.5406,40.5 22.7222,40.5C 12.9038,40.5 4.9444,32.5406 4.9444,22.7222C 4.9444,17.813 4.9444,9.38885 0.5,0.5C 9.38892,4.94446 17.813,4.94446 22.7222,4.94446 Z M 22.7222,9.38892C 15.3583,9.38892 9.38873,15.3585 9.38873,22.7222C 9.38873,30.0861 15.3583,36.0556 22.7222,36.0556C 30.0859,36.0556 36.0555,30.0861 36.0555,22.7222C 36.0555,15.3585 30.0859,9.38892 22.7222,9.38892 Z ">
            <Path.Fill>
                <LinearGradientBrush StartPoint="0.556337,0.0110369" EndPoint="0.556337,0.986647">
                    <GradientStop Color="#FF003EAB" Offset="0"/>
                    <GradientStop Color="#FF061CAF" Offset="0.991525"/>
                </LinearGradientBrush>
            </Path.Fill>
        </Path>

        <Path x:Key="PinRightRed" Width="23" Height="29" Stretch="Fill" StrokeThickness="2" StrokeLineJoin="Round" Stroke="#FF7E0901" Data="F1 M 6.99988,1L 64.9999,1C 68.3136,1 71,3.68628 71,7L 71,25C 71,28.3137 68.3136,31 64.9999,31L 51,31L 41,40.9982L 41,30.9982L 6.99988,31C 3.68628,31 1,28.3137 1,25L 1,7C 1,3.68628 3.68628,1 6.99988,1 Z ">
            <Path.Fill>
                <LinearGradientBrush StartPoint="0.569443,0.0257521" EndPoint="0.569443,0.978173">
                    <GradientStop Color="#FFFF5710" Offset="0.205479"/>
                    <GradientStop Color="#FFA30A00" Offset="0.991525"/>
                </LinearGradientBrush>
            </Path.Fill>
        </Path>

        <Path x:Key="PinLeftGreen" Width="23" Height="29" Stretch="Fill" RenderTransformOrigin=".5,.5" StrokeThickness="2" StrokeLineJoin="Round" Stroke="#FF216A13" Data="F1 M 6.99988,1L 64.9999,1C 68.3136,1 71,3.68628 71,7L 71,25C 71,28.3137 68.3136,31 64.9999,31L 51,31L 41,40.9982L 41,30.9982L 6.99988,31C 3.68628,31 1,28.3137 1,25L 1,7C 1,3.68628 3.68628,1 6.99988,1 Z ">
            <Path.RenderTransform>
                <ScaleTransform ScaleX="-1" ScaleY="1"/>
            </Path.RenderTransform>
            <Path.Fill>
                <LinearGradientBrush StartPoint="0.569443,0.0257521" EndPoint="0.569443,0.978173">
                    <GradientStop Color="#ff41f032" Offset="0.205479"/>
                    <GradientStop Color="#FF40A32D" Offset="0.991525"/>
                </LinearGradientBrush>
            </Path.Fill>
        </Path>

        <Path x:Key="PinLeftBottomPurple" Width="31" Height="32.86" Stretch="Fill" StrokeThickness="2" StrokeLineJoin="Round" Stroke="#FF60049B" Data="F1 M 18.1334,66.4996L 87.1334,66.4996C 93.7607,66.4996 99.1334,71.8722 99.1334,78.4996L 99.1334,148.5C 99.1334,155.127 93.7607,160.5 87.1334,160.5L 19.9417,160.5C 15.7417,165.5 9.33333,170.5 0.5,170.5C 6.92782,167.332 9.45052,162.311 10.1927,157.497C 7.70329,155.298 6.13338,152.082 6.13338,148.5L 6.13338,78.4996C 6.13338,71.8722 11.5059,66.4996 18.1334,66.4996 Z ">
            <Path.Fill>
                <LinearGradientBrush StartPoint="0.549655,0.0164806" EndPoint="0.549655,0.890694">
                    <GradientStop Color="#FFA048DA" Offset="0"/>
                    <GradientStop Color="#C2D2A5F0" Offset="0.929"/>
                    <GradientStop Color="#FFCFCBD2" Offset="1"/>
                </LinearGradientBrush>
            </Path.Fill>
        </Path>

Hope this helps some, I will look into this some more when I get a chance.
Coordinator
Sep 29, 2008 at 3:51 AM
I agree, the current hardcoded XAML in the pin class is not very good.
But do we build a complex pushpin class with various option to specifiy colour, xaml path etc or do we leave the core as it is, with an improved simple pin and let the developer impliment their own shape or many shapes as they see fit? IF the default pin handles placement and resizing based on zoom, the developer need only inherit from the default class and override the XAML for the look and feel.
I'm very aware of our filesize and would love the control to be around the 70KB mark. I think we should provide all the hooks required, a solid default pin and keep the filesize down.

So with XAML is there something similar to CSS? Can we define a shape but set the colour somewhere else?
Developer
Sep 29, 2008 at 11:47 AM
I agree that we should keep the size down to a minimum, good point! That being said, I think the best route is to have a solid pushpin that others can inherit. We need to add the ability for our pushpins to be somewhat generic, as in, our base pushpin should be a UIElement so others can override the template and have any visual that silverlight supports. So we need some way to expose the pushpins template as it is now ... or have the pushpin (or shapelayer?) inherit from UIElement or Control and then we can override the OnApplyTemplate and have others do that as they see fit.

CSS in XAML terms would be templating and styles. Essentially, any control can have any look that the developer / designer wishes. They just need to override the template of that control, either in pure XAML or through code. If you would like more info on this topic I would be more than happy to elaborate.
Developer
Sep 30, 2008 at 3:15 PM
soulsolutions, please take a look at the latest code that I have committed (changeset 16028). I have added the ability for the pushpin class to accept a FrameworkElement instead of a UIElement so that we can take advantage of the width / height properties that FrameworkElements have. This also allows us to have anything as our pushpins; complex XAML code, buttons, some sort of usercontrol that contains deepZooming capabilities ... the possibilities are now endless.

I believe this will increase the size of the project minimally since I have added a new usercontrol called PushpinVisual.xaml that defines the default pushpin. I have also added the ability for anyone to set a property on a pushpin, called PushPinVisual that will take a userControl and this will represent what new pushpins will look like.

One last note, if no width or height is set on a custom pushpin then the Measure() method is called on it to figure out what width / height it should take. This may cause the custom pushpin to be a very tiny size that is only visible at close zoom levels. A suggestion would be to set MinHeight / MinWidth values or just set height and width values. There is now error checking in UpdateElement() of the pushpin class to not fail if the width of the pushpin is to be a negative value.

I believe this is a step in the right direction, let me know if anything looks out of the ordinary or if I should take a different approach.
Developer
Sep 30, 2008 at 3:32 PM
that sounds pretty cool aquaseal, you could have movies/media elements and things like that as pushpins
Developer
Sep 30, 2008 at 3:37 PM
Exactly! One thing that would be pretty cool to implement / see is to read in some feed that has lat / long coords and also streaming media that you can then host inside the pushpins. The basic idea behind this pushpin implementation was to keep it light weight for the core functionaltiy, but give the user enough freedom to really push the bounds if they choose to. I look forward to seeing some great examples from this project!
Coordinator
Sep 30, 2008 at 10:11 PM
Yeah, there is no reason why the pushpin can't have its own complex logic and be tracking a truck, person or plane showing real time camera footage for example.

So what did you think of core logic in the pushpin that that provides a size ratio based on zoomlevel? I notice the code is there but no longer working. Should we have this as an optional property and some configuration around it - desired max and min size (max at max zoom level, ie street level)

Also with the rotation, thanks for that, it is getting there. Looks like the panning delta's need to consider the rotation. And we need to work out how to oversize the control to fill the screen.

Great work!
Developer
Oct 1, 2008 at 12:23 PM
I thought that the size ratio was working slightly, but I may be wrong. I would suggest we keep the power law algorthim (sorry if I broke this) and maybe have an option to turn that feature off and if it is off then either provide a max and min as you suggest or just keep it as a static size.

With the rotation, I would like to also look into animating the rotation which should be very simple since we already have a rotateTransform and we can simply have a doubleAnimation that eases the new value when the user rotates the map.
Coordinator
Oct 1, 2008 at 1:21 PM
Agreed, I though it would make a nice modification to the dashboard - have NSEW markers and simply rotate the compass as such to rotate the map. Nice smooth animation would be awesome.
Still looking forward to seeing the HDView teams infinate horizontal panning code. So many cool features over the js control :)
I should ask around and see if there is a real project that could use this now that we can impliment offically supported tile access for VE. Great to spend alot more time in here.
Developer
Oct 3, 2008 at 1:28 PM
As of changeset 16092, pushpins now implement scaling via the power law algorithm. If users wish to have their pushpins scale you need to add a ScaleTransform to the base of your pushpin visual, as so (the below code assumes the base is a UserControl):

    <UserControl.RenderTransform>
        <ScaleTransform ScaleX="1" ScaleY="1" />
    </UserControl.RenderTransform>

If you do not want your pushpin to scale (it will stay a static size at all zoom levels) you can either override UpdateElement() (in Pushpin.cs) or simply do not include a ScaleTransform on your pushpin.
Feb 14, 2009 at 2:29 PM
This doesn't seem to be implemented any more, Pushpin.cs only has it's constructor and nothing else.
What is the current solution to scale pushpins when zooming?
Coordinator
Feb 14, 2009 at 4:08 PM
The hook is still there as all pins have a ScaleTransform.  However, in the reworking on the updates to support new modes, this feature is now "Opt-In" vs. "Opt-Out".   This fits with the minimalistic approach of the DeepEarth core and an additional performance benefit.  If anyone has time, we should reimplement this feature in the prototype perhaps in Pushpin.cs.  Should be relatively easy.
Feb 15, 2009 at 12:08 AM
is there an example floating around anywhere to help me Opt-In to it?
Coordinator
Feb 16, 2009 at 7:32 AM
Here is my overly dramatic version ;)

using System;
using System.Windows;
using DeepEarth.Geometry;

namespace SQLSpatialSL.Controls
{
    public class Pushpin : PointBase
    {

        public Pushpin()
        {
            Style = Application.Current.Resources["PushpinStyle"] as Style;
        }

        /// <summary>
        /// The adjustment scale for the Pin
        /// </summary>
        public override double ScaleAdjustment
        {
            get { return _ScaleAdjustment; }
            set
            {
                _ScaleAdjustment = value;
                if (_IsLoaded)
                {
                    _ScaleTransform.ScaleX = Math.Sqrt(_ScaleAdjustment) * 10;
                    _ScaleTransform.ScaleY = Math.Sqrt(_ScaleAdjustment) * 10;
                }
            }
        }
    }
}

with the XAML in the app.xaml using a nice vector:

      <!--Style Pushpin -->
        <Style x:Name="PushpinStyle" TargetType="Controls1:Pushpin" >
            <Setter Property="RenderTransformOrigin" Value="0.5,0.5" />
            <Setter Property="AnchorPoint" Value="0.5,0.5" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Controls1:Pushpin">

                        <Grid>
                            <Grid.RenderTransform>
                                <ScaleTransform x:Name="_ScaleTransform" ScaleX="1" ScaleY="1" />
                            </Grid.RenderTransform>
                            <Canvas x:Name="Layer_1" Width="50" Height="50" Canvas.Left="0" Canvas.Top="0">
                                <Path x:Name="DropShaddow" Width="24.3496" Height="39.3937" Canvas.Left="26.3333" Canvas.Top="-16.43966" Stretch="Fill" Fill="#44C6C6C6" Data="F1 M 16.3333,44.8333L 22.5,36.4453C 22.5,36.4453 29.7467,38.7493 33.3333,30.4453C 35.5306,25.3132 32.7806,21.5632 31.5,20C 32.3333,18.444 33,17.2227 33,17.2227C 33,17.2227 37.944,17.444 39.7773,14.444C 41.6107,11.444 40.556,8.16667 37.556,6.5C 34.4909,4.79753 30.9056,5.12565 28.9681,7.62565C 27.2656,10.1094 27.612,13.1107 28.6667,15.1107C 27.6667,17.056 27.5306,17.1257 26.9453,18.1667C 25.2806,17.8757 22.3893,18.2227 21.5,19.1667C 20.6107,20.1107 19.4225,21.459 18.7923,21.7292C 18.4023,21.8958 18.7077,25.8118 18.7077,25.8118C 18.7077,25.8118 25.444,27.4453 23.444,33.3333C 22.3984,36.4134 17.3092,37.6628 16.9375,37.6882C 16.6257,37.7077 16.3333,44.8333 16.3333,44.8333 Z "/>
                                <Path x:Name="Pinpoint" Width="2.35482" Height="7.14648" Canvas.Left="24.5833" Canvas.Top="16.6868" Stretch="Fill" Fill="#FF727272" Data="F1 M 14.5833,37.7708L 15.334,42.112L 16.334,44.8333L 16.778,42L 16.9382,37.6868C 16.9382,37.6868 15.6243,37.9375 14.5833,37.7708 Z "/>
                                <Path x:Name="Round_base" Width="16.339" Height="12.681" Canvas.Left="17.49589" Canvas.Top="4.1536" Stretch="Fill" Data="F1 M 7.5,32C 7.66797,36.7799 13.1667,37.8346 15.3346,37.8346L 15.3346,37.8346C 17.4987,37.8346 23.7682,36.9349 23.8346,31.1133L 23.8346,31.1133C 23.888,26.0547 16.776,25.2214 15.8893,25.1667L 15.8893,25.1667C 15.7376,25.1589 15.5814,25.1536 15.4238,25.1536L 15.4238,25.1536C 11.9642,25.1536 7.33854,27.4277 7.5,32 Z ">
                                    <Path.Fill>
                                        <RadialGradientBrush RadiusX="0.560988" RadiusY="0.722815" Center="0.731262,0.259524" GradientOrigin="0.731262,0.259524">
                                            <RadialGradientBrush.RelativeTransform>
                                                <TransformGroup/>
                                            </RadialGradientBrush.RelativeTransform>
                                            <GradientStop Color="#FF115EDB" Offset="0"/>
                                            <GradientStop Color="#FF083174" Offset="0.649877"/>
                                            <GradientStop Color="#FF00040E" Offset="1"/>
                                        </RadialGradientBrush>
                                    </Path.Fill>
                                </Path>
                                <Path x:Name="Base" Width="6.33333" Height="9.14486" Canvas.Left="22.5" Canvas.Top="0.556" Stretch="Fill" Fill="#FF125FDB" Data="F1 M 18.8333,21.556L 18.7773,29.112C 18.7773,29.112 16.1667,32.612 12.5,29.2227L 12.5,21.6667C 16.2507,22.5117 17.5521,22.0918 18.8333,21.556 Z "/>
                                <Path x:Name="base_shaddow" Width="9.86133" Height="14.7219" Canvas.Left="18.222" Canvas.Top="0.7272" Stretch="Fill" Fill="#83000000" Data="F1 M 12.5703,21.7272C 12.5703,21.7272 15.1113,22.7227 18.0007,22.1107C 18.0553,28.1667 18.0833,29.8958 18.0833,29.8958C 18.0833,29.8958 12.263,36.6309 11.722,36.4453C 9.61133,35.7227 8.222,33.7786 8.222,33.7786C 8.222,33.7786 8.66732,30.6667 12.5007,29.2227C 12.6113,27.6667 12.5703,21.7272 12.5703,21.7272 Z "/>
                                <Path x:Name="Hightlight" Width="1.56287" Height="2.89583" Canvas.Left="28.1667" Canvas.Top="5.9375" Stretch="Fill" Fill="#C5FFFFFF" Data="F1 M 18.8333,26.9375C 18.8333,26.9375 19.7507,27.6042 19.7292,28.2493C 19.7064,28.9616 19.6563,29.1777 19.2507,29.5827C 19.0625,29.7708 18.1667,29.8333 18.1667,29.8333L 18.8548,29.1042L 18.8333,26.9375 Z "/>
                                <Path x:Name="pin_head" Width="12.3359" Height="9.3425" Canvas.Left="19.83203" Canvas.Top="-8.0534" Stretch="Fill" Data="F1 M 9.83203,17.7227C 9.83203,20.0013 11.8893,22.556 16.0534,22.388L 16.0534,22.388C 20.2214,22.2227 22.168,19.944 22.168,17.6654L 22.168,17.6654C 22.168,15.8333 20.1107,13.1107 16.0534,13.0534L 15.9694,13.0534L 15.9694,13.0534C 12.8333,13.0534 9.83203,15.4642 9.83203,17.7227 Z ">
                                    <Path.Fill>
                                        <RadialGradientBrush RadiusX="0.72757" RadiusY="0.960692" Center="0.675586,0.0954698" GradientOrigin="0.675586,0.0954698">
                                            <RadialGradientBrush.RelativeTransform>
                                                <TransformGroup/>
                                            </RadialGradientBrush.RelativeTransform>
                                            <GradientStop Color="#FF115EDB" Offset="0"/>
                                            <GradientStop Color="#FF093BA7" Offset="0.649877"/>
                                            <GradientStop Color="#FF021973" Offset="1"/>
                                        </RadialGradientBrush>
                                    </Path.Fill>
                                </Path>
                            </Canvas>

                        </Grid>

                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>