Calculate a geodesic path between two points and measure its distance.
Use case
A geodesic distance provides an accurate, real-world distance between two points. Visualizing flight paths between cities is a common example of a geodesic operation since the flight path between two airports takes into account the curvature of the earth, rather than following the planar path between those points, which appears as a straight line on a projected map.
How to use the sample
Click anywhere on the map. A line graphic will display the geodesic line between the two points. In addition, text that indicates the geodesic distance between the two points will be updated. Click elsewhere and a new line will be created.
How it works
- Create a
Point
in New York City and display it as aGraphic
. - Obtain a new point when a click occurs on the
MapView
and add this point as a graphic. - Create a
Polyline
from the two points. - Execute
GeometryEngine::densifyGeodetic
by passing in the created polyine then create a graphic from the returnedGeometry
. - Execute
GeometryEngine::lengthGeodetic
by passing in the two points and display the returned length on the screen.
Relevant API
- GeometryEngine::densifyGeodetic
- GeometryEngine::lengthGeodetic
About the data
The Imagery basemap provides the global context for the displayed geodesic line.
Tags
densify, distance, geodesic, geodetic
Sample Code
// [WriteFile Name=GeodesicOperations, Category=Geometry]
// [Legal]
// Copyright 2018 Esri.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// [Legal]
#ifdef PCH_BUILD
#include "pch.hpp"
#endif // PCH_BUILD
#include "GeodesicOperations.h"
#include "Map.h"
#include "MapQuickView.h"
#include "GraphicsOverlay.h"
#include "Graphic.h"
#include "SimpleMarkerSymbol.h"
#include "SimpleLineSymbol.h"
#include "GeometryEngine.h"
#include "SpatialReference.h"
#include "PolylineBuilder.h"
#include "Point.h"
using namespace Esri::ArcGISRuntime;
GeodesicOperations::GeodesicOperations(QQuickItem* parent /* = nullptr */):
QQuickItem(parent)
{
}
void GeodesicOperations::init()
{
// Register the map view for QML
qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView");
qmlRegisterType<GeodesicOperations>("Esri.Samples", 1, 0, "GeodesicOperationsSample");
}
void GeodesicOperations::componentComplete()
{
QQuickItem::componentComplete();
// find QML MapView component
m_mapView = findChild<MapQuickView*>("mapView");
// Create a map using the imagery basemap
m_map = new Map(BasemapStyle::ArcGISImageryStandard, this);
// Set map to map view
m_mapView->setMap(m_map);
// Create a GraphicsOverlay
GraphicsOverlay* graphicsOverlay = new GraphicsOverlay(this);
m_mapView->graphicsOverlays()->append(graphicsOverlay);
// Create Graphic Symbols
SimpleMarkerSymbol* markerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbolStyle::Circle, QColor("blue"), 10.0f /*size*/, this);
SimpleLineSymbol* pathSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle::Dash, QColor("blue"), 5.0f /*width*/, this);
// Create NYC graphic
const Point nycPoint(-73.7781, 40.6413, SpatialReference(4326));
m_nycGraphic = new Graphic(nycPoint, markerSymbol, this);
graphicsOverlay->graphics()->append(m_nycGraphic);
// Create destination graphic
m_destinationGraphic = new Graphic(this);
m_destinationGraphic->setSymbol(markerSymbol);
graphicsOverlay->graphics()->append(m_destinationGraphic);
// Create path graphic
m_pathGraphic = new Graphic(this);
m_pathGraphic->setSymbol(pathSymbol);
graphicsOverlay->graphics()->append(m_pathGraphic);
// connect to mouse clicked signal
connect(m_mapView, &MapQuickView::mouseClicked, this, [this, nycPoint](QMouseEvent& mouseEvent)
{
// re-project the point to match the NYC graphic
const Point clickedPoint = m_mapView->screenToLocation(mouseEvent.x(), mouseEvent.y());
const Point destination = GeometryEngine::project(clickedPoint, m_nycGraphic->geometry().spatialReference());
// update the destination graphic
m_destinationGraphic->setGeometry(destination);
// create line with start/end points
const QList<Point> points = {nycPoint, destination};
const Polyline polyline = pointsToPolyline(points);
// densify the path as a geodesic curve and show it with the path graphic
constexpr double maxSegmentLength = 1.0;
const LinearUnit unitOfMeasurement(LinearUnitId::Kilometers);
constexpr GeodeticCurveType curveType = GeodeticCurveType::Geodesic;
const Geometry pathGeometry = GeometryEngine::densifyGeodetic(polyline, maxSegmentLength, unitOfMeasurement, curveType);
// update the graphic
m_pathGraphic->setGeometry(pathGeometry);
// calculate the path's geodetic length
m_distanceText = QString::number(GeometryEngine::lengthGeodetic(pathGeometry, unitOfMeasurement, curveType), 'f', 2);
emit distanceTextChanged();
});
}
// helper function to create a new Polyline from a list of points
Polyline GeodesicOperations::pointsToPolyline(const QList<Point>& points)
{
PolylineBuilder polylineBuilder(SpatialReference(4326));
for (const Point& point : points)
polylineBuilder.addPoint(point);
return polylineBuilder.toPolyline();
}
QString GeodesicOperations::distanceText() const
{
return m_distanceText;
}