多的不说了直接上代码:
首先是添加控件(使用的是自带控件Control)
//由于该功能属于项目的一部分,所以部分使用到的外部变量可能看不懂
//根据API更替为自己的变量即可
osg::ref_ptr<osg::Group> MAP_GR_CreateScale::createScale()
{
using namespace osgEarth::Util::Controls;
osg::ref_ptr<VBox> center = new VBox(Control::ALIGN_RIGHT, Control::ALIGN_BOTTOM, 0, 0);
// Add a text label:
{
osg::ref_ptr<HBox> labelBox = new HBox();
labelBox->setHorizFill( true );
labelBox->setChildVertAlign( Control::ALIGN_LEFT );
labelBox->setMargin(0);
osg::ref_ptr<LabelControl> label1 = new LabelControl( "0" );
label1->setFont( osgEarth::Registry::instance()->getDefaultFont() );
label1->setFontSize( 10.0f );
label1->setHorizAlign( Control::ALIGN_LEFT );
label1->setMargin( 0 );
labelBox->addControl( label1.get() );
osg::ref_ptr<LabelControl> label2 = new LabelControl( "1000km" );
label2->setFont( osgEarth::Registry::instance()->getDefaultFont() );
label2->setFontSize( 10.0f );
label2->setHorizAlign( Control::ALIGN_CENTER );
label2->setMargin( 0 );
labelBox->addControl( label2.get() );
osg::ref_ptr<LabelControl> label3 = new LabelControl( "2000km" );
label3->setFont( osgEarth::Registry::instance()->getDefaultFont() );
label3->setFontSize( 10.0f );
label3->setHorizAlign( Control::ALIGN_RIGHT );
label3->setMargin( 0 );
labelBox->addControl( label3.get() );
center->addControl(labelBox.get());
m_label2 = label2;
m_label3 = label3;
}
//Add Bar
{
osg::ref_ptr<HBox> barBox = new HBox();
barBox->setChildSpacing(0);
barBox->setChildVertAlign( Control::ALIGN_LEFT );
barBox->setMargin(0);
osg::ref_ptr<HBox> bar1 = new HBox();
bar1->setSize(m_defaultPixel, 10);
bar1->setBackColor( osgEarth::Symbology::Color::Blue );
bar1->setMargin(0);
osg::ref_ptr<HBox> bar2 = new HBox();
bar2->setSize(m_defaultPixel, 10);
bar2->setBackColor( Color::White );
bar2->setMargin(0);
barBox->addChild(bar1);
barBox->addChild(bar2);
center->addControl(barBox.get());
m_bar1 = bar1;
m_bar2 = bar2;
}
auto l_mapData = MAP_GLOBALDATA_APPData::getInstance()->g_mapData;
if(!l_mapData->valid)
return nullptr;
osg::ref_ptr<ControlCanvas> cs = ControlCanvas::getOrCreate( l_mapData->pView );
cs->addControl(center.get());
m_pScale = center;
m_pCanvas = cs;
return m_pCanvas;
}
然后是实时更新(注册一个handler,在Scroll事件时调用即可)
void MAP_GR_CreateScale::calScale()
{
if(!m_pCanvas)
return;
//默认的像素值(80)对应的距离
int defaultPixelDistance = MAP_OSG_OSGTool::getDistanceDifferByPixel(m_defaultPixel);
// qDebug() << defaultPixelDistance;
//计算默认距离对应有效数字为一位的距离(显示数值)
int bit = QString::number(defaultPixelDistance).size();
double value = std::round(defaultPixelDistance / pow(10, bit - 1));
double nearDistance = value * pow(10, bit - 1);
double width = MAP_OSG_OSGTool::getPixelByDistance(nearDistance);
m_bar1->setWidth(width);
m_bar1->dirty();
m_bar2->setWidth(width);
m_bar2->dirty();
QString label1;
QString label2;
// km
double showValue = nearDistance / 1000;
if(showValue < 1.0)
{
label1 = QString::number(showValue * 1000) + "m";
label2 = QString::number(2 * showValue * 1000) + "m";
}
else
{
label1 = QString::number(showValue) + "km";
label2 = QString::number(2 * showValue) + "km";
}
m_label2->setText(label1.toStdString());
m_label3->setText(label2.toStdString());
}
附工具函数
osg::Vec2 MAP_OSG_OSGTool::getLatLongDegree(double x, double y)
{
osg::Vec2 pos(0.0, 0.0);
auto l_mapData = MAP_GLOBALDATA_APPData::getInstance()->g_mapData;
if(!l_mapData->valid)
return osg::Vec2();
osgViewer::View* l_pViewer = l_mapData->pView;
auto l_mapNode = osgEarth::MapNode::findMapNode(l_pViewer->getSceneData());
osg::NodePath l_nodePath;
l_nodePath.push_back(l_mapNode->getTerrainEngine());
// 获取当前点
osgUtil::LineSegmentIntersector::Intersections intersection;
l_pViewer->computeIntersections(x, y, l_nodePath, intersection);
osgUtil::LineSegmentIntersector::Intersections::iterator iter
= intersection.begin();
if (iter != intersection.end())
{
osgEarth::GeoPoint point;
point.fromWorld(l_mapNode->getMap()->getSRS(), iter->getWorldIntersectPoint());
pos.x() = point.x();
pos.y() = point.y();
return pos;
}
return osg::Vec2();
}
osg::Vec3 MAP_OSG_OSGTool::windowsXYToLatLongDegree(double x, double y)
{
auto l_mapData = MAP_GLOBALDATA_APPData::getInstance()->g_mapData;
if(!l_mapData->valid)
return osg::Vec3();
auto camera = l_mapData->pView->getCamera();
osg::Matrix VPW = camera->getViewMatrix() *
camera->getProjectionMatrix() *
camera->getViewport()->computeWindowMatrix();
osg::Matrix inverseVPW;
inverseVPW.invert(VPW);
osg::Vec3d world = osg::Vec3d(x,y,0) * inverseVPW;
osgEarth::GeoPoint point;
point.fromWorld(l_mapData->pMapSRS, world);
return osg::Vec3(point.x(), point.y(), point.z());
}
osg::Vec2 MAP_OSG_OSGTool::LatLongDegreeToWindowsXY(double latDegree, double longDegree)
{
auto l_mapData = MAP_GLOBALDATA_APPData::getInstance()->g_mapData;
if(!l_mapData->valid)
return osg::Vec2();
auto camera = l_mapData->pView->getCamera();
osg::Matrix VPW = camera->getViewMatrix() *
camera->getProjectionMatrix() *
camera->getViewport()->computeWindowMatrix();
osg::Vec3d world;
bool b = l_mapData->pMapSRS->transformToWorld(osg::Vec3d(longDegree, latDegree, 0.0), world);
if(!b)
return osg::Vec2();
auto windows = world * VPW;
return osg::Vec2(windows.x(), windows.y());
}
osg::Vec2d MAP_OSG_OSGTool::getLatLongDifferByPixel(double hPixel, double vPixel)
{
osg::Vec2d differ(0.0, 0.0);
auto widget = MAP_GLOBALDATA_APPData::getInstance()->g_osgWidget;
if(!widget)
return differ;
double centerX = widget->width() / 2;
double centerY = widget->height() / 2;
osg::Vec2 centerLatLong = getLatLongDegree(centerX, centerY);
double differX = centerX + hPixel;
double differY = centerY + vPixel;
osg::Vec2 differLatLong = getLatLongDegree(differX, differY);
differ.x() = abs(differLatLong.x() - centerLatLong.x());
differ.y() = abs(differLatLong.y() - centerLatLong.y());
return differ;
}
double MAP_OSG_OSGTool::getDistanceDifferByPixel(double hPixel)
{
auto widget = MAP_GLOBALDATA_APPData::getInstance()->g_osgWidget;
if(!widget)
return -1;
double centerX = widget->width() / 2;
double centerY = widget->height() / 2;
osg::Vec2 centerLatLong = getLatLongDegree(centerX, centerY);
double differX = centerX + hPixel;
osg::Vec2 differLatLong = getLatLongDegree(differX, centerY);
return osgEarth::GeoMath::distance(osg::Vec3(centerLatLong.x(), centerLatLong.y(), 0),
osg::Vec3(differLatLong.x(), differLatLong.y(), 0),
MAP_GLOBALDATA_APPData::getInstance()->g_mapData->pMapSRS);
}
double MAP_OSG_OSGTool::getPixelByDistance(double distance)
{
auto widget = MAP_GLOBALDATA_APPData::getInstance()->g_osgWidget;
if(!widget)
return -1;
double centerX = widget->width() / 2;
double centerY = widget->height() / 2;
osg::Vec2 centerLatLong = getLatLongDegree(centerX, centerY);
double nextLat = 0;
double nextLong = 0;
osgEarth::GeoMath::destination(osg::DegreesToRadians(centerLatLong.y()),
osg::DegreesToRadians(centerLatLong.x()),
90, distance, nextLat, nextLong);
nextLat = osg::RadiansToDegrees(nextLat);
nextLong = osg::RadiansToDegrees(nextLong);
auto nextWindowXY = MAP_OSG_OSGTool::LatLongDegreeToWindowsXY(nextLat, nextLong);
return distanceByPoints(osg::Vec2(centerX, centerY), nextWindowXY);
}
double MAP_OSG_OSGTool::distanceByPoints(osg::Vec3 first, osg::Vec3 second)
{
return std::sqrt(pow(first.x() - second.x(), 2) + pow(first.y() - second.y(), 2) +
pow(first.z() - second.z(), 2));
}
double MAP_OSG_OSGTool::distanceByPoints(osg::Vec2 first, osg::Vec2 second)
{
return std::sqrt(pow(first.x() - second.x(), 2) + pow(first.y() - second.y(), 2));
}
另,有一个小问题,就是如果是在handler中Scroll事件触发即刻调用更新,会导致地图还没来得及放缩,这个时候更新用的地图是原来的地图,肯定导致一定的误差。我的思路是:
void MAP_GR_CreateScale::updateScale()
{
QTimer::singleShot(100, this, &MAP_GR_CreateScale::calScale);
}
对,没错,就是万能的Timer~
代码较为离散,毕竟是抠出来的一部分啦~