Files
qt5-legatus-legacy/Lts/Viewer/SituationMap/ElevationMap/elevation_map.cpp
T
2026-08-13 18:33:10 +02:00

976 lines
31 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @RDE_HEADER@
* adapting GeISiR source to SituationMap
* $Id$
* $HeadURL$
* $Author$
*/
#include "elevation_map.h"
#include <QtConcurrent>
#include <QPolygonF>
#include <MathUtils/math_const.h>
#include <QElapsedTimer>
#include <gdal.h>
#include <iostream>
#include <limits>
#include <memory>
#include <math.h>
namespace SituationMap
{
class BoundingRect
{
double m_top = 0.0;
double m_bottom = 0.0;
double m_left = 0.0;
double m_right = 0.0;
void swapBounds()
{
if (m_top < m_bottom)
std::swap(m_bottom, m_top);
if (m_right < m_left)
std::swap(m_left, m_right);
} // void swapBounds()
public:
BoundingRect() = default;
BoundingRect(double left, double bottom, double right, double top)
: m_top(top), m_bottom(bottom), m_left(left), m_right(right)
{
swapBounds();
} // BoundingRect( double left, double bottom, double right, double top)
BoundingRect(const BoundingRect &) = default;
BoundingRect &operator =(const BoundingRect &) = default;
~BoundingRect() = default;
inline void operator()(double left, double bottom, double right, double top)
{
m_top = top;
m_bottom = bottom;
m_left = left;
m_right = right;
swapBounds();
} // inline void operator() (double left, double bottom, double right, double top)
double top() const { return m_top; }
double bottom() const { return m_bottom; }
double left() const { return m_left; }
double right() const { return m_right; }
bool contains(const QPointF &p) const { return ((p.x() >= m_left) && (p.x() <= m_right) && (p.y() >= m_bottom) && (p.y() <= m_top)); }
QRectF toQRectF() const
{
return QRectF(QPointF(m_left, m_top), QPointF(m_right, m_bottom));
} // toQRectF
}; // BoundingRect
struct ElevationMap::Internal
{
BoundingRect _location; // Lage der Karte in Lat/Lon
struct PointDouble
{
void setX(double x) { _x = x;}
void setY(double y) { _y = y;}
double x() { return _x;}
double y() { return _y;}
double _x{0.0};
double _y{0.0};
};
PointDouble _elexelSize; // LatLon Size of 1 elevation sample, redundat
int _totalColumns = 0;
int _totalRows = 0;
double _toMeterFactor = 1.0;
std::vector<int> _elevationField;
template<class BufferType> bool copyBufferData(GDALRasterBandH hBand, int xBlkSize, int yBlkSize)
{
// NLDEVCTRL-804: Konsistenz d. Z-Werte zwischen 2D/3D (also ElevationMap vs. ESRI::BaseSurface)
// This ElevationMap was using an approach similar to following.
// Now it´s more or less identical and the difference using 'Altmark_dt1.tif'
// are gone. AHTC is also fine :-)
/*
CPLErr GetHistogram( GDALRasterBand *poBand, GUIntBig *panHistogram )
{
memset( panHistogram, 0, sizeof(GUIntBig) * 256 );
CPLAssert( poBand->GetRasterDataType() == GDT_Byte );
int nXBlockSize, nYBlockSize;
poBand->GetBlockSize( &nXBlockSize, &nYBlockSize );
int nXBlocks = (poBand->GetXSize() + nXBlockSize - 1) / nXBlockSize;
int nYBlocks = (poBand->GetYSize() + nYBlockSize - 1) / nYBlockSize;
GByte *pabyData = (GByte *) CPLMalloc(nXBlockSize * nYBlockSize);
for( int iYBlock = 0; iYBlock < nYBlocks; iYBlock++ )
{
for( int iXBlock = 0; iXBlock < nXBlocks; iXBlock++ )
{
int nXValid, nYValid;
poBand->ReadBlock( iXBlock, iYBlock, pabyData );
// Compute the portion of the block that is valid
// for partial edge blocks.
poBand->GetActualBlockSize(iXBlock, iYBlock, &nXValid, &nYValid)
// Collect the histogram counts.
for( int iY = 0; iY < nYValid; iY++ )
{
for( int iX = 0; iX < nXValid; iX++ )
{
panHistogram[pabyData[iX + iY * nXBlockSize]] += 1;
}
}
}
}
}
Source https://gdal.org/api/gdalrasterband_cpp.html
*/
const size_t bufSize = xBlkSize * yBlkSize;
std::unique_ptr<BufferType[]> bufferPtr(new BufferType[bufSize]);
int nXBlocks = (GDALGetRasterBandXSize(hBand) + xBlkSize - 1) / xBlkSize;
int nYBlocks = (GDALGetRasterBandYSize(hBand) + yBlkSize - 1) / yBlkSize;
for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
{
for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
{
CPLErr err = GDALReadBlock( hBand, iXBlock, iYBlock, bufferPtr.get() ); // Gdal liest spaltenweise !! x <-> y
if (err != CE_None)
{
return false;
}
int nXValid, nYValid;
// Unfortunately GDALGetActualBlockSize is not available at Suse 42.3 (gdal 2.1.3)
// so check bounds using _totalColumns, _totalRows:
#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2, 2, 0)
# define HAS_ACTUAL_BLOCKSIZE
#endif
#ifdef HAS_ACTUAL_BLOCKSIZE
GDALGetActualBlockSize(hBand, iXBlock, iYBlock, &nXValid, &nYValid);
#else
nXValid = xBlkSize;
nYValid = yBlkSize;
#endif // HAS_ACTUAL_BLOCKSIZE
const int curYOffset = (iYBlock * yBlkSize);
const int curXOffset = (iXBlock * xBlkSize);
for (int iY = 0; iY < nYValid; ++iY)
{
const int row = iY + curYOffset; // Line
#ifndef HAS_ACTUAL_BLOCKSIZE
if (row >= _totalRows)
continue;
#endif // HAS_ACTUAL_BLOCKSIZE
for (int iX = 0; iX < nXValid; ++iX)
{
// identical to gdallocationinfo
const int column = iX + curXOffset; // Pixel
#ifndef HAS_ACTUAL_BLOCKSIZE
if (column >= _totalColumns)
continue;
#endif // HAS_ACTUAL_BLOCKSIZE
int bi = iX + (iY * xBlkSize);
const int z = static_cast<int>( static_cast<double>(bufferPtr[bi]) / _toMeterFactor );
_elevationField[column + row * _totalColumns] = z;
} // for
} // for
} // for
} // for
return true;
} // template<class BufferType> void copyBufferData(BufferType * bufferPtr, int xBlkSize, int yBlkSize)
}; // struct ElevationMap::Internal
ElevationMap::ElevationMap(const QFileInfo &fileInfo)
: m_internal(new Internal())
, m_aproximation(true)
, m_valid(false)
, m_logger(getLogger("ElevationMap"))
{
GDALAllRegister();
m_fileName = fileInfo.absoluteFilePath();
INFO_LOG(m_logger) << " ctor ElevationMap: map of file '" << m_fileName << "'"
<< ", this=" << this;
if (!fileInfo.exists())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " elevation data file \'" << m_fileName << "\' doesn't exists.";
return;
}
if (!fileInfo.isReadable())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " elevation data file \'" << m_fileName << "\' isn't readable.";
return;
}
auto cb = [this]()
{
return readFromFile();
};
auto cbFinished = [this]()
{
NOTICE_LOG(m_logger) << " Finished reading elevation map of file '" << m_fileName << "'"
<< ", this=" << this;
};
QObject::connect(&m_gdalReadWatcher, &QFutureWatcher<bool>::finished,
&m_gdalReadWatcher, cbFinished);
m_gdalReadWatcher.setFuture(QtConcurrent::run(cb));
// sync init, takes ~800ms during sitmap loading 72MB file
// QElapsedTimer t;
// t.start();
// m_gdalReadWatcher.waitForFinished();
// NOTICE_LOG(m_logger) << "Time loading elevation map: " << t.elapsed();
} // ElevationMap::ElevationMap( const QFile &file )
ElevationMap::~ElevationMap()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_gdalReadWatcher.cancel();
m_gdalReadWatcher.waitForFinished();
delete m_internal;
m_internal = nullptr;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
} // ElevationMap::~ElevationMap
bool ElevationMap::waitForInitialization()
{
m_gdalReadWatcher.waitForFinished();
return m_valid;
}
bool ElevationMap::isValid() const
{
// N. Thoden, 27.04.2020
// isFinished seems to be unreliable:
// if mainThread request ::isValid() diretly after ::waitForInitialization()
// you get false (by 'if (!m_gdalReadWatcher.isFinished())'
// but m_valid is true. So skip the additional check.
// if (!m_gdalReadWatcher.isFinished())
// return false;
return m_valid;
}
QString ElevationMap::filename() const
{
return m_fileName;
}
bool ElevationMap::readFromFile()
{
m_valid = false;
GDALDatasetH hDataset = GDALOpen(m_fileName.toLocal8Bit().constData(), GA_ReadOnly);
if (nullptr == hDataset)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " GDALOpen failed, may be calling of GDALAllRegister is missing";
return false;
}
// Detect basic parameters
//
GDALDriverH hDriver = GDALGetDatasetDriver(hDataset);
int nRasterXSize = GDALGetRasterXSize(hDataset);
int nRasterYSize = GDALGetRasterYSize(hDataset);
double adfGeoTransform[6];
CPLErr err = GDALGetGeoTransform(hDataset, adfGeoTransform); // XY Spatial Referenz WGS84 (4326) expected!!!
if (err != CE_None)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " GDAL failed to get geo raster transform.";
GDALClose(hDataset);
return false;
}
if (nullptr == hDriver) { GDALClose(hDataset); return false; }
if (nRasterXSize <= 0) { GDALClose(hDataset); return false; }
if (nRasterYSize <= 0) { GDALClose(hDataset); return false; }
double pixWidth = adfGeoTransform[1];
double pixHeight = adfGeoTransform[5];
auto toRAD = [](double x) -> double { return x * MathConst::pi / 180.0; };
m_internal->_location(toRAD(adfGeoTransform[0]) // adfGeoTransform is in degrees
, toRAD(adfGeoTransform[3])
, toRAD(adfGeoTransform[0] + double(nRasterXSize) * pixWidth + double(nRasterYSize) * adfGeoTransform[2])
, toRAD(adfGeoTransform[3] + double(nRasterYSize) * pixHeight + double(nRasterXSize) * adfGeoTransform[4]));
m_internal->_elexelSize.setX(fabs(toRAD(pixWidth)));
m_internal->_elexelSize.setY(fabs(toRAD(pixHeight)));
GDALRasterBandH hBand = GDALGetRasterBand(hDataset, 1);
if (nullptr == hBand)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " GDAL failed to read raster band.";
GDALClose(hDataset);
return false;
}
int xBlkSize;
int yBlkSize;
GDALGetBlockSize(hBand, &xBlkSize, &yBlkSize);
if (xBlkSize <0 || yBlkSize < 0)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no buffer, next operations will possible fail....";
return false;
}
m_internal->_totalColumns = nRasterXSize;
m_internal->_totalRows = nRasterYSize;
m_internal->_elevationField.resize(nRasterXSize * nRasterYSize);
const GDALDataType type = GDALGetRasterDataType(hBand);
switch (type)
{
case GDT_UInt16:
m_internal->_toMeterFactor = 1.0;
m_valid = m_internal->copyBufferData<unsigned short int> ( hBand, xBlkSize, yBlkSize );
break;
case GDT_Int16:
m_internal->_toMeterFactor = 1.0;
m_valid = m_internal->copyBufferData<short int> ( hBand, xBlkSize, yBlkSize );
break;
case GDT_Float32:
m_internal->_toMeterFactor = 0.01;
m_valid = m_internal->copyBufferData<float> ( hBand, xBlkSize, yBlkSize );
break;
case GDT_Float64:
m_internal->_toMeterFactor = 0.01;
m_valid = m_internal->copyBufferData<double> ( hBand, xBlkSize, yBlkSize );
break;
default:
{
ERROR_LOG(m_logger) << LOGFUNCTION << " data type isn't supported type or a floating point type"
<< " (type=" << type << ")";
GDALClose(hDataset);
return false;
}
} // switch
if (!m_valid)
{
CPLErrorNum errNum = CPLGetLastErrorNo();
ERROR_LOG(m_logger) << LOGFUNCTION << " GDAL failed to read data block (error no " << int(errNum) << ")";
}
GDALClose(hDataset);
INFO_LOG(m_logger) << LOGFUNCTION << " loaded, filename =" << m_fileName << "'"
<< ", m_valid=" << m_valid;
return m_valid;
} // bool ElevationMap::readFromFile(const QString & fileName)
QRectF ElevationMap::extent() const
{
if (!isValid())
return QRectF();
return m_internal->_location.toQRectF();
}
QPair<double, double> ElevationMap::elexelSize() const
{
return {m_internal->_elexelSize._x, m_internal->_elexelSize._y};
}
int ElevationMap::getElevation(int column, int row) const
{
row = m_internal->_totalRows - row;
const size_t index = column + row * m_internal->_totalColumns;
if (m_internal->_elevationField.empty() || m_internal->_elevationField.size() < index)
return std::numeric_limits<int>::min();
return m_internal->_elevationField.at(index);
} // int ElevationMap::getElevation( int column, int row)
double ElevationMap::elevation(double lon, double lat) const
{
if (!isValid())
return 0;
return getElevation(lon, lat) * m_internal->_toMeterFactor;
} // double ElevationMap::elevation( double lon, double lat)
double ElevationMap:: getElevation(double lon, double lat) const
{
if (!m_internal->_location.contains(QPointF(lon, lat)))
{
return std::numeric_limits<int>::min(); // ausserhalb der Höhenkarte !!
}
double x = (lon - m_internal->_location.left()) / m_internal->_elexelSize.x();
int column = floor(x);
double y = (lat - m_internal->_location.bottom()) / m_internal->_elexelSize.y();
int row = floor(y);
int elevation = getElevation(column, row);
if (elevation == std::numeric_limits<int>::min())
return elevation;
if (!m_aproximation)
return elevation;
/*
** Approximation: e0, e1, e2, e3 Hoehenwerte der 4 benachbarten Stützpunkte
**
** + e3 + e2
** y
** ^ + er er = e0 + x*(e1-e0) + y*(e3-e0) + x*y(e2-e3+e0-e1)
** |
** |
** |
** | + e0 + e1
** -----> x
**
** Besonderheiten: die Stützpunkte liegen definitiongemaess jeweils in der Mitte des Rasters,
** so dass je nach Groesse von x, y andere Nachbarpunkte zu berücksichtigen sind, und x, sowie y entsprechend
** angepasst werden muesen.
** Bei Randrasterpunkten kann keine Interpolation erfolgen, so dass hier entsprechend extrapoliert wird.
*/
x = (x - (double)column); // Residuum normiert auf Stützpunktabstand
y = (y - (double)row); // Residuum normiert auf Stützpunktabstand
x -= 0.5; // Mitte berücksichtigen
y -= 0.5; // Mitte berücksichtigen
if (x < 0.0) { column--; x += 1.0;} // linke Spalte zur Interpolation nutzen
if (y < 0.0) { row--; y += 1.0;} // untere Reihe zur Interpolation nutzen
// Randrasterpunkte berücksichtigen: extrapolieren statt interpolieren
if (column <= -1) { x = -x; column = 0;}
else if (column >= (m_internal->_totalColumns - 1)) { x += 1.0; column = m_internal->_totalColumns - 2;}
if (row == -1) { y = -y; row = 0;}
else if (row >= (m_internal->_totalRows - 1)) { y += 1.0; row = m_internal->_totalRows - 2;}
// Die Approximation:
double substitute = elevation;
double e0 = getOrSubstituteElevation(column, row, substitute);
double e1 = getOrSubstituteElevation(column + 1, row, substitute);
double e2 = getOrSubstituteElevation(column + 1, row + 1, substitute);
double e3 = getOrSubstituteElevation(column, row + 1, substitute);
double er = e0 + x * (e1 - e0) + y * (e3 - e0) + x * y * (e2 - e3 + e0 - e1);
return er; // floor(er+0.5);; runden herausgenommen 19.8.2008 JT
} // double ElevationMap:: getElevation( double lon, double lat)
double ElevationMap::getOrSubstituteElevation(int column, int row, double substitute) const
{
int elevation = getElevation(column, row);
if (elevation != std::numeric_limits<int>::min())
return elevation;
int substCount = 0;
double calcSubstitute = 0.0;
elevation = getElevation(column + 1, row);
if (elevation != std::numeric_limits<int>::min()) {substCount++; calcSubstitute += elevation; }
elevation = getElevation(column - 1, row);
if (elevation != std::numeric_limits<int>::min()) {substCount++; calcSubstitute += elevation; }
elevation = getElevation(column, row + 1);
if (elevation != std::numeric_limits<int>::min()) {substCount++; calcSubstitute += elevation; }
elevation = getElevation(column, row - 1);
if (elevation != std::numeric_limits<int>::min()) {substCount++; calcSubstitute += elevation; }
if (substCount)
substitute = calcSubstitute / substCount;
return substitute;
} // double ElevationMap::getOrSubstituteElevation( int column, int row, double substitute)
double ElevationMap::distance(const QPointF &start, const QPointF &end) const
{
if (!isValid())
return 0;
if (!m_internal->_location.contains(start) || !m_internal->_location.contains(end))
{
return -1.0; // ausserhalb{ der Höhenkarte !!
}
double start_z = getElevation(start.x(), start.y());
if (start_z <= std::numeric_limits<int>::min()) return -1.0;
double end_z = getElevation(end.x(), end.y());
if (end_z <= std::numeric_limits<int>::min()) return -1.0;
const QPointF &dist = distance_in_meter_WGS84(start, end);
double dz = end_z - start_z;
double d = sqrt(dist.x() * dist.x() + dist.y() * dist.y() + dz * dz) ;
return d;
} // double ElevationMap::distance( const QPointF &start, const QPointF &end )
double ElevationMap::profileDistance(const QPointF &start, const QPointF &end) const
{
if (!isValid())
return 0;
if (!m_internal->_location.contains(start) || !m_internal->_location.contains(end))
{
return -1.0; // ausserhalb der Höhenkarte !!
}
Vec3dVector elevProfile;
getSamplePoints(start, end, elevProfile);
for (Vec3dVector::iterator itr = elevProfile.begin(); itr != elevProfile.end(); ++itr)
itr->setZ(getElevation(itr->x(), itr->y()));
// Einzellaengen aufsummieren, Stützstellen mit undefinierten Hoehen werden ausgelassen!
Point3D curPos(*elevProfile.begin());
double sumLng = 0.0;
for (Vec3dVector::iterator itr = elevProfile.begin() + 1; itr != elevProfile.end(); ++itr)
{
if ((*itr).z() > std::numeric_limits<int>::min())
{
if (curPos.z() > std::numeric_limits<int>::min())
{
const QPointF &dist = distance_in_meter_WGS84(QPointF(curPos.x(), curPos.y()), QPointF(itr->x(), itr->y()));
double dz = itr->z() - curPos.z();
sumLng += sqrt(dist.x() * dist.x() + dist.y() * dist.y() + dz * dz) ;
}
else
{
double dx = itr->x() - curPos.x();
double dy = itr->y() - curPos.y();
sumLng += sqrt(dx * dx + dy * dy);
}
curPos = *itr;
}
}
return sumLng;
} // double ElevationMap::profileDistance( const QPointF &start, const QPointF &end)
unsigned int ElevationMap::elevationProfile(const QPointF &start, const QPointF &end, Vec3dVector &elevProfile) const
{
unsigned int startSize = elevProfile.size();
getSamplePoints(start, end, elevProfile);
for (Vec3dVector::iterator itr = elevProfile.begin() + startSize; itr != elevProfile.end(); ++itr)
itr->setZ(getElevation(itr->x(), itr->y()));
return (elevProfile.size() - startSize);
} // unsigned int ElevationMap::elevationProfile( const QPointF &start, const QPointF &end, Vec3dVector &elevProfile)
bool ElevationMap::contains(const QPointF &point) const
{
return m_internal->_location.contains(point);
}
bool ElevationMap::intersects(const QPolygonF &polygon) const
{
const BoundingRect &bbox = m_internal->_location;
// maybe QPointF uses float, but we can accept double to be reduced to float:
const QPolygonF &ourBorder = QPolygonF({QPointF(bbox.left() , bbox.bottom()),
QPointF(bbox.left() , bbox.top()),
QPointF(bbox.right(), bbox.top()),
QPointF(bbox.right(), bbox.bottom())});
#if QT_VERSION < QT_VERSION_CHECK(5,10,0)
return ourBorder.intersected(polygon).size() > 0;
#else
return ourBorder.intersects(polygon);
#endif
}
void ElevationMap::getSamplePoints(const QPointF &start, const QPointF &end, Vec3dVector &sPnts) const
{
sPnts.clear();
sPnts.push_back(Point3D::fromQPointF(start));
bool endIsClipped = false;
QPointF cStart(start);
QPointF cEnd(end);
if (!m_internal->_location.contains(start) || !m_internal->_location.contains(end))
{
int res = clipp2Map(start, end, cStart, cEnd);
if (res == 0) // no intersection
{
sPnts.push_back(Point3D::fromQPointF(end));
return;
}
if (res & 1) // start clipped
sPnts.push_back(Point3D::fromQPointF(cStart));
if (res & 2) // end clipped
endIsClipped = true;
}
QPointF delta(cEnd - cStart);
// Berechne alle Schnittpunkte des Vektors( cStart, cEnd) mit den x-Grenzen der Hoehenstuetzrechtecke.
// Die Stützpunkte liegen definitiongemaess jeweils in der Mitte des Raster-Rechtecks
int startCol = floor((cStart.x() - m_internal->_location.left()) / m_internal->_elexelSize.x());
if (startCol < 0) startCol = 0;
else if (startCol >= m_internal->_totalColumns) startCol = m_internal->_totalColumns - 1;
int endCol = floor((cEnd.x() - m_internal->_location.left()) / m_internal->_elexelSize.x());
if (endCol < 0) endCol = 0;
else if (endCol >= m_internal->_totalColumns) endCol = m_internal->_totalColumns - 1;
int incCol = 0;
if (endCol > startCol) incCol = 1;
else if (endCol < startCol) incCol = -1;
double m = delta.y() / delta.x();
Vec2dVector xCuts;
xCuts.reserve(std::abs(endCol - startCol));
for (int col = startCol; col != endCol; col += incCol)
{
double x = m_internal->_location.left() + ((col + 0.5) + (incCol * 0.5)) * m_internal->_elexelSize.x();
double y = cStart.y() + (x - cStart.x()) * m;
xCuts.push_back(QPointF(x, y));
}
// Berechne alle Schnittpunkte des Vektors( cStart, cEnd) mit den y-Grenzen der Hoehenstuetzrechtecke.
// Die Stützpunkte liegen definitiongemaess jeweils in der Mitte des Raster-Rechtecks
int startRow = floor((cStart.y() - m_internal->_location.bottom()) / m_internal->_elexelSize.y());
if (startRow < 0) startRow = 0;
else if (startRow >= m_internal->_totalRows) startRow = m_internal->_totalRows - 1;
int endRow = floor((end.y() - m_internal->_location.bottom()) / m_internal->_elexelSize.y());
if (endRow < 0) endRow = 0;
else if (endRow >= m_internal->_totalRows) endRow = m_internal->_totalRows - 1;
int incRow = 0;
if (endRow > startRow) incRow = 1;
else if (endRow < startRow) incRow = -1;
m = delta.x() / delta.y();
Vec2dVector yCuts;
yCuts.reserve(std::abs(endRow - startRow));
for (int row = startRow; row != endRow; row += incRow)
{
double y = m_internal->_location.bottom() + ((row + 0.5) + (incRow * 0.5)) * m_internal->_elexelSize.y();
double x = cStart.x() + (y - cStart.y()) * m;
yCuts.push_back(QPointF(x, y));
}
// xCuts und yCuts zusammenführen, unter Berücksichtigung der Reihenfolge
Vec2dVector mergeCuts;
Vec2dVector::iterator xCutItr = xCuts.begin();
Vec2dVector::iterator yCutItr = yCuts.begin();
while ((xCutItr != xCuts.end()) && (yCutItr != yCuts.end()))
{
if (incCol > 0)
{
if ((*xCutItr).x() < (*yCutItr).x()) { mergeCuts.push_back(*xCutItr); ++xCutItr; }
else { mergeCuts.push_back(*yCutItr); ++yCutItr; }
}
else
{
if ((*xCutItr).x() > (*yCutItr).x()) { mergeCuts.push_back(*xCutItr); ++xCutItr; }
else { mergeCuts.push_back(*yCutItr); ++yCutItr; }
}
}
while ((xCutItr != xCuts.end())) { mergeCuts.push_back(*xCutItr); ++xCutItr; }
while ((yCutItr != yCuts.end())) { mergeCuts.push_back(*yCutItr); ++yCutItr; }
// mergeCuts enthält sequenziell alle Schnittpunkte des Vektors mit den Hoehengridkanten
// Die 2 Schnittpunkte pro Hoehenwert werden gemittelt zu einem zusammengefasst
if (mergeCuts.size() > 1)
{
for (Vec2dVector::iterator itr = mergeCuts.begin() + 1; itr != mergeCuts.end(); ++itr)
sPnts.push_back(Point3D::fromQPointF((*(itr - 1) + *itr) * 0.5));
}
sPnts.push_back(Point3D::fromQPointF(cEnd));
// DEBUG_LOG(m_logger) << " xCuts.si " << xCuts.size()
// << ", yCuts.si " << yCuts.size()
// << ", mergeCuts.si " << mergeCuts.size();
if (endIsClipped) sPnts.push_back(Point3D::fromQPointF(end));
} // void ElevationMap::getSamplePoints( const QPointF &start, const QPointF &end, Vec3dVector & sPnts)
// start und/oder end liegt ausserhalb !! in Lat/Lon !!
int ElevationMap::clipp2Map(const QPointF &start, const QPointF &end, QPointF &cStart, QPointF &cEnd) const
{
Vec2dVector clippVec;
if (end.x() != start.x())
{
double x = m_internal->_location.right();
double m = (end.y() - start.y()) / (end.x() - start.x());
if (isInIntervall(x, start.x(), end.x()))
{
double yc = start.y() + (x - start.x()) * m;
if ((yc <= m_internal->_location.top()) && (yc >= m_internal->_location.bottom()))
clippVec.push_back(QPointF(x, yc));
}
x = m_internal->_location.left();
if (isInIntervall(x, start.x(), end.x()))
{
double yc = start.y() + (x - start.x()) * (end.y() - start.y()) / (end.x() - start.x());
if ((yc <= m_internal->_location.top()) && (yc >= m_internal->_location.bottom()))
clippVec.push_back(QPointF(x, yc));
}
}
if (end.y() != start.y())
{
double y = m_internal->_location.top();
double m = (end.x() - start.x()) / (end.y() - start.y());
if (isInIntervall(y, start.y(), end.y()))
{
double xc = start.x() + (y - start.y()) * m;
if ((xc < m_internal->_location.right()) && (xc > m_internal->_location.left()))
clippVec.push_back(QPointF(xc, y));
}
y = m_internal->_location.bottom();
if (isInIntervall(y, start.y(), end.y()))
{
double xc = start.x() + (y - start.y()) * m;
if ((xc < m_internal->_location.right()) && (xc > m_internal->_location.left()))
clippVec.push_back(QPointF(xc, y));
}
}
int size = (int)clippVec.size();
if (size == 0)
{
return 0; // komlett ausserhalb ( oder komplett innerhalb)
}
if (size == 1)
{
if (m_internal->_location.contains(start))
{
cEnd = clippVec[0]; return 2;
}
else
{
cStart = clippVec[0]; return 1;
}
}
if (size == 2)
{
if (squareLength(clippVec[0] - start) < squareLength(clippVec[1] - start))
{
cStart = clippVec[0];
cEnd = clippVec[1];
}
else
{
cStart = clippVec[1];
cEnd = clippVec[0];
}
return 3;
}
ERROR_LOG(m_logger) << LOGFUNCTION << "Internal Clipp Error!";
return 0;
} // int ElevationMap::clipp2Map( const QPointF &start, const QPointF &end, QPointF &cStart, QPointF &cEnd)
// Line of Sight Profile: berücksichtigt Erdkrümmung und WGS84 Erdellipsoid
void ElevationMap::LOSProfile(const QPointF &start, const QPointF &end, LOSVector &losProfile)
{
m_gdalReadWatcher.waitForFinished();
if (!m_internal->_location.contains(start)) return ; // ausserhalb der Höhenkarte !!
bool clipped = false;
double totalLng;
QPointF startG = start;
QPointF endG = end;
if (!m_internal->_location.contains(end)) // Clippen
{
clipped = true;
totalLng = length(endG - startG);
if (endG.x() < m_internal->_location.left())
{
double m = (endG.y() - startG.y()) / (endG.x() - startG.x());
double x = m_internal->_location.left();
endG.setX(x);
endG.setY(startG.y() + m * (x - startG.x()));
}
else if (endG.x() > m_internal->_location.right())
{
double m = (endG.y() - startG.y()) / (endG.x() - startG.x());
double x = m_internal->_location.right();
endG.setX(x);
endG.setY(startG.y() + m * (x - startG.x()));
}
if (endG.y() < m_internal->_location.bottom())
{
double m = (endG.x() - startG.x()) / (endG.y() - startG.y());
double y = m_internal->_location.bottom();
endG.setY(y);
endG.setX(startG.x() + m * (y - startG.y()));
}
else if (endG.y() > m_internal->_location.top())
{
double m = (endG.x() - startG.x()) / (endG.y() - startG.y());
double y = m_internal->_location.top();
endG.setY(y);
endG.setX(startG.x() + m * (y - startG.y()));
}
}
// Korrektursteigung aufgrund WGS84 Erdellipsoid, Lineare Approximation, Fehler bis 200 km: < 1.0m
double startRadius = radiusWGS84(startG.y());
double corrM = (radiusWGS84(endG.y()) - startRadius) / length(endG - startG);
Vec3dVector elevProfile;
getSamplePoints(start, end, elevProfile);
for (Vec3dVector::iterator itr = elevProfile.begin(); itr != elevProfile.end(); ++itr)
{
double dist = length(distance_in_meter_WGS84(start, itr->toPointF()));
double height = elevation(itr->x(), itr->y());
height += corrM * dist; // WGS84 Erdellipsoid Korrektur
height -= (startRadius * (1 - cos(atan(dist / startRadius)))); // Einbeziehen der Erdkruemmung
// alternativ als Naeherung: height -= ( dist * dist) / (startRadius * 2.0);
losProfile.push_back(LOSentry(dist, height));
}
if (clipped)
losProfile.push_back(LOSentry(totalLng, -NAN));
qCritical() << " losProfile.isze() = " << losProfile.size();
} // void ElevationMap::LOSProfile( const QPointF &start, const QPointF &end, LOSVector &losProfile)
} // SituationMap
/* -----------------------------------------------------------------------------------------------
** CVS-Id/Release ...: $Header$
** -----------------------------------------------------------------------------------------------
**
** Historie:
**
** $Log: not supported by cvs2svn $
** Revision 1.17.1.1 2008/06/16 06:05:37 joerg
** Transparenz und Bias einheitlich für alle Objekte implementiert
**
** Revision 1.17 2006/03/24 09:05:01 klaus
** Elevationprofile: Begrenzung auf Kartengröße aufgehoben ( Clipping)
**
** Revision 1.16 2005/11/09 13:44:36 klaus
** Umsetzung auf Linux;
**
** Revision 1.15 2005/04/15 13:05:45 joerg
** Resource Control
**
** Revision 1.14 2005/03/04 14:29:26 klaus
** Elevationmap mit CachedTileList versehen und automatischem Entladen von Tiles
**
** Revision 1.13 2005/03/03 12:37:53 klaus
** dynamischen Laden und Entladen der ElevationTiles für Reliefmap
**
** Revision 1.12 2005/03/01 12:36:59 klaus
** Reliefmap eingeführt, aus Elevationmap entfernt
**
** Revision 1.11 2005/02/25 15:32:56 klaus
** Ergänzung um Reliefkarte
**
** Revision 1.10 2005/02/10 14:15:19 klaus
** Bias in Basisklasse MapObject überführt
**
** Revision 1.9 2005/02/10 13:50:33 klaus
** Bias für SightFans eingeführt
**
** Revision 1.8 2005/02/09 16:45:26 klaus
** 1. Version der SightFans
**
** Revision 1.7 2005/01/26 12:26:23 klaus
** todos bearbeitet, Fehlerrückmeldnugen
**
** Revision 1.6 2004/12/14 10:29:48 klaus
** 7. Erweiterung für Höhenkarten
**
** Revision 1.5 2004/12/07 11:08:28 klaus
** 5. Erweiterung für Höhenkarten
**
** Revision 1.4 2004/12/03 16:25:14 klaus
** 4. Erweiterung für Höhenkarten
**
** Revision 1.3 2004/11/30 13:18:13 klaus
** 3. Erweiterung für Höhenkarten
**
** Revision 1.2 2004/11/23 14:36:40 klaus
** 2. Erweiterung für Höhenkarten
**
**
** ----------------------------------------------------------------------------------------------- */