How to make a symbol render with real world units?

612
6
08-26-2019 08:32 AM
RichardReinicke
Occasional Contributor II

Hello,

I have a new task and I'm not sure if this is possible with the Pro SDK.

My goal is to create a PointSymbol either from a picture or a character marker which represents a real world object of known size and to make the symbol show the object in natural size in scale range from 1:1 to 1:100.

In theory, if I look at the API, it should be enouth to set:

symbol.UseRealWorldSymbolSizes = true;
symbol.SetSize(23.75);

If map is set to meters, it should render the symbol with 23.75 m (map units) but when I zoom out, I would now expect the icon to stay at 23.75m and therefore to shrink appropriately. But it still holds its size at any zoom level.

This is my complete snippet:

CIMCharacterMarker marker = SymbolFactory.Instance.ConstructMarker(
                    66,
                    "Schiff_real",
                    "Regular",
                    _config.ShipIconSize,
                    ColorFactory.Instance.BlackRGB) as CIMCharacterMarker;

var symbol = SymbolFactory.Instance.ConstructPointSymbol(marker);
symbol.UseRealWorldSymbolSizes = true;
symbol.SetSize(23.75);

return symbol;‍‍‍‍‍‍‍‍‍‍‍‍‍


Can someone please help me?

0 Kudos
6 Replies
UmaHarano
Esri Regular Contributor

Hi Richard,

UseRealWorldSymbolSizes property allows you to use Map Units to size the symbol instead of Pixels. To allow your symbols to scale based on zoom level of the map, use Map reference scales.

Thanks

Uma

0 Kudos
RichardReinicke
Occasional Contributor II

Hello Uma,

I still don't get it. You write exactly what's written in the docs and you're referencing docs but I still don't get the expected result, no matter what I do in code. I'm now setting the reference cale for my Map initially to 1:1:

await QueuedTask.Run(() => 
{
    MapView.Active.Map.SetReferenceScale(1.0);
});

My symbol remains as in the upper post:

CIMCharacterMarker marker = SymbolFactory.Instance.ConstructMarker(
    66, "Schiff_real", "Regular", _config.ShipIconSize, ColorFactory.Instance.BlackRGB) 
    as CIMCharacterMarker;

var symbol = SymbolFactory.Instance.ConstructPointSymbol(marker);
symbol.SetRealWorldUnits(true);
symbol.SetSize(23.75)

I would now expect that my symbol is set to 23.75 m (map units) and will have this size ONLY at scale 1:1 but I don't see any difference to my symbol. It still has the same size on each zoom level.

Do you please have a simple example for me how I can create symbol from a picture packaged with an add-in as 'content' and how to make it always have the size of let's say 50m (map units). So it will be rendered very small on small scales and bigger on bigger scales?

Can you help me again please?

0 Kudos
UmaHarano
Esri Regular Contributor

Hi Richard,

Of course! Here is how I made this work:

I first picked the map scale at which I want my symbol to draw at the desired size.  In the screenshot below, I picked the map scale of 1:1,650,255.

In my code, I then used this this scale as the "Map Reference scale".  The map will now use this scale and as you zoom in and out, the symbol size will be resized.  Screenshot below:

Here is the code I used:

        protected override async void OnClick()
        {
            var lyr = MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().Where(f => f.ShapeType == esriGeometryType.esriGeometryPoint).FirstOrDefault();
            await QueuedTask.Run(() => {
                var symbol = CreateSymbol();
                var renderer = lyr.GetRenderer() as CIMSimpleRenderer;
                renderer.Symbol = symbol.MakeSymbolReference();
                lyr.SetRenderer(renderer);
                MapView.Active.Map.SetReferenceScale(1650255);
            });           
        }
        private CIMPointSymbol CreateSymbol()
        {
            CIMCharacterMarker marker = SymbolFactory.Instance.ConstructMarker(
                    57,
                    "ESRI Business",
                    "Regular",
                    15,
                    ColorFactory.Instance.BlackRGB) as CIMCharacterMarker;
            var symbol = SymbolFactory.Instance.ConstructPointSymbol(marker);
            symbol.UseRealWorldSymbolSizes = true;
            symbol.SetSize(23.75);
            return symbol;
        }
        }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
RichardReinicke
Occasional Contributor II

Hello Uma,

thank you for your code! I'm glad to see that symbols react to reference scale but for some reason it doesn't do in my case:

Can you please have a look if you see some problem with my code?

I first create a single SymbolReference on add-in start:

 

_gpsPositionSymbol = await CreateGpsPositionSymbolReference().ContinueWith(result => result.Result);

private Task<CIMSymbolReference> CreateGpsPositionSymbolReference()
{
    return QueuedTask.Run(() =>
    {
        CIMCharacterMarker marker = SymbolFactory.Instance.ConstructMarker(
            66, "Schiff_real", "Regular", _config.ShipIconSize, 
            ColorFactory.Instance.BlackRGB) as CIMCharacterMarker;

        var symbol = SymbolFactory.Instance.ConstructPointSymbol(marker);
        symbol.SetRealWorldUnits(true);
        symbol.SetSize(23.75); // Kormoran ship size in real units?

        return symbol.MakeSymbolReference();
    });
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

Also I react on MapView initialized event and set the reference scale once:

// Do not wonder I have a custom class that listens to Map and MapView events and 
// provides some more specific events
private async void MapViewInitialized(MapViewEventArgs eventArgs)
{
    // Do something in main app when map view is loaded
    await QueuedTask.Run(() =>
    {
        MapView.Active.Map.SetReferenceScale(4000);
    });
}‍‍‍‍‍‍‍‍‍‍

 

Later in my code I have a MapOverlay with a single point which is regularly updated per second. I set the symbol rotation to a specific angle:

((CIMPointSymbol)_gpsPositionSymbol.Symbol).SetAngle(targetCourseAngle);

Then I create or update my overlay:

if (_gpsPositionOverlay == null)
{
    _gpsPositionOverlay = MapView.Active.AddOverlay(mapPoint, _gpsPositionSymbol);
}
else
{
    MapView.Active.UpdateOverlay(_gpsPositionOverlay, mapPoint, _gpsPositionSymbol);
}‍‍‍‍‍‍‍‍

My symbol still holds size after each zoom.

0 Kudos
UmaHarano
Esri Regular Contributor

Hi Richard

Thank you for the clear explanation on what you are trying to accomplish. I was able to see the issue once I changed my code to use Map overlays.  Look like Overlay graphics don't resize with the map reference scale.  

I will update this post once I have some information as to when this will be implemented.

Thanks

Uma

0 Kudos
RichardReinicke
Occasional Contributor II

Hello Uma,

okay no problem. It's good to know that it doesn't depend on my code. When I think about it closer, the behaviour is also clear to me. The MapOverlay seems to be nothing more than that a map overlay so however it is created, it will be placed like that above the map and will not change. A FeatureLayer however is part of the map itself and therefore reacts and changes with the map renderer.

For now, I will try a InMemory FeatureLayer with a single Feature in it. The problem is that our feature gets update quite often so it probably takes much resources to edit a data source instead of adding a 'simple' overlay.

Thank you once again for checking this!

0 Kudos