OSGEARTH开发三维地球
背景介绍
由于先前搞了一个qgis的二维地图,现在项目组提出新的需求,需要二维三维地图进行联动。经过前期的调研,发现qgis好像并不具备三维地图展示的能力,于是搜索到osg, osgearth ,经过的几天的编译折腾,环境搞定了。我们的项目主要涉及,点,线,面绘制,shp文件,tif文件加载。飞机模型演示等,具体需求相对简单。
经过几天的摸索,初步完成了三维地图的加载,点,线,面的绘制工作。并且将osgearth与Qt进行了结合。下面来看看代码。
Qt 与 osgearth示例代码
main.cpp文件
#include "MWidget.h"
#include <QMainWindow>
#include <QDebug>
int usage(const std::string& msg, osg::ArgumentParser& args)
{
OE_NOTICE << msg << std::endl << std::endl;
OE_NOTICE << "USAGE: " << args[0] << " file.earth" << std::endl;
return -1;
}
int main(int argc, char** argv)
{
qDebug() << "argv[1]:" << argv[1] ;
//参数解析器,
osg::ArgumentParser args(&argc, argv);
if (args.find("--help") >= 0)
return usage("Help", args);
// load something 获取场景操作的节点.
osg::ref_ptr<osg::Node> node = osgDB::readNodeFiles(args);
if (!node.valid())
return usage("Can't load a scene!", args);
// Qt setup:
QApplication q(argc, argv);
//osg::Node* scene get获取node指针.
MWidget m(args, node.get());
m.show();
return q.exec();
}
MWidget.h 文件
#pragma once
#include <QWidget>
#include "ui_MWidget.h"
#include <osg/Notify>
#include <osgViewer/CompositeViewer>
#include <osgEarthUtil/EarthManipulator>
#include <osgEarthQt/ViewWidget>
#include <osgEarth/Random>
#include <osgEarth/FileUtils>
#include <QApplication>
#include <QDialog>
#include <QMainWindow>
#include <QPushButton>
#include <QLayout>
#include <QDebug>
//
#include <osgEarth/MapNode>
#include <osgEarthUtil/EarthManipulator>
#include <osgEarthUtil/ExampleResources>
#include <osgEarthAnnotation/ImageOverlay>
#include <osgEarthAnnotation/CircleNode>
#include <osgEarthAnnotation/RectangleNode>
#include <osgEarthAnnotation/EllipseNode>
#include <osgEarthAnnotation/PlaceNode>
#include <osgEarthAnnotation/LabelNode>
#include <osgEarthAnnotation/LocalGeometryNode>
#include <osgEarthAnnotation/FeatureNode>
#include <osgEarthAnnotation/ModelNode>
#include <osgEarthAnnotation/AnnotationEditing>
#include <osgEarthAnnotation/ImageOverlayEditor>
#include <osgEarthSymbology/GeometryFactory>
class MWidget : public QWidget
{
Q_OBJECT
public:
MWidget(QWidget *parent = Q_NULLPTR);
MWidget( osg::ArgumentParser& args, osg::Node* scene, QWidget *parent = Q_NULLPTR);
void paintEvent(QPaintEvent* e);
public slots:
void addView();
public:
QTimer _timer;
osgViewer::CompositeViewer _viewer;
osg::ref_ptr<osg::Node> _scene;
osg::ref_ptr<osg::Node> node;
private:
Ui::MWidget ui;
};
MWidget.cpp 文件
#include "MWidget.h"
using namespace osgEarth;
using namespace osgEarth::Util;
using namespace osgEarth::QtGui;
using namespace osgEarth::Annotation;
using namespace osgEarth::Features;
//暂时使用这种形式,
//extern osg::ref_ptr<osg::Node> node;
MWidget::MWidget(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
}
MWidget::MWidget( osg::ArgumentParser& args, osg::Node* scene, QWidget *parent)
: QWidget(parent), _viewer(args), _scene(scene)
{
ui.setupUi(this);
node = scene;
_viewer.setThreadingModel(_viewer.SingleThreaded);
// timer fires a paint event.
connect(&_timer, SIGNAL(timeout()), this, SLOT(update()));
_timer.start(20);
connect(ui.pushButton, SIGNAL(clicked()), this, SLOT(addView()));
//构造器中必须要有.否则没有任何效果.
addView();
}
void MWidget::paintEvent(QPaintEvent* e)
{
// refresh all the views.
if (_viewer.getRunFrameScheme() == osgViewer::ViewerBase::CONTINUOUS ||
_viewer.checkNeedToDoFrame())
{
_viewer.frame();
}
}
void MWidget::addView()
{
// the new View we want to add:
osgViewer::View* view = new osgViewer::View();
// a widget to hold our view:
QWidget* viewWidget = new osgEarth::QtGui::ViewWidget(view); //将osgViewer 转换为 QWidget, 方便进行填充编程.
///
osg::Group* root = new osg::Group();
// find the map node that we loaded.
MapNode* mapNode = MapNode::findMapNode(node);
if (!mapNode)
;
// Group to hold all our annotation elements.
//创建一个组用来保存所有的元素.
osg::Group* annoGroup = new osg::Group();
MapNode::get(node)->addChild(annoGroup);
// Make a group for labels
osg::Group* labelGroup = new osg::Group();
annoGroup->addChild(labelGroup);
osg::Group* editGroup = new osg::Group();
MapNode::get(node)->addChild(editGroup);
// Style our labels:
Style labelStyle;
labelStyle.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_CENTER_CENTER;
labelStyle.getOrCreate<TextSymbol>()->fill()->color() = Color::Yellow;
// A lat/long SRS for specifying points.
const SpatialReference* geoSRS = mapNode->getMapSRS()->getGeographicSRS(); //获取当前地图的空间地理引用
//--------------------------------------------------------------------
// A series of place nodes (an icon with a text label)
{
Style pm;
pm.getOrCreate<IconSymbol>()->url()->setLiteral("../data/placemark32.png");
pm.getOrCreate<IconSymbol>()->declutter() = true;
pm.getOrCreate<TextSymbol>()->halo() = Color("#5f5f5f");
// bunch of pins:
labelGroup->addChild(new PlaceNode(GeoPoint(geoSRS, -74.00, 40.71), "New York", pm));
labelGroup->addChild(new PlaceNode(GeoPoint(geoSRS, -77.04, 38.85), "Washington, DC", pm));
labelGroup->addChild(new PlaceNode(GeoPoint(geoSRS, -118.40, 33.93), "Los Angeles", pm));
labelGroup->addChild(new PlaceNode(GeoPoint(geoSRS, -71.03, 42.37), "Boston", pm));
labelGroup->addChild(new PlaceNode(GeoPoint(geoSRS, -157.93, 21.35), "Honolulu", pm));
labelGroup->addChild(new PlaceNode(GeoPoint(geoSRS, 139.75, 35.68), "Tokyo", pm));
labelGroup->addChild(new PlaceNode(GeoPoint(geoSRS, -90.25, 29.98), "New Orleans", pm));
labelGroup->addChild(new PlaceNode(GeoPoint(geoSRS, -80.28, 25.82), "Miami", pm));
labelGroup->addChild(new PlaceNode(GeoPoint(geoSRS, -117.17, 32.72), "San Diego", pm));
// test with an LOD:
osg::LOD* lod = new osg::LOD();
lod->addChild(new PlaceNode(GeoPoint(geoSRS, 14.68, 50.0), "Prague", pm), 0.0, 2e6);
labelGroup->addChild(lod);
// absolute altitude:
labelGroup->addChild(new PlaceNode(GeoPoint(geoSRS, -87.65, 41.90, 1000, ALTMODE_ABSOLUTE), "Chicago", pm));
}
//--------------------------------------------------------------------
// a box that follows lines of latitude (rhumb line interpolation, the default)
// and flashes on and off using a cull callback.
{
struct C : public osg::NodeCallback {
void operator()(osg::Node* n, osg::NodeVisitor* nv) {
static int i = 0;
i++;
if (i % 100 < 50)
traverse(n, nv);
}
};
Geometry* geom = new osgEarth::Symbology::Polygon();
geom->push_back(osg::Vec3d(0, 40, 0));
geom->push_back(osg::Vec3d(-60, 40, 0));
geom->push_back(osg::Vec3d(-60, 60, 0));
geom->push_back(osg::Vec3d(0, 60, 0));
Feature* feature = new Feature(geom, geoSRS);
feature->geoInterp() = GEOINTERP_RHUMB_LINE;
Style geomStyle;
// geomStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::Cyan;
geomStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::Red;
geomStyle.getOrCreate<LineSymbol>()->stroke()->width() = 5.0f;
geomStyle.getOrCreate<LineSymbol>()->tessellationSize() = 75000;
geomStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
geomStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
FeatureNode* fnode = new FeatureNode(feature, geomStyle);
fnode->addCullCallback(new C());
annoGroup->addChild(fnode);
LabelNode* label = new LabelNode("Rhumb line polygon", labelStyle);
label->setPosition(GeoPoint(geoSRS, -30, 50)); //geoSRS 空间参考坐标系
labelGroup->addChild(label);
}
//--------------------------------------------------------------------
// another rhumb box that crosses the antimeridian
{
Geometry* geom = new osgEarth::Symbology::Polygon();
geom->push_back(-160., -30.);
geom->push_back(150., -20.);
geom->push_back(160., -45.);
geom->push_back(-150., -40.);
Style geomStyle;
Feature* feature = new Feature(geom, geoSRS);
feature->geoInterp() = GEOINTERP_RHUMB_LINE;
geomStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::Lime;
geomStyle.getOrCreate<LineSymbol>()->stroke()->width() = 3.0f;
geomStyle.getOrCreate<LineSymbol>()->tessellationSize() = 75000;
geomStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
geomStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
FeatureNode* gnode = new FeatureNode(feature, geomStyle);
annoGroup->addChild(gnode);
LabelNode* label = new LabelNode("Antimeridian polygon", labelStyle);
label->setPosition(GeoPoint(geoSRS, -175, -35));
labelGroup->addChild(label);
}
//--------------------------------------------------------------------
// A path using great-circle interpolation.
// Keep a pointer to it so we can modify it later on.
FeatureNode* pathNode = 0;
{
Geometry* path = new LineString();
path->push_back(osg::Vec3d(-74, 40.714, 0)); // New York
path->push_back(osg::Vec3d(139.75, 35.68, 0)); // Tokyo
Feature* pathFeature = new Feature(path, geoSRS);
pathFeature->geoInterp() = GEOINTERP_GREAT_CIRCLE;
Style pathStyle;
pathStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::White;
pathStyle.getOrCreate<LineSymbol>()->stroke()->width() = 1.0f;
pathStyle.getOrCreate<LineSymbol>()->stroke()->smooth() = true;
pathStyle.getOrCreate<LineSymbol>()->tessellationSize() = 75000;
pathStyle.getOrCreate<PointSymbol>()->size() = 8;
pathStyle.getOrCreate<PointSymbol>()->fill()->color() = Color::Red;
pathStyle.getOrCreate<PointSymbol>()->smooth() = true;
pathStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
pathStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
pathStyle.getOrCreate<RenderSymbol>()->depthOffset()->enabled() = true;
//OE_INFO << "Path extent = " << pathFeature->getExtent().toString() << std::endl;
pathNode = new FeatureNode(pathFeature, pathStyle);
annoGroup->addChild(pathNode);
LabelNode* label = new LabelNode("Great circle path", labelStyle);
label->setPosition(GeoPoint(geoSRS, -170, 61.2));
labelGroup->addChild(label);
}
//--------------------------------------------------------------------
// Two circle segments around New Orleans.
{
Style circleStyle;
circleStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Cyan, 0.5);
circleStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
circleStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
CircleNode* circle = new CircleNode();
circle->set(
GeoPoint(geoSRS, -90.25, 29.98, 1000., ALTMODE_RELATIVE),
Distance(300, Units::KILOMETERS),
circleStyle,
Angle(-45.0, Units::DEGREES),
Angle(45.0, Units::DEGREES),
true);
annoGroup->addChild(circle);
editGroup->addChild(new CircleNodeEditor(circle));
}
{
Style circleStyle;
circleStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Red, 0.5);
circleStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
circleStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
CircleNode* circle = new CircleNode();
circle->set(
GeoPoint(geoSRS, -90.25, 29.98, 1000., ALTMODE_RELATIVE),
Distance(300, Units::KILOMETERS),
circleStyle,
Angle(45.0, Units::DEGREES),
Angle(360.0 - 45.0, Units::DEGREES),
true);
annoGroup->addChild(circle);
editGroup->addChild(new CircleNodeEditor(circle));
}
//--------------------------------------------------------------------
// An extruded ellipse around Miami.
{
Style ellipseStyle;
ellipseStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Orange, 0.75);
ellipseStyle.getOrCreate<ExtrusionSymbol>()->height() = 250000.0; // meters MSL
EllipseNode* ellipse = new EllipseNode();
ellipse->set(
GeoPoint(geoSRS, -80.28, 25.82, 0.0, ALTMODE_RELATIVE),
Distance(250, Units::MILES),
Distance(100, Units::MILES),
Angle(0, Units::DEGREES),
ellipseStyle,
Angle(45.0, Units::DEGREES),
Angle(360.0 - 45.0, Units::DEGREES),
true);
annoGroup->addChild(ellipse);
editGroup->addChild(new EllipseNodeEditor(ellipse));
}
{
Style ellipseStyle;
ellipseStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Blue, 0.75);
ellipseStyle.getOrCreate<ExtrusionSymbol>()->height() = 250000.0; // meters MSL
EllipseNode* ellipse = new EllipseNode();
ellipse->set(
GeoPoint(geoSRS, -80.28, 25.82, 0.0, ALTMODE_RELATIVE),
Distance(250, Units::MILES),
Distance(100, Units::MILES),
Angle(0, Units::DEGREES),
ellipseStyle,
Angle(-40.0, Units::DEGREES),
Angle(40.0, Units::DEGREES),
true);
annoGroup->addChild(ellipse);
editGroup->addChild(new EllipseNodeEditor(ellipse));
}
//--------------------------------------------------------------------
{
// A rectangle around San Diego
Style rectStyle;
rectStyle.getOrCreate<ExtrusionSymbol>()->height() = 1250000.0; // meters MSL
rectStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Green, 0.5);
rectStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
rectStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
RectangleNode* rect = new RectangleNode(
GeoPoint(geoSRS, -117.172, 32.721),
Distance(300, Units::KILOMETERS),
Distance(600, Units::KILOMETERS),
rectStyle);
annoGroup->addChild(rect);
editGroup->addChild(new RectangleNodeEditor(rect));
}
//--------------------------------------------------------------------
// An extruded polygon roughly the shape of Utah. Here we demonstrate the
// FeatureNode, where you create a geographic geometry and use it as an
// annotation.
{
Geometry* utah = new osgEarth::Symbology::Polygon();
utah->push_back(-114.052, 37.0);
utah->push_back(-109.054, 37.0);
utah->push_back(-109.054, 41.0);
utah->push_back(-111.040, 41.0);
utah->push_back(-111.080, 42.059);
utah->push_back(-114.080, 42.024);
Style utahStyle;
utahStyle.getOrCreate<ExtrusionSymbol>()->height() = 1250000.0; // meters MSL
utahStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Red, 0.8);
Feature* utahFeature = new Feature(utah, geoSRS);
FeatureNode* featureNode = new FeatureNode(utahFeature, utahStyle);
annoGroup->addChild(featureNode);
}
//--------------------------------------------------------------------
// an image overlay.
{
ImageOverlay* imageOverlay = 0L;
osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile("../data/USFLAG.TGA");
if (image.valid())
{
imageOverlay = new ImageOverlay(mapNode, image.get());
imageOverlay->setBounds(Bounds(-100.0, 35.0, -90.0, 40.0));
annoGroup->addChild(imageOverlay);
editGroup->addChild(new ImageOverlayEditor(imageOverlay));
}
}
//--------------------------------------------------------------------
// a model node with auto scaling.
{
Style style;
style.getOrCreate<ModelSymbol>()->autoScale() = true;
style.getOrCreate<ModelSymbol>()->url()->setLiteral("../data/red_flag.osg.50.scale");
ModelNode* modelNode = new ModelNode(mapNode, style);
modelNode->setPosition(GeoPoint(geoSRS, -100, 52));
annoGroup->addChild(modelNode);
}
//
// a dialog to hold the view widget: 向widget中添加一个dialog对象
QDialog* win = new QDialog(this);
win->setModal(false); //设置非模态对话框.
win->setLayout(new QHBoxLayout()); //设置水平排列
win->layout()->addWidget(viewWidget); //
int x = osgEarth::Random().next(1024); //产生随机数,0 ~ 1024
int y = osgEarth::Random().next(768); //产生随机数, 0 ~ 768
qDebug() << "x:" << x << " y:" << y << endl;
win->setGeometry(x, y, 640, 480);
win->show();
// set up the view, 相当于node 节点.
view->setCameraManipulator(new osgEarth::Util::EarthManipulator);
view->setSceneData(_scene.get());
view->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true, false);
// add it to the composite viewer.
_viewer.addView(view);
}
效果展示
点击addview 按钮不断的产生新图片.