/* ** ----------------------------------------------------------------------------------------------- ** ** @RDE_HEADER@ ** Implementation of fan of sight ** $Id$ ** $HeadURL : svn ://192.168.133.21/TDS/branches/product/devel/trunk/Lts/Viewer/SituationMap/Rubberbands/rubberband.cpp $ ** $Author : ehrlicher $ ** ** ----------------------------------------------------------------------------------------------- ** ** Hardware - Environment : Intel - PC ** Software - Environment : Windows 7, Qt 5.x.ArcGISRuntimeQt 100.2 ** ** Language ........ : C++ 11 ** ** ----------------------------------------------------------------------------------------------- ** ** Autor ........... : Nikolaus Hanekamp, SEDG, Tel. 2906 ** Jörg Turla, SPDG, Tel. 3132 ** ** ----------------------------------------------------------------------------------------------- ** ** Notices ......... : ** ** 01.02.2005, NH : Urversion ** 15.05.2018, JT : adapting GeISiR source to SituationMap ** ** ----------------------------------------------------------------------------------------------- */ #include "sight_fan_impl.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace std::chrono_literals; namespace SituationMap { // class SightFanImpl::Internal; enum SightMode { VISIBLE = 0, INVISIBLE, UNDEFINED, START }; class SightEntry { public: SightEntry() : _dist(0.0), _mode(START) {;} SightEntry(double dist, SightMode mode) : _dist(dist), _mode(mode) {;} ~SightEntry() = default; double & dist() { return _dist; } SightMode & mode() { return _mode; } double dist() const { return _dist; } SightMode mode() const { return _mode; } private: double _dist; SightMode _mode; }; // SightEntry class SightBeam : public Referenced { public: explicit SightBeam(int beamId, const QPointF &directionB, const QPointF &directionE) : _beamId(beamId) , _directionBeg(directionB) , _directionEnd(directionE) , _sightEntryVec() , _graphicalQuadVec() {;} SightBeam() = delete; ~SightBeam() = default; QPointF direction() const { auto normalize = [](const QPointF &v) -> QPointF { double l = sqrt(v.x()*v.x() + v.y()*v.y()); return v / l; }; return normalize(QPointF((_directionBeg + _directionEnd)*0.5)); } // QPointF direction() void add(const SightEntry &sightE) { _sightEntryVec.push_back(sightE); } // void add( const SightEntry &sightE) void clear() { _sightEntryVec.clear(); } // void draw(const QPointF &beamCenter, osg::Matrixd mat, QColor colors[]); // todo: Farbtabelle void updateGraphic( MapImpl *mapImpl, AbstractMapObjectImpl *meMapObj, SightFanImpl::Internal * sightFanInternal ); int _beamId; protected: QPointF _directionBeg; // Einheitsvektoren die den Beam begrenzen QPointF _directionEnd; using SightEntryVec = QVector; SightEntryVec _sightEntryVec; QVector> _graphicalQuadVec; }; /* SightBeam */ class SightFanImpl::Internal { public: using SightBeamVec = std::vector> ; QString m_layerName; std::shared_ptr m_eMapManager; QPointF m_center; double m_centHat; double m_objHat; double m_radius; double m_innerRadius; double m_startAngle; double m_dAngle; unsigned int m_nrFans; double m_alpha; QRectF m_location; // Lage des SightFans in Kartenkoordinaten SightBeamVec m_sightBeamVec; QColor m_colors[3]; // 0 : visibleCol, 1 : invisibleCol, 2 : undefinedCol QColor m_fanColorVec[2][4]; float m_fanMult; // Farbabsenkung jedes 2. Beams < 1.0 ! MapImpl *m_mapImpl; Esri::ArcGISRuntime::Surface *m_surface; LOGGER_T m_logger; ///< Interface of the Logging-Facility Internal() : m_layerName() , m_center() , m_centHat(1.8) , m_objHat(0.5) , m_radius(0.0) , m_innerRadius(0.0) , m_startAngle(0.0) , m_dAngle(2.0*MathConst::pi/180) , m_nrFans(180) , m_alpha(0.8) , m_fanMult(0.75) , m_mapImpl(nullptr) , m_surface(nullptr) , m_logger(getLogger("SightFanImpl")) {;} void setFanColorMap( double brightness, double bias, double alpha) { for (int i = 0; i < 3; i++) { double r = (m_colors[i].redF() * brightness) + bias; double g = (m_colors[i].greenF() * brightness) + bias; double b = (m_colors[i].blueF() * brightness) + bias; double a = m_colors[i].alphaF() * alpha; m_fanColorVec[0][i] = QColor::fromRgbF(r,g,b,a); m_fanColorVec[1][i] = QColor::fromRgbF(r*m_fanMult,g*m_fanMult,b*m_fanMult,a); } m_fanColorVec[0][3] = m_fanColorVec[0][2]; m_fanColorVec[1][3] = m_fanColorVec[1][2]; } // void setFanColorMap() }; // SightFanImpl::Internal SightFanImpl::SightFanImpl( const QString &debugInfo ) : SightFan() , QObject() , AbstractMapObjectImpl(debugInfo) , m_internal(new Internal()) , m_moveDelayTimer(new QTimer(this)) { initColors(); m_internal->setFanColorMap(1, 0, 1); m_moveDelayTimer->setSingleShot(true); m_moveDelayTimer->setInterval(250ms); connect(m_moveDelayTimer, &QTimer::timeout, this, &SightFanImpl::delayedMove); } // SightFanImpl::SightFanImpl SightFanImpl::~SightFanImpl() { delete m_internal; } // SightFanImpl::~SightFanImpl() void SightFanImpl::initColors() { m_internal->m_fanMult = 0.75; m_internal->m_colors[0] = QColor(0, 255, 0, 180); m_internal->m_colors[1] = QColor(255, 0, 0, 180); m_internal->m_colors[2] = QColor(0, 0, 255, 180); } // void SightFanImpl::initColors() void SightFanImpl::setFanColor( const QColor &visible, const QColor &invisible, const QColor &undefined) { m_internal->m_colors[0] = visible; m_internal->m_colors[1] = invisible; m_internal->m_colors[2] = undefined; } // void SightFanImpl::setFanColor( const QColor &visible, const QColor &invisible, const QColor &undefined) void SightFanImpl::setFanColorMark( float colMult) { if( colMult > 1.0) colMult = 1.0; if( colMult < 0.0) colMult = 0.0; m_internal->m_fanMult = colMult; } // void SightFanImpl::setFanColorMark( float colMult) void SightFanImpl::setCenter(const QPointF ¢er) { m_internal->m_center = center; } void SightFanImpl::setCenter(const Point3D ¢er) { m_internal->m_center = center.toPointF(); m_internal->m_centHat = center.z(); } void SightFanImpl::setCenter(const QPointF ¢er, double centerHat) { m_internal->m_center = center; m_internal->m_centHat = centerHat; } void SightFanImpl::setCenterHat(double centerHat) { m_internal->m_centHat = centerHat; } void SightFanImpl::setObjectHat(double objHat) { m_internal->m_objHat = objHat; } void SightFanImpl::setRadius(double radius) { m_internal->m_radius = radius; } void SightFanImpl::setInnerRadius(double radius) { m_internal->m_innerRadius = radius; } void SightFanImpl::setStartAngle(double startAngle) { m_internal->m_startAngle = startAngle; } void SightFanImpl::setdeltaAngle(double dAngle) { m_internal->m_dAngle = dAngle; } void SightFanImpl::setNoOfFans(double noFans) { m_internal->m_nrFans = noFans; } void SightFanImpl::setAlpha(double alpha) { if (alpha < 0.0) alpha = 0.0; if (alpha > 1.0) alpha = 1.0; m_internal->m_alpha = alpha; } // void SightFanImpl::setAlpha(double alpha) QPointF SightFanImpl::center() const { return m_internal->m_center; } double SightFanImpl::centerHat() const { return m_internal->m_centHat; } double SightFanImpl::objectHat() const { return m_internal->m_objHat; } double SightFanImpl::innerRadius() const { return m_internal->m_innerRadius; } double SightFanImpl::radius() const { return m_internal->m_radius; } double SightFanImpl::startAngle() const { return m_internal->m_startAngle; } double SightFanImpl::dAngle() const { return m_internal->m_dAngle; } double SightFanImpl::alpha() const { return m_internal->m_alpha; } unsigned int SightFanImpl::noOfFans() const { return m_internal->m_nrFans; } bool SightFanImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) { if (mapImpl) { m_internal->m_mapImpl = mapImpl; m_internal->m_layerName = logicalLayerName; m_internal->m_eMapManager = mapImpl->getElevationMapManager(); if (!m_internal->m_eMapManager) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " failure, no ElevationMapManager"; return false; } if (!AbstractMapObjectImpl::init(mapImpl, logicalLayerName)) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " failure, AbstractMapObjectImpl::init failed"; return false; } return true; } ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " init SightFanImpl failed"; return false; } // bool SightFanImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) void SightFanImpl::boundingBox(BoundingBox &bbox) const { bbox.reset(); CoordTranslationFunctor * coordTransFunc = m_internal->m_mapImpl->coordTranslationFunctor(); if (coordTransFunc) { const QPointF ¢erMap = coordTransFunc->geographicToMap(m_internal->m_center); double diameter = 2 * m_internal->m_radius; bbox = BoundingBox( BoundingBox::ExtentType::Sim, Point3D::fromQPointF(centerMap), diameter, diameter ); } } // void SightFanImpl::boundingBox(BoundingBox &bbox) const //--------------------------------------------------------------------------------------------------- void SightFanImpl::initFan( const Point3D ¢er , double objHat , double radius , double innerRadius , double startAngle , double dAngle , unsigned int nrFans , double alpha ) { initFan( center.toPointF(), center.z(), objHat, radius, innerRadius, startAngle, dAngle, nrFans, alpha ); } // void SightFanImpl::init(...) void SightFanImpl::initFan( const QPointF ¢er , double centHat , double objHat , double radius , double innerRadius , double startAngle , double dAngle , unsigned int nrFans , double alpha ) { if (!m_internal->m_mapImpl) { ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " no mapImpl"; return; } if (alpha < 0.0) alpha = 0.0; if (alpha > 1.0) alpha = 1.0; m_internal->m_center = center; m_internal->m_centHat = centHat; m_internal->m_objHat = objHat; m_internal->m_radius = radius; m_internal->m_innerRadius = innerRadius; m_internal->m_startAngle = startAngle; m_internal->m_dAngle = (dAngle < 0) ? -dAngle : dAngle; m_internal->m_nrFans = nrFans; m_internal->m_alpha = alpha; QPointF offc(m_internal->m_radius, -m_internal->m_radius); m_internal->m_location = QRectF(m_internal->m_center - offc, m_internal->m_center + offc); if ((m_internal->m_nrFans*m_internal->m_dAngle) > (MathConst::pi*2.0)) { m_internal->m_nrFans = floor((MathConst::pi*2.0) / m_internal->m_dAngle + 0.5); // To avoid error with floor; 30.3.2006 JT } calculateSightBeamVec(); } /* SightFan::SightFan */ void SightFanImpl::calculateSightBeamVec() { m_internal->m_sightBeamVec.clear(); double dAlfa = (m_internal->m_dAngle / 2.0) + 0.00025; // 0.00025 wegen Überlappung, auf antialiazed Systemen nicht nötig for (unsigned int i = 0; i < m_internal->m_nrFans; i++) { double alfa = m_internal->m_startAngle + m_internal->m_dAngle * i; double alfaBeg = alfa - dAlfa; double alfaEnd = alfa + dAlfa; RefPointer sBeam = RefPointer(new SightBeam(i, QPointF(cos(alfaBeg), sin(alfaBeg)), QPointF(cos(alfaEnd), sin(alfaEnd)))); calculateSightBeam(*(sBeam.get())); m_internal->m_sightBeamVec.push_back(sBeam); } } // void SightFanImpl::calculateSightBeamVev() void SightFanImpl::move(const QPointF ¢er, double centHat, double objHat) { m_internal->m_centHat = centHat; m_internal->m_objHat = objHat; move(center); } // void SightFanImpl::move(ElevationMap &eMap, QPointF ¢er, double centHat, double objHat) void SightFanImpl::move(const QPointF & center) { m_moveDelayedCenter = center; m_moveDelayTimer->start(); } void SightFanImpl::show() { move( m_internal->m_center ); } // void SightFanImpl::show bool SightFanImpl::updateViewImplPre() { return true; } // bool SightFanImpl::updateViewImplPre() bool SightFanImpl::updateViewImpl() { show(); return true; } // bool SightFanImpl::updateViewImpl() bool SightFanImpl::updateViewImplPost() { return true; } // bool SightFanImpl::updateViewImplPost() inline QPointF mittelBreitenRechnung(const QPointF &start, const QPointF &direction, double distance) { // --------------------------------------------------------------- // Vec2d mittelBreitenRechnung( Vec2d start, Vec2d direction, double distance ) // Berechnet näherungsweise die Koordinate der Endposition eines // Beams gemäß der ersten Aufgabe der Besteckrechnung durch das // Verfahren der Mittelbreite. // Siehe http://astrosail.de/de/static/tutorial/best3.php?cat=42 // Start ist die Anfangskoordinate im Bogenmaß, direction der // normierte Richtungsvektor, distance die Entfernung in Metern. // --------------------------------------------------------------- double cosKurs = direction.y(); double sinKurs = direction.x(); double distanceBogen = distance / MathConst::EarthRadius; double lat = distanceBogen * cosKurs + start.y(); double mittelbreite = (start.y() + lat) * 0.5; double lon = distanceBogen * sinKurs / cos(mittelbreite) + start.x(); return {lon, lat}; } inline QPointF endOfBeam( const QPointF &start, const QPointF &direction, double distance ) { return mittelBreitenRechnung(start,direction,distance); } void SightFanImpl::calculateSightBeam(SightBeam &sBeam) { const QPointF &direction = sBeam.direction(); const QPointF &end = endOfBeam(m_internal->m_center, direction, m_internal->m_radius); LOSVector losProfile; m_internal->m_eMapManager->LOSProfile(m_internal->m_center, end, losProfile); calculateSightBeam(losProfile, sBeam); sBeam.updateGraphic(m_internal->m_mapImpl, this, m_internal); } // void SightFanImpl::calculateSightBeam(ElevationMap &eMap, SightBeam &sBeam) void SightFanImpl::calculateSightBeam( const LOSVector &losProfile, SightBeam &sBeam ) { sBeam.clear(); if (losProfile.empty()) // Startpunkt undefiniert oder ausserhalb der Karte { sBeam.add( SightEntry(m_internal->m_innerRadius, START) ); sBeam.add( SightEntry(m_internal->m_radius, UNDEFINED) ); return; } LOSVector::const_iterator itr = losProfile.begin(); double centerHeight = itr->height() + m_internal->m_centHat; double d1 = 0.0; double height1 = itr->height(); // überspringen bis _innerRadius erreicht ist ! while(++itr != losProfile.end()) { double d2 = itr->dist(); // *EarthRadius; double height2 = itr->height(); if (d2 >= m_internal->m_innerRadius) { height1 = height1 + (m_internal->m_innerRadius - d1) * ( height2 - height1)/(d2 - d1); d1 = m_internal->m_innerRadius; --itr; break; } d1 = d2; height1 = height2; } // bool visible = true; SightMode mode = START; double curM = (height1 - centerHeight ) / d1; // bestimmende Steigung sBeam.add(SightEntry( d1, START)); if (itr != losProfile.end()) { while (++itr != losProfile.end()) { double d2 = itr->dist(); // *EarthRadius; double height2 = itr->height(); if (std::isnan(height2)) // Undefined { if (mode != START) { sBeam.add(SightEntry(d1, mode)); } mode = UNDEFINED; break; } double hObj = (height2 + m_internal->m_objHat - centerHeight ); double objM = hObj / d2; // Steigung bezogen auf Objekte( Objekte verdecken nicht !) SightMode newMode = ((objM >= curM)) ? VISIBLE : INVISIBLE; if(newMode != mode) { if ((mode != START) && (d2 != d1)) { // Korrekturwert für Distanz berechnen, unter Berücksichtigung der Objekthoehe !! double localM = (height2 - height1) /(d2 -d1); // localM == curM kann nicht auftreten, da newMode != mode double dCorr = (hObj - (curM * d2)) / (localM - curM); if (dCorr < 0.0) dCorr = 0.0; dCorr = d2 - dCorr; if (dCorr < d1) dCorr = d1; sBeam.add( SightEntry( dCorr, mode) ); } mode = newMode; } double terrM = (height2 - centerHeight) / d2; // Steigung bezogen auf Gelaende if (terrM >= curM) { curM = terrM; // neue bestimmende Steigung } height1 = height2; d1 = d2; } /* while */ } sBeam.add( SightEntry(m_internal->m_radius, mode) ); } /*! * \brief SightFanImpl::delayedMove * \param center * \details Delays the move call to prevent crash of map beacause of the heavy load while moving */ void SightFanImpl::delayedMove() { m_internal->m_center = m_moveDelayedCenter; m_internal->setFanColorMap(1, 0, 1); QPointF offc(m_internal->m_radius, -m_internal->m_radius); m_internal->m_location = QRectF(m_internal->m_center - offc, m_internal->m_center + offc); if (m_internal->m_nrFans == m_internal->m_sightBeamVec.size()) { for (RefPointer sBeam : qAsConst(m_internal->m_sightBeamVec)) { calculateSightBeam(*sBeam.get()); } } else { calculateSightBeamVec(); } } void SightBeam::updateGraphic(MapImpl *mapImpl, AbstractMapObjectImpl *meMapObj, SightFanImpl::Internal *sightFanInternal) { if (!mapImpl || !meMapObj || !sightFanInternal) return; if (_sightEntryVec.size() <= 1) return; Esri::ArcGISRuntime::GraphicsOverlay *graphicLayer = sightFanInternal->m_mapImpl->getGraphicsLayer(sightFanInternal->m_layerName); if (!graphicLayer) { ERROR_LOG(sightFanInternal->m_logger) << LOGFUNCTION << " Creating graphic overlayer \'" << sightFanInternal->m_layerName << "\' failed. Can't render sight fan"; return; } // _sightEntryVec[0] is SightMode::START // resize _graphicalQuadVec according _sightEntryVec // (_sightEntryVec.size() <= 1 is chexck above) const int newGraphicalSize = _sightEntryVec.size() - 1; _graphicalQuadVec.resize(newGraphicalSize); CoordTranslationFunctor *geoToMap = sightFanInternal->m_mapImpl->coordTranslationFunctor(); int quadNo = 0; SightEntryVec::iterator itr = _sightEntryVec.begin(); const QPointF ¢er = sightFanInternal->m_center; QPointF v0 = endOfBeam(center, _directionBeg, itr->dist()); QPointF v1 = endOfBeam(center, _directionEnd, itr->dist()); if (geoToMap) { v0 = geoToMap->geographicToMap(v0); v1 = geoToMap->geographicToMap(v1); } PositionCont locationVec; // skip first, we know mode==SightMode::START while (++itr != _sightEntryVec.end()) { // GraphicalObjectPath already created : QSharedPointer &spGopRef = _graphicalQuadVec[quadNo]; if (!spGopRef) { GraphicalObjectPath * grQuad = new GraphicalObjectPath( GraphicalObjectPathType::Polygon , mapImpl->getEsriGeoView() , graphicLayer , QStringLiteral("FanOfSight-Beam-%1").arg(_beamId) , meMapObj->uniqueMapId() ); grQuad->setPickable(false); grQuad->rLineStyle().setLinePattern(LinePattern::Null); grQuad->rFillStyle().setFillPattern(FillPattern::Solid); // write to _graphicalQuadVec spGopRef = QSharedPointer(grQuad); } // : GraphicalObjectPath already created GraphicalObjectPath * grQuad = _graphicalQuadVec[quadNo].data(); Q_ASSERT_X(grQuad, "SightBeam", "GraphicalObjectPath invalid"); SightMode mode = itr->mode(); double dist = itr->dist(); QColor quadColor = sightFanInternal->m_fanColorVec[_beamId % 2][int(mode)]; quadColor.setAlphaF( quadColor.alphaF()*sightFanInternal->m_alpha ); grQuad->rFillStyle().setFillColor(quadColor); QPointF v2 = endOfBeam(center, _directionBeg, dist); QPointF v3 = endOfBeam(center, _directionEnd, dist); if (geoToMap) { v2 = geoToMap->geographicToMap(v2); v3 = geoToMap->geographicToMap(v3); } locationVec.clear(); locationVec << Point3D::fromQPointF(v0); locationVec << Point3D::fromQPointF(v1); locationVec << Point3D::fromQPointF(v2); locationVec << Point3D::fromQPointF(v3); grQuad->setPositions(locationVec); grQuad->updateView(); v0 = v3; v1 = v2; ++quadNo; } /* while */ } // void SightBeam::updateGraphic(MapImpl *mapImpl, AbstractMapObjectImpl *meMapObj, SightFanImpl::Internal *sightFanInternal) #ifdef NEVER bool SightFan::draw( const AbstractView * const view, osg::Matrixd mat, float alpha, float brightness, float bias, MapObject::RenderState rstate ) // todo: Farbtabelle { if ((rstate & _renderState) == 0) return false; if( !( _location.intersects( view->rectBound()) )) return false; // isCulled ColorVec colors[2][4]; for( int i = 0; i < 3; i++) { colors[0][i] = (_colors[i] * brightness) + bias; colors[1][i] = colors[0][i] * _fanMult; colors[0][i].alpha = alpha * _colors[i].alpha; colors[1][i].alpha = alpha * _colors[i].alpha; } colors[0][3] = colors[0][2]; colors[1][3] = colors[1][2]; GLint shadeModel; glGetIntegerv( GL_SHADE_MODEL, &shadeModel ); glShadeModel(GL_FLAT); glEnable( GL_BLEND); int n = 0; for( SightBeamVec::iterator itr = _sightBeamVec.begin(); itr != _sightBeamVec.end(); ++itr) { (*itr).get()->draw( _center, mat, colors[(++n)%2]); } glDisable( GL_BLEND); glShadeModel(shadeModel); return true; } /* SightFan::draw */ void SightBeam::draw( Vec2d center, osg::Matrixd mat, ColorVec colors[]) // todo: Farbtabelle !? { float z = 0; if( _sightEntryVec.size() <= 1) return; SightEntryVec::iterator itr = _sightEntryVec.begin(); Vec2d v0 = endOfBeam( center, _directionBeg, (*itr).dist() ); Vec2d v1 = endOfBeam( center, _directionEnd, (*itr).dist() ); osg::Vec3d _v0 = mat.preMult( osg::Vec3d( v0[0], v0[1], 0.0 )); osg::Vec3d _v1 = mat.preMult( osg::Vec3d( v1[0], v1[1], 0.0 )); glBegin( GL_QUAD_STRIP); { glVertex3f( _v0.x(), _v0.y(), z); glVertex3f( _v1.x(), _v1.y(), z); while( ++itr != _sightEntryVec.end() ) { SightMode mode = (*itr).mode(); glColor4f( colors[mode].red, colors[mode].green, colors[mode].blue, colors[mode].alpha); v0 = endOfBeam( center, _directionBeg, (*itr).dist() ); v1 = endOfBeam( center, _directionEnd, (*itr).dist() ); _v0 = mat.preMult( osg::Vec3d( v0[0], v0[1], 0.0 )); _v1 = mat.preMult( osg::Vec3d( v1[0], v1[1], 0.0 )); glVertex3f( _v0.x(), _v0.y(), z); glVertex3f( _v1.x(), _v1.y(), z); } /* while */ } glEnd(); } /* SightBeam::draw */ #endif } // SituationMap