Select to view content in your preferred language

Arrows along a line

277
3
Jump to solution
07-26-2024 07:18 AM
Labels (3)
JustinSteventon
Occasional Contributor

Hi folks,

I have a GPS track and I would like to have directional arrows displayed along it. I see this has been done in the JS library:
Arrows along a line | Sample Code | ArcGIS Maps SDK for JavaScript 4.30 | Esri Developer

I have tried creating point symbols and orienting them, but it is messy because they would need to be moved at different zoom levels and are an expensive solution.

Is there a recommended way to do this?

Thanks!

0 Kudos
1 Solution

Accepted Solutions
LucasDanzinger
Esri Frequent Contributor

Hi Justin,

 

This can be achieved with Multilayer (CIM) symbols. You could take a few different avenues to get the end result. The simplest port of the JS samples is to take the Symbol JSON they used and use it in Maps SDK.

 

Here's an example I tested with:

 

auto builder = new PolylineBuilder(SpatialReference::wgs84(), this);
builder->addPoint(0,0);
builder->addPoint(0,10);
builder->addPoint(0,20);
builder->addPoint(30,30);
builder->addPoint(50,50);
builder->addPoint(60,50);

auto g = new Graphic(builder->toGeometry(), this);
auto go = new GraphicsOverlay(this);
go->graphics()->append(g);
m_mapView->graphicsOverlays()->append(go);


auto symbol = R"({
    "type": "CIMSymbolReference",
    "symbol": {
      "type": "CIMLineSymbol",
      "symbolLayers": [{
        "type": "CIMSolidStroke",
        "enable": "true",
        "width": 1,
        "color": [0, 0, 0, 255]
      },
      {
        "type": "CIMVectorMarker",
        "enable": "true",
        "size": 5,
        "markerPlacement": {
          "type": "CIMMarkerPlacementAlongLineSameSize",
          "endings": "WithMarkers",
          "placementTemplate": [19.5],
          "angleToLine": "true"
        },
        "frame": {
          "xmin": -5,
          "ymin": -5,
          "xmax": 5,
          "ymax": 5
        },
        "markerGraphics": [{
          "type": "CIMMarkerGraphic",
          "geometry": {
            "rings": [
              [
                [-8, -5.47],
                [-8, 5.6],
                [1.96, -0.03],
                [-8, -5.47]
              ]
            ]
          },
          "symbol": {
            "type": "CIMPolygonSymbol",
            "symbolLayers": [{
              "type": "CIMSolidFill",
              "enable": "true",
              "color": [0, 0, 0, 255]
            }]
          }
        }]
      }]
    }
})";

auto s = Symbol::fromJson(symbol);    
g->setSymbol(s);

 

LucasDanzinger_0-1722027556473.png

 

 

You could further modify the CIM symbol JSON to your liking by referencing the CIM spec, or using a tool like the online CIM Symbol Builder.

 

If you don't want to sling JSON around, you could use our MultilayerSymbol API, which provides a subset of the CIM spec in a higher level easier to use API, but it might not have all the functionality you need.

 

A final option would be to use Pro to create a Symbol that you like, and export that to a style file that you can reference in your app. Here's a blog I wrote about this pattern.

 

View solution in original post

3 Replies
LucasDanzinger
Esri Frequent Contributor

Hi Justin,

 

This can be achieved with Multilayer (CIM) symbols. You could take a few different avenues to get the end result. The simplest port of the JS samples is to take the Symbol JSON they used and use it in Maps SDK.

 

Here's an example I tested with:

 

auto builder = new PolylineBuilder(SpatialReference::wgs84(), this);
builder->addPoint(0,0);
builder->addPoint(0,10);
builder->addPoint(0,20);
builder->addPoint(30,30);
builder->addPoint(50,50);
builder->addPoint(60,50);

auto g = new Graphic(builder->toGeometry(), this);
auto go = new GraphicsOverlay(this);
go->graphics()->append(g);
m_mapView->graphicsOverlays()->append(go);


auto symbol = R"({
    "type": "CIMSymbolReference",
    "symbol": {
      "type": "CIMLineSymbol",
      "symbolLayers": [{
        "type": "CIMSolidStroke",
        "enable": "true",
        "width": 1,
        "color": [0, 0, 0, 255]
      },
      {
        "type": "CIMVectorMarker",
        "enable": "true",
        "size": 5,
        "markerPlacement": {
          "type": "CIMMarkerPlacementAlongLineSameSize",
          "endings": "WithMarkers",
          "placementTemplate": [19.5],
          "angleToLine": "true"
        },
        "frame": {
          "xmin": -5,
          "ymin": -5,
          "xmax": 5,
          "ymax": 5
        },
        "markerGraphics": [{
          "type": "CIMMarkerGraphic",
          "geometry": {
            "rings": [
              [
                [-8, -5.47],
                [-8, 5.6],
                [1.96, -0.03],
                [-8, -5.47]
              ]
            ]
          },
          "symbol": {
            "type": "CIMPolygonSymbol",
            "symbolLayers": [{
              "type": "CIMSolidFill",
              "enable": "true",
              "color": [0, 0, 0, 255]
            }]
          }
        }]
      }]
    }
})";

auto s = Symbol::fromJson(symbol);    
g->setSymbol(s);

 

LucasDanzinger_0-1722027556473.png

 

 

You could further modify the CIM symbol JSON to your liking by referencing the CIM spec, or using a tool like the online CIM Symbol Builder.

 

If you don't want to sling JSON around, you could use our MultilayerSymbol API, which provides a subset of the CIM spec in a higher level easier to use API, but it might not have all the functionality you need.

 

A final option would be to use Pro to create a Symbol that you like, and export that to a style file that you can reference in your app. Here's a blog I wrote about this pattern.

 

JustinSteventon
Occasional Contributor

Thanks for the fast response, Lucas. I plugged this in, but the Symbol::fromJson(symbol) returns null. I verified that the JSON was well formed. I am using 100.15. I will try with the MultilayerSymbol API and report back.

0 Kudos
JustinSteventon
Occasional Contributor

Reporting back. The line in the JSON that caused the failure was "angleToLine" - perhaps that was not supported in 100.15. In any case, that seems to be the default, so removing it was fine.

I had good luck creating the classes manually, but there seems to be no way to modify the marker placement. Did I miss something?

 

auto geometricEffects = QList<GeometricEffect *> { new DashGeometricEffect(QList<double>{0.1, 10}, this) };
auto solidStrokeSymbolLayer = new SolidStrokeSymbolLayer(lineWidth, QColor(0, 0, 255, 200), geometricEffects, this);

auto simpleFillSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle::Solid, QColor(0, 0, 255, 200), this);
auto arrowJson = R"({"rings": [[[-8, -5.47],[-8, 5.6], [1.96, -0.03], [-8, -5.47]]]})";
auto symbolElementPolygon = new VectorMarkerSymbolElement(Geometry::fromJson(arrowJson), simpleFillSymbol->toMultilayerSymbol(this), this);
auto vectorMarkerSymbolLayer = new VectorMarkerSymbolLayer(QList<VectorMarkerSymbolElement*> {symbolElementPolygon}, this);

auto symbolLayers = QList<SymbolLayer*>{ solidStrokeSymbolLayer, vectorMarkerSymbolLayer };
auto multilayerSymbol = new MultilayerPolylineSymbol(symbolLayers, this);
graphic->setSymbol(multilayerSymbol);

 

In any case, creating the structures from JSON is straightforward and easy to work with, so I am going to stick with that approach.

Thanks!

0 Kudos