Custom vector images for each location?

771
6
Jump to solution
02-24-2021 01:54 PM
dioG9T
by
New Contributor II

I'm evaluating porting our application to ArcGIS Runtime for Qt, as we're now using an ancient map tool which nobody really knows well anymore. However, after browsing the documentation of ArcGIS Runtime for Qt, I'm worried that we won't be able to replicate one of our features.

We're visualizing radio antennas on a map, and for each antenna we want to show the direction it faces and the beam angle. So, for example, one antenna may face north and have a beam width of 60°, another one may face north-north-east and have a beam width of 30°, and yet another one might be omnidirectional. The user can change the parameters at any time. Sometimes a single location may have multiple antennas. The user can change the parameters (direction, angle, radius) at any time, so it's not something we would be able to prepare in advance as predefined shapes. This visualization should be independent of map scale, but anchored to the point where we believe the antenna is installed. I've attached an image of what I have in mind.

Our current tool draws tiles, has a function to translate from geographic coördinates to screen coördinates, then simply redraws the arcs and lines using standard canvas methods using only screen coördinates every time the map changes the zoom level.

So, is it possible to implement simple vector drawings like these in ArcGIS Runtime for Qt? Thank you!

0 Kudos
2 Solutions

Accepted Solutions
TannerYould1
Esri Contributor

Oh I see, my mistake, I originally had it as a set distance in meters, but what you're suggesting is possible as well.

One thing that would work is to create a connection that executes code every time the user changes the mapView's current viewpoint scale. If that happens, then redraw the arcs with the distance being a function of the current scale using:

m_mapView->currentViewpoint(ViewpointType::CenterAndScale).targetScale() / frequency

 So appended to my earlier code that would look like this: 

QList<Point> result = createPointsFromMoveGeodetic(centerPt, m_mapView->currentViewpoint(ViewpointType::CenterAndScale).targetScale()/50, 0, 30);

I attached example screenshots showing the arc at different scales. Is that what you had in mind?

View solution in original post

TannerYould1
Esri Contributor

No worries and thanks again for your time.

As an additional thought, it sounds like the LocationToScreenResult class may have suited you better than the solution I came up with. This class has a member function that takes a geographic point and returns a screen point. To specifically quote the documentation: "A screen point result is the x/y screen position where the x/y/z scene location would appear, and whether the x/y/z position is visible, off the screen, or hidden from the camera by objects in the scene." From there you would be able to draw whatever you need with the standard canvas methods.

Regardless, thank you for your consideration and I hope you find something that works for you!

View solution in original post

0 Kudos
6 Replies
TannerYould1
Esri Contributor

Hi @dioG9T, thanks for taking the time to evaluate ArcGIS Runtime for Qt! I took some time to test out a concept and I believe we can achieve what you're looking for with a somewhat equivalent solution.

Instead of creating vector drawings that must be calculated and redrawn every time the map moves, I utilized ArcGIS Runtime API's GeometryEngine class. I used the `moveGeodetic` member function to calculate a new point at a specific distance and angle for every degree of the beam arc. I then used `convexHull` to create a polygon around the resulting points and then added that polygon to the map view.

I've implemented this rough proof of concept in the C++ API, but it can also be done in the QML API. I wrote this in the default template provided to our users and all of this functionality is available right out of the box.

void GeodeticMove::setMapView(MapQuickView* mapView)
{
  // ** Extraneous map initialization code **
  if (!mapView || mapView == m_mapView)
    return;

  m_mapView = mapView;
  m_mapView->setMap(m_map);

  // *****************************************

  // initial setup

  m_graphicsOverlay = new GraphicsOverlay(this);
  GraphicsOverlay* arcOverlay = new GraphicsOverlay(this);

  arcOverlay->setOpacity(0.5);

  m_mapView->graphicsOverlays()->append(m_graphicsOverlay);
  m_mapView->graphicsOverlays()->append(arcOverlay);

  // Create center point (radio tower) and symbol
  const Point centerPt(-119, 34, SpatialReference::wgs84());
  SimpleMarkerSymbol* ptSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbolStyle::Circle, QColor(Qt::blue), 10, this);

  // Add center point to graphics overlay
  Graphic* pointGraphic = new Graphic(centerPt, ptSymbol, this);
  m_graphicsOverlay->graphics()->append(pointGraphic);

  // Create a list of geometries
  QList<Geometry> arcPoints;
  
  // This is a custom function implemented below
  QList<Point> result = createPointsFromMoveGeodetic(centerPt, 10000, 0, 30);

  // geodeticMove returns points so let's crudely convert those (there is a better way to do this)
  for (const Point& point : result)
  {
    arcPoints.append(point);
  }
  // Add center point to polygon for the full triangle
  arcPoints.append(centerPt);

  // Create the view polygon, make it a graphic, and display it on the map
  const Geometry arcPolygon = GeometryEngine::convexHull(arcPoints, true).first();
  SimpleFillSymbol* arcSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle::Solid, QColor(Qt::red), this);
  arcOverlay->graphics()->append(new Graphic(arcPolygon, arcSymbol, this));

  m_mapView->setViewpointCenter(centerPt, 100000);

  emit mapViewChanged();
}

// Custom function to create points that will make up the beam polygon
QList<Point> GeodeticMove::createPointsFromMoveGeodetic(Point initialPt, double distance, double heading, double width)
{
  QList<Point> newPoints;

  double startDegree = heading - width;
  double endDegree = heading + width;

  // Handle cases of where degrees go from 360 -> 0
  int mod = 0;
  if (startDegree < 0)
  {
    startDegree += 360;
  }
  if (startDegree > endDegree)
  {
    mod = 360;
  }

  for (double i = startDegree; i < endDegree+mod; ++i)
  {
    if (startDegree > 360)
    {
      startDegree -= 360;
    }
    newPoints.append(GeometryEngine::moveGeodetic({ initialPt }, distance, LinearUnit::meters(), i, AngularUnit::degrees(), GeodeticCurveType::Geodesic));
  }
  return newPoints;
}

 

The result is the screenshot I've attached below.

 

From there, for example, you can make the values variable and editable by reading from point features defined in a ArcGIS Online Feature Layer. We have a lot of sample code available for our users to reference, and the Update attributes (feature service) sample may be a good reference if you're interested in that.

I hope this helps answer your question, please don't hesitate to follow up or ask additional questions, and thanks again for evaluating the ArcGIS Runtime API for Qt!

- Tanner Yould,
ArcGIS Runtime API for Qt Sample Product Engineer

0 Kudos
dioG9T
by
New Contributor II

Thank you for your effort! I am analysing the code and have a question: If I zoom the map, will the arcs become larger or smaller in terms of screen space? I would specifically like to avoid that, as the radius does not represent a distance; instead it is a representation of any user-defined quantity (e.g. frequency or amount of traffic). Hence we are defining the radius in terms of screen dimensions (pixels), so that regardless of map scale, the radii stay the same in terms of screen space.

0 Kudos
TannerYould1
Esri Contributor

Oh I see, my mistake, I originally had it as a set distance in meters, but what you're suggesting is possible as well.

One thing that would work is to create a connection that executes code every time the user changes the mapView's current viewpoint scale. If that happens, then redraw the arcs with the distance being a function of the current scale using:

m_mapView->currentViewpoint(ViewpointType::CenterAndScale).targetScale() / frequency

 So appended to my earlier code that would look like this: 

QList<Point> result = createPointsFromMoveGeodetic(centerPt, m_mapView->currentViewpoint(ViewpointType::CenterAndScale).targetScale()/50, 0, 30);

I attached example screenshots showing the arc at different scales. Is that what you had in mind?

View solution in original post

dioG9T
by
New Contributor II

While your code does indeed look like it is implementing the feature as described, this is not the right kind of abstraction I would like to work with and tells me that what I seek is not available in ArcGIS Runtime for Qt. Thank you again for your effort!

0 Kudos
TannerYould1
Esri Contributor

No worries and thanks again for your time.

As an additional thought, it sounds like the LocationToScreenResult class may have suited you better than the solution I came up with. This class has a member function that takes a geographic point and returns a screen point. To specifically quote the documentation: "A screen point result is the x/y screen position where the x/y/z scene location would appear, and whether the x/y/z position is visible, off the screen, or hidden from the camera by objects in the scene." From there you would be able to draw whatever you need with the standard canvas methods.

Regardless, thank you for your consideration and I hope you find something that works for you!

View solution in original post

0 Kudos
dioG9T
by
New Contributor II

Oh, that's a much more compelling feature! Thank you, I must have missed it somehow during the initial evaluation.