How to rotate renderer icon/graphic dynamically with the map rotation?

4094
6
Jump to solution
10-04-2015 09:35 PM
PaulHaakma
Regular Contributor

When setting a renderer on a feature layer, a PictureMarkerSymbol rotates with the map rotation.

There is an angle field on a PictureMarkerSymbol. If you set this then the icon/graphic will be displayed rotated on the map. I tried binding the angle property to the map.mapRotation property but the icon didn't rotate with the map.

Is there any way to alter the rotation of the graphic dynamically with the map? My intent is actually to keep the graphic unrotated on the screen regardless of the map rotation underneath it.

UniqueValueRenderer{

       attributeNames: ["Verified"]

        defaultSymbol: PictureMarkerSymbol { image: "image.png"; width: 40; height: 32; angle: (map.mapRotation * -1.00)}

         }

0 Kudos
1 Solution

Accepted Solutions
LucasDanzinger
Esri Frequent Contributor

I don't know what all of the code looks like for this, but it seems like it is "by design" for 10.2.x release of Runtime. The issue is probably that Symbols are value objects, so the binding will not work. Once you set a symbol on a Renderer or a Unique Value Info, it is set. If you then change the original symbol, or if the binding emits that the property updates, it will indeed update the symbol, but that symbol is a copy of the one that is actually applied to the renderer or unique value info.

So with that said, the workflow you need to do is every single time the map's rotation changes, update each symbol's rotation to be the same as the map's, then re-set all of the symbols to the renderer. Quite a lot of work for something so simple, but I believe this is how it will have to be done.

Our Quartz release will have symbols as references, so if you update the symbol (or use property binding in the case of QML), it will update the symbol, and the renderer will automatically update, as it holds a reference to the original symbol.

Hope this helps.

-Luke

View solution in original post

0 Kudos
6 Replies
HannahFerrier
Regular Contributor

Please see the Rendering Mode Sample in AppStudio.

You can set the rendering mode for a Graphics Layer to be Static or Dynamic:

        GraphicsLayer {
            id: graphicsLayerDynamic
            renderingMode: Enums.RenderingModeDynamic
        }

This will preserve or rotate the graphics with the map;

dynamic.PNG

Hope this helps,

Hannah

GarethWalters
Occasional Contributor III

Hi Paul,

If you want to continue  to user a feature layer, I found that just using mapRotation will tend to keep the icon upright. I must admit it doesn't seem perfectly react to  the mapRotation.

FeatureLayer {

            id: featureLayer
            featureTable: featureServiceTable
            renderer: SimpleRenderer {
                symbol:  PictureMarkerSymbol { 
                    image: "./Alfred_E._Neuman.png"
                    width: 32
                    height: 32
                    angle: (map.mapRotation)
                }
            }
        }
0 Kudos
PaulHaakma
Regular Contributor

Hi Gareth

Had another look at this one yesterday. It seems the rotation binding works for the default symbol, but not for the ones that get added. Below is the code I used, and it appears that when the 'notVerifiedUvi' is added it uses the current map rotation but this then doesn't remain as a binding so isn't updated dynamically.

Any idea of an alternate method of creating this renderer that would retain the dynamic binding for all UniqueValueInfo objects?

   UniqueValueRenderer{

        attributeNames: ["Verified"]

        defaultLabel: "Verification unknown"

        defaultSymbol: PictureMarkerSymbol { image: "images/FMP_GREY_SupplyFlag.png"; width: 40; height: 32; angle: (map.mapRotation)}

        Component.onCompleted: { addValue(notVerifiedUvi);   }

    }

    UniqueValueInfo{

        id: notVerifiedUvi;

        value: ["N"]

        label: "Not Verified"

        symbol: PictureMarkerSymbol { image: "images/FMP_RED_SupplyFlag.png"; width: 40; height: 32; angle: (map.mapRotation) }

    }

0 Kudos
GarethWalters
Occasional Contributor III

Hi Paul,

A nice curly one for the morning! Straight out I think it is going to be a bug/by design.

A couple of things I quickly thought of.

  • Declare the PictureMarkerSymbol outside of the UVI. Perhaps then it will refer to the binding correctly.
  • Create a custom connection the map rotation for the angle. Then you will have forced the binding yourself and then at least it still within the component.
    • Pseudo code: angle:
  •                 map.onMapRotationChanged.connect(function(){ text = map.mapRotation})

http://doc.qt.io/qt-5/qtqml-syntax-signals.html

I tried this on a Text component at both the text property and the Compoonent.onCompleted and the  rotation updated.

Let me know how you go.

P.S. Lucas Danzinger​ any other thoughts?
0 Kudos
LucasDanzinger
Esri Frequent Contributor

I don't know what all of the code looks like for this, but it seems like it is "by design" for 10.2.x release of Runtime. The issue is probably that Symbols are value objects, so the binding will not work. Once you set a symbol on a Renderer or a Unique Value Info, it is set. If you then change the original symbol, or if the binding emits that the property updates, it will indeed update the symbol, but that symbol is a copy of the one that is actually applied to the renderer or unique value info.

So with that said, the workflow you need to do is every single time the map's rotation changes, update each symbol's rotation to be the same as the map's, then re-set all of the symbols to the renderer. Quite a lot of work for something so simple, but I believe this is how it will have to be done.

Our Quartz release will have symbols as references, so if you update the symbol (or use property binding in the case of QML), it will update the symbol, and the renderer will automatically update, as it holds a reference to the original symbol.

Hope this helps.

-Luke

0 Kudos
PaulHaakma
Regular Contributor

Hi Gareth and Luke

Thanks for your input - taking your comments, I have got it working by doing the following...

Defining PictureMarkerSymbol objects outside of the UniqueValueInfo object.

Then adding a function to the renderer that removes all UVIs, reapplies the symbols to the UVIs and re-adds them to the renderer, then reapplies the renderer itself back to the layer.

Then, last step was to add a handler that catches the map rotating, updates the rotation angle of each of the PictureMarkerSymbols before calling the refresh function on the renderer.

A bit longwinded but gets there in the end! A small delay as it updates typically several hundred labels on the screen but not too bad.

onMapRotationChanged: {

            //console.log("the map rotation changed to: ", map.mapRotation)

            redFlagPictureMarkerSymbol.angle = map.mapRotation;

            greenFlagPictureMarkerSymbol.angle = map.mapRotation;

            greyFlagPictureMarkerSymbol.angle = map.mapRotation;

            myRenderer.refresh();

            }

    UniqueValueRenderer{

        id:myRenderer      

        defaultSymbol: greyFlagPictureMarkerSymbol;

        Component.onCompleted: { addValue(uvi1); addValue(uvi2)  }

        function refresh(){

            removeAll();

            uvi1.symbol = redFlagPictureMarkerSymbol

            uvi2.symbol = greenFlagPictureMarkerSymbol

            addValue(uvi1);

            addValue(uvi2);

            myLayer.renderer = myRenderer;

        }

    }